SGG Devlog

May 2024

Worked on events, queue, and Gameplay Ability System

  ·   5 min read

Unlocking a Living Game World with Event-Driven C++ - A Developer’s Guide

The importance of being alive

Have you ever played a game that felt alive? A game where your actions and decisions dynamically shaped the course of history, creating a truly immersive and interactive experience? This feeling of a living world is a hallmark of some of the most engaging games, and achieving it requires a sophisticated and flexible system to handle the diverse amount of interactions and changes within the game.

In my obstinate quest to create such a dynamic and responsive game world, I have been testing different approaches and techniques. One of the most crucial techniques I have found is the implementation of an event-driven system. This approach has been instrumental in breathing life into my game, allowing for a world that reacts to player actions and evolves over time.

Event driven architecture 101

In an event-driven architecture, software components communicate with each other by using events and listeners. This design pattern allows for greater flexibility and modularity, making it easier to manage complex systems such as games.

The Basics of Event-Driven Architecture

At its core, event-driven architecture relies on the concept of events and listeners. An event is an action or occurrence that is recognized by the software, while a listener is a component that waits for and responds to specific events. When an event occurs, the listeners that have subscribed to that event are notified and can execute their respective functions.

Decoupling Through Events

One of the key benefits of using events is the decoupling of software components. When you have two components connected by events, you essentially decouple them from one another. These components can exist independently within the game and still communicate effectively when they need to. This decoupling is achieved by having components subscribe to events rather than directly calling methods on each other.

The nitty-gritty part

Unreal Engine provides a robust framework for handling events through the use of delegates and events, which can be utilized both in C++ and Blueprints. This allows developers to create highly responsive and interactive game environments.

Overview of Delegates and Events

In Unreal Engine, delegates are essentially function pointers that can be dynamically assigned and invoked. Events are a specific type of delegate that are designed to be broadcasted to multiple listeners. This system allows for a flexible and decoupled architecture where different parts of your game can communicate without tight integration.

Using Delegates in C++

Delegates in C++ are defined using the DECLARE_DELEGATE macros provided by Unreal Engine. Here’s a simple example:

  1. Defining a Delegate:

    DECLARE_DELEGATE(FSimpleDelegate);
    
  2. Binding a Function to a Delegate:

    FSimpleDelegate MyDelegate;
    MyDelegate.BindUFunction(this, FName("MyFunction"));
    
  3. Executing the Delegate:

    MyDelegate.ExecuteIfBound();
    

In this example, MyFunction is a member function of the class where the delegate is defined. When ExecuteIfBound is called, it will trigger MyFunction if it has been bound.

Using Event Dispatchers in Blueprints

Blueprints provide a visual way to handle events using Event Dispatchers. Here’s how you can set up an event dispatcher in Blueprints:

  1. Creating an Event Dispatcher:

    • Open your Blueprint.
    • In the My Blueprint panel, click on the Event Dispatchers section and create a new Event Dispatcher.
  2. Binding to the Event Dispatcher:

    • In the Blueprint where you want to listen for the event, add a Bind Event node.
    • Select your Event Dispatcher from the dropdown list.
    • Connect the Bind Event node to an appropriate event, like Begin Play.
  3. Calling the Event Dispatcher:

    • In the Blueprint where the event should be triggered, add a Call node for your Event Dispatcher.
    • Connect this node to the appropriate place in your Blueprint logic.
  4. Handling the Event:

    • In the Blueprint that bound to the Event Dispatcher, you’ll see a custom event node connected to the Bind Event node.
    • Add your logic to this custom event node to handle the event.

The event queue mechanism

In my game, events are managed through a queue system, which ensures they are processed efficiently and in the correct order. Here’s a brief overview of how it works:

How it Works

The event queue is essentially a large array of event structures stored in the Game Instance. These event structures contain all the necessary information to handle specific events when they are triggered.

Adding and Processing Events

Events are added to the queue whenever specific actions or conditions are met in the game. For example, when a player collects an item or an NPC completes a task, an event is created and placed in the queue.

An event is triggered every in-game hour, prompting the queue to check if there are any events scheduled for that particular time. If there are, the events are processed in the order they were added, ensuring that all necessary actions are taken.

Handling Multiple Events

When multiple events need to be processed simultaneously, the queue handles them one by one in the order they were added. This sequential processing ensures that events do not conflict with each other and that the game state remains consistent.

By implementing this event queue mechanism, my game can handle numerous events efficiently, maintaining a dynamic and responsive game world without sacrificing performance.

Conclusion

Event-driven programming has been key to creating a dynamic, responsive game world in my project. By decoupling components and managing events through a queue, I’ve achieved a highly interactive and immersive experience.

Using events and listeners allows game components to communicate asynchronously, enhancing flexibility and scalability. Unreal Engine’s event management framework and the event queue system ensure efficient processing, maintaining game performance and responsiveness.

In summary, event-driven programming is essential for developing complex, interactive game worlds. It offers a scalable, efficient solution for managing interactions, leading to a richer player experience. As I refine this system, I look forward to making my game world even more dynamic and alive.