0
\$\begingroup\$

I have this script that points a missile's z+ axis at its target:

 public class Missile : MonoBehaviour
 {
     Vector3 target;
     public Transform car;

     void Update()
     {
         car = GameObject.FindGameObjectWithTag("Cube").transform;
         target = Vector3.Normalize(car.position - transform.position);
         Quaternion direction = Quaternion.LookRotation(target);
         transform.rotation = Quaternion.Slerp(transform.rotation, direction, 4*Time.deltaTime);
         transform.position = transform.position + 10* transform.up* Time.deltaTime;

     }
}

But my missile model has its "front" pointing in the direction of the y+ axis, not z.

Missile model

How can I convert the LookRotation code above to rotate the y axis to my target, instead of the z? If possible, I'd love to see a diagram that explains how the conversion works.

I have referred to some previous answers about this topic, but still couldn't understand how to do it in this case.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Some coding tips: 1) you don't need to declare target as a member variable. You can just use it as a temporary inside Update and throw it out at the end of the function. 2) don't search for your target with GameObject.Find() every frame - this is slow. Do it only on start-up, or when the target changes (say the previous target got destroyed mid-flight and you need to pick a new one). 3) You're using a non-linear blend for your rotation, so the extra cost of Slerp over Lerp isn't doing anything useful for you here. 4) Give type names an initial uppercase letter. \$\endgroup\$
    – DMGregory
    Commented Mar 13, 2022 at 15:01

2 Answers 2

3
\$\begingroup\$

Let's see if I can write a version of this answer so general-purpose that I never have to write another one. 😆

public static Quaternion OmniLookRotation(
             Vector3 exactAxis,       Vector3 exactTarget,
             Vector3 approximateAxis, Vector3 approximateTarget
) {
    // Compute a rotation that takes the z+ and y+ axes to our custom axes.
    var zyToCustom = Quaternion.LookRotation(exactAxis, approximateAxis);
    // Invert this, to map our custom axes to z+ and y+.
    var customToZY = Quaternion.Inverse(zyToCustom);

    // Compute a rotation that takes the z+ and y+ axes to our target directions.
    var zyToTarget = Quaternion.LookRotation(exactTarget, approximateTarget);
    
    // Chain these two rotations so that exactAxis maps to exactTarget,
    // and approximateAxis maps as closely as it can to approximateTarget.
    var customToTarget = zyToTarget * customToZY;

    return customToTarget;
}

So, let's say I want to point my local y+ axis (Vector3.up) exactly toward my target (let's call that toTarget to distinguish this offset from the target's absolute position), while pointing my local x+ axis (Vector3.right) as close as possible toward world "down" (Vector3.down). I'd write it like this:

var targetRotation = OmniLookRotation(
                       Vector3.up,    toTarget,    // "point my y+ at target"
                       Vector3.right, Vector3.down // "and my x+ down"
                   );

// This is already a non-linear, not correctly time-adjusted blend,
// so if we're being approximate anyway we may as well use Lerp
// and save the added cost of Slerp.
transform.rotation = Quaternion.Lerp(
                       transform.rotation, 
                       targetRotation,
                       4*Time.deltaTime
                   );
\$\endgroup\$
4
  • \$\begingroup\$ Thanks for the great answer! I was rather curious about the answer too, but why did you use static? I haven't used this keyword a lot so I want to know what happens to the code without writing static \$\endgroup\$
    – Arian_ki
    Commented Mar 13, 2022 at 15:04
  • \$\begingroup\$ You'll notice that nothing inside the body of that method depends on member variables. So, this does not need to be a member function of any particular object. You could throw it in a utilities class and call it from any object, much like Quaternion.LookRotation() itself. If you don't have the static keyword, then the compiler will insist that you have a reference to some object to call OmniLookRotation on, even though that's completely unnecessary to the math you want to do. So static frees us from that restriction. \$\endgroup\$
    – DMGregory
    Commented Mar 13, 2022 at 15:06
  • \$\begingroup\$ I see, thanks for explanation👍 \$\endgroup\$
    – Arian_ki
    Commented Mar 13, 2022 at 15:11
  • \$\begingroup\$ Let me take some time to digest this as Im bad at 3d imagination 😅 \$\endgroup\$
    – chuackt
    Commented Mar 13, 2022 at 15:58
1
\$\begingroup\$

A quick and easy way to deal with a model not being lined up the way you want it is to make the game object with the model be a child of the game object that has the movement script. In this case, Change the local rotation of the child to orient the model's y axis along the parent object's z axis. That way, as your movement script points the parent object, the child will move with it. No fancy math needed.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .