Introducing a second enemy movement in Unity

The exercise here demonstrates an implementation of additional enemy movement that originates from the left-end corner of the screen at an inclined angle, and upon reaching nearly halfway of the screen, it reclines to another angle to exit the window.

Before going further into this, kindly make sure that the enemy and spawn manager objects are in good standings since they are needed to add this feature here.

Here is the high-level pseudo-code.

  • Start by getting the laser prefab in the inspector, duplicate it and give both an empty game object as their parent to control both simultaneously.
  • After giving proper names, make sure that the angles are set at 45 forward rotation as the movement will be starting from this angle onward.
  • Tag them with the same tag that the enemy lasers use for distinguishing themselves from the player lasers so that their inner collisions do not destroy enemies. (We only want player enemies to affect enemies and this is the shortest config to categorize the same laser beams to different objects.)
  • Assign them the enemy script and make this laser object a prefab.
  • Declare a serialized field in the enemy script to assign the newly created laser shot as a prefab.
[SerializeField]
private GameObject _enemyAngularPrefab;
Unity

Let us make a few additions to the enemy script in form of a new coroutine and a new method to enable the second movement.

  • Create a coroutine that interacts with the current z rotation value and keeps decrementing it by 1 degree, until it hits the 0 value of the Y axes. The moment it does, change the rotation to -45.
IEnumerator ForwardAngleChange()
{

Vector3 newRotationAngles = transform.rotation.eulerAngles;
newRotationAngles.z -= 1;
yield return new WaitForSeconds(0.1f);
transform.rotation = Quaternion.Euler(newRotationAngles);

if (transform.position.y >=0 && transform.position.y <=1)
{
newRotationAngles.z = -45.0f; //Angle Changes here.
transform.rotation = Quaternion.Euler(newRotationAngles);
}
}
  • Create a new method to one, start the translate method, and two, instantiate the angular lasers we created before.
  • Also, use the coroutine created above in the new method.
public  void AngularEnemyMovement()
{
transform.Translate(Vector3.down * _speed * Time.deltaTime);
StartCoroutine(ForwardAngleChange());

if (transform.position.y < -5.3f)
{
Destroy(this.gameObject);
}

if (Time.time > _canFire)
{
_fireRate = Random.Range(3.0f, 8.0f);
_canFire = Time.time + _fireRate;
GameObject EnemyLaser = Instantiate(_enemyAngularPrefab, transform.position, Quaternion.identity);
//We are first getting hold of the prefab and then extracting its children.
Laser[] enemyLaserChild = EnemyLaser.GetComponentsInChildren<Laser>();

for (int i = 0; i < enemyLaserChild.Length; i++)
{
enemyLaserChild[i].SetEnemyLaser();
// Laser type classified here in the laser script.
}
}
}
  • Now, there is a need here to trigger two different movement methods from the update method and if it does not get addressed, the enemy movement will be a combination of two different methods as well as the speed will be doubled.
  • To take care of this concern, first declare a private int variable that can be used in a switch statement, and second, create a public method that can be called from the spawn manager.
private int _enemyType;public void EnemyType(int value)
{
_enemyType = value;
}
  • Implement a switch statement in the update method of enemy that triggers both movements upon selection.
private void Update()
{
switch (_enemyType)
{
case 0:
EnemyMovement(); // Regular Movement from top to bottom.
break;
case 1:
AngularEnemyMovement(); // Left angular movement.
break;
}

That is it for the enemy script. The next part is to call the second method from the spawn manager to loop the enemies at a controlled interval.

IEnumerator LeftAngularEnemySpawnRoutine()
{

while (_stopSpawn == false)
{

Vector3 location = new Vector3(Random.Range(-11.0f, -6.0f), 7.6f, 0);
GameObject NewEnemy = Instantiate(_enemyPrefab, location, Quaternion.AngleAxis(45,Vector3.forward));
NewEnemy.transform.parent = _enemyContainer.transform;
NewEnemy.GetComponent<Enemy>().EnemyType(1);
yield return new WaitForSeconds(5.0f);

}
}
  • Also, use the EnemyType( ) method to call the regular enemy movement in the regular routine of the enemy.

That should be enough for the second movement to occur flawlessly.

Here is the final result :)

Thank you very much

Work in Progress.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store