Changing A Part's Orientation based on the direction the part is moving

  1. What do you want to achieve? Keep it simple and clear!
    Currently, I have a bezier that allows for the smooth movement of the part. I want the curve to not only change the position, but also orientation. The orientation should be facing the direction the part is moving.

  2. What is the issue? Include screenshots / videos if possible!


    I haven’t found a way to make the part’s orientation face the direction the part is moving.

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I’ve tried subtracting the previous position by the new position and using its unit to get the direction. Yes, but they were mostly about humanoid direction instead of a part
    After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

This code is part of a moduleScript, and is a function of the custom class Enemy. It is stored in Replicated Storage.

function Enemy:Move()
	local wayPoints = workspace.PointFolder:GetChildren()
	table.sort(wayPoints, function(a, b)
		return a:GetAttribute("Count")<b:GetAttribute("Count")
	end)
	--SimplePath
	local count = 0
	local target = nil
	self.FireEvent = game:GetService("RunService").PostSimulation:Connect(function()
		count+=1
		if count>=10 then
			count = 0
			local Data = PackageData(self.Model:GetAttribute("UniqueId"), self.Model, self.Model.Location.Value, target, self.Speed)
			PackageEvent:FireAllClients(Data)
		end
	end)
	local Location:CFrameValue = self.Model:WaitForChild("Location")
	local newWayPoints = wayPoints
	table.remove(newWayPoints, 1)
	for i, v:Part in pairs(newWayPoints) do
		target = v
		local lookat = CFrame.lookAt(Location.Value.Position, Vector3.new(v.CFrame.X, Location.Value.Y, v.CFrame.Z))
		Services.TweenService:Create(Location, TweenInfo.new(self.Speed/20, Enum.EasingStyle.Linear, Enum.EasingDirection.In), {Value = lookat}):Play()
		task.wait(0.5)
		local duration = (Location.Value.Position - v.Position).Magnitude/self.Speed
		local _, y, _ = Location.Value:ToOrientation()
		local goal = {Value = CFrame.new(v["1"].Position.X, Location.Value.Y, v["1"].Position.Z) * CFrame.Angles(0, y, 0)}
		local tween = Services.TweenService:Create(Location, TweenInfo.new(duration, Enum.EasingStyle.Linear, Enum.EasingDirection.In), goal)
		tween:Play()
		tween.Completed:Wait()
		local bezDuration = (v["1"].Position-v["2"].Position).Magnitude/self.Speed
		local old = Vector3.new(Location.Value.Position)
		for i = 0, 1, 0.01 do
			local newPos = QuadraticBezier(i, v["1"].Position, v.Position, v["2"].Position)
			Location.Value = CFrame.lookAt(Location.Value.Position, newPos)
			Location.Value = CFrame.new(newPos)
			old = Location.Value.Position
			task.wait(bezDuration/100)
		end
	end
end
2 Likes

You can construct a rotation using CFrame.fromMatrix and use the part’s velocity as the forward direction.

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

local PreviousPartPosition = Part.CFrame.Position
RunService.Heartbeat:Connect(function(deltaTime)
	local PartVelocity = (Part.CFrame.Position - PreviousPartPosition) / deltaTime
	PreviousPartPosition = Part.CFrame.Position
	
	if PartVelocity.Magnitude > 0 then
		local ForwardVector = PartVelocity.Unit
		local UpVector = Vector3.yAxis
		local RightVector = ForwardVector:Cross(UpVector)
		local NewUpVector = ForwardVector:Cross(RightVector)
		
		Part.CFrame = CFrame.fromMatrix(Part.CFrame.Position,RightVector,NewUpVector,ForwardVector)
	end
end)
4 Likes

This solution looks right (haven’t tested it though) but I believe it would be simpler to do:

Part.CFrame = CFrame.LookAt(Part.CFrame.Position, Part.CFrame.Position + PartVelocity, Vector3.yAxis)
instead of the CFrame.fromMatrix solution because it’s easier to read. I haven’t tested it either though.

2 Likes

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