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. Dismiss Notice

How would I go about creating "pop up" events in my simulation game?

Discussion in 'General Discussion' started by tomowale, Jul 14, 2022.

  1. tomowale

    tomowale

    Joined:
    Feb 14, 2015
    Posts:
    308
    If you've ever played the mobile game "BitLife", you know that every time you press the "age" button, an event pops up (I've attached a screenshot of what this looks like. They all basically contain some sort of prompt that describes the event and then buttons that serve as your "response" to said event). I'm creating a similar game to this, but I'm pretty stumped on how to go about it.

    I've thought about making the "events" scriptable objects, all with their own text, images, and Unity Events that I can map to the buttons that each event has. Then putting those scriptable objects into collections (lists/arrays etc), and then sort of randomly calling from these collections each time the player "ages". But this seems like a roundabout way, and probably has little flexibility, especially if I want to access things like the player's stats/relationships etc.

    I was wondering how games like BitLife achieve it, my guess is that the events are hardcoded, and access the players variables/stats etc, and every time the "age" button is pressed, they use some code to map out the event visually, but I'm not sure.

    Regardless, I'd be interested to know if anyone has any ideas on how I can go about creating a system like this.
     

    Attached Files:

  2. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,324
    Your idea is mostly correct.

    You can also store those events as json file of sorts, but in this case the events will be hackable by users. Or as a readonly sqlite database.

    In case of event effects you can simply prepare all possible event effects in advance and store them as sort of enum + numerical field determining its strength.

    You could also embed some mini scripting language for more complex interactions.
     
    Ryiah likes this.
  3. tomowale

    tomowale

    Joined:
    Feb 14, 2015
    Posts:
    308
    Interested in what you meant by the "mini scriting language" , could you elaborate?

    Also, would it be fair to say the idea of using Scriptable Objects for the events is probably not the best way?
     
  4. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,324
    It means anything that can parse a simple text expression, execute it and return its result.
    For example, if you have "life > 10" stored as a string in your event condition, and your script can check that at a runtime, then it is technically a mini scripting language.

    I think JoeStrout maintained Miniscript which may or may not be of use, but I've never used it myself.

    You do not HAVE to use a scripting language, you can instead parametrize your events in such way that you can implement every possible condition and action without scripting or writing extra code. This would usually involve writing a bunch of enums, but is also manageable.

    No, it is a decent/good idea, especially if you do not plan to have user-provided events. Scriptable objects work fairly well to store data in your project as assets, so this usage case fits scriptable objects. I'd likely use scriptable objects myself, unless there are some extra requirements.
     
    Ryiah likes this.
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,895
    I'd say they'd be a great choice. Does mean you don't need to rely on tools outside of Unity to author your data.

    This might be a situation where you want to look at using SerializeReference on a collection with some editor extensions to make your choices editable as a polymorphic list. That way, you just have a base class and a shallow hierarchy for all the codified outcomes of said choices. Makes it easy to both reuse them, or add more.
     
    Ryiah likes this.
  6. tomowale

    tomowale

    Joined:
    Feb 14, 2015
    Posts:
    308
    I'm interested in what you mean by not using a scripting language, do you mean that I'd assign enums to certain events (that I suppose would essentially describe every possible condition the certain event could be called?) and then sort of call the events by the enums assigned to them? Sorry if I'm a bit lost.

    Good to hear my scriptable object idea wasn't too bad, the only downside was that I thought storing them and calling them each turn could get a bit messy. I thought about maybe storing the "events" in some sort of queue that gets created each time the player ages, as the code checks for certain player variables (like if your "health" for example is low, whatever health event would get pushed to the front of the queue and then shown to the player), but that method seems a bit messy. I suppose this is why you mentioned the enum solution as it'd probably account for every situation the player could find themselves in
     
  7. tomowale

    tomowale

    Joined:
    Feb 14, 2015
    Posts:
    308
    Thank you for the reply, prior to this I'd never heard of the SerializeReference attribute, so I'm looking into that. Your method sounds smarter, the way I initally thought to do it was to have UnityEvents on each of the ScriptableObjects that correspond to whatever choices that certain event has, but it'd also require me to write a whole bunch of methods to account for the various outcomes that each "choice" has. I'll try to look into it.
     
  8. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,324
    For example... let's say you have stats. Strength, Money, Life. For example.

    A condition for script to trigger can be represented as Argument1, Operation and Argument2.
    life < 50
    . And

    So, in your scriptable object, your entire condition can be represented as something like:
    Code (csharp):
    1.  
    2. [System.Serializable]
    3. public class Condition{
    4.     public bool inverted = false; //"not"
    5.     public Argument leftArgument = Argument.Zero;
    6.     public Operation operation = Operation.Equal;
    7.     public Argument rightArgument = Argument.Zero;
    8.     public float userValue = 0.0f;
    9. }
    10.  
    And argument can be something like:
    Code (csharp):
    1.  
    2. public enum Argument{
    3.     Zero, Life, Money, Strength, UserValue
    4. }
    5.  
    and operation can be:
    Code (csharp):
    1.  
    2. public enum Operation{
    3.     Never, Always, Equal, NotEqual, Less, LessEqual, Greater, GreaterEqual
    4. }
    5.  
    See where I'm going with this?

    You use COnidition, and grab value representing leftArgument, rightArgument, apply "operation" to it, and then invert the result if "inverted" is true. That will allow you to implement ANY check that only involves two values.

    You can make it more fancy. You can have multiple conditions, chaining them with and/or, you can have them nested, etc.

    That would be enough to implement majority of checks. In inspector of scriptable object it will be implemented as dropdown menu for arguments and operation.
     
  9. tomowale

    tomowale

    Joined:
    Feb 14, 2015
    Posts:
    308
    First of all, thank you very much for providing this example with the code and everything. Very helpful. So if I've got it right, basically each scriptable would have Argument1, Operation and Argument2. And this would be the trigger for the scriptable to be used, right? For example, if I had a scriptable and I set say Argument1 to "Health" and Operation to LessThan and Argument2 was say "Money", then that scriptable would only be triggered under those conditions right?

    Only thing I'm confused on is the relevancy of the "inverted" boolean, you mentioned inverting the result if "inverted" is true, I'm a bit confused on that part.
     
  10. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,324
    You have to program it. "Command" is simplhy a class that stores data you should be able to display in unity inspector. You can make it into a scriptable object. You can make it into a PART of scriptable object.

    You need to write a function that takes that class as an argument and calculates its result based on data it stores. When it how it happens, depends on how you implement it. It won't calculate anything unless you program it.


    "Inverted" represents "!" operation in C#. If you don't need it, do not implement it. This is just an example. It is sometimes convenient to be able to turn (a < b) into !(a < b).
     
  11. tomowale

    tomowale

    Joined:
    Feb 14, 2015
    Posts:
    308
    Got it, my immediate thinking was to make the "Condition" Class a part of the event scriptable objects, so that I could set the arguments/operation enum variables for each event, and then only display these events to the player under the conditions specified by the Condition class (that is a part of the Event ScriptableObject.), though I think your intention was a bit different.