Free-floating object losing velocity while turning

One of the not-so-fun parts of piloting a space ship is that even if you turn, your existing velocity will keep you flying in the same direction.

https://gfycat.com/WhichArtisticIrrawaddydolphin

To control your direction, you have to constantly shift between thrusting in one direction, and then thrusting into the opposite to kill velocity, allowing travel in another direction. Most players don’t find this enjoyable, so I added compensation that redirects existing velocity to match the direction of the ship:

https://gfycat.com/SpryAdorableLhasaapso

Unfortunately, this compensation causes the ship to lose speed while turning for some reason. This is how I’ve implemented the compensating force:

game:GetService("RunService").Stepped:Connect(function(_,dt)
	local shipRotation = primary.CFrame - primary.CFrame.p
	local currentVelocity = ship.PrimaryPart.Velocity
	local horizontalVelocity = currentVelocity - Vector3.new(0,currentVelocity.Y,0)
	local targetHorizontalVelocity = (shipRotation * CFrame.new(0,0,-horizontalVelocity.magnitude)).p
		
	local maxDelta = zDirection == 0 and 0 or (zDirection*MAX_SPEED - horizontalVelocity.magnitude)
	local acceleration = math.clamp(maxDelta, -MAX_ACCELERATION, MAX_ACCELERATION)

    --[[ Movement ]]-- (both of these are VectorForces)
	compensator.Force = shipMass * (targetHorizontalVelocity - horizontalVelocity)
	mover.Force = shipMass * Vector3.new(0,0,-acceleration)
		
	--[[ Turning ]]--
    ...
end)

With the compensator line commented out, the issue does not occur. I’ve tried setting the velocity of the ship directly instead of using a VectorForce, but the problem is still present. Why does this happen? The ship’s velocity shouldn’t change because the net result of +targetHorizontalVelocity and -horizontalVelocity are 0 – they have the same magnitude. Is it because physics updates at 240 Hz (IIRC), while Stepped only updates at 60 Hz, causing the ship to be compensating in the wrong direction for a few ms? If so, how do I work around that?

2 Likes

I would print this out every time. I suspect it’s decaying just based on how it iterates (that is, if there’s any moment of time where the velocity’s magnitude dips below your original, it will decay). It places an upper limit on your ship’s velocity, and absent of additional thrust, that upper limit will permanently lower the velocity.

1 Like

The main engine’s force is limited by the ship’s speed but the compensator is only limited by the difference in direction.

For example consider this situation

image

The difference in magnitude between the target velocity and actual velocity is small. In-fact by the way you have defined it, they have exactly the same magnitude. This means the main engine can provide no force if the ship is currently at max speed. The compensation force however is almost horizontal! All it is doing is shedding the horizontal velocity but providing nearly no force forward. This reduces the overall velocity of the ship where I assume the main engine then brings it back up to full speed.

I’d probably recommend trying to limit the target horizontal velocity to be as long as the current velocity’s z component (in local space). This should allow the main engine to provide force while the compensator shed’s the horizontal speed. Although this would require careful balance of forces to keep velocity truly constant.

(Side note: The compensator alone will get to the target velocity eventually, but the magnitude in the mean time changes as the compensator is acting in a direct line whereas constant velocity would require it to act tangentially . This is similar to lerp vs slerp. So you could try making the compensator only act orthogonally to maintain the current velocity)

5 Likes

I think magnalite is right about the problem but also why don’t you have a damper to decay previous velocity instead of a compensator?

If you’re going to stick with this though I think a good solution would be to calculate acceleration limited by compensator magnitude not target horizontal velocity

1 Like

Thanks! I changed

compensator.Force = shipMass * (targetHorizontalVelocity - horizontalVelocity)

to

local relativeVelocity = shipRotation:vectorToObjectSpace(horizontalVelocity)
local compensation = shipRotation:vectorToWorldSpace(Vector3.new(-relativeVelocity.X,0,0))
compensator.Force = shipMass * (compensation / dt)

so that the compensator is only orthogonal to the direction the ship is facing. At this point I can probably change the compensator from world to object space as well to save an extra step.

There’s still a minor slowdown (i.e. 0.1 studs/s), but I’m guessing this is due to accumulation of floating point issues or something similar.

1 Like