Godot Version
.net v4.2.1
Context
While working on an ability framework, I’m creating a test-ability that damages and knocks back all colliders (let’s call these collider
) that are hit by a capsule (using ShapeCasting).
To damage entities, I determine whether the collider
has a child node that is of type Health
(my script). This is implemented and works as expected. However…
…to knock back entities, I have to perform a dynamic function call to apply_central_impulse
instead of casting the collider
as a Rigidbody3D
and calling ApplyCentralImpulse()
. The reason I have to do this is because some of the Rigidbody3D
s in my scene has a script attached that inherits from Node3D
- not RigidBody3D
. Because of this, the Rigidbody3D
is not present in the static context.
The described code (health, and knockback):
var result = owner.Shapecast(sh, pos, pos);
// Damage and push all colliders hit
for (int i = 0; i < result.Count; i++)
{
Node3D n = (Node3D)result[i]["collider"];
// Node3D operations
if (n.IsValid())
{
// Damage the node's health
n.Dmg(damage);
// NOTE: I guess this is how you call a "script-node"'s methods?
if (n.HasMethod("apply_central_impulse"))
{
Vector3 toColl = n.GlobalPosition - owner.GlobalPosition;
float distFactor = (radius - toColl.Length()) / radius;
Vector3 imp = (toColl.XZ().Normalized() * knockbackDirection.X + Vector3.Up * knockbackDirection.Y) * knockbackForce * distFactor;
n.Call("apply_central_impulse", imp);
}
}
}
Below you can find additional code if you want the full code context:
ShapeCast Extension
public static Godot.Collections.Array<Godot.Collections.Dictionary> Shapecast(this Node3D n, Shape3D shape, Vector3 start, Vector3 end)
{
// Create a raycast query
var spaceState = n.GetWorld3D().DirectSpaceState;
var query = new PhysicsShapeQueryParameters3D()
{
Shape = shape,
CollideWithBodies = true,
Transform = new Transform3D(Basis.Identity, start),
};
// Exclude this object from the raycast
if (n is CollisionObject3D)
query.Exclude = new Godot.Collections.Array<Rid> { (n as CollisionObject3D).GetRid() };
var result = spaceState.IntersectShape(query);
return result;
}
Dmg Extension
public static void Dmg(this Node n, float amount)
{
if (n.TryGetNode(out Health h))
{
h.Damage(amount);
}
}
TryGetNode Extension
public static bool TryGetNode<T>(this Node n, out T node) where T : Node
{
for (int i = 0; i < n.GetChildCount(); i++)
{
if (n.GetChild(i) is T)
{
node = (T)n.GetChild(i);
return true;
}
}
node = null;
return false;
}
IsValid Extension
public static bool IsValid(this GodotObject go)
{
return Godot.GodotObject.IsInstanceValid(go);
}
Question
What is the preferred method, or best practice, for coding an interaction with a Rigidbody3D
that has a script attached to it?
I’ve got some ideas of my own (see below), but I want to hear the thoughts of people with more Godot experience than me.
My ideas
- Avoid attaching scripts to
Rigidbody3D
s (not viable) - Nest the
Rigidbody3D
inside aNode3D
and attach the script to the new parent (annoying…) - Implement an interface on all scripts, that is assumed to be attached to
RigidBody3D
s, that provides access to theRigidBody3D
methods in the static context. (not viable)