I'm putting together a football/soccer match engine in Godot 4.3 as a learning exercise and I've run into a problem trying to freeze/attach a the ball to one of the players. Here's what happens:
What actually happens is that the ball gets frozen as expected but as soon as the player moves forward, the ball shifts position - it doesn't stay in front of the player.
I've debugged this, even with a sleep timer and the freeze looks to work correctly. I had half expected the physics to not have been complete so it shifted but the sleep timer discounts that theory - I think.
To demonstrate the problem here are a couple of images:
1.The point the ball enters the Area3D and "freezes"
The on body entered method looks like this:
public void OnFeetEntered(Node3D body)
{
if(CurrentState == PlayerState.KickingOff || CurrentState == PlayerState.MovingToPosition)
{
return;
}
if (body is Ball ball)
{
HasBall = true;
CurrentState = PlayerState.JustTakenBall;
ball.LinearVelocity = Vector3.Zero;
ball.AngularVelocity = Vector3.Zero;
ball.FreezeMode = RigidBody3D.FreezeModeEnum.Kinematic;
ball.Freeze = true;
ball.GlobalPosition = ball.GlobalPosition;
var localPosition = ToLocal(ball.GlobalPosition);
AddChild(ball);
ball.Position = localPosition;
}
}
And this is how the player is told to move (target position is hard-coded to Vector3(-10.0f, 0.252f, -8.0f) for testing purposes):
private void MoveToTarget(Vector3 targetPosition)
{
Vector3 direction = GlobalPosition.DirectionTo(targetPosition);
float distance = GlobalPosition.DistanceTo(targetPosition);
if (distance > stopThreshold)
{
Velocity = direction * Speed;
MoveAndSlide();
}
else
{
Velocity = Vector3.Zero; // Stop movement
CurrentState = PlayerState.Idle;
Look();
}
}
So what am I doing wrong here? I just want that ball to stay at the position it enters the player when that player moves forward or rotates.
EDIT
Thanks to @liggiorgio for pointing out that I need to remove the parent before calling AddChild
.
I've done this now but I get another problem. Now when I run the game and try to move the player forward, the game crashes with a stack overflow error:
Exception of type 'System.StackOverflowException' was thrown.
There's no more information present, no inner exception or anything very helpful.
What might be causing this? I'm at a bit of a loss!
EDIT 2
The second issue I ran into was simple recursion, I didn't appreciate that the on entered method would be called repeatedly. This was causing the parent to get removed and added in an infinite loop causing the stack overflow exception. My player is now happily moving forward with the ball in the correct position!
You can't use AddChild()
on nodes that already have a parent, otherwise the method will fail. Instead, you should reparent the ball Node to your player Node:
public void OnFeetEntered(Node3D body)
{
//...
if (body is Ball ball)
{
//...
ball.Reparent(self);
//...
}
}
And revert the parenting relationship when a player throws the ball away:
public void KickBall(...)
{
if (!HasBall) return;
// Assuming `ball` and `ball_parent` exist
ball.Reparent(ball_parent);
}
The optional argument keep_global_transform
of function Node.Reparent()
defaults to true
, which is what you want: the ball changes parent but its absolute position stays the same.