Moving objects around spaceship (illusion of movement), CFrame Math

I am creating a spaceship system for a single player project where a group control a spaceship.

To allow for smooth gameplay I am attempting to make a movement system where instead of the spaceship moving around objects in space, the objects are moved around the spaceship, essentially creating the illusion of the spaceship moving.

I like the idea of this system, a main advantage is that players will be able to walk around the ‘moving’ ship smoothly without stuttering like they normally do on moving platforms, as the spaceship itself doesn’t physically move.

Objects moving relative to the ship (Ship is rotating on the Y axis, and is then moving forward):
https://gyazo.com/82c75016fa8a43085665f4dd8c48c5ed

The ship movement works perfectly when moving the objects around the ship on only the Y axis, however the system breaks once the objects have to rotate around the ship on the Z or X axis.

Objects not rotating correctly relative to the ship (Ship is rotating on the Z axis, and then moves foward)
https://gyazo.com/0db10f6fdbe2135c8b72ac42062becea

To create this movement system I am essentially orbiting objects physically around the static ship when the ship rotates or moves, through CFrame math. Due to the ship not moving physically, a virtual ship position and rotation value is calculated from the ship ‘velocity’ and ‘rotation velocity’. An offset between the object and ships position and rotation is calculated, and the object is then CFramed by these offsets to give it the illusion of moving around the ship.

local RotAcceleration = ShipInfo.RotAcceleration.Value
ShipInfo.Rotation.Value += Vector3.new(RotAcceleration.x, RotAcceleration.y, RotAcceleration.z)

local ShipRot = ShipInfo.Rotation.Value
local ShipPosition = ShipMain.CFrame * CFrame.Angles(math.rad(ShipRot.x), math.rad(ShipRot.y), math.rad(ShipRot.z))
	
local Acceleration = ShipInfo.Acceleration.Value
ShipInfo.Position.Value +=ShipPosition * CFrame.new(Vector3.new(Acceleration.x, Acceleration.y, Acceleration.z)).Position

for i,Object in pairs(workspace.Enviroment:GetChildren())do
	local ObjectMain = Object.PrimaryPart
	local ObjectInfo = Object.ObjectInfo
	
	local function Process()
		local Pos = ShipInfo.Position.Value
		local Rot = ShipInfo.Rotation.Value

		local PosOff = -(Pos - ObjectInfo.Position.Value)
		local RotOff = -(Rot - ObjectInfo.Rotation.Value)

		local OffsetCFrame = ShipMain.CFrame * CFrame.Angles(math.rad(RotOff.x), math.rad(RotOff.y), math.rad(RotOff.z)):ToObjectSpace() * CFrame.new(PosOff)
		ObjectMain.CFrame = ShipMain.CFrame:ToObjectSpace(CFrame.Angles(math.rad(RotOff.x), math.rad(RotOff.y), math.rad(RotOff.z)) * CFrame.new(PosOff))
	end
	
	coroutine.wrap(Process)
	Process()
	wait(0.01)
end

If I had to guess what the issue was I would pin the blame to my CFrame math, I don’t think I’m calculating the rotation of the objects relative to the ship correctly, and after hours of trying to fix it, I’m still stuck.

1 Like

Something like this should work, you were very close. I’ll go and explain what each thing does (the expected inputs and etc).

local ship = workspace:WaitForChild('Ship') -- the ship (the part the other parts move around)
local accel = ship:WaitForChild('Velocity') -- a Vector3 in object space describing the linear velocity of our ship (translation; studs/sec)
local rot = ship:WaitForChild('RotVelocity') -- a Vector3 in object space in degrees describing the rotational velocity of our ship (degs/sec)

local objects = workspace:WaitForChild('Objects')

local function velocityCFrame(dt) -- this gives us an offset for this frame taking into account the velocity
	-- we multiply everything by dt because acceleration is in studs/second and rot is in degrees/second and our RunService events pass the time in seconds since the last frame
	-- finally, we have to use - on everything because we are moving objects around the ship, not the other way around
	
	local rotVel = -rot.Value * dt
	
	return CFrame.new(-accel.Value * dt) * CFrame.fromOrientation(math.rad(rotVel.X), math.rad(rotVel.Y), math.rad(rotVel.Z))
end

game:GetService('RunService').RenderStepped:Connect(function(dt)
	if accel.Value.Magnitude <= 0.0001 and rot.Value.Magnitude <= 0.0001 then -- no movement
		return
	end
	
	local localOffset = ship.CFrame * velocityCFrame(dt) -- we apply a supposed offset to the ship's real CFrame, this will be the offset that the other objects take on relative to the ship
	
	for _, object in objects:GetDescendants() do
		if object:IsA('BasePart') then
			local objectSpace = ship.CFrame:ToObjectSpace(object.CFrame) -- the object's current CFrame relative to the real ship CFrame
			object.CFrame = localOffset:ToWorldSpace(objectSpace) -- the object's new CFrame relative to the supposed local offset above^^^
		end
	end
end)

also note that you should use another event if you’re moving stuff on the server (ie .Heartbeat instead of .RenderStepped)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.