How can I create the equivalent of Unity LookAt in Unreal blueprint?
Asked Answered
U

4

6

In Unreal, I want

  1. to rotate an actor where the forward vector points at target's current position,
  2. to ensure that the up vector of my actor is the same at the up vector of my target

In Unity3D, it's super simple. It's a single line of code (source):

transform.LookAt(target, Vector3.up);

In blueprint, there's a Node called "Find Look at Rotation". The problem is that there's no Up vector parameter, so when you are close to the target, you can have a unwanted roll rotation.

So, how can I create the equivalent of Unity LookAt in Unreal blueprint?

Unguarded answered 5/11, 2019 at 21:46 Comment(0)
R
0

I modified a solution I found here:

https://forums.unrealengine.com/development-discussion/c-gameplay-programming/1482788-posting-the-source-code-for-lookrotation-for-those-who-need-it

The source actor was looking the target with his z vector (Up vector in Unreal). I changed it to be the x vector because in Unreal, the forward vector is the x axis.

The lookAt parameter is the position of the actor you want to look at (the target). UpDirection is the up vector of the target you want to match with the up vector of the source.

Just set the rotation of the source with the FRotator value returned by the function below, called in the C++ code of the source:

FRotator MyLookRotation(FVector lookAt, FVector upDirection)
{
    FVector forward = lookAt - GetActorLocation();
    FVector up = upDirection;


   forward = forward.GetSafeNormal();
   up = up - (forward * FVector::DotProduct(up, forward));
   up = up.GetSafeNormal();

   ///////////////////////


   FVector vector = forward.GetSafeNormal();
   FVector vector2 = FVector::CrossProduct(up, vector);
   FVector vector3 = FVector::CrossProduct(vector, vector2);
   float m00 = vector.X;
   float m01 = vector.Y;
   float m02 = vector.Z;
   float m10 = vector2.X;
   float m11 = vector2.Y;
   float m12 = vector2.Z;
   float m20 = vector3.X;
   float m21 = vector3.Y;
   float m22 = vector3.Z;

   float num8 = (m00 + m11) + m22;
   FQuat quaternion = FQuat();

   if (num8 > 0.0f)
   {
     float num = (float)FMath::Sqrt(num8 + 1.0f);
     quaternion.W = num * 0.5f;
     num = 0.5f / num;
     quaternion.X = (m12 - m21) * num;
     quaternion.Y = (m20 - m02) * num;
     quaternion.Z = (m01 - m10) * num;
     return FRotator(quaternion);
   }

   if ((m00 >= m11) && (m00 >= m22))
   {
     float num7 = (float)FMath::Sqrt(((1.0f + m00) - m11) - m22);
     float num4 = 0.5f / num7;
     quaternion.X = 0.5f * num7;
     quaternion.Y = (m01 + m10) * num4;
     quaternion.Z = (m02 + m20) * num4;
     quaternion.W = (m12 - m21) * num4;
     return FRotator(quaternion);
   }

   if (m11 > m22)
   {
     float num6 = (float)FMath::Sqrt(((1.0f + m11) - m00) - m22);
     float num3 = 0.5f / num6;
     quaternion.X = (m10 + m01) * num3;
     quaternion.Y = 0.5f * num6;
     quaternion.Z = (m21 + m12) * num3;
     quaternion.W = (m20 - m02) * num3;
     return FRotator(quaternion);
   }

   float num5 = (float)FMath::Sqrt(((1.0f + m22) - m00) - m11);
   float num2 = 0.5f / num5;
   quaternion.X = (m20 + m02) * num2;
   quaternion.Y = (m21 + m12) * num2;
   quaternion.Z = 0.5f * num5;
   quaternion.W = (m01 - m10) * num2;


   return FRotator(quaternion);
}

enter image description here

Rooks answered 6/11, 2019 at 19:4 Comment(3)
This is exactly what I'm looking for! Thx!Unguarded
Why not simply return UKismetMathLibrary::MakeRotFromXZ(forward.GetSafeNormal(), upDirection.GetSafeNormal()); after FVector forward = lookAt - GetActorLocation();?Ayo
Yeah, what the actual frick is going on there with all this code?Pancreatotomy
A
8

You can use Make Rot From XZ to do this pretty easily:

blueprint implementation

And for the default unity value of worldUp, you would use (0,0,1) for that.

Calling it on tick with a cube (with forward&up directions shown) as LookingActor, the position of the player character (mannequin) position as TargetPosition, and (0,0,1) as WorldUp produces a result like this:

animation showing block facing mannequin


And if you'd rather tuck away the function definition in C++:

void UMyBlueprintFunctionLibrary::MyLookAt(AActor LookingActor, FVector TargetPosition, 
        FVector WorldUp = FVector::UpVector)
{
    FVector Forward = TargetPosition - LookingActor.GetActorLocation();
    FRotator Rot = UKismetMathLibrary::MakeRotFromXZ(Forward, WorldUp);
    LookingActor.SetActorRotation(Rot, true);
}
Ayo answered 6/11, 2019 at 18:2 Comment(4)
The problem with this solution is the same as the find look at rotation node. It causing an unwanted rotation because you don't place the up vector of the target in the same position as the source up vector position. The actor is not locked and it causing a rolling rotation.Brainbrainard
@ÉmilePettersen-Coulombe up vectors dont have positions, they just have directions... And it works fine for me.Ayo
@ÉmilePettersen-Coulombe Please see here for an example with WorldUp=(0,0,1). I would recommend you refer to the documentation on MakeRotFromXZ because that does something that FindLookAtRotation doesn't do.Ayo
@ÉmilePettersen-Coulombe I am still not quite sure what you were talking about, can you elaborate?Ayo
S
3
inline FQuat MyLookAt(const FVector& lookAt, const FVector& upDirection)
{
    return FRotationMatrix::MakeFromXZ(lookAt, upDirection).ToQuat();
}
Swansdown answered 20/4, 2021 at 13:31 Comment(1)
A note that input vectors need to be normalizedDemulsify
P
0

I'm not familiar with unreal but look at is basically this:

yourLocation-target location this is a vector to the target then you should do this thing

cosInvert((a*b)/|a|*|b|)

this will give you the angle you should look at

  • a will be to the target angle
  • and b will be (0,0,1),(0,1,0),(1,0,0)

Now you have 3 angles to set to the rotation of your object.

Pugging answered 6/11, 2019 at 8:6 Comment(1)
How to you define the Up vector with your solution? I want it to be the same as the target.Unguarded
R
0

I modified a solution I found here:

https://forums.unrealengine.com/development-discussion/c-gameplay-programming/1482788-posting-the-source-code-for-lookrotation-for-those-who-need-it

The source actor was looking the target with his z vector (Up vector in Unreal). I changed it to be the x vector because in Unreal, the forward vector is the x axis.

The lookAt parameter is the position of the actor you want to look at (the target). UpDirection is the up vector of the target you want to match with the up vector of the source.

Just set the rotation of the source with the FRotator value returned by the function below, called in the C++ code of the source:

FRotator MyLookRotation(FVector lookAt, FVector upDirection)
{
    FVector forward = lookAt - GetActorLocation();
    FVector up = upDirection;


   forward = forward.GetSafeNormal();
   up = up - (forward * FVector::DotProduct(up, forward));
   up = up.GetSafeNormal();

   ///////////////////////


   FVector vector = forward.GetSafeNormal();
   FVector vector2 = FVector::CrossProduct(up, vector);
   FVector vector3 = FVector::CrossProduct(vector, vector2);
   float m00 = vector.X;
   float m01 = vector.Y;
   float m02 = vector.Z;
   float m10 = vector2.X;
   float m11 = vector2.Y;
   float m12 = vector2.Z;
   float m20 = vector3.X;
   float m21 = vector3.Y;
   float m22 = vector3.Z;

   float num8 = (m00 + m11) + m22;
   FQuat quaternion = FQuat();

   if (num8 > 0.0f)
   {
     float num = (float)FMath::Sqrt(num8 + 1.0f);
     quaternion.W = num * 0.5f;
     num = 0.5f / num;
     quaternion.X = (m12 - m21) * num;
     quaternion.Y = (m20 - m02) * num;
     quaternion.Z = (m01 - m10) * num;
     return FRotator(quaternion);
   }

   if ((m00 >= m11) && (m00 >= m22))
   {
     float num7 = (float)FMath::Sqrt(((1.0f + m00) - m11) - m22);
     float num4 = 0.5f / num7;
     quaternion.X = 0.5f * num7;
     quaternion.Y = (m01 + m10) * num4;
     quaternion.Z = (m02 + m20) * num4;
     quaternion.W = (m12 - m21) * num4;
     return FRotator(quaternion);
   }

   if (m11 > m22)
   {
     float num6 = (float)FMath::Sqrt(((1.0f + m11) - m00) - m22);
     float num3 = 0.5f / num6;
     quaternion.X = (m10 + m01) * num3;
     quaternion.Y = 0.5f * num6;
     quaternion.Z = (m21 + m12) * num3;
     quaternion.W = (m20 - m02) * num3;
     return FRotator(quaternion);
   }

   float num5 = (float)FMath::Sqrt(((1.0f + m22) - m00) - m11);
   float num2 = 0.5f / num5;
   quaternion.X = (m20 + m02) * num2;
   quaternion.Y = (m21 + m12) * num2;
   quaternion.Z = 0.5f * num5;
   quaternion.W = (m01 - m10) * num2;


   return FRotator(quaternion);
}

enter image description here

Rooks answered 6/11, 2019 at 19:4 Comment(3)
This is exactly what I'm looking for! Thx!Unguarded
Why not simply return UKismetMathLibrary::MakeRotFromXZ(forward.GetSafeNormal(), upDirection.GetSafeNormal()); after FVector forward = lookAt - GetActorLocation();?Ayo
Yeah, what the actual frick is going on there with all this code?Pancreatotomy

© 2022 - 2024 — McMap. All rights reserved.