Following a Path3D, can't decide whether to use CharacterBody3D or RigidBody3D or?

Godot Version

v4.2.2.stable.official [15073afe3]

What I’m building

I’m creating a game involving trains.

I’ve got moving along the railway working using Path3D <- PathFollow3D <- RemoteTransform3D. The RemoteTransform3D is linked to my player which is currently a CharacterBody3D.

I’ve also got tilting working based on the curve of the track and the trains speed. The tighter the bend and the faster you are going the more the train will tilt (rotate). If the train tilts too far then it will derail.

What I want to achieve

When the train derails it crashes and interacts with the environment until it gets reset a few seconds later.

What I tried

I’ve managed to get the physics engine to take over in the event of a derail, but I realised CharacterBody3D doesn’t take into account a bunch of physics stuff. For example, even though the train is rotated it doesn’t continue to rotate and fall over onto it’s side, the rotation stays the same.

So I started to think maybe using a RigidBody3D, which seems to handle all the physics stuff. However, it sounds like manually moving a RigidBody3D is a bad idea?

I also realised during normal gameplay, I’m not actually using any of the CharacterBody3D methods to move the player, which feels like a bit of a smell.

My ideas

  1. Use CharacterBody3D and implement the missing physic interactions.
  2. Use RigidBody3D and reimplement my railway moving/tilting code to use physics.
  3. Use RigidBody3D while keeping my existing railway movement/tilting code.
  4. Spawn a RigidBody3D at the point of derailing, and hide the player node.

One, seems like a lot of work to implement the physics which would already be handled by RigidBody3D, so I’m not really liking that idea.

Two, I have no idea how I’d use physics to move the RigidBody3D while also constraining it to follow the Path3D?

Three, I’m not sure how to stop my movement code fighting/desyncing with the physics engine? Also how (if I needed too?) I could reset the RigidBody3D when the train resets back onto the track?

Four, seem like it might be the easiest? However, I’m not entirely sure how to do this yet.

Help!

Any help or advice is very much appreciated! I’m happy to hear completely new ideas as well.

FYI, I’ve got a lot of general programming experience but none in game development. I started messing about with Godot a few weeks ago, so sorry if this doesn’t make any sense!

I managed to get idea 3 “Use RigidBody3D while keeping my existing railway movement/tilting code.”

I did it by setting custom_integrator to true on ready. Then switching it back to false when crashing so the default physics takes over.

func _integrate_forces(state):
	if custom_integrator:
		state.linear_velocity = global_basis.z * -current_speed
	else:
		current_speed = state.linear_velocity.length()

This seems to do exactly what I want and doesn’t seem to cause any issues with the physics server. I’m still not sure if this is the best idea, and I’m a little worried it just appears to be working by coincidence rather than design…

Any thoughts/ideas/help still very much appreciated.

I also attempted idea 2 “Use RigidBody3D and reimplement my railway moving/tilting code to use physics.”, but I couldn’t get that working. The train just started accelerating immediately without any input, I assume this was caused by the RemoteTransform3D moving the RigidBody3D manually :man_shrugging:

1 Like

Maybe a more proper way would be to use freeze on the rigid body, then it can act like a AnimatableBody3D until un-frozen

I switched to using freeze and that worked, and the code did seem “nicer” :+1: