0
\$\begingroup\$

I am currently building a turn-based framework (Unity,C#)

I was tired of my former tight-coupled systems and opted for a state machine this time, basically all components subscribe to the state-Enter and state-Exit event. I am aiming for scalability and structure.

But I ran into a problem. If the components cannot communicate directly, how to store and forward data in between states?

Example: The game starts with unit placement. The player has the option to select a unit in his pool via the ui and place it on the board. The Unit-creation logic is and should(?) be handled inside the level script. How does it know where to create which unit? Ive done a workaround with a static container class, with is basically my clipboard. The player assigns a grid cell for his selected unit and the game leaves the state. While leaving the state the level scripts scans the clipboard and creates any assigned units.

But I feel like a cheated my attempt to really work strictly within a firm structure this time to guide the design process.

Is there a better way?

\$\endgroup\$
  • \$\begingroup\$ A fine way to handle in a discrete way communications between entities is the Oberver pattern. Take a look at it. \$\endgroup\$ – Leggy7 May 24 '16 at 22:50
  • \$\begingroup\$ I just impletented a simple version of the observer pattern. But how do I get a reference of the observing object? I need it to add it as an observer to the list. \$\endgroup\$ – user60245 May 25 '16 at 9:56
  • \$\begingroup\$ better if I write an answer \$\endgroup\$ – Leggy7 May 25 '16 at 10:12
0
\$\begingroup\$

Yes, setting a global variable and then calling a method which behaves differently based on its value is a dangerous anti-pattern known as "Accumulate and Fire". The remedy is to pass all data which is required by a method as parameters.

In your case the solution is to not use generic event handlers to enter a state. Give each state an unique Enter method with unique parameters which takes the arguments needed by that state and call these methods directly.

For example, StateSelectUnit switches to StateGiveCommand by calling StateGiveCommand.Enter(selectedUnit). The semantic is that you do not enter a generic StateGiveCommand, you enter a specific StateGiveCommand for that unit, which is a different state than giving a command to a different unit.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I like this. If I understood it correctly each state has a data structure as a "result" which can be used by the following state? \$\endgroup\$ – user60245 May 25 '16 at 10:54
  • \$\begingroup\$ @openend Not necessarily one result. The parameters for an Enter method of a specific state could be any number of parameters, including none. But generally yes, each state would pass everything to the next state it needs to know. \$\endgroup\$ – Philipp May 25 '16 at 10:56
  • \$\begingroup\$ Thanks! I ditched the state machine entirely (for now) and went with delegates only, since I can do a simple Event-> Reaction setting. Maybe this will work out. Thanks for answer, it inspired me to really think about what information I need where and when. \$\endgroup\$ – user60245 May 25 '16 at 12:03
0
\$\begingroup\$

A fine way to handle in a discrete way communications between entities is the Oberver pattern. Take a look at it.

each entity who need observation will need to add an observer to its list

public void AddObserver(Observer o)
{
    obs_l.Add(o);
}

Then when you define your Notify function you will baasically pass a reference to the object calling, if it is your need. Take a look of a sample of my Notify function:

protected void Notify(Object entity, KCEvent e, Object target, List<float> f_args, Vector3 pos, Quaternion rot)
    {
        foreach (var o in obs_l)
        {
            o.OnNotify(entity, e, target, f_args, pos, rot);
        }
    }

this is because when I notify observers what is happening I need to make them aware of a few things, like:

  • entity, the Entity which notifies the event
  • e, the event (just an enum in my case)
  • target, another entity where the event could happen on
  • f_args, a list of float which represent different data needed by the observer to do things
  • pos, is a position vector where things could happen
  • rot, possibly a rotation needed to apply to something

All of these are purely chosen on my needs. You will do the same.
To answer you comment when I call Notify I will pass this as first parameter.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thats exactly what I did. But from where would you call the method AddObserver? The observed object does not know the observer and vice versa. If they would, they would be tight coupled again. \$\endgroup\$ – user60245 May 25 '16 at 10:33
  • \$\begingroup\$ Actually you can do that in the Awake() method of your observed object, or whereever you initialize it. \$\endgroup\$ – Leggy7 May 25 '16 at 10:42
  • \$\begingroup\$ I am really sorry, Leggy, but you can't. I appreciate your effort, but I tested your solution, and tried different implementations of the observer pattern. It may be due to my limited abilities, but I think this does not work in Unity. I cannot use the Awake() method, I have no reference to the object I want to observe. \$\endgroup\$ – user60245 May 25 '16 at 10:52
  • \$\begingroup\$ whether the object is a monobehavior or another class you have either the Awake() method or an Initialization method for that object. whereever you do that you add the observer to the list that your object obviously has \$\endgroup\$ – Leggy7 May 25 '16 at 10:58
  • \$\begingroup\$ I think you do not understand the problem. Of course I have a method in which I could add things to a list. But I have no reference to these things. \$\endgroup\$ – user60245 May 25 '16 at 11:12

Your Answer

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