Sequential/Concurrent Event Handling

Hello all! Looking for a little advice on my project- specifically on the event data storage/loading system. This is regarding event handling in a traditional JRPG setting. Be prepared for a wall of text here, scroll down first and make sure you’ve got time to be wasting on this: it’s quite long and there’s no specific questions, just “is this a good way to go, is there a better way for any of the various methods I’m using, or am I an idiot and reinventing the wheel and there’s a perfect solution elsewhere?”. If that type of thing bothers you, no need to read further :slight_smile:

A few weeks ago I started a project and, as I’ve always been somewhat fascinated by the idea of breaking a project into completely independent ‘modules’, immediately went to implement a type-safe event message system (partially stolen from a half dozen sources around the net and then pieced together myself). It uses struct types (which I call “messages”, all implementing a common interface “message”) as dict keys and allows listeners to add/remove themselves (and their delegates methods, obviously) for those specific struct types. Raising a message event sends that message (again, a struct that contains information on the event), to all of the subscribers for that event/struct type, thus making it possible for one script to trigger an event that’s sent to ONLY the one or two scripts that need the information without knowing anything about the recipients.

My concern is actually toward the event storage/loading, or rather, sequential and concurrent event handling. I’ve spent a week now without coding at all, just browsing over the internet, into a few C# books I purchased a few weeks ago (I’m new to this), and on this forum, and I’ve come up mostly dry. I need a way for someone to write sequential event data outside of the Unity project itself, a way to import that data into Unity, and a way to run those sequences.

Here’s a slightly more graphical representation of the problem I face:

<Some sort of requirement checking here, is there another event sequence that has to be done first? A benchmark to be reached? A relationship level with the character? etc…>
Event Sequence #1 (conversation in front of the house) →
Event #1 → CharacterX appears at area entrance at location X,Y
Event #2 → CharacterX moves to X,Y position (wait for confirmation)
Event #3 → DialogueBox, CharacterX speaking, portrait image X, left side of screen, dialogue contents (wait for confirmation)
Event #4 → DialogueBox, Protagonist speaking, portrait image Y, right side of screen, dialogue contents (wait for confirmation)
etc…

Now, since the scripts that actually manage each of these very different event types (the character controllers for movement and the GUI controller for dialogue, for instance) are not in any real way connected to the event controller, that means using the message system I already made to send that data out. If I’m using the message system to SEND the events, then I may as well use the message structs themselves to contain the information for them in the first place (then I can just launch them out through the messenger without any sort of pre-processing). In other words, I could make a dict for “areaEvents” in the event controller (events that are area-specific) that contains the areaID as a key and the array of message-interface-type-objects as a value, then do a type-check on the objects to find the SPECIFIC type of message-struct they are and send them out accordingly.

Callbacks are one concern. A pre-assigned callback message (with an event ID maybe?) could be included as an optional property of the messages, like “if a callback message struct exists here, then send it out when you’re done”. That way, the event controller can know when the events have been “finished” on the various scripts they’re sent to. Is it better to send the delegate directly with the message rather than a message-within-a-message to use as a callback?

Also, if all I have is a list of message objects like this, then how do I do requirement checking for the whole sequence beforehand?

EDIT:
The problem, then, becomes how to do requirement checking. The message-structs can be whatever I want, as long as they implement the Message interface, so they can contain a lot of information or they can be empty (ie: receiving the message IS the message). If I’m storing all of the information as message-structs in an array, then the requirements have to be a message-struct as well. That being the case, I can use the FIRST element of a sequence as a sort of “dummy message” that contains the requirement data to check against, but won’t actually ever be sent out as a message in the messaging system. Seems simple enough.

Now the problem is in how I can get this to my scriptwriter and allow him to write his own event sequences. Obviously I’ll have to go in and add all of the X,Y coordinates myself, and probably the Area ID and Character IDs for the various lists that they’ll go into, but what’s the most effective method of allowing him to write this data and importing it into Unity scripts at launch. XML seems viable, but bringing in the parser for XML data appears to be a pretty large addition to a project and there may be a more complete solution available that I don’t know about.

As I mentioned at the top of this post, I don’t have any SPECIFIC problems, like a math issue or a code syntax issue, this is more about logic, efficiency, and reinventing the wheel. Because I’m new to programming, I have very little knowledge of programming terminology, which is more a hindrance than the logic of it all (the logic is pretty easy). That being the case, I’m not even completely sure what half of the concepts I’ve mentioned here are properly called, how to search for them, or whether they’re some horribly wrong approach, already existing in a more complete solution elsewhere that I can use, etc… I need advice on the design. Is this a good way to go? Am I horribly off the mark? Why can’t I find this kind of issue being discussed all over the game development forums (it seems like a massive portion of the game logic, and yet all I see is “graphics this” and “audio that”)?

Any help at all, even just tangentially related ideas or suggestions, are greatly appreciated!

The questions you are asking are all the right ones. You’ve clearly given this a lot of thought.

However, I suspect you might be overwhelmed with complexity just coming off the starting line.

I suggest you try some simple text-based messaging things, like perhaps write a Space Invaders style game where all the different objects register themselves and communicate by your proposed string method.

This will a) give you an idea of what you’re going to need, b) give you a sense of how it might interoperate, c) give you an easily-reachable goal (Space Invaders is pretty simple), and d) set you up for better understanding of your problem space.

As for external sequencing, if your messaging is by string, then you can always just “feed in” a series of text files as separate entities. You could create a little meta language in text files, which import directly into Unity as TextAssets, and have these files specify how the game plays.

It’s actually not by string at all, I hate magic strings in principle so I did everything I could to avoid using them. Below is a slightly simplified example of the system I’m using. I knew the terminology I was using wouldn’t properly describe it, so I prepared this beforehand. EventMessenger is a singleton class, but I’m leaving out the singleton code, as well as most of the GameEvent-derived structs (messages), and the members of the most common struct I’ve left in there for example purposes.

public interface GameEvent {}
public struct GE_ChangeGameState : GameEvent {}

public sealed class EventMessenger{
    public delegate void EventDelegate<T>(T e) where T : GameEvent;
    readonly Dictionary<Type, Delegate> _delegates = new Dictionary<Type, Delegate>();

    ///Add a subscriber for a specific event message type.
    public void AddListener<T>(EventDelegate<T> listener) where T : GameEvent{
        Delegate d;
        if (_delegates.TryGetValue(typeof(T), out d)){
            _delegates[typeof(T)] = Delegate.Combine(d, listener);
        }else{
            _delegates[typeof(T)] = listener;
        }
    }

    ///Remove a subscriber for a specific event message type.
    public void RemoveListener<T>(EventDelegate<T> listener) where T : GameEvent{
        Delegate d;
        if (_delegates.TryGetValue(typeof(T), out d)){
            Delegate currentDel = Delegate.Remove(d, listener);
           
            if (currentDel == null){
                _delegates.Remove(typeof(T));
            }else{
                _delegates[typeof(T)] = currentDel;
            }
        }
    }

    ///Send a message to all subscribers of that message type.
    public void Raise<T>(T e) where T : GameEvent{
        if (e == null){
            throw new ArgumentNullException("e");
        }
       
        Delegate d;
        if (_delegates.TryGetValue(typeof(T), out d)){
            EventDelegate<T> callback = d as EventDelegate<T>;
            if (callback != null){
                callback(e);
            }
        }
    }
}

Allright, my original comment still stands. Use that system to write a very simple game, such as even pong or something. It will prove out your event handling idea in a real world situation that you can then scale up to your intended game usage.