transform.position not updating the position of all my objects.

So I’ve been trying to recreate the classic space invaders as a way to practice game development but now I have run up against a problem that I do not fully understand why it is happening and would very much appreciate if some one could take a look at this.

My main problem resides in my EnemyMovement Script. In this script, I control in which direction the invaders move and at what rate.

using UnityEngine;

public class EnemyMovement : MonoBehaviour
{
    private Animator invaderAnimator;

    private static   Vector3 invaderHorizontalVelocity = new Vector3(0.1f, 0, 0); // needs to be static so that every invader X position gets updated accordingly.
    private readonly Vector3 INVADER_VERTICAL_SPEED = new Vector3(0, -0.1f, 0);

    public readonly string[] INVADER_TRIGGERS = {"GoToMovement2", "GoToMovement1"};

    private float timePassed = 0.0f;
    private float movementDelay = 1.0f;

    private int animationIndex;

    private static bool collidedWithWall = false; // needs to be static so that every invader is aware that we have hit a wall.

    void Start()
    {
        invaderAnimator = GetComponent<Animator>();
        animationIndex = 0;
    }

    void Update()
    {
        timePassed += Time.deltaTime;

        if (canMove() && !isDead())
        {
            setAnimationTrigger();

            if (!collidedWithWall)
                transform.position += invaderHorizontalVelocity;
            else
            {
                transform.position += INVADER_VERTICAL_SPEED;
                invaderHorizontalVelocity.x = -invaderHorizontalVelocity.x;
                collidedWithWall = false;
            }

            timePassed = 0.0f;
        }
    }

    private bool canMove()
    {
        return timePassed > movementDelay;
    }

    private bool isDead()
    {
        return invaderAnimator.GetBool("InvaderDies");
    }

    private void setAnimationTrigger()
    {
        invaderAnimator.SetTrigger(INVADER_TRIGGERS[animationIndex]);
        animationIndex++;
        animationIndex %= INVADER_TRIGGERS.Length;
    }

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "WestWall" || collision.gameObject.tag == "EastWall")
        {
            collidedWithWall = true;
        }
    }
}

In the main game scene, I have two colliders with the tag East and West Walls that are used to detected if one of my invaders have hit a wall. This also means that each invader has its own rigidbody2D and box2D collider with kinematic mode and isTrigger mode enabled.

Now, everything works great up until the point where either the right most or left most invaders collide with the wall. What happens in my game scene is this line code

transform.position += INVADER_VERTICAL_SPEED;

only updates one of my invaders(This is out of the 55 invaders that I have active); particularly, the top left most invader. Why only one of them? I do not know why exactly. What I do know is that once I added this few lines of code, then all the invaders Y position got updated.

            if (!collidedWithWall)
                transform.position += invaderHorizontalVelocity;
            else
            {
                moveDown()
                invaderHorizontalVelocity.x = -invaderHorizontalVelocity.x;
                collidedWithWall = false;
            }
           ......

          private void moveDown()
          {
              
            GameObject[] invaderArmy = GameObject.FindGameObjectsWithTag("Invader");

             foreach (GameObject invader in invaderArmy)
             {
                 if(invader.activeInHierarchy)
                     invader.transform.position += INVADER_VERTICAL_SPEED
             }
          }

Which I dont want to do because I going to be constantly doing a find each time one of the invaders collide with a wall and that is unnecessary.

The unity version that I’m running is 5.5.2f1. Just in case you were wondering.

So if you know why it is only updating one of my invaders in the Y position when one of the invaders collide with the wall, please post your answer because I’ve tried everything I could think of.

collidedWithWall is a static boolean, meaning it is shared between all of the instances of your script.

When an invader hits a wall, collidedWithWall becomes true.
Then we start moving the invaders.
For the first invader, collidedWithWall is true, so we move vertically. Then we set collidedWithWall to false.
For the second invader… collidedWithWall is false! So we move horizontally.
So on and so forth.

The easiest way to fix it (though certainly not the cleanest!) is to reset collidedWithWall in LateUpdate rather than Update. That way all of your invaders will have a chance to cycle through once with a true value before it gets swapped back to false.

Thanks for the reply Daniel. Your explanation makes sense but alas it did not work for me. Maybe I applied your advice wrongly so here is how I did it. If you need further information just let me know.

void Update()
    {
        timePassed += Time.deltaTime;

        if (canMove() && !isDead())
        {
            setAnimationTrigger();

            if (!collidedWithWall)
                transform.position += invaderHorizontalVelocity;

            timePassed = 0.0f;
        }
    }

    void LateUpdate()
    {
        if (collidedWithWall)
        {
            transform.position += INVADER_VERTICAL_SPEED;
            invaderHorizontalVelocity.x = -invaderHorizontalVelocity.x;
            collidedWithWall = false;
        }
    }

LateUpdate should only reset collidedWithWall to false and invert the horizontalVelocity if collidedWithWall is true. The movement should still take place in Update.

Well now the problem is that none of them are moving down. Here is how it looks as of now.

    void Update()
    {
        timePassed += Time.deltaTime;

        if (canMove() && !isDead())
        {
            setAnimationTrigger();

            if (!collidedWithWall)
                transform.position += invaderHorizontalVelocity;
            else
                transform.position += INVADER_VERTICAL_VELOCITY;

            timePassed = 0.0f;
        }
    }

    void LateUpdate()
    {
        if (collidedWithWall)
        {
            invaderHorizontalVelocity.x = -invaderHorizontalVelocity.x;
            collidedWithWall = false;
        }
    }

