What do you want to achieve?
I’m attempting to write a direction detection script that makes a part turn until it faces another part. I know that I can just rotate it to face the part with a single CFrame rotation but that is not what I want to do.
What is the issue?
I have the script working really for the most part, it can detect when looking Left or Right of the target and when it does it changes its turn direction to turn the opposite direction. The problem is that even though it detects the look direction at the proper point, it doesn’t actually start rotating the opposite direction until well after that. I can’t figure out why the direction change is so delayed.
What solutions have you tried so far?
I have rewritten this script several times but I’m not sure what I can do other than what I’m already doing. I have no idea why the direction change is delayed like it is.
Here is the script:
local lookPart = script.Parent
local goalPart = game.Workspace.RedPart
local goalAngle = nil
local looking = nil
local degree = 0
local direction = 1
--Create CFrames to use for detecting left and right sides of the goal
local lookCFrame = CFrame.new(lookPart.Position, goalPart.Position)
local rightCFrame = lookCFrame * CFrame.Angles(0,math.rad(45),0) * CFrame.new((lookCFrame.Position - goalPart.Position).Magnitude, 0, 0)
local leftCFrame = lookCFrame * CFrame.Angles(0,math.rad(135),0) * CFrame.new((lookCFrame.Position - goalPart.Position).Magnitude, 0, 0)
local rightPart = Instance.new("Part", game.Workspace)
rightPart.Anchored = true
rightPart.Shape = "Ball"
rightPart.BrickColor = BrickColor.Green()
rightPart.CFrame = rightCFrame
rightPart.Name = "RightPart"
local leftPart = Instance.new("Part", game.Workspace)
leftPart.Anchored = true
leftPart.Shape = "Ball"
leftPart.BrickColor = BrickColor.White()
leftPart.CFrame = leftCFrame
leftPart.Name = "Leftpart"
--Determine Angle of facing direction between one cframe and a goal Cframe
local function CheckAngle(cframe1, cframe2, name)
local Distance = (cframe2.Position - cframe1.Position);
local LookVector = (cframe1.lookVector);
local x = Distance:Dot(LookVector) / (Distance.Magnitude * LookVector.Magnitude);
local xFixed = math.clamp(x, -1, 1)
x = xFixed
--print("X-"..name..": "..x);
local angle = math.deg(math.acos(x));
return angle;
end
--Determine if facing left or right direction of goal part
local function lookDirection(lookPart, goalPart)
--Get Left and right Angles
local leftAngle = CheckAngle(lookPart.CFrame, leftCFrame, "Left")
local rightAngle = CheckAngle(lookPart.CFrame, rightCFrame, "Right")
if(rightAngle > 45 and leftAngle <= 45)then
return "L"
elseif(rightAngle <= 45 and leftAngle > 45)then
return "R"
end
if(rightAngle > 135 and leftAngle <= 135)then
return "BL"
elseif(leftAngle > 135 and rightAngle <= 135)then
return "BR"
end
return nil
end
--Main Loop
while true do
game:GetService("RunService").Stepped:wait()
goalAngle = CheckAngle(lookPart.CFrame, goalPart.CFrame, "Goal")
print(goalAngle)
--Get Looking direction
looking = lookDirection(lookPart, goalPart)
if(looking == "L" or looking == "BL")then
degree = degree - .01
lookPart.CFrame = lookPart.CFrame * CFrame.Angles(0, math.rad(degree), 0)
print("Looking Left, Turning Right")
elseif(looking == "R" or looking == "BR")then
degree = degree + .01
lookPart.CFrame = lookPart.CFrame * CFrame.Angles(0, math.rad(degree), 0)
print("Looking Right, turning Left")
end
end
I’ve said what it does in the original post. It makes a part rotate to face a goal position and changes rotation direction based on if facing left or right of the goal.
Because I need it to be able to detect the required direction changes dynamically. Using a tween for the rotation would lock the rotation in until the tween finishes, I need it to be able to change direction at any moment.
I started printing out the degree value that is used for the rotation and I can see that it isn’t working as (I) intended. When the script detects its need to change direction, the degree value is reduce or incremented based on direction. But the rotation doesn’t actually start going the other direction until the degree value becomes negative or positive. It just turns at a lesser degree I guess the same direction.
i think lerping the cframe every frame to the desired lookat cframe is the best way to make a part slowly look towards a point, tweens can also be used, if none of them work for ya, the last possible way is AlignOrientation constraint, basically the new bodymovers that use physics, so you’d need the part to be unanchored, i can’t think of any other ways that i’m aware of doing what you want.
oh and lastly, you mentioned that you were trying to calculate the angle between steps? atleast that’s what your script is “trying” to do, heres a little thread about something you might be interested in: A Couple of Advanced CFrame Tricks
by @EgoMoose , i don’t completly understand it but hopefully it helps you in some way
Thanks for your suggestions. I solved the problem. I was simply using the CFrame rotation code in the wrong way. Instead of increasing or decreases the degree value I simply keep it at a specific value and flip its sign based on the direction I need to turn.
local unit=--your rotation per tick
local a,b,c=actualVector.X,actualVector.Y,actualVector.Z
local ta,tb,tc=targetVector.X,targetVector.Y,targetVector.Z
local na,nb,nc
if ta-a>0 then
fa=fa+unit
elseif ta-a<0
fa=fa-unit
end
--same for b and c
local newVector=Vector3.new(na,nb,nc)
Here is the working script for this if anyone else is interested in using it. Also if anyone believes it can be made more efficient please do share your modifications.
--[[
Rotate LookPart to face goalPart based on current Look direction.
This works no matter where the lookPart is positioned or its current orientation.
--Script By Emskipo
--]]
local lookPart = script.Parent --The part that is looking/rotating
local goalPart = game.Workspace.GoalPart --The Part to look at.
local lookDir = nil --Current look direction
local prevLookDir = nil --Previous look direction
local degree = .1 --Degree amount to rotate
--Determine Angle of facing direction between one cframe and a goal Cframe
--Borrowed from VineyardVine and modified slightly
local function CheckAngle(cframe1, cframe2)
local Distance = (cframe2.Position - cframe1.Position);
local x = Distance:Dot(cframe1.lookVector) / (Distance.Magnitude * cframe1.lookVector.Magnitude);
x = math.clamp(x, -1, 1)--Clamp x value between -1 and 1 the domain for arccosine
return math.deg(math.acos(x));
end
--Determine if facing left or right direction of goal part
local function lookDirection(lookPart, goalPart)
--Create a CFrame for the lookPart that faces the goalPart
local lookCFrame = CFrame.new(lookPart.Position, goalPart.Position)
--Create two other CFrames and position them the same distance away from the lookPart as the goalPart but rotated around the lookPart
--The right side CFrame is rotated around the lookPart at 45 degrees and the left side is rotated around the lookPart at 135 degrees
local rightCFrame = lookCFrame * CFrame.Angles(0,math.rad(45),0) * CFrame.new((lookCFrame.Position - goalPart.Position).Magnitude, 0, 0)
local leftCFrame = lookCFrame * CFrame.Angles(0,math.rad(135),0) * CFrame.new((lookCFrame.Position - goalPart.Position).Magnitude, 0, 0)
--Get Left and right Angles
local leftAngle = CheckAngle(lookPart.CFrame, leftCFrame)
local rightAngle = CheckAngle(lookPart.CFrame, rightCFrame)
--Use Left and Right angles to determine which direction the
--lookPart is looking relative to the goalPart
if(rightAngle > 45 and leftAngle <= 45)then
return "L"
elseif(rightAngle <= 45 and leftAngle > 45)then
return "R"
end
if(rightAngle > 135 and leftAngle <= 135)then
return "BL"
elseif(leftAngle > 135 and rightAngle <= 135)then
return "BR"
end
return nil
end
--Main Loop
while true do
game:GetService("RunService").Stepped:wait()
--Get Look direction values
prevLookDir = lookDir
lookDir = lookDirection(lookPart, goalPart)
--Check for direction switch
if(prevLookDir ~= lookDir)then
if((prevLookDir == "L" or prevLookDir == "BL") and (lookDir == "R" or lookDir == "BR"))then
degree = degree * (-1)
elseif((prevLookDir == "R" or prevLookDir == "BR") and (lookDir == "L" or lookDir == "BL"))then
degree = degree * (-1)
end
end
--Rotate lookPart the appropriate direction based on look direction
lookPart.CFrame = lookPart.CFrame * CFrame.Angles(0, math.rad(degree), 0)
--Say whats happening
if(lookDir == "L")then
print("Looking Left, Turning Right")
elseif(lookDir == "R")then
print("Looking Right, turning Left")
elseif(lookDir == "BL")then
print("Looking Back Left, Turning Right")
elseif(lookDir == "BR")then
print("Looking Back Right, turning Left")
end
end --End Main Loop