Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Quaternion not working and having weird effects.

Discussion in 'Scripting' started by McGluckerson, Jun 30, 2021.

  1. McGluckerson

    McGluckerson

    Joined:
    Apr 10, 2020
    Posts:
    7
    Sorry if this is a stupid question (and spaghetti code) but I'm 14 and new to programming. My problem is that I can never get math inside quaternion to work.

    Here's an example:
    bullet rotation is the rotation in which I want the bullet to come out of the gun.
    it works normally until I try to do math in it.
    *this is not the full script. I removed parts that don't have to do with the problem

    Code (CSharp):
    1.  
    2.  
    3. public GameObject player;
    4.  
    5.     // shooting
    6.     private float TimeTillNextShot;
    7.  
    8.     // AssaultRifle
    9.     public GameObject AssaultRifle;
    10.     public GameObject AssaultRifleProjectile;
    11.     public float AssaultRifleProjectileSpeed;
    12.     public float AssaultRifleShootDelay;
    13.     public float AssaultRifleSpread;
    14.     public float AssaultRifleDamage;
    15.  
    16. private void Update()
    17.     {
    18.  
    19.         if (TimeTillNextShot > 0)
    20.         {
    21.             TimeTillNextShot -= Time.deltaTime;
    22.         }
    23.  
    24.         if (TimeTillNextShot < 0)
    25.         {
    26.             TimeTillNextShot = 0;
    27.         }
    28.  
    29.         // shooting
    30.         if (Input.GetMouseButton(0) && weaponEquiped == 2 && TimeTillNextShot == 0)
    31.         {
    32.             AssaultRifleShoot();
    33.         }
    34.  
    35. void AssaultRifleShoot()
    36.     {
    37.         var nextShotSpread = Random.Range(-AssaultRifleSpread, AssaultRifleSpread);
    38.         var bulletRotation = new Quaternion(player.transform.rotation.x, player.transform.rotation.y, player.transform.rotation.z, player.transform.rotation.w);
    39.  
    40.         Instantiate(AssaultRifleProjectile, transform.position, bulletRotation);
    41.  
    42.         TimeTillNextShot = AssaultRifleShootDelay;
    43.     }
    44.     }


    here's it with spread added

    Code (CSharp):
    1. var bulletRotation = new Quaternion(player.transform.rotation.x, player.transform.rotation.y + nextShotSpread, player.transform.rotation.z, player.transform.rotation.w);
    *note the + nextShotSpread



    As you can see the player always shoots downwards

    I also tried it with
    Code (CSharp):
    1. Instantiate(AssaultRifleProjectile, transform.position, Quaternion.Euler(player.transform.rotation.x, player.transform.rotation.y + nextShotSpread, player.transform.rotation.z));
    but I does the same thing but always shoots upwards.
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,735
    Your problem is that you don't understand Quaternions. That's ok. Most people don't. the x/y/z/w components of a quaternion are not angles and cannot be simply treated and manipulated as such. It's better to forget about individual components of Quaternions and use the Quaternion API to do what you need. First off, replace this monstrosity:
    Code (CSharp):
    1. var bulletRotation = new Quaternion(player.transform.rotation.x, player.transform.rotation.y, player.transform.rotation.z, player.transform.rotation.w);
    With this equivalent but much more readable code:
    Code (CSharp):
    1. Quaternion bulletRotation = player.transform.rotation;
    Next, if you want to add some rotation on the Y axis for example, you can compose quaternions. Try this to add spread:

    Code (CSharp):
    1. Quaternion bulletRotation = player.transform.rotation;
    2. // Our spread is just a simple Y-axis-only rotation.
    3. Quaternion spreadToAdd = Quaternion.Euler(0, nextShotSpread, 0);
    4. // Using the '*' operator we can"compose" quaternions.
    5. bulletRotation = bulletRotation * spreadToAdd;
    6.  
    7. Instantiate(AssaultRifleProjectile, transform.position, bulletRotation);
     
  3. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,748
    The Quaternion class is designed so that you never have to interact with its base components, and if you're trying to interact with WXYZ in any way and don't have a math degree, you're almost certainly going about using Quaternions the wrong way.

    In your last code sample you've also mixed up Euler angles with Quaternion values, which are not compatible at all.

    Video with more detailed stuff about rotations

    So don't ever touch any Quaternion's WXYZ components. Instead, use the many helper functions in Quaternion as well as the * operator to combine them as needed. Something like:
    Code (csharp):
    1. Quaternion bulletRotation = player.transform.rotation * Quaternion.AngleAxis(nextShotSpread, Vector3.up);
     
    Kurt-Dekker and PraetorBlue like this.
  4. McGluckerson

    McGluckerson

    Joined:
    Apr 10, 2020
    Posts:
    7
    Thank you! i was stuck on this problem for a very long time.