Maybe I can I use a coroutine that waits x seconds until they have all move down. Not sure if that would work though.

I set up a quick test with your script and it worked fine.

Well the question is how did you test it? Did you create one game object that was the parent of all 55 invader objects and associated this script with just the parent object? Or did you associate this script with each invader object and have one invader notify the rest that we have hit a wall(using the OnTriggerEnter2D function of course)? More than likely how we are setting everything up is was causing this difference in our results. So if you need further information in how I’m doing everything, please let me know.

I didn’t do anything special, the only objects in the scene were:
camera
light
invaders x3
walls x2

No parenting, the script applied to all 3 invaders.

And were the 3 invader horizontally or vertically aligned? Because I tried both scenarios with 3 invaders and in both instances, they only moved left and right never down. So I don’t know. Maybe there is an easier way to alert every invader to move downwards.

Horizontally, but it shouldn’t matter.

A cleaner way to manage it is to not have static variables and instead have an EnemyManager that has references to all enemies, and each enemy has a reference to the EnemyManager.

Then, when one enemy hits a wall it tells the manager. The manager then moves all the enemies down and flips their movement direction.

That seems like a good idea. I’ll try that.

Hey Daniel, So going back to that idea you gave me.

So the way I been thinking about implementing your idea is by

1.) Creating an empty object called EnemyController
2.) Create a script called EnemyController and add that script to the object EnemyController
3.) In the EnemyController script, I will at startup do Gameobject [ ] invaderArmy = Gameobject.FindObjectsWithTag(“Invader”) So now EnemyController has references to every single invader.
4.) In the EnemyMovement script, remove all the code that controls the direction in which the invader moves, the animation triggers and the signaling of all the invaders to go down and move all of that code to the EnemyController script. That way the only thing that the invaders have to worry about is moving. At least in that script.
5.) Finally, in the EnemyMovement script I will add public GameObject EnemyController so that each invader has a reference to the EnemyController Object.

How is that?

Sounds good!

One nitpick is that it would be better if the EnemyController had references to each EnemyMovement script rather than the GameObject it is attached to.

Could you explain why that would be better? If I make each invader a child to the enemy controller object, all I will need to do is move the transform component of the enemy controller and then each invader will automatically move after that.

While you’re at it, would this be the best to find all objects with the script I’m looking for

invaderMovements = (EnemyMovement[])Resources.FindObjectsOfTypeAll(typeof(EnemyMovement));

Where EnemyMovement is the script and invaderMovements is of type EnemyMovements [ ]. So it should be getting all objects that are associated with that script.

So here is what I got. Tell if there is anything else I can do to improve it.

EnemyController Script

using UnityEngine;

public class EnemyController : MonoBehaviour
{
    public Vector3 invaderHorizontalVelocity = new Vector3(0.1f, 0, 0);
    public readonly Vector3 INVADER_VERTICAL_VELOCITY = new Vector3(0, -0.1f, 0);
    private bool collidedWithWall = false;

    void Start()
    {
        GameObject[] invaders = GameObject.FindGameObjectsWithTag("Invader");

        foreach (GameObject invader in invaders)
        {
            invader.transform.parent = transform;
        }
    }

    public void moveDown()
    {
        transform.position += INVADER_VERTICAL_VELOCITY;
    }

    public void setCollidingWithWall(bool wallCollision)
    {
        collidedWithWall = wallCollision;
    }

    public bool isCollidingWithWall()
    {
        return collidedWithWall;
    }

    public void changeHorizontalDirection()
    {
        invaderHorizontalVelocity.x = -invaderHorizontalVelocity.x;
    }
}

EnemyMovement Script

using UnityEngine;

public class EnemyMovement : MonoBehaviour
{
    private EnemyController enemyController;

    private Animator invaderAnimator;

    public readonly string[] INVADER_TRIGGERS = {"GoToMovement2", "GoToMovement1"};

    private float timePassed = 0.0f;
    private float movementDelay = 1.0f;

    private int animationIndex;

    void Start()
    {
        GameObject enemyControllerObject = GameObject.FindWithTag("EnemyController");

        if (enemyControllerObject != null)
        {
            enemyController = enemyControllerObject.GetComponent<EnemyController>();
        }

        if (enemyControllerObject == null)
        {
            Debug.Log("Could not find EnemyController Object.");
        }

        invaderAnimator = GetComponent<Animator>();
        animationIndex = 0;
    }

    void Update()
    {
        timePassed += Time.deltaTime;
        if (canMove() && !isDead())
        {
            setAnimationTrigger();

           
            if(!enemyController.isCollidingWithWall())
                transform.position += enemyController.invaderHorizontalVelocity;
            else
            {
                enemyController.moveDown();
                enemyController.changeHorizontalDirection();
                transform.position += enemyController.invaderHorizontalVelocity;
                enemyController.setCollidingWithWall(false);
            }
            timePassed = 0.0f;
        }
    }

    private bool canMove()
    {
        return timePassed > movementDelay;
    }

    private bool isDead()
    {
        return invaderAnimator.GetBool("InvaderDies");
    }

    private void setAnimationTrigger()
    {
        invaderAnimator.SetTrigger(INVADER_TRIGGERS[animationIndex]);
        animationIndex++;
        animationIndex %= INVADER_TRIGGERS.Length;
    }

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "WestWall" || collision.gameObject.tag == "EastWall")
        {
            enemyController.setCollidingWithWall(true);
        }
    }
}