Rotate a part vertically (on a wall)

  1. What do you want to achieve? Keep it simple and clear!
    Get a part to rotate on it’s local y axis. This happens while it’s forward face is toward the sky, and it’s Orientation.Y could be any value. (“it’s forward face is toward the sky” means Orientation.X is always 90°.)
  • alternate explanation:
    get a part to rotate in the same fashion that a skultulla does in LOZ OOT.
  1. What is the issue? Include screenshots / videos if possible!
    I really tried, and I partly figured it out. The part seems like it rotates correctly when it is aligned with the x or y axis. If it is not aligned though, the orientation breaks.

  2. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I did look, and I have been very persistent with trying to figure it out on my own, (hence the partial solution). It basically comes down to vertex transformation math.

  • Side note: Because Orientation.Y could be any value, I’m fairly certain that I cannot just set orientation without vertex transformation. (I tried. It’s mentioned in the forums here.)

This script works as a server script, and is made to go into a normal part.

local RunService = game:GetService("RunService")
local foe = script.Parent
local Orientation = Vector3.new(math.rad(foe.Orientation.X),math.rad(foe.Orientation.Y),math.rad(foe.Orientation.Z))
local OrientationStart = Orientation
local OrientationStartDirection = Vector3.new(foe.Orientation.X,foe.Orientation.Y,foe.Orientation.Z)

local angle = 0
local pointToFollow = nil

-- this is a ghetto solution, but it works.
local localUp = Vector3.new(math.sin(OrientationStart.Y),0,math.cos(OrientationStart.Y))

local function lerp(a, b, t)
	return (a * (1-t) + b * t)
end

RunService.PostSimulation:Connect(function(delta)
	-- scales values made for 60 FPS into any FPS.
	local deltaBend = 1/((1/delta)/60)

	angle = angle+0.01

	-- this is the hard part
	local lerpValue = -(math.abs(((OrientationStartDirection.Y % 180)-90)/90)-1)
	pointToFollow = foe.Position+Vector3.new(lerp(math.sin(angle),0,lerpValue),lerp(math.cos(angle),math.sin(angle),lerpValue),lerp(0,math.cos(angle),lerpValue))
	-- x good pointToFollow = ActualPositon+Vector3.new(math.sin(angle),math.cos(angle),0)
	-- y good pointToFollow = ActualPositon+Vector3.new(0,math.sin(angle),math.cos(angle))
	
	foe.CFrame = CFrame.lookAt(foe.Position,pointToFollow,localUp)
end)

Edit: removed a useless function and re-named a variable contained only in notes, to it’s correct name.

Pretty sure you could use VectorToWorldSpace() function or something I don’t really remember.
I think it was on the CFrames page of the documentation

local part = script.Parent

while true do task.wait()
	part.CFrame=part.CFrame*CFrame.Angles(0,math.rad(1), 0)
end

Just move the math.rad(1) to whatever spot you need it to work.
Anchor the part also.

This is what i ended up going with. It should work for anyone as long as they have their part oriented correctly, (the front of the part’s model is also the front of the part).

local RunService = game:GetService("RunService")
local part = script.Parent

local angularSpeed = 0
local forwardSpeed = 0
local lateralSpeed = 0

RunService.PostSimulation:Connect(function(delta)
	part.CFrame *= CFrame.Angles(0,angularSpeed*delta, 0)
	part.Position = part.Position+part.CFrame:VectorToWorldSpace(Vector3.new(lateralSpeed*delta,0,-forwardSpeed*delta))
end)

Now the part needs to look at a point in 3d space and I cant figure out how i am supposed to do it. Because of how I’m working with CFrames, the only thing I have to work with is angular speed… I think I need to make this work with the CFrame.lookAt(…) command, so I have to go back to my old code.

On the bright side, all I need to figure out now, is how to set PointToFollow correctly.

Here is the new version of my old code.

local RunService = game:GetService("RunService")
local foe = script.Parent
local OrientationStartDirection = Vector3.new(foe.Orientation.X,foe.Orientation.Y,foe.Orientation.Z)

local angle = 0
local pointToFollow = nil

local localUp = foe.CFrame:VectorToWorldSpace(Vector3.new(0,1,0))

local function lerp(a, b, t)
	return (a * (1-t) + b * t)
end

RunService.PostSimulation:Connect(function(delta)
	angle = angle+0.01

	-- this is the hard part
	local lerpValue = -(math.abs(((OrientationStartDirection.Y % 180)-90)/90)-1)
	pointToFollow = foe.Position+Vector3.new(lerp(math.sin(angle),0,lerpValue),lerp(math.cos(angle),math.sin(angle),lerpValue),lerp(0,math.cos(angle),lerpValue))
	-- x good pointToFollow = ActualPositon+Vector3.new(math.sin(angle),math.cos(angle),0)
	-- y good pointToFollow = ActualPositon+Vector3.new(0,math.sin(angle),math.cos(angle))

	foe.CFrame = CFrame.lookAt(foe.Position,pointToFollow,localUp)
end)

I found out what to do. This topic (How can I “rotate” a Vector3) had the right information, but it was difficult to know how to use it. The calculations need to happen all at once, and the needed axis can be something other than what you thought you needed. I made a useful function that does what Imperiector was referring to. I wish I would have had it earlier.

function VectorRotated(V: Vector3, angle: number, Axis: IntValue)
	local returnVector
	if Axis == 1 then
		returnVector = Vector3.new(V.X * math.cos(angle) - V.Y * math.sin(angle),V.X * math.sin(angle) + V.Y * math.cos(angle),V.Z)
	elseif Axis == 2 then
		returnVector = Vector3.new(V.X * math.cos(angle) - V.Z * math.sin(angle),V.Y,V.X * math.sin(angle) + V.Z * math.cos(angle))
	elseif Axis == 3 then
		returnVector = Vector3.new(V.X,-V.Y * math.cos(angle) + V.Z * math.sin(angle),V.Y * math.sin(angle) + V.Z * math.cos(angle))
	end
	return returnVector
end

If you have a part that is vertical (like it is on a wall), and you intermittently want it to look at a point in space, then this is how to script it.

local RunService = game:GetService("RunService")
local part = script.Parent

local orientationStart = Vector3.new(part.Orientation.X,part.Orientation.Y,part.Orientation.Z)
local localUpStart = part.CFrame:VectorToWorldSpace(Vector3.new(0,1,0))
local angle = 0
local lookPoint = nil

RunService.PostSimulation:Connect(function(delta)
	angle = angle+(0.6*delta)
	lookPoint = part.Position+Vector3.new(math.sin(angle)*math.cos(math.rad(orientationStart.Y)),math.cos(angle),math.sin(angle)*math.sin(math.rad(orientationStart.Y)))
	part.CFrame = CFrame.lookAt(part.Position,lookPoint,localUpStart)
end)

Edit: An argument was named incorrectly in VectorRotated.
Edit 2: If your project is like mine, you will probably need to know how to project a point onto a plane when the vertical part looks at stuff. localUpStart and part has the same values as above.

-- DO ONCE
local localUpStart = part.CFrame:VectorToWorldSpace(Vector3.new(0,1,0))
local part = script.Parent

-- DO CONSTANTLY
local dist = (lookAtPosition-part.Position):Dot(localUpStart)
lookPosition = lookAtPosition - (dist*localUpStart);

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