Help with a direction detecting script

  1. 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.

  2. 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.

  3. 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
1 Like

Why???

Because if that was sufficient for my needs I wouldn’t be writing this script.

But what do you need, you are just saying it, that you need to do lookAt, pls describe it more.

I’m trying to reveal as little as possible as to why I need this script. Can we just focus on why the script isn’t working.

No, because I dont understand what it may do.

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.

Ya, but why you dont use CFrame and Tween?

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.

1 Like

Or you can do

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)

Sry for mistakes, I am on mobile.

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