General design question - game mechanics for a spaceship

Hi I have recently finished the Complete Godot 3D: Develop Your Own 3D Games Using Godot 4 and after Project Boost I have an idea for a game.

I am making a game with a spaceship in 3d. You control the ship by turning on and off individual engines. If you turn on right engine the ship turns left and vice versa. Turning on both engines makes the ship go straight ahead.

Alt A: Sounds simple enough and I have a working prototype that uses rigidbody3d for the engines and the ship, I use joints for connecting them together. I add forces to the engines and they push the spaceship.

This is not working so great, it is wiggly and janky, the engines move in weird angles.

Alt B: So I tried making the engines characterbodies instead, just adding variables and engine positions from center to them so that the main ship can use to apply the correct forces to the ship.

This also kind of worked but I lost the turning functionality and had to calculate and apply torque to make the ship turn. I also got a problem where the ship turns a little at first but then suddenly turns exponential.

Alt C: The third thing that I am trying now is to use character body for the ship and not use forces at all. I think this is the way to go since I really don’t actually need physics. And it will make online multiplayer easier to implement.

My question is:

  1. How would someone with more experience of godot design this mechanic? A, B, C or perhaps a whole other approach.
  2. If I am going with alternative 3 how woud I calculate the combined forces of the engines and apply them to the characterbody3d(Ship)

To clarify I am not looking for someone to give me code, I am prepared to figure it out for myself but I need some direction to avoid wasting to much time this early in the project.

Best regards Michael

ship

That’s a neat idea. Kinda sounds like you’re already half way there as well.

I’ve played enough Main Assembly to know that approaches A and B aren’t likely to turn out the way you expect. Maybe if I had a pdf in physics I could suggest something to make those approaches work, but you’re right: moving just the ship is a lot easier, and there’s no reason you can’t use a heap of smoke and mirrors to just emulate it all. I’d try something like this:

  • Ship moves “forward” based on combined thrust. “Forward” is defined by the ship’s angle, so you need component vectors to figure out what the forward trajectory actually looks like. Basically, you’re travelling along the hypotenuse of an ever-changing right-triangle. I recently used this approach for something similar in the 2D course and it worked really well.
  • Activating an engine adds thrust and angular velocity (either directly, or in the form of angular acceleration); when both are on, the increases to thrust combine (not necessarily at exactly 2x) while the individual angular velocity changes cancel each other out (again, not necessarily right away).
  • You also need to define behaviour for when the engines are not activated; otherwise it’ll behave more like a glider, since angular deceleration’s effect on angle would actually change the rocket’s trajectory otherwise.
  • Finally, in the 2D course, I found that mixing dynamic (physics-engine-based) with kinematic movement caused all sorts of weirdness. Definitely have a look at integrate_forces() if you find they’re not playing nicely in the sandbox (I don’t know if it’s the same for 3D or not).

Hope you can make some good use of this =)

Thank you for sending me in the right direction. I learned a lot and using the right terminology made it easier to find.

Now I have some code and it feel like I am close but I cant get it to move the way I want.

extends CharacterBody3D

var combined_force: Vector3 = Vector3.ZERO

func _process(delta: float) → void:
if Input.is_action_pressed(“ui_accept”):
# Add forces from engines.
var local_engines = get_tree().get_nodes_in_group(“ngines”)
for ng in local_engines:
# Calculate vector from ship’s center to engine’s position
var engine_to_center = ng.position - position

		# Calculate force from engine and add to combined force
		var engine_force = engine_to_center.normalized() * ng.thrust
		combined_force += engine_force
		
	# Apply the combined force to the ship's velocity
	var velocity = combined_force.normalized()
	
	# Reset the combined force.
	combined_force = Vector3.ZERO
	
	# Iterpolate and rotate the ship.
	var original_trans = Quaternion(transform.basis)
	var new_trans = Quaternion(transform.basis.z,velocity)

	var int_trans = original_trans.slerp(new_trans,0.1)

	transform.basis = Basis(int_trans)

	move_and_slide()

Cheers; I’m not always successful with terms, but I do my best for exactly this reason.

I’ve not explored 3D Godot whatsoever so I might not be much help anymore, but I can get within a stone’s throw of understanding what you have here and I’d agree that this solution probably just needs tweaking somewhere.

For starters, I did notice that you’re doing all of this in _process() vs. _physics_process(). I’m not exactly sure what would happen, but my understanding is that these two functions are called at different frequencies and _process() is not intended for physics calculations as a result. Could potentially be responsible for some laggy jankiness or uneven motion.

Also, probably not the issue since I’m assuming the engine placement is symmetrical, but the way you are calculating engine-to-ship vectors at the moment would suggest that the engines are pointing toward the ship’s center. This might be creating much harsher angles than you are intending.

The only other thing I can suggest at this point is some abstract debugging strategies: gap analysis, problem decomposition, and the scientific method. What’s the behaviour you’re seeing now (detailed) vs. what you expect to see? What components of your solution are working more-or-less as expected? Is there anything you can rule out to narrow the scope of the problem (yes), and are there artificial tests you can conduct to confirm each hypothesis (absolutely there are)?

Following this mindset, the first thing I would do is isolate and test the thrust calculation and the quaternion-based rotation separately. For each of those, throw in some dummy values/input for the other parts to create an experimental control and watch what happens. You will learn something about the situation this way, because the results are mutually-exclusive and each one will rule something out (if it works, it ain’t that, and if it doesn’t work, then it either plays a direct role or is dependent upon something that does).

It’s fairly likely you already know all of that, but those were some of the most valuable things I got out of my original programming classes. Good luck!

Privacy & Terms