Touch input rotation help

HI,

i’ve made a game where you are a stionairy object in the middle of the screen and you fire projectiles out towards monsters, the game works fine with a mouse but i am not trying to port it to mobile and am having some issue with the rotation of the projectiles. I initially made them by having a separate object underneath the player sprite which rotated with the position of the mouse to shoot projectiles towards the mouse. When using touch it seems like the rotation is always that of the previous click rather than the current click, ie. if i click on the right once then on the left twice, the game will fire the first projectile down (i’m assuming this is default position) then one right then one left. It seems to me that the firePoint rotation is being updated after the projectile is Instantiated. does anyone happen to know how to fix this?

Below is the rotation script on the object under the play sprite, i have modified this a little since trying to port to mobile:

using UnityEngine;

public class FirePointRotation : MonoBehaviour
{

  public Rigidbody2D rb; 
  public Camera cam;
  Vector2 mousePos;

    // Update is called once per frame
    void Update()
    {
      if (Input.touchCount >0)
      {
       Touch touch = Input.GetTouch(0);
       Debug.Log(touch.position);
       Vector2 touchPos = touch.position;
       mousePos = cam.ScreenToWorldPoint(touchPos);

       Vector2 lookDir = mousePos - rb.position;
       float angle = Mathf.Atan2(lookDir.y, lookDir.x) * Mathf.Rad2Deg - 270f;
       rb.rotation = angle;
      }
    }

Below is the shoot script, which listens for a touch and calls the Shoot() function, which in turn instantiates the bullet with rotation from previous script:

using System.Collections.Generic;
using UnityEngine;

public class FireShoot : MonoBehaviour
{
    public Transform firePoint;
    public GameObject bulletPrefab;
    public float bulletForce = 20f;
    
   
    void Update()
    {
       
       for (int i = 0; i <Input.touchCount; i++){
         if(Input.GetTouch(i).phase == TouchPhase.Began)
        {
                Shoot();
        }
       }
    }


    void Shoot()
    {
        GameObject bullet = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
        Rigidbody2D rb =  bullet.GetComponent<Rigidbody2D>();
        rb.AddForce(firePoint.up * bulletForce * -1, ForceMode2D.Impulse);
    }
}

Any help would be appreciated, let me know if more information is needed

@michaellopes98

Try doing the rotation in Shoot instead? That is what I would do…

Now it is running in another script’s Update.

There is no guarantee which one runs first AFAIK.

1 Like

thanks for the reply, i tried what you mentioned above, but i still seem to be having the same issue where it appears that the shot is firing from the previous rotation.

Below is the script all together now with rotation being called before shoot but it still doesn’ seem to work

using System.Collections.Generic;
using UnityEngine;

public class FireShoot : MonoBehaviour
{
    public Rigidbody2D rb; 
    public Camera cam;
    Vector2 mousePos;

    public Transform firePoint;
    public GameObject bulletPrefab;
    public float bulletForce = 20f;
 
   
    void Update()
    {
       // cam.ScreenToWorldPoint(Input.mousePosition);
       for (int i = 0; i <Input.touchCount; i++){
         if(Input.GetTouch(i).phase == TouchPhase.Began)
        {
           
            rotateShot();
            Shoot();
        }
       }
    }


    void Shoot()
    {
        GameObject bullet = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
        Rigidbody2D rb =  bullet.GetComponent<Rigidbody2D>();
        rb.AddForce(firePoint.up * bulletForce * -1, ForceMode2D.Impulse);
    }

    void rotateShot()
    {
            Touch touch = Input.GetTouch(0);
            Vector2 touchPos = touch.position;
            mousePos = cam.ScreenToWorldPoint(touchPos);
            Vector2 lookDir = mousePos - rb.position;
            float angle = Mathf.Atan2(lookDir.y, lookDir.x) * Mathf.Rad2Deg - 270f;
            rb.rotation = angle;
    }
}

I notice that you are setting the rotation of a RigidBody2D but then firing the shot using the rotation of the Transform. I don’t use physics much, but it wouldn’t surprise me if there’s a delay between when you rotate the RigidBody and when that affects the Transform.

Since you’re already calculating the angle you want to fire at, could you just fire at the angle you calculated, instead of reading an angle out of the Transform?

Also: Your Update function is looping over ALL touches, but your angle calculation ONLY looks at touch #0. That suggests that if I touch with multiple fingers, it will fire as many shots as I use fingers, but they’ll all point towards the first finger. Is that what you want?

3 Likes

thanks for the suggestion, i did think this might work but unfortunately i am met with this error

Assets\Scripts\FireShoot.cs(39,75): error CS1503: Argument 3: cannot convert from ‘float’ to ‘UnityEngine.Quaternion’

I’m not really sure how i would go about converting this to something usable value, so any idea? i think this solution is close.

snipped of what the script looks like now:

using UnityEngine;

public class FireShoot : MonoBehaviour
{
    public Rigidbody2D rb; 
    public Camera cam;
    Vector2 mousePos;

    public Transform firePoint;
    public GameObject bulletPrefab;
    public float bulletForce = 20f;
 
   
    void Update()
    {
       for (int i = 0; i <Input.touchCount; i++){
         if(Input.GetTouch(i).phase == TouchPhase.Began)
        {
           
           // rotateShot();
            Shoot();
        }
       }
    }


    void Shoot()
    {
        Touch touch = Input.GetTouch(0);
        Vector2 touchPos = touch.position;
        mousePos = cam.ScreenToWorldPoint(touchPos);
        Vector2 lookDir = mousePos - rb.position;
        float angle = Mathf.Atan2(lookDir.y, lookDir.x) * Mathf.Rad2Deg - 270f;
        rb.rotation = angle;  
        GameObject bullet = Instantiate(bulletPrefab, firePoint.position, angle);
        Rigidbody2D rbb =  bullet.GetComponent<Rigidbody2D>();
        rbb.AddForce(firePoint.up * bulletForce * -1, ForceMode2D.Impulse);
    }

Use Quaternion.AngleAxis or maybe Quaternion.LookRotation to convert your angle into a Quaternion.

1 Like

hmm i tried this with the following snippet for my bullet instantiation:

        Touch touch = Input.GetTouch(0);
        Vector2 touchPos = touch.position;
        mousePos = cam.ScreenToWorldPoint(touchPos);
        Vector2 lookDir = mousePos - rb.position;
        float angle = Mathf.Atan2(lookDir.y, lookDir.x) * Mathf.Rad2Deg - 270f;
        rb.rotation = angle;
        GameObject bullet = Instantiate(bulletPrefab, firePoint.position,Quaternion.LookRotation(angle, Vector3.zero));

but it didn’t seem to work, there was still the one click delay and it also had an odd side effect of no longer rotating the bullet prefabs in the direction they were being fired at, not really sure where to go from here. i think the issue is definitely something to do with the fact that it needs to recognise where a new click is and snap the rotation to it as it was working when using get input from a mouse.

Um. I’m surprised that even compiled. You shouldn’t pass an angle directly to LookRotation, it wants a direction vector; and you shouldn’t pass zero as the second parameter, because that also wants a direction and the zero-vector is not a valid direction. The first argument should be a vector pointing in the direction you want to look, and the second should be a vector pointing in the direction of “up” (because if you don’t control this, you could end up looking in the correct direction but upside-down or something). This is explained (with an example) on that page I linked.

Either of those functions I linked is going to require you make some adjustments based on which two axes you are using for your 2D plane and which of its own local axes your bullet is pointing towards relative to which local axis your turret points along, so I can’t just give you a code snippet that will reliably work without knowing all those details. You’re going to actually have to read what they do and think about how they apply to your circumstances.

you are indeed correct, that probably woudn’t have compiled i pasted the wrong snippet (was trying the other link you had sent) below was what i had actually done:

Touch touch = Input.GetTouch(0);
        Vector2 touchPos = touch.position;
        mousePos = cam.ScreenToWorldPoint(touchPos);
        Vector2 lookDir = mousePos - rb.position;
        float angle = Mathf.Atan2(lookDir.y, lookDir.x) * Mathf.Rad2Deg - 270f;
        rb.rotation = angle;
        GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.AngleAxis(angle, Vector3.zero));

This compiled but caused the issues i mentioned above, i think it is okay to pass angle directly into Quaternion.AngleAxis (correct me if i’m wrong please). I had also tried using Vector3.up but this was doing weird things to the prefab (probably due to the local axis thing you were talking about). Using Vector3.zero fixed that issue but they still all face the same direction even when being shot at different angles(image of this below). Still the issue isnt so much that the prefabs face the wrong way its just that they are always shooting one click behind the current direction. I thought, like you suggested, that it might have just been a case of there being a delay between the rotation of the rigid body and the transformation actually being updated, but seeing as i have now been able to compile and run a script where we are directly using the angle as the rotation i’m no longer sure this is in fact the problem.

6402856--714361--upload_2020-10-9_23-2-7.png

if you have any other ideas even if they are a long shot i’d be glad to try them out as well, all part of the learning process i guess.

You can indeed pass an angle directly to AngleAxis, but using Vector3.zero as the second argument defeats the purpose of the call. The second argument is the axis that you want to rotate around.

In a typical 3D game, the ground would be on the XZ plane, with the Y axis being “up”, and so rotating around the Y axis is the most common thing you’d do (though certainly not the only thing). In a 2D game, it’s quite possible your game is taking place on the XY plane rather than the XZ plane, in which case you probably want to rotate around the Z axis (Vector3.forward).

Regarding your other problem: your recent code snippet only shows you using the angle to determine what direction the bullet should look when you spawn it. You need to make sure that your AddForce call is also adding force in the direction you want it to move. Try using the bullet’s up instead of the turret’s up.

1 Like

Thanks for explaining the planes stuff, it’s clicked now i did end up using Vector3(0f,0f,1f), just found it easier to understand than Vector3.forward for future reference.

As for the second part of your comment… THANK YOU, I have been thinking about this all day and it never occurred to me, “the projectiles are facing the correct way why not just apply a force in that direction”. This was the solution, they now fire in the correct angle and rotation (maybe a little too fast but i can always tone down the force im applying.)

For reference this is what the working snippet looks like:

using UnityEngine;

public class FireShoot : MonoBehaviour
{
    public Rigidbody2D rb; 
    public Camera cam;
    Vector2 mousePos;

    public Transform firePoint;
    public GameObject bulletPrefab;
    public float bulletForce = 20f;
    public GameObject campFirePos;
 
   
    void Update()
    {
       for (int i = 0; i <Input.touchCount; i++){
         if(Input.GetTouch(i).phase == TouchPhase.Began)
        {
           
            Shoot();
        }
       }
    }


    void Shoot()
    {
        Touch touch = Input.GetTouch(0);
        Vector2 touchPos = touch.position;
        mousePos = cam.ScreenToWorldPoint(touchPos);
        Vector2 lookDir = mousePos - rb.position;
        float angle = Mathf.Atan2(lookDir.y, lookDir.x) * Mathf.Rad2Deg - 270f;
        rb.rotation = angle;  
        GameObject bullet = Instantiate(bulletPrefab, firePoint.position,
         Quaternion.AngleAxis(angle, new Vector3(0f,0f,1f)));
        Rigidbody2D rbb =  bullet.GetComponent<Rigidbody2D>();
        rbb.AddForce(lookDir * bulletForce * 1, ForceMode2D.Impulse);
    }