0
\$\begingroup\$

I want to make an enemy spawner with random rotation left and right in unity. Its an 2d platformer. The random rotation is working, but the enemy is not moving after it's spawned.

I put the rigidbody of the enemy in the value enemiesrb in the script EnemySpawn and gave it an .velocity = new Vector.

I have 2 scripts

EnemyPatrol:

public float moveSpeed;
public bool moveRight;

public Transform wallCheck;
public float wallCheckRadius;
public LayerMask whatIsWall;
private bool hitWall;

public float lockPos = 0;

// Use this for initialization
void Start () {

}

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

    hitWall = Physics2D.OverlapCircle(wallCheck.position, wallCheckRadius, whatIsWall);


    if (hitWall)
    {
        moveRight = !moveRight;
    }


    if (moveRight)
    {
        transform.localScale = new Vector3(-1f, 1f, 1f);
        //GetComponent<Rigidbody2D>().velocity = new Vector2(moveSpeed, GetComponent<Rigidbody2D>().velocity.y);
    }
    else
    {
        transform.localScale = new Vector3(1f, 1f, 1f);
        //GetComponent<Rigidbody2D>().velocity = new Vector2(-moveSpeed, GetComponent<Rigidbody2D>().velocity.y);
    }

and EnemySpawn

public GameObject enemies;
private Rigidbody2D enemiesrb;

public float spawnTime;
public int maxEnemies;
public int lofr;

private int amount;

public float moveSpeed;
void Start()
{
    enemiesrb = enemies.GetComponent<Rigidbody2D>();
}


void Update()
{
    //enemies = GameObject.FindGameObjectsWithTag("Enemy");
    //amount = enemies.Length;

    if (amount != maxEnemies)
        InvokeRepeating("spawnEnemy", spawnTime , spawnTime);

}

void spawnEnemy()
{
    lofr = Mathf.Abs(Random.Range(0, 1));

    if (lofr == 1)
    {
        Instantiate(enemies , transform.position, Quaternion.Euler(1,180,1));
        enemiesrb.velocity = new Vector2(moveSpeed, GetComponent<Rigidbody2D>().velocity.y);
    }
    else if(lofr == 0)
    {
        Instantiate(enemies , transform.position, Quaternion.Euler(1,1,1));
        enemiesrb.velocity = new Vector2(-moveSpeed, GetComponent<Rigidbody2D>().velocity.y);
    }
    CancelInvoke();
}

the lofr means left or right. it defined if it need to spawn left or right

\$\endgroup\$
  • \$\begingroup\$ As an aside, you say your random rotation script is working, but I'd find that very surprising given how it's written. If it's using the float version of Random.Range, then lofr == 0 will trigger with very low probability, and lofr == 1 will never be hit at all. If it's using the int version, then lofr == 0 will be hit every time. You may want instead if(Random.value < 0.5f) { } else { } which will give an approximately fair coin flip. \$\endgroup\$ – DMGregory Nov 29 '15 at 14:41
1
\$\begingroup\$

This is a common misconception about how Instantiate() works in Unity.

Say my spawning script has an inspector property:

public GameObject enemyPrefab;

Then later I create an instance of the enemy and customize it somehow:

Instantiate(enemyPrefab);
enemyPrefab.GetComponent<SomeComponent>().DoSomething();

That second line there is acting on the enemy prefab - not the instance I just spawned.

Since you define enemiesrb in your spawning script's Start() it has this same problem - it's referring to the Rigidbody2D on the source enemy you assigned in the inspector, not the one you just spawned.

But Instantiate() returns a reference to the instance it just created, so we can use that (we just need to cast it because it returns a very generic type):

GameObject spawnedEnemy = (GameObject)Instantiate(enemyPrefab);
Rigidbody2D spawnedEnemyRB = spawnedEnemy.GetComponent<Rigidbody2D>();
...

This correctly points at the Rigidbody2D attached to the spawned instance, not the one read from the prefab in Start().


One last trick: if your spawning script requires enemies to have a Rigidbody, it can be helpful to specify that, so you can never accidentally assign an incompatible prefab in the Inspector and cause null reference exceptions at runtime when the script tries to use a component that isn't there.

So instead of defining our enemyPrefab as any old GameObject, we can say it must have a Rigidbody2D:

public Rigidbody2D enemyPrefab;

Now the inspector will only accept enemyPrefabs that have a Rigidbody2D, and Instantiate() will return a reference to the spawned Rigidbody2D directly:

Rigidbody2D spawnedEnemyRB = (Rigidbody2D)Instantiate(enemyPrefab);

This still spawns the whole enemy with all its nested structure and other components intact, it just signals "this is the important component - make sure to give me a valid reference to this" and saves you a GetComponent<>() step.

\$\endgroup\$
  • \$\begingroup\$ I didn't know your "Last trick", that seems useful. +1 \$\endgroup\$ – S. Tarık Çetin Nov 29 '15 at 14:42
  • \$\begingroup\$ @SamedTarıkÇETİN It's something I'd love to see in more tutorials, because it really helps make scripts more robust and clear for other people to use (or myself, months after writing it when I've forgotten how it works). ;) \$\endgroup\$ – DMGregory Nov 29 '15 at 14:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.