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?

Ruzihm
  • 19,749
  • 5
  • 36
  • 48
probitaille
  • 1,899
  • 1
  • 19
  • 37

4 Answers4

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);
}
Ruzihm
  • 19,749
  • 5
  • 36
  • 48
  • 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. – Émile Pettersen-Coulombe Nov 07 '19 at 14:20
  • 1
    @ÉmilePettersen-Coulombe up vectors dont have positions, they just have directions... And it works fine for me. – Ruzihm Nov 07 '19 at 14:29
  • @ÉmilePettersen-Coulombe [Please see here](https://i.imgur.com/bgzUySn.gif) for an example with WorldUp=(0,0,1). I would recommend you refer to the documentation on [`MakeRotFromXZ`](https://docs.unrealengine.com/en-US/BlueprintAPI/Math/Rotator/MakeRotfromXZ/index.html) because that does something that [`FindLookAtRotation`](https://docs.unrealengine.com/en-US/API/Runtime/Engine/Kismet/UKismetMathLibrary/FindLookAtRotation/index.html) doesn't do. – Ruzihm Nov 07 '19 at 14:36
  • @ÉmilePettersen-Coulombe I am still not quite sure what you were talking about, can you elaborate? – Ruzihm Jan 14 '20 at 03:09
3
inline FQuat MyLookAt(const FVector& lookAt, const FVector& upDirection)
{
    return FRotationMatrix::MakeFromXZ(lookAt, upDirection).ToQuat();
}
Jason
  • 61
  • 2
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.

probitaille
  • 1,899
  • 1
  • 19
  • 37
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