0

I'm actually not sure if this is possible, I have the following implementations on a turn based combat system

public abstract class State{

protected FightSystem FightSystem;

public State(FightSystem fightSystem)
{
    FightSystem = fightSystem;
}

public virtual IEnumerator Start()
{
    yield break;
}
}

public abstract class StateMachine : MonoBehaviour{

protected State State;

public void SetState (State state)
{
   State = state;
    StartCoroutine(State.Start());
}

}

Currently, the FightSystem class holds all objects and references, when it is the player's turn and the button for example the basic attack is pressed, it calls

FightSystem.SetState(new PlayerBasicAttack(FightSystem));

the behaviour is executed and it goes through a transport class to check all turns, reset positions etc. When an enemy unit has a turn, it is also called through

FightSystem.SetState(new EnemyGhostPhaseAttack(FightSystem));

I currently use a enum on the enemy unit class to keep track of which States it has in the FightSystem and a switch case to select which one to execute

My issue here is, as my project is scaling I will eventually have a switch case with 50+ states being called every turn which is not my preference. Is it it possible to store 3-4 of such states in a variable on unit prefabs and have them called? (randomly but that'd be just a random range)

Another solution I had in mind, but is not very extensible or clean, is to have a region of all the set this state methods and have the unit classes hold multiple delegates to call these.

I know this is a complex question, I usually respond quick please let me know if there's any way I can clarify things further.

This project is 2D, Thank you for your time.

Further Details: The current state selection method

public List<StateMachine.EnemyStates> statesOnThisUnit; // on the unit class

public enum EnemyStates // the states enum on the state machine class
{
    GhostAttack,
    GhostDefend,
    ...,
}

public void SelectState(EnemyStates state, FightSystem fightSystem) // on the fight system
{
    switch (state)
    {
        case EnemyStates.GhostAttack:
            SetState(new GhostAttack(fightSystem));
            break;
        case EnemyStates.GhostDefend:
            SetState(new GhostDefend(fightSystem));
            break;
        // ...
        default:
            break;
    }
    
}
  • Your setup is a bit odd to me. If you have a state `EnemyState`, I would make this an abstract base class and derive enemies from the class. Instead of using an enum `EnemyStates`, you'd just make a new script per enemy. I assume each enemy would have an attack, defend, death, etc state, so override the base. That way, you can generically call an attack function or defend function on any unit, then have it's specific derived logic run. No huge mega script with a 50+ enum, just 50 unique scripts that serve a specific purpose. – TEEBQNE Sep 14 '21 at 06:26
  • 1
    You *could* continue using a giant `switch` statement as a switch statement once large enough they are transformed into direct look-up tables meaning to find a case, should be near O(1) runtime. Calling it multiple times in a frame should not have any performance issues. Complicating the calls by grouping delegate callbacks will not speed up the calls at all and will make the code less readable. You can store delegates for specific enemies if you want though. You would just need to define the delegate signature then can store it in any data structure. – TEEBQNE Sep 14 '21 at 06:29
  • I wasn’t entirely sure about switch statement efficiency hence why I sought out to replace these current ones. Might I ask what exactly does “transformed into look up tables” mean? It’s the first time I’m designing such a system and I wanted each enemy to be entirely unique and have custom behaviour –  Sep 14 '21 at 06:39
  • 1
    A lookup table is a generic name for a structure that holds static data that uses a primary value to point to a secondary value. In your case, the switch statement has cases, which would be the primary, and secondary being the logic within the case. [`After 5 case`](https://stackoverflow.com/questions/767821/is-else-if-faster-than-switch-case/767849#767849) a switch statement is converted. It effectively means you can access a case with little to no overhead. And what do you by completely unique? Are there not general cases of each having an attack, a defend, etc.? – TEEBQNE Sep 14 '21 at 07:24
  • That’s really interesting! I already have more about 15 cases so I suppose with that notation and it being 2d I don’t have to worry about cost. And yes by completely unique I wanted them to have other prompts and different types of interactions with the player. Like shooting a “brain slug” (Futurama reference) off an enemy adds 2 new attack states and removes old ones from ‘statesOnThisUnit’ which would have been more annoying to make if it only had one generic attack state –  Sep 14 '21 at 07:47
  • A further benefit is also chaining different attacks directly in the states themselves to set or remove states on a unit than creating an algorithm or randomizing state selection in between turns. This way I can have an enemy debuff the player and directly follow up with an attack but this is going more into game design than coding –  Sep 14 '21 at 08:03
  • 1
    A derived class can have completely unique logic inside of it. It's just a cleaner way to implement than a huge switch statement. Each derived `attack` can have its own specific logic for each state. But your current implementation will work so if it makes sense to you and you can maintain it, go for it. – TEEBQNE Sep 14 '21 at 15:28

1 Answers1

1

As pointed out by TEEBQNE, I wasn't aware of the direct look-up table transformation through the compiler. The notation being O(1) is very good performance for me, rather than O(N) (N being the number of cases) which I thought was the case. I may still convert it to derived classes in the future or on my next implementation of a turn based system. So my question here is answered.

Thanks TEEBQNE :)