© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2023
C. PittProcedural Generation in Godothttps://doi.org/10.1007/978-1-4842-8795-8_12

12. Interaction Systems

Christopher Pitt1  
(1)
Durbanville, South Africa
 

Before we wrap up our journey together, I’d like to spend a bit of time talking about how we enable interactions between players and the world.

In this chapter, we’re going to look at how to handle proximity-based interactions and how to display interactions when they happen.

It’s a little difficult to show these concepts without the context of a game, because they are so dependent on the specifics of the game; but we’ll try anyway.

I’ll talk through different code approaches, but I don’t expect you to create a dedicated experiment for the concepts in this chapter. The approach you take will definitely depend on the game you're building.

Managing Interactions

Think back to when we made our own version of Invasion. The player can move through the map, encountering survivors.

A screenshot of a game has different graphical elements and an encircled area.

Interacting with survivors

The way we coded it was that the survivors would immediately start to follow the player when they were close enough to each other. We could make the game a lot deeper by giving the player a choice to rescue or abandon the survivor.

One practical way to do this would be to use Area2D nodes to detect the approach of the player. If the player is in range of something that can be interacted with, we can present them with a prompt to let them know that interaction options are available. Many games do this, including one of my favorites:

A screenshot of a game has different graphical elements, including three hearts and stones.

Interacting with things in Forager

In Forager, when you are close enough to interact with something, it gets this square indicator. Sometimes, you’ll need to press a specific button to begin the interaction. Other times you’ll be able to whack at the tree or stone with the tool you're holding.

The way I usually code this sort of thing is to have a dedicated node, called Interactable:

A screenshot of the scene page has the option for interactable highlighted. The middle panel has a few lines of code for the interactable. On the right two tabs are tabs for the inspector and node, where node is selected.

Making a reusable interaction manager

These Area2D-based nodes should have their collision layers and masks set to a layer dedicated to this purpose.

A screenshot of the project settings window has general settings highlighted. It has options for input devices, X R, G U I, layer names, file system, and navigation. On the right side, there are 12 to 32 layers, and layer 20 is selected.

Setting an unused layer for interactables

The base Interactable node doesn’t have a collision shape or collision polygon defined, because this should be set as it is added to another node.

Keeping it simple, we can use a CollisionShape2D:

A screenshot of the scene page displays the option for colorRect. On the right, the output image for the interaction experiment is visible.

Creating an example to assess functionality

If we were to switch to remote view while running code like this, we'd see the Other Interactable variable is null:

A screenshot of the scene and file system page displays the option for interactable. The middle panel has the input image for the interaction experiment. The panel on the right displays the Inspector tab.

Not collisions…

If we then moved the player so that it collided with the NPC, we’d see the link created:

A screenshot of the scene page highlights the option for interactable. The middle panel has the input image for the interaction experiment. The right panel highlights the Inspector tab.

…Until we move the player closer

At this point, we could show that interactions are possible. If the player pressed the appropriate key, we could even cause the interaction to begin.

The way the player moves closer depends on the game. It could be via keyboard input, like we had in Bouncy Cars. It could be from click-to-move navigation, like we had in Invasion. The point is that the collision detection and behavior are no longer in the players’ scripts, but rather in a dedicated node.

As we saw in Invasion, there is still the issue of CharacterBody2D collision shape avoidance preventing us from using arbitrary Area2D nodes in this way. We’d need to change the survivors to not be CharacterBody2D nodes if we wanted to retrofit this interaction model on to Invasion.

Since we’re using signals, we can attach listeners to run code when the signals are emitted. A signal for receiving the interaction (from Player → NPC) could show a new conversation or start some NPC code:

A screenshot of the scene page highlights the option for interactable. The middle panel has a few lines of code for the interaction experiment. The right panel has the tab for the node.

Handling initiated interactions

If we wanted to show that interaction was possible, even before the player presses a key to start the interaction, we could add more signals to Interactable:
extends Area2D
class_name Interactable
signal interactable_entered(other)
signal interactable_exited(other)
signal receive_interaction(me)
var other_interactable : Area2D = null
func initate_interaction() -> void:
    if other_interactable != null:
        other_interactable.receive_interaction.emit(self)
func _on_interactable_area_entered(interactable : Area2D) -> void:
    other_interactable = interactable
    interactable_entered.emit(interactable)
func _on_interactable_area_exited(interactable : Area2D) -> void:
    interactable_exited.emit(interactable)
    other_interactable = null

How This Could Apply to Invasion

Let’s think about how this approach might work if we added it to Invasion. The screenshot I showed earlier was from my first build of the game and is a bit more feature-rich than the version we built together.

If it already includes soldiers and when you get close enough to them, they chase you down. You can escape them by moving to another screen. If they catch you and you have a survivor already following you, then the survivor freezes in place and you can no longer rescue them.

I didn’t have an interaction system in the original version of Invasion, but if I had to add one, I might try the following:
  • Allowing the player to decide whether they rescue survivors they encounter. Perhaps some survivors will slow the player down or cost the player something to rescue. Allowing the player to make the decision to rescue would be more interesting than the survivor immediately following the player.

  • Allowing the player to decide what to do when a soldier catches up to the player. Could you choose what happens to the survivor? Could you keep the survivor by giving the solider something else? What if a currency spawned inside the map and you could trade it for safe passage?

Having Conversations

A common way for interactions to happen in games is for the main characters to have conversations. This could be a detective asking questions during an investigation, or a boss monologging before their untimely demise.

The original version of Invasion included dialog between the player, survivors, and soldiers:

A screenshot of the game window has a representation of a survivor and other graphical elements. It has a text that reads, do you know the way to the hospital with a question mark.

Conversations with survivors

On spawning, I assigned each soldier and survivor with a portrait. When the player walked in range of a survivor, I’d select and play a random survivor line. When a soldier caught up to the player, I’d play a random soldier line.

I handled all the dialog with a third-party add-on, called Dialogic. You can find the add-on and installation instructions over at GitHub. There’s also a companion website, with links to documentation:

A screenshot of the dialogic website has text for installation and other options.

Dialogic website

Dialogic has a custom editor, which allows you to preconfigure different conversations. The interface changes from time to time, but it should resemble this layout:

A screenshot of a window has the tab for interaction experiment selected. On the right panel there are settings options, main, logic, timeline, audiovisual, Godot, and others.

The Dialogic interface

This is where you can create new conversations, with custom portraits, sound, and decision trees. There’s a lot of depth to this add-on, but I want to focus on the simplest of setups.

There are some steps to showing dialog:
  • You need to create a few characters.

  • You need to set up timelines.

  • You can start a timeline with the Dialogic.start_timeline method.

I thought it might be useful to show you what I did for the first version of Invasion, so you have a sense of what’s possible.

Dialog in Invasion

I followed the first two steps in that list so that I had a full set of characters and some example timelines for my game:

A screenshot of a window has an option for timelines, characters, and soldiers. The panel on the right has a few boxes for character join, text event, and character leave.

Setting up characters and timelines

It’s worth noting that this screenshot is from Godot 3.x and Dialogic 1.x. The interface and methods with which we start timelines will be different to Godot 4.x and Dialogic 2.x.

As you can see, there are a ton of soldier profile pictures, around 30 in total. I created the soldier profile pictures with a red hue and then re-colored them to yellow for the survivor profile pictures.

The example timelines were a sequence of events and text that model the typical dialog structures I wanted for Invasion:

A screenshot of a window has a few boxes for character join, text event, and character leave. The panel on the right has options for main events, logic, timeline, audio, and Godot.

Creating example timelines

Dialogic has methods we can use to play these timelines. Playing a pre-created timeline is ok, but I wanted to achieve something a bit more dynamic. Fortunately, these timelines are saved in text files, and inspecting them allowed me to set up dynamic conversations:

A screenshot of a window has a few lines of code for different dialogs, events, actions, characters, emit signals, and background.

Dynamic conversations

A lot of this data is what I call “magic variables,” inasmuch as they’re static internal values that Dialogic understands. The interesting thing is that creating these timelines dynamically means we can substitute the characters and lines at runtime.

In this version of Invasion, I randomly selected the soldier and survivor profiles when the soldiers and survivors were added to each room. Remembering what each survivor looks like means we can show the same profile pictures for them over the course of a level.

Summary

I hope you have a better sense of some of the things you can add to your games, which will help them feel more immersive and interactive. I’m sure they will be useful in the final game we’re going to make.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset