Calling a function from a body on collision in C#

Godot Version

Godot 4.2

Question

Hi! I’m new to Godot, and I wanted to learn it in C# since it’s a useful language even outside of gamedev. I’ve been following some GDscript tutorials from Brackeys and GDQuest, converting the code to C#. Today, I ran into a problem in the GDQuest “First 2D Game” -tutorial that I can’t seem to fix.

The idea is simple: you have a bullet scene, and once that bullet scene collides with an enemy mob, it calls a function from the mob script to deal damage. So, the function is stored in mob.cs, but the on body entered -signal is in the bullet.cs.

In GDscript it’s super simple, and looks like this:

func _on_body_entered(body):
         queue_free()
         if body.has_method(take_damage):
               body.take_damage()

This is apparently called ducktyping? However, the has_method part does not work in C#. I tried to look for a solution, and found this thread: 'Node2D' does not contain a definition for 'CollectCoin' - #4 by numbers11

Here, it is proposed that you could write something like this:

	private void _on_body_entered(Node2D body)
		{
			QueueFree();
			if (body is mob)
			{
				body.TakeDamage();
			}
		}

And that this way, the body should be cast to mob, which does include the function. This doesn’t work for me either. I get this error message for now:

CS1061: ‘Node2D’ does not contain a definition for ‘TakeDamage’ and no accessible extension method ‘TakeDamage’ accepting a first argument of type ‘Node2D’ could be found (are you missing a using directive or an assembly reference?) C:\Users\Public\Godot\GDQuest tutorial\Bullet.cs(30,10)

I’ve tried creating a reference to the mob.cs script or the mob node inside the bullet.cs script, but nothing I’ve tried works. Anyone more experience have any idea how I can get this working?

Here is the function I’m trying to call btw:

public void TakeDamage()
	{
		health -= 1;
		if (health == 0)
		{
			QueueFree();
		}
	}

Thank you! Keep in mind that I’m almost a complete beginner and I’ve probably missed something here. I’m a bit in over my heard converting all of this script myself, but I thought it’d be a great learning experience, and I’m having fun.

1 Like

should look like this

	private void OnBodyEntered(Node2D body)
		{
		
			if (body is Mob mob)
			{
		  		mob.TakeDamage();
			}
	     QueueFree();
		}

mob needed have Mob class and have method public void TakeDamage()
if there more things than enemies who can be damaged you could consider make and use interface like this

public interface IDamageable
{
    void TakeDamage(int damageTaken);
}

your Mob class needed just add interface to work,
something like:
public sealed partial class Mob: CharacterBody2D, IDamageable
still now your code is forcing you to use public void TakeDamage(int damageTaken)

public void TakeDamage(int damageTaken)
	{
		health -= damageTaken;
		if (health <= 0)
		{
			QueueFree();
		}
	}

and the damage code will look like this now

private void OnBodyEntered(Node2D body)
		{
		
			if (body is IDamageable damageable)
			{
		  		damageable.TakeDamage( 1);
			}
	     QueueFree();
		}

_on_body_entered is snake style, don’t use if your project don’t use this style, don’t mix styles of naming in one project

1 Like

You check if body os of type mob, but C# still considers the body variable to be of type Node2D. The type check doesn’t actually change the way the variable is viewed by C#. You need to do a type cast.

What you are looking for is the as keyword in C#. Use it like this:

var mobBody = body as Mob
if (mobBody != null) {
    # This means the body REALLY IS a Mob
    # See I call the method on the "converted" mobBody, not the original body.
    mobBody.TakeDamage()
}
QueueFree()

Edit: The oneliner type cast from @Moreus is pretty nice too, I didn’t know about that trick :slight_smile:

2 Likes

Thank you! The Moreus method already worked, but I’ll try this one as well as a learning experience. Thank you for the explanation as well, that’s the thing I was wondering about, since the error message clearly stated that it still considered it a Node2D. I just couldn’t figure out how to cast it. The as keyword is new to me, I’ll read up on it. Thanks!

1 Like

Thank you so much! I knew it was something really minor, I’ve run into similar errors before, and it’s usually a simple error (typo, getting a class name, a node name or an instance name mixed up, forgetting a cast etc) but this time I just didn’t spot it. This was an instant fix.

Thank you for all of the extra info as well, I’ll look into implementing that interface later, would probably be good practice.

About the snake_case signal name, that is something auto-generated by Godot when I double click a signal, and as far as I know it actually stops working if I type it as _OnBodyEntered? But I haven’t looked at that too much, so maybe that’s not the exact function name in C#.

Thank you again!

1 Like

You can pick any method with compatible arguments with “Pick” Button
obraz