Chorche
Chorche

Reputation: 403

Unity, Changing the rotation of a child object. Cannot access the child object

I'm designing the logic for aiming and shooting from the player in a 2D game. When the user clicks and drags on the screen, it is supposed to rotate around the sprite a line formed by 3 dots with the same angle as the player dragged so he can aim. Like in a bubble shooter.

The "Player" prefab has a child Object called "Sight" which is a transform with 4 dots sprites as child objects with their corresponding offsets, so rotating "Sight" will rotate the whole line.

enter image description here

In each Update loop I look, for the sight to rotate it according to the mouse position but it seems that the variable sight is null. I tried to get the object by tag, and having a public variable so I can assign the prefab from the inspector. Also I tried to grab only the transform component. I'm sure that is a pretty dumb issue but I can't see it. I would appreciate any help.

Aiming logic:

public class PlayerShooting : MonoBehaviour {

    public GameObject BulletPrefab;
    public float coolDownTimer = 0.0f;
    public float aimDistance = 0.5f;

    private Vector2 originPoint = new Vector2 (0f,0f);
    private Vector2 endPoint = new Vector2(0f,0f);

    private bool dragging = false;
    public GameObject sight;

    // Update is called once per frame
    void Update () {

        // TOUCH DOWN
        if (Input.GetMouseButtonDown(0) == true) {
            originPoint = getPointInWorld(Input.mousePosition);
            endPoint = originPoint;
            dragging = true;
        }

        // TOUCH UP
        if (Input.GetMouseButtonUp(0) == true) {
            endPoint = getPointInWorld(Input.mousePosition);
            dragging = false;
            FireBullet();
        }

        // DRAGGING
        if (dragging) {


            endPoint = getPointInWorld(Input.mousePosition);
            // AIM
            // !!!: THIS IS THE ISSUE
            sight = GameObject.Find("Player/Sight");
            if (sight != null && endPoint != originPoint) {
                Vector2 dir = originPoint - endPoint;
                dir.Normalize();    
                float zAngle = Mathf.Atan2 (dir.y,dir.x) * Mathf.Rad2Deg - 90;
                Quaternion newRotation = Quaternion.Euler(0,0,zAngle);
                sight.transform.rotation = newRotation;
            }
            else
            {
                Debug.Log("Error: cannot aim because Sight was not instantiated");
            }
        }
    }

    void FireBullet (){

    }

    Vector2 getPointInWorld (Vector3 point){
        Vector2 pos = Camera.main.ScreenToWorldPoint(point);
        return pos;
    }
}

Upvotes: 0

Views: 2644

Answers (2)

Chorche
Chorche

Reputation: 403

After taking a step back it seems that the no object could be assigned to the variable sight because the scene file was corrupt. It was clonned from a Bitbucket repository. The original approach Works fine and the instruction

sight = GameObject.Find("Player/Sight");

is able to find the object. I decided to assign it from the script and not from the editor so I can enable and disable the sight if the player is shooting or not.

Upvotes: 0

RaidenF
RaidenF

Reputation: 3521

Ok, you have multiple unrecommended practices in your script. As you said, you should prefer FindObjectWithTag, and even better, assign it through the inspector. In order to do that, do the following: create a public variable (or a private with the [Serializable] attribute) called sightReference. Then save your script. After having attached it to your player, drag "Sight" to sightReference, in the script in your inspector (having selected the player object, you should see an empty field if you have expanded the script); currently, it says None (Game Object) in the picture that you posted, drag sight there.

In addition, in order to detect dragging, you should use events, not infer it through Input. If you want a great tutorial on events, watch this one (you can watch it from the beginning, but what you need starts around that time). Either implement the interfaces (I would recommend that, even if it seems hard at the beginning it's easy), or use the event trigger component in unity.

Old answer:

Ok, let's say that you want the line to always be 3 dots. What I would do, is create a sprite of 3 dots. This way, it's just one object (you can do it with 3 separate sprites, but it could get more complicated, so for simplicity's sake I'll go with this assumption - you can change it later).

Make sure that the pivot point of the aiming line sprite, is at the bottom middle of the sprite (you can look it up online, setting the pivot is in the sprite editor). You could also leave it at the middle, and create a parent object that is at the bottom middle, and use this as a pivot, but again, simplicity for now.

Now you have an object with that sprite, that rotates around its pivot point, which is on its bottom middle, so it works like a clock hand. Try changing the rotation in the inspector to verify that.

Now, make the parent of this aiming line, be the player, so that it follows the player around. Adjust its position, so that it doesn't overlap with the player.

Now, write a script and attach it to the aiming line. In C#:

void Update()
{
    Vector3 mouseWorldPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    Vector3 lineWorldPosition = this.transform.parent.position;
    Vector3 lookAtVector = mouseWorldPosition - lineWorldPosition;
    float angle = Mathf.Atan2(lookAtVector.x, lookAtVector.y) * Mathf.Rad2Deg;
    this.transform.localRotation = Quaternion.Euler(0, 0, -angle);
}

This assumes that with rotation 0,0,0 the aiming line, aligns with the Y axis (it points upwards).

set it up like this

Upvotes: 1

Related Questions