Porting FindLookAtRotation to Verse With Rotation Issues

I’m struggling to implement a simple solution for FindLookAtRotation
I’ve written this type of code many times before in Unreal and Unity, but I can’t get it to work quite right in UEFN

Basically ObjectA’s forward rotation needs to constantly rotate toward ObjectB (Z axis only)

I’ve tested my algorithms externally and they seem to provide correct values. Perhaps a keen eye can help me see my issue.

My attempts usually have 4 different outputs:

  1. The creative_prop rotates 180 degrees in the opposite direction
  2. The creative_prop rotates away from the player (player moves right, enemy rotates to the right–instead of to the left to follow the player)
  3. It rotates correctly, but constant snaps back 180 degrees and then snaps back to the correct rotation on alternating loop intervals
  4. My rotation gets really close to working, but has a 30-40 degree offset in either direction (never fully rotating toward ObjectB)
    # Code to rotate enemy's local forward toward player
    # Have tried various speeds and with/without delta time                       
    RotateEnemy(Enemy : creative_prop, Player : player)<suspends>:void=
        var PreviousTime : float = GetSimulationElapsedTime()
        Speed := 5.0
        loop:
            Sleep(0.00)

            CurrentTime := GetSimulationElapsedTime()
            DeltaTime := CurrentTime - PreviousTime
            set PreviousTime = CurrentTime
            DeltaSpeed := Speed * DeltaTime
            if (Enemy.IsValid[]):
                EnemyTransform := Enemy.GetTransform()
                var PlayerTransform : transform = transform{}
                if (Agent := agent[Player], FortChar := Agent.GetFortCharacter[], set PlayerTransform = FortChar.GetTransform()) {}
                EnemyLocation := EnemyTransform.Translation
                PlayerLocation := PlayerTransform.Translation

                # This is likely where our issue begins
                LookRotation := MathExt.FindLookAtRotation(EnemyTransform, PlayerTransform)

                ShortestRotation := MakeShortestRotationBetween(EnemyTransform.Rotation, LookRotation)
                if (SlerpRotation := Slerp[EnemyTransform.Rotation, LookRotation, DeltaSpeed], Enemy.TeleportTo[EnemyLocation, SlerpRotation]) {}
            else:
                break

FindLookAtRotation Attempt A

    # WORKS THE BEST SO FAR. Rotates properly, though choppy, but always keeps
    # A strange offset (never perfectly facing player) around 30 degrees in either direction
    FindLookAtRotation(LookFromTransform: transform, LookToTransform: transform): rotation =
        var LookFromForward: vector3 = LookFromTransform.Rotation.GetLocalUp()
        var LookToPosition: vector3 = LookToTransform.Translation
        var LookFromPosition: vector3 = LookFromTransform.Translation
        var LookDirection: vector3 = Normalize(LookToPosition - LookFromPosition)
        var LookAngle: float = Atan2(-LookDirection.Y, -LookDirection.X)
        var LookRotation: rotation = MakeRotation(vector3{X := 0.0, Y := 0.0, Z := 1.0}, LookAngle)
        var TargetDirection: vector3 = LookRotation.RotateVector(LookFromForward)
        var Dot: float = DotProduct(LookDirection, TargetDirection)
        if (Abs(Dot) < 1.0 - 0.0001):
            var Cross: vector3 = CrossProduct(TargetDirection, LookDirection)
            var CrossMagnitude: float = Magnitude(Cross)
            if (CrossMagnitude > 0.0001):
                var RotationAxis: vector3 = vector3{X := 0.0, Y := 0.0, Z := 1.0}
                var RotationAngle: float = ArcCos(Dot)
                var RotationDelta: float = RotationAngle * Sgn(Dot) / 2.0
                var DeltaRotation: rotation = MakeRotation(RotationAxis, RotationDelta)
                set LookRotation = ComposeQuat(LookRotation, DeltaRotation)
        return LookRotation

FindLookAtRotation Attempt B

    # Flips the ObjectA transform to 180 and basically rotates from a backward direction
   # And in the wrong left and right rotations
    FindLookAtRotation(ObjectA : transform, ObjectB : transform) : rotation=
        # Calculate the direction vector from ObjectA to ObjectB
        var DirVec : vector3 = ObjectB.Translation - ObjectA.Translation
        set DirVec = vector3{ X := DirVec.X, Y := DirVec.Y, Z := 0.0}
        
        # Normalize the direction vector
        Normalized := DirVec / DirVec.Length()
        
        # Calculate the angle between the direction vector and the positive X axis
        var Angle : float = ArcCos(DirVec.X)
        
        # If the direction vector's Y component is negative, flip the angle
        if (Normalized.Y < 0.0):
            set Angle = -Angle
        
        # Construct a rotation around the Z axis by the calculated angle
        return MakeRotation(vector3{X := 0.0,  Y:= 0.0, Z := 1.0}, Angle)
1 Like

Looking for something like this as well. Can’t find anything online about it unfortunately.

1 Like

This might help (I found it in another post which I didn’t book-mark):

        # where V is the vector you want to look along
        if (Vn := V.MakeUnitVector[]):
            Yaw := RadiansToDegrees(ArcTan(Vn.Y, Vn.X))
            Pitch := RadiansToDegrees(ArcTan(V.Z, Sqrt((Vn.X * Vn.X + Vn.Y * Vn.Y))))
            Roll := 0.0  # I fixed this to zero, YMMV
            R := MakeRotationFromYawPitchRollDegrees(Yaw, Pitch, Roll)

Note: I don’t trust this at all… I suspect it’ll end up with ‘gimbal lock’ in some configurations. I’d be a lot happier with Quaternions!

5 Likes