Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Game Event System Design Advice

Discussion in 'Scripting' started by GroZZleR, Sep 8, 2017.

  1. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Preface: In this context, game event refers to an event happening in the game world. "We love the Emperor Day" granting all colonies a production bonus, and not an OnClick event or something of that nature.

    I'm looking for some advice on designing the game event system for my strategy game. The end goal is something that's easy to configure, extensible and basically entirely data driven. I would like to use the inspector as much as possible when creating or modifying events. The system should function similarly to Paradox's model where an event is displayed, optional choices are presented and an effect happens based on the player's choice.

    I'll describe the system as I have it now and although I find it overly clunky and obtuse to create events, the system itself works just fine.

    Trigger


    This is a generic enum that acts as a type flag for game events to decide if they're interested in responding. It's raised by any and all systems in the game when a particular situation occurs.

    Examples: New colony founded, population changed, unit destroyed, game day change, economy tick.

    Condition

    This acts as an additional requirement for the game event to fire and generally comes in the form of boolean or flag based logic. Right now, these are ScriptableObjects but I don't think that's a smart fit because it's not possible to customize SOs on a per-instance based like a MonoBehaviour (I'm pretty sure?), so I end up having to create a bunch of different assets: Colony has building type A, colony has building type B, colony has building type A and B. I would ideally like to have just one condition object like Colony has building and then customize the list of required buildings on a per-event basis.

    Examples: Colony has a granary, unit health is full, population below ten, player hasn't met another civilization yet.

    Effect

    This describes what happens when the player chooses a certain response in the dialog window. This is also a ScriptableObject and handles all of the internal logic of the effect itself. I think this is OK to remain a ScriptableObject because different events can reuse the same effect: Ten different events can all result in your population starving, so creating one PopulationStarvesEffect is OK. It also requires quite a bit of logic under the hood, so the solution needs to be custom tailored.

    Examples: Colony gains an effect, unit is promoted, nothing happens.

    Code (csharp):
    1.  
    2.        // population begins starving effect example:
    3.        public override void Fire(GameObject target)
    4.        {
    5.            Colony colony = target.GetComponent<Colony>();
    6.  
    7.            if (colony != null)
    8.            {
    9.                // kill off some random citizens
    10.                colony.population -= Random.Range(2, 5);
    11.  
    12.                // add the starving effect
    13.                colony.effects.Add(new StarvingEffect());
    14.            }
    15.        }
    16.  
    Game Event

    The actual event. This is a ScriptableObject that acts as a container for the details of the event: description, trigger, list of conditions, list of labels (for the UI) and corresponding effects. It's fairly generic and is mostly driven by the logic of the SOs it contains. I don't really see a reason to change how this works.

    Result

    So here's what a complete game event looks like in the inspector:


    The default ScriptableObject inspector works fairly well as an event editor in this case, though writing a custom one wouldn't hurt. But the thing that immediately jumps out is the Population Below 25 instance of a PopulationTriggerCondition. It's an absolute nightmare to hand create every single condition as an individual ScriptableObject asset.

    So that's that. Any ideas on how to change or improve the design?

    Thanks.
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    One bump for possible ideas then I'll take another pass at it myself.
     
  3. MatthiasStuetzer

    MatthiasStuetzer

    Joined:
    Oct 5, 2016
    Posts:
    17
    Sorry to revive this old thread. I am currently working on something similar and arrived at the same design problem. Do you still remember how you solved it? :)
     
  4. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    What I ended up doing was writing a bunch of custom editor GUIs that instantiated a new copy of the "effect" ScriptableObjects, then merged them into the unique GameEvent SO asset using AssetDatabase.AddObjectToAsset().

    Something like this, very simplified version:
    Code (csharp):
    1.  
    2. if(GUILayout.Button("Add Effect") == true)
    3. {
    4.    PopulationEffect effect = ScriptableObject.CreateInstance<PopulationEffect>();
    5.  
    6.    gameEvent.effects.Add(effect); // add it to the target game event list
    7.  
    8.    AssetDatabase.AddObjectToAsset(effect, target); // merge it
    9.    AssetDatabase.SaveAssets(); // save the asset
    10.  
    11.    EditorUtility.SetDirty(target); // mark it dirty
    12. }
    13.  
    After it's merged, you can expand the asset itself in the editor and edit the values of the unique SO instance. You'll need to do some reading on editor scripting, if you're not familiar, and definitely play around with merging and deleting the sub-assets before you fully commit to the system.
     
    MatthiasStuetzer likes this.