Event Delegation in Unity

What is Event Delegation?

“…a helper object, known as a delegate, is given the responsibility to execute a task for the delegator
– Delegation Pattern Wikipedia

Why use Event Delegation?

A good practice when working with Unity (or any framework) is to decouple your UI logic from your game logic. It’s fine to have the UI code reach down to the game logic layer and call methods directly on it but having the game logic layer directly call methods on the UI logic’s layer is, in general, a bad practice because doing so “marries” you to whatever UI you are currently using. So to get around this we can use C#’s Delegates and Events to fire off methods throughout our UI and even on the game logic layer.

How to use Event Delegation with Unity

All we have to do is:

  • create a static Event Manager class
    • Add delegates and events to this Event Manager
  • Listeners will add functions to the events
  • Delegators will fire off the events

The Manager: Defining events

public class EventManager {
    public delegate void EnemyHovered(int laneIndex, int targetRange);
    public static event EnemyHovered EnemyTargetedRequest;
    public static void EnemyTargeted(int laneIndex, int targetRange) {
        if(EnemyTargetedRequest != null) EnemyTargetedRequest(laneIndex, targetRange);
    }
}

In this example we create a delegate with the type EnemyHovered which takes the arguments laneIndex and targetRange. Then we make an event EnemyTargetRequest which is the event that the listeners will subscribe functions to. Finally, there is a static method that can be called by delegators when they want to fire off this event, triggering all the functions that are subscribed to it.

The Listener: Subscribing to the event

In another class we can add the following:

void Awake() {
    EventManager.EnemyTargetedRequest += DoSomething;
}

public void DoSomething(int targetedIndex, int targetRange){
    // do something
}

In this class we define some function called DoSomething that takes the same arguments as our delegate. In the Awake() method we add the function to the static class’s EnemyTargetedRequest event.

The Delegator: Firing off events

In yet another class we can do the following:

public void OnPointerEnter(PointerEventData eventData) {      
    EventManager.EnemyTargeted(_index, _targetCount);
}

Here we’re just using Unity’s OnPointerEnter event as the UI event that will trigger our EnemyTargeted event. Because our Listener class subscribed it’s DoSomething function to the EnemyTargetRequest event when SignalManager.EnemyTargeted is called it will fire off the DoSomething function!

So with this, you can keep all your events off in the static Event Manager, subscribe to them when you need an object to react to an event and fire off the events whenever you need to which allows you to keep UI and game logic separated. You could even have GameObjects subscribe to events and unsubscribe from them as they are created and destroyed which is very useful when these objects are created and destroyed dynamically.

 

Events

In Nano, Events are the primary way the player interacts with the game world. Events are also the most direct way the player learns about the environment they are taking part in, meaning Events play double duty by providing game play and story elements. While the story is something that will develop overtime, the functionality of Events is something that the game needs right away. Events are also the feature that I wrestled with the most when trying to come up with a solid development design.

It was clear what Events had to be, they were to display some story text to the player, a few buttons with text on them representing the player’s available choices, and upon selecting a choice a result is shown that also has story text as well as displaying any rewards the player is given. Think of the event system in games like FTL. This type of system is generally referred to as a dialog tree, decision tree, or most specifically a simple directed graph. There are many plugins for Unity that provide varying levels of decision tree functionality, but I wanted to take a stab at rolling my own first.

So far a very simple Event model is in place along with Choices and Results. When a player enters a new location an Event is chosen from the list of possible Events which is then displayed on screen. To manage the Event’s display is the Event UI Controller class which is attached to prefab in Unity. This Event prefab uses Unity’s new UI features to display text and buttons. This is my first attempt at working with this new UI system, before this all objects displaying text used Text Mesh components which I find to be clunky and lacking a lot of features that are needed for displaying even a modest amount of text.

The early Event UI screen.
The early Event UI screen.

There is still a lot to be done to get Events fully functional. The player still needs to be able to select one of the choices and be presented with the result. Events will also be the primary way that combat is triggered. Some choices should only be available to the player if certain conditions are met, like having a particular item equipped. There is also the daunting task of creating a large amount of Events all with multiple choices which in turn have a variety of rewards.

Combat goes in the Combat Box

Long story short, I got the Combat screen to display within the World view via ajax. What does this mean? Means the game looks like this when in combat now…

Featuring background scene art by Koishii Kitty.
Featuring background scene art by Koishii Kitty.

This turned out to be easier than I expected when implementing, but took a while to plan out properly. The result is a smoother transition from Event to Combat. There are still a bunch of things I need to refactor now that this is how the combat screen will be displayed, but it’s a step in the right  direction.

I still need to change the card’s ‘Use’ button to use ajax and the players stats in the top left need to update after each combat round.

I also ajax’d the close button for the inventory screen, meaning now, going from the World screen to your Inventory, using an item and then closing the inventory screen to return to the world screen is all seamless. I’m going to start looking into fancy javascript animations for the opening and closing of screens. While the current transition requires no full page refresh, the switch is somewhat startling. I think all it needs is a slight fade as it transitions.

Implementing Events 2 – Expanding and Frequency

There is still more to be said about Events so here is part 2!

Expanding the Events Feature

If all the game did was pick a random event from a pool of every event, the world wouldn’t seem very… alive…duuude. So, some events should only happen in particular climates (I call them Biomes). For example coming across a pool of water might be something that can happen in any biome but an event like “Falling Tree” might only happen in a Forest biome and not in a Plains biome.

To allow for this Events have a Biome, Terrain and hasPriority property. When the game’s engine creates the pool of available events to choose from it grabs all events that can happen in any biome as well as the events that can only happen in the biome the character is currently in. So a character in a forest biome, Big Tree terrain would have an event pool that contains events that can happen in Any biome, in the Forest biome specifically and events that can only happen in the Big Tree terrain tile.

The hasPriority property is only used if a certain event should always be used in a given terrain tile. Such as a Throne Room terrain tile in a dungeon should always have a boss encounter. That event would then have it’s Terrain property set to ‘Throne Room’ and hasPriority set to True (as that variable is a bool).

Event Frequency

There is also a Frequency property to each event and event reward. This number give some measure of control over which events are more common over others. Not too much to say about that one really. While the concept is fairly straightforward, implementing it was not so simple. So here is what I did:

After creating the initial Event Pool, I had a list of possible events. I then added together all the event’s Frequency properties. I used this number as my MaxRandom number, the maximum number the game could roll when picking an event from the pool. I also made a second list called the Event Pick List which only had 2 properties: Event and SlotID. This list was then populated with a number of entries for each event equal to the event’s Frequency. So, an event with a frequency of 10 would have 10 entries in the Pick List. The engine also applied SlotID numbers in sequence.

For Example:
An event pool with 3 events:
A Pool of Water (Freq. 10)
A Falling Tree (Freq. 5)
Strange Mushrooms (Freq. 5)

MaxRandom = 20 (10+5+5)

Pick List:
A Pool of Water (slotID = 1)
A Pool of Water (slotID = 2)
A Pool of Water (slotID = 3)
A Pool of Water (slotID = 4)
A Pool of Water (slotID = …)
A Falling Tree (slodID = 11)
A Falling Tree (slodID = 12)
A Falling Tree (slodID = 13)
A Falling Tree (slodID = …)
Strange Mushrooms (slotID = 16)
Strange Mushrooms (slotID = 16)
Strange Mushrooms (slotID = 18)
Strange Mushrooms (slotID = …)

Then the random number is picked between 1 and MaxRandom. The number picked corresponds to the Pick List’s slotID and that is the event assigned to the character.

With some form of filtering being applied to the possible events and then frequency being taken into account to allow for common/rare events, when a character encounters an event it will hopefully feel more meaningful.

Implementing Events Part 1 – Being Stateless

I’ve started implementing the Events feature into Nano. So far my posts haven’t been too technical or in depth, so this one might be a bit heavy for some. First, allow me to describe the overarching idea behind Events in Nano.

The Idea

As a player navigates the world they occasionally encounter an Event upon entering a new location. An Event is basically a description of something that is happening and a list of choices about how to react being made available to the player. Once the player has made a choice they are given some type of result, such as stat alterations, finding treasure, or getting into combat with an enemy.

Example Event

A player enters a new location called ‘The Open Plain’. Upon entering this location they are shown an Event screen, for the event ‘A Pool of Water’. This event has a description; “You see a pool of water. It looks clean enough to drink.” and a couple of choices:
Choice 1: “Drink the water.”
Choice 2: “Ignore the pool of water, bugs have probably pooped in it.”

The player selects Choice 1, “Drink the water.” (Ignoring the possibility of bug poop!). This choice results in the character’s Thirst being reduced by 100. Had the player ignored the water, they would not have received that reward. A second screen is displayed given both a description and a breakdown of the results.

“You drink the bug poop free water. Ahh, refreshing!”
Thirst: -100

How to Organize the Data?

So now that we know the overall idea of what we’re trying to do. How do we execute that in code? It’s never as straightforward as you think and even when you’re prepared for it to become complex, it’ll go and get even more complex than that. This is because at this stage of development I’m really building a system, not just a ‘if this, then that’ scenario.

How does the database look?

First I had to figure out how to organize the information in the database. Will the events, choices and rewards all be in one table or would it be better to have a separate table for each event element?

After a lot of thought, I decided on having separate tables for Events, Event Choices and Choice Rewards. The reason is that each of these has a One-to-Many relationship with the next. For example an Event has many Choices and each Choice can have many Rewards. This would suggest that using a separate table for each piece would be the best way to manage them.

Being Stateless

One major pain that I’m always dealing with in this project is that as a web game using MVC4 and standard postbacks (ie. I’m not using Flash) is that this game is Stateless. Most games can save information about the player’s current location, stat values, and what screen their looking at in your computer’s memory, but not Nano. Nano needs to save all these variables in a database and access that database after every player choice.

Being Stateless Allows Cheating

So given the Stateless limitation, how does the player’s choice get sent back to the server? Another problem with being Stateless is that user input cannot be trusted, they could send any value they want back to the server and the server just accepts that value, never thinking that the user might be a big fat cheater! For example, if a player is in combat and clicks the Use Kick button, the link they click actually forms a URL that ends in /UseCard#CardID=3 which calls the UseCard method with CardID=3 (3 being the ID # for the Kick card).

So there you go, that’s how you can try to exploit Nano. During a combat just change the url to have the CardID equal a number of an amazing card like ‘Sword of a Thousand Truths Stab’ and you can use that card even if it’s not in your hand or even in your deck!

Just kidding, you can’t do any of that because I had to take special measures to stop that form of abuse. Again, because the game is stateless, when the user sends a request to use a card with a particular ID I first check to see if that card is in the user’s hand. If not the user will get a message saying it’s an invalid card and then it will taunt them for trying to cheat. But the point is, this is something many games don’t have to be concerned about since they can store information in memory.

Record the Character’s Current Event

Something that is nice about working in this Stateless environment is that it allows the player to leave the game at any moment and when they return they will be right back where the left off. Whether that is standing in the middle of a forest, at a crucial choice for an event, or right in the middle of combat.

But this also means that the game must record everything to allow for this seamless return. For Events, I decided to add a property to my Character records called ActiveEvent this field stores the ID# of the event that the character is currently involved in. Upon page load the game says “Is the character in an event?” If no, then the ActiveEvent field is null and the game displays the location the character is in. If yes, then the game looks up which event it should display based on this ActiveEvent field.

Sum it up

So that’s how working in a stateless environment for a game adds a layer of complexity to the design of the game. I have to think about everything in steps and each step has to end in a single output of information for the player to react to. In the next post I’ll get into making events more interesting by limiting their frequency and availability.