Trying to clamp rotation on UFO

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve?
    I want to clamp the rotation of the UFO so that it doesn’t flip over itself. The vehicle uses align orientation and a vector force pushing under the UFO.

  2. What is the issue?
    The clamp code I am using bugs out, I am not completely sure about the reason. If anyone could suggest something that would be great

  3. What solutions have you tried so far?
    The clamp code I am using is

seat.CFrame = CFrame.new(seat.CFrame.p) * CFrame.fromOrientation(math.rad(limX), 0, math.rad(limZ))

Below is the code and video

local rs = game:GetService("RunService")

local character = script.Parent
local humanoid = character:WaitForChild("Humanoid")

local connection = nil
local seat = nil
local attachment0 = nil
local vf = nil
local trueOrient = nil
local allignOrientation = nil
local oldseatPos = nil

local offset = Vector3.new(0,-80,0)

local parametersRay = nil

local rayPart = Instance.new("Part", workspace)
rayPart.Size = Vector3.one
rayPart.Shape = Enum.PartType.Ball
rayPart.CanQuery = false
rayPart.CanCollide = false
rayPart.CanTouch = false
rayPart.Anchored = true

local thrust = 2000
local minimum_thrust = 1000
local drag = 5
local velocityRay = .6
local velocityStabilize = .4
local velocityHorizontal = .8

local lerp = false

local orientationClamp = 35
local X, Y, Z
local limX
local limY
local limZ
local stabilizeX = CFrame.Angles(0,0,0)
local stabilizeZ = CFrame.Angles(0,0,0)

local horizontalRayPart = Instance.new("Part", workspace)
horizontalRayPart.Size = Vector3.new(4,4,4)
horizontalRayPart.Shape = Enum.PartType.Ball
horizontalRayPart.CanQuery = false
horizontalRayPart.CanCollide = false
horizontalRayPart.CanTouch = false
horizontalRayPart.Anchored = true

local function Loop (deltaTime)
	oldseatPos = seat.Position
	
	local rayTarget = seat.Position + offset + seat.AssemblyLinearVelocity * velocityRay
	local result = workspace:Raycast(seat.Position, rayTarget - seat.Position, parametersRay)
	local horizontalTarget = (oldseatPos - seat.Position) + seat.AssemblyLinearVelocity * velocityHorizontal
	local horizontalResult = workspace:Raycast(oldseatPos, horizontalTarget, parametersRay)
	
	X, Y, Z = seat.CFrame:ToOrientation()
	limX = math.clamp(math.deg(X), -orientationClamp, orientationClamp)
	limZ = math.clamp(math.deg(Z), -orientationClamp, orientationClamp)
		
	if result == nil then
		rayPart.Position = rayTarget
		rayPart.BrickColor = BrickColor.new("Really red")
		
		vf.Force = Vector3.new(0, minimum_thrust, 0)
	else
		rayPart.Position = result.Position
		rayPart.BrickColor = BrickColor.new("Lime green")
		
		local magnitude = (result.Position - rayTarget).Magnitude
		vf.Force = Vector3.new(0, minimum_thrust + magnitude * thrust, 0)
	end
	
	local velocity = seat.CFrame:VectorToObjectSpace(seat.AssemblyLinearVelocity)
	if velocity.Magnitude > 0 then vf.Force -= velocity.Unit * (drag * velocity.Magnitude ^ 2) end
	

	if horizontalResult == nil then
		horizontalRayPart.Position = seat.Position + horizontalTarget
		horizontalRayPart.BrickColor = BrickColor.new("Really red")

	else
		horizontalRayPart.Position = horizontalResult.Position
		horizontalRayPart.BrickColor = BrickColor.new("Lime green")
		vf.Force -= velocity.Unit * (drag * velocity.Magnitude ^ 3)
	end
	
	--trueOrient *= CFrame.fromOrientation(0 , -seat.SteerFloat * seat.TurnSpeed * deltaTime, 0)
	local tilt = CFrame.fromOrientation(-seat.ThrottleFloat * seat.Torque, 0, -seat.SteerFloat * seat.Torque)
	if math.abs(limX) ~= orientationClamp and math.abs(limZ) ~= orientationClamp then
		allignOrientation.CFrame = trueOrient * tilt
	else
		allignOrientation.CFrame = trueOrient * tilt
		seat.CFrame = CFrame.new(seat.CFrame.p) * CFrame.fromOrientation(math.rad(limX), 0, math.rad(limZ))
	end
end
		

spawn(function()
	while wait(.2) do
		if seat then
			print(limX .. " " .. limZ)
		end
	end
end)

local function Seated (active, CurrentSeat)
	if active == false then
		if connection == nil then return end
		connection:Disconnect()
		connection = nil
		attachment0:Destroy()
		vf:Destroy()	
		
	elseif CurrentSeat.Name == "UFOseat"then
		seat = CurrentSeat
		
		attachment0 = Instance.new("Attachment", seat)
		
		vf = Instance.new("VectorForce")
		vf.Parent = seat
		vf.ApplyAtCenterOfMass = true
		vf.Force = Vector3.zero
		vf.Attachment0 = attachment0
		
		trueOrient = CFrame.fromMatrix(Vector3.zero, seat.CFrame.LookVector:Cross(Vector3.yAxis), Vector3.yAxis)
		allignOrientation = Instance.new("AlignOrientation", seat)
		allignOrientation.Mode = Enum.OrientationAlignmentMode.OneAttachment
		allignOrientation.CFrame = trueOrient
		allignOrientation.Responsiveness = 1
		allignOrientation.Attachment0 = attachment0
		
		parametersRay = RaycastParams.new()
		parametersRay.FilterType = Enum.RaycastFilterType.Exclude
		parametersRay.FilterDescendantsInstances = {character, seat:FindFirstAncestorOfClass("Model")}
		
		connection = rs.PostSimulation:Connect(Loop)
	end
end

humanoid.Seated:Connect(Seated)
1 Like

The issue is that X or Z might be able to be greater than 180, so clamping the position might not be the best solution.

It shouldn’t be necessary to clamp it in the first place. The best hacky method I can come up with is to make some sort of part inside the UFO with a ball socket constraint that cannot turn more than 35 degrees, but it never flips over.

1 Like

Seems like a good way to do it. I’m not completely familiar with all the constraints, but ball socket seems simple to learn. Thanks!

1 Like


I’m struggling to understand how this constraint really works? I thought that it wouldnt let it go past the green markers, but it doesn’t seem to do that. Please help :crying_cat_face:

Edit:
This might be because align orientation doesn’t actually rotate the part, but it aligns it. How do I go about this.

2 Likes

The part the constraint is attached to must not rotate. AlignOrientation should be set to OneAttachment.

Make sure the part is attached to isn’t welded to the main vehicle.

1 Like

I had the rotation part welded, so it moves now. However, although the rotation part is limited, the UFO isn’t.

2 Likes

The rotation part needs to have mass. It also shouldn’t be able to rotate at all, with an AlignOrientation with RigidityEnabled set to true.

It works, but the speed of the rotation is way too quick. I liked the slow ramping speed before. Is this the only way for the ball constraint to work?

I meant there should be a separate AlignOrientation inside of the ball socket constrained part that prevents it from rotating at all. The original AlignOrientation still does the rotation of the UFO, just the rigid one prevents it from going too far.

1 Like

Oh, I think you can tell I am a newbie to constraints lol. It works fine now thanks!

1 Like

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