2
\$\begingroup\$

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?

\$\endgroup\$
  • \$\begingroup\$ I general delegates have the advantage to be type safe and faster than Unity's message system. In your case having MonoBehaviour for movement and bullet patterns will allow you to tweak everything within the editor. \$\endgroup\$ – aggsol Nov 26 '14 at 8:04
1
\$\begingroup\$

What I've found more useful in Unity is to really take advantage of editor, and realise the fact that you're working towards a solution that is drag and drop able rather than flexible in code.

An enemy movement in Unity could be represented by a single method, but I believe it would be much better, and much more to the style of Unity to have separate components for these different movements. Where you would pass a delegate into a method, you would drag and drop a "Movement" component.

I would start by defining a interface, so we can interface with these components in a common way:

public interface IMovable
{
    float   Speed      { get; set; }
    void    DoMove();
}

That's obviously very minimal, but it'll do for this example. This is where DoMove moves in some way by speed amount. This is really the same as the delegate you were using.

So, now we have a common interface, but now what do we do with it? Well, we make more specific components. But then we'll face the same problem with parameters as delegates had, right? Well this is where the Unity editor comes in. Let's define a simple Follow component that implements IMovable.

public class Follow : MonoBehaviour, IMovable
{
    public float      speed;
    public GameObject target;


    // Let's provide a Speed property, like we "promised" to IMovable.
    public float Speed
    {
        get
        {
            return speed;
        }
        set
        {
            speed = value;
        }
    }


    // Let's provide a DoMove method, like we also "promised" to IMovable.
    public void DoMove()
    {
        // Move towards the target, if there is one.
        if( target != null )
        {
            Vector3 normal = ( target.transform.position - transform.position ).normalized;
            transform.Translate( normal * Time.deltaTime * speed, Space.World );
        }
    }
}

As you can see here, we provide the IMovable interface, but we don't purely rely on the information given to us through it. So, how do you add a target to follow? Drag and drop it from the editor, maybe set target in some code that knows about the true implementation of the interface.

You can go crazy making all these different movement components. All you have to do is drag and drop one onto the object and you have that functionality.

\$\endgroup\$
  • \$\begingroup\$ I agree, staying within the component model of Unity. Interfaces also helps tomake enemy behaviour easy exchangable. \$\endgroup\$ – aggsol Nov 26 '14 at 8:02
  • \$\begingroup\$ This seems like a good solution. Once i get around to implementing it and it works i'll accept the answer. Will it look something like Component comp = enemy.AddComponent<SomeMovementBehaviour>();. comp.speed = 10? \$\endgroup\$ – Joe Staines Nov 26 '14 at 10:13
  • \$\begingroup\$ @JoeStaines You'll want to add and tweak components from the editor as much as possible. If you have to do it in the code, then yeah. It'll look something like that. Except not with a variable type of Component, rather SomeMovementBehaviour. \$\endgroup\$ – Ben Nov 26 '14 at 10:24
  • \$\begingroup\$ @Ben how would i go about using the editor to add when the enemies are created at run time? I will be having multiple clones of an object that will be given different components that relate to that clones movement, so im unsure how i would be able to define specific components through the editor. \$\endgroup\$ – Joe Staines Nov 26 '14 at 10:54
  • \$\begingroup\$ @JoeStaines You know about prefabs right? \$\endgroup\$ – Ben Nov 26 '14 at 11:54

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.