I'm creating a shmup type game and trying to implement the enemy wave system, which includes the associated enemy behaviours like their movement and bullet patterns. I've tried to separate the concerns of each aspect through classes and then eventually linking them together like so (part psuedo code to remove irrelevant pieces) :
Class WaveInstantiate {
private EnemyMovement enemymovement;
public void MakeEnemy(Vector3 pos, Quaternion rot, Enemy.EnemyMove behaviour) {
GameObject enemy = Instantiate (ship, pos, rot) as GameObject;
enemy.GetComponent<Enemy> ().moveEnemy = behaviour;
}
public IEnumerator Wave1() {
// Set spawn spots and no. of enemies
// Make the calls to: MakeEnemy(pos, rot, enemymovement.SomeFunctionToMoveEnemy)
}
public IEnumerator Wave2() // etc
}
WaveInstantiate defines the objects that are in the waves. Waves are determined by a GameController keeping track of the waves. There is a MakeEnemy
function that will Instantiate
the enemy and apply the movement behaviour that is given from the wave
Class Enemy {
public delegate IEnumerator EnemyMove(Enemy enemy);
public EnemyMove moveEnemy;
void Start() {
// Do some setup
StartCoroutine(moveEnemy (this));
}
}
The Enemy class is the main class that encapsulates all of the things necessary for the Enemy to function. Will start a coroutine that runs the function that was given to moveEnemy
delegate by the waveinstantiate class.
Class EnemyMovement {
private BulletPatterns bulletpatterns;
public IEnumerator ForwardThenBack(Enemy enemy) {
// Move object forward
yield return StartCoroutine(bulletpatterns.BulletsAtPlayer (enemy.rigidbody.position));
// Move object back
}
// Define some other movements here also
}
A class that defines all of the movement behaviour and controls when the ship will shoot. Makes a call to a BulletPatterns function to determine what kind of bullet pattern this. These functions are used as the delegate implementations for moveEnemy
Class BulletPatterns {
private GameObject playerShip;
public IEnumerator BulletsAtPlayer(Vector3 spawnPos) {
// Create some bullets that are being aimed at the player
}
}
Defines various types of bullet patterns that may be called independently.
Now my implementation of this structure works, but the problem is when i want to tweak certain movement behaviours. It's natural to parametrise certain things you want to tweak and let the caller handle the values, but since the movement is defined by a delegate, it becomes difficult when certain movement behaviour functions need different parameters to tweak that behaviour. But delegates also provide a nice separation and abstraction away from the movement of any particular enemy; if you want to make some new movement behaviour you can just create a function that handles it and pass in that function to the delegate that handles moving.
Is using delegates like this salvageable in any way or does it require another way of thinking about this?
MonoBehaviour
for movement and bullet patterns will allow you to tweak everything within the editor. \$\endgroup\$