godot icon indicating copy to clipboard operation
godot copied to clipboard

Problem with Area2D, body_entered signal and Tilemap in a .NET version

Open OlegDzhuraev opened this issue 1 year ago • 1 comments

Godot version

v4.0.stable.mono.official [92bee43ad]

System information

Windows 10, Vulkan API 1.3.224, Forward+, NVIDIA GeForce GTX 1660 SUPER

Issue description

Problem with Area2D signal and Tilemap. Can be reproduced in the C# version.

When the signal body_entered of the Area2D attached to code and being fired by colliding Tilemap, this error appears:

System.Object System.Runtime.CompilerServices.CastHelpers.ChkCastAny(System.Void* , System.Object ): System.InvalidCastException: Unable to cast object of type 'Godot.TileMap' to type 'Godot.PhysicsBody2D'.
  <Error C++>   System.InvalidCastException
  <Source code C++>:0 @ System.Object System.Runtime.CompilerServices.CastHelpers.ChkCastAny(System.Void* , System.Object )
  <Stacktrace>:0 @ System.Object System.Runtime.CompilerServices.CastHelpers.ChkCastAny(System.Void* , System.Object )
                 VariantUtils.generic.cs:385 @ T Godot.NativeInterop.VariantUtils.ConvertTo<T >(Godot.NativeInterop.godot_variant& )
                 Projectile_ScriptMethods.generated.cs:34 @ Boolean Projectile.InvokeGodotClassMethod(Godot.NativeInterop.godot_string_name& , Godot.NativeInterop.NativeVariantPtrArgs , Godot.NativeInterop.godot_variant& )
                 CSharpInstanceBridge.cs:24 @ Godot.NativeInterop.godot_bool Godot.Bridge.CSharpInstanceBridge.Call(IntPtr , Godot.NativeInterop.godot_string_name* , Godot.NativeInterop.godot_variant** , Int32 , Godot.NativeInterop.godot_variant_call_error* , Godot.NativeInterop.godot_variant* )

Can be stable reproduced even in a new empty project. Maybe I get something wrong, but looks like a bug.

Steps to reproduce

  1. Create Tilemap on the scene, fill it with premade Tileset resource
  2. Add new Physics layer, setup physics for any tile, paint this tile on the scene.
  3. Create C# script, derive it from the Area2D. Add a new method to receive body_entered signal.
  4. Attach script to the Area2D node.
  5. Attach body_entered signal to the receive method.
  6. Place this area near painted tilemap on scene.
  7. Hit play and you will receive error after signal will be fired.

Minimal reproduction project

2DAreaTilemapReproduce.zip

OlegDzhuraev avatar Mar 05 '23 13:03 OlegDzhuraev

This is expected behavior. The signal body_entered takes a Node2D parameter:

https://github.com/godotengine/godot/blob/5dccc940e73d39a1ac4f3d64ccc92373e6609add/doc/classes/Area2D.xml#L157-L162

But your method is declared as follows:

public void OnSomeBodyEntered(PhysicsBody2D body);

When the signal is emitted with a body of a different Node2D type, as is the case with TileMap, the parameter can't be converted so an exception is thrown. This is how OOP works.

graph TD;
      Node-->CanvasItem-->Node2D-->CollisionObject2D-->PhysicsBody2D;
      Node2D-->TileMap;

As you can see in the diagram there's no way to go from the TileMap type to the PhysicsBody2D type. Note how the signal's description says (emphasis mine):

Emitted when the received body enters this area. body can be a PhysicsBody2D or a TileMap. TileMaps are detected if their TileSet has collision shapes configured. Requires monitoring to be set to true.

This means, in order for the method to work with both, it needs to use a common ancestor. That's why the signal is defined taking a body parameter of type Node2D.

raulsntos avatar Mar 05 '23 16:03 raulsntos

Yeah, I should've paid more attention to it, missed thing. Sorry for wasting your time.

OlegDzhuraev avatar Mar 05 '23 22:03 OlegDzhuraev