Godot Version
4.3
Question
Using a CharacterBody2D and a CollisionShape2D of Rectangle, I have a weird quirk/bug/feature occurring:
When colliding with a tilemap, CollisionShape2D returns a collision position in integer terms that doesn’t match the tile it’s colliding with. For example; when colliding with a tile at tilemap coordinates (10,10) using a 16x16 tilemap, the collision from the top and from the left ALWAYS returns an X coordinate of 176. Putting this into the tilemap coordinates, you get (11,10) returned. Which is not the tile we just collided with.
Is there any better way to get exactly which tile was collided with while using a rectangle collision box? I noticed using a circular one avoided this quirk. I imagine it having something to do with the position returned on KinematicCollision2D, given it’s probably just comparing sides of the rectangle and returning a single one per collision from whatever its’ doing in the background programming. I’d prefer to just get the deepest point of collision between the overlapping objects rather than simply the outer edge of the tile.
Steps to recreate:
Create a scene of Node2D. Add a child TileMapLayer. Add a tileset with the standard 16x16 tile size to said TileMapLayer. Place one single tile in the level, at say, (10,10) in the tiles coordinates: this translates to 160,160 in global coordinates. Give the tile set whatever physics layer it needs so it can collide with CharacterBody2D
Now create a CharacterBody2D with a physics mask to hit the tile. Make it a child of Node2D. Add a CollisionShape2D child. Make it a Rectangle of
Give it a collision shape of Rectangle. Now place it anywhere that it will fall on or overlap
I attached this script to CharacterBody2D, the only custom part is in the physics processing with the print fields.
extends CharacterBody2D
var tilemap : TileMapLayer
const SPEED = 300.0
const JUMP_VELOCITY = -400.0
func _ready() -> void:
tilemap = $"../TileMapLayer2"
pass
func _physics_process(delta: float) -> void:
for i in get_slide_collision_count():
var collision = get_slide_collision(i)
print("collision is ",collision.get_position())
print("tilemap collision is ",tilemap.local_to_map(collision.get_position()-Vector2(-4,-4)))
print(tilemap.get_cell_tile_data(tilemap.local_to_map(collision.get_position())))
#Add the gravity.
if not is_on_floor():
velocity += get_gravity() * delta
# Handle jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
var direction := Input.get_axis("ui_left", "ui_right")
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
move_and_slide()