Dialogue & Events in Godot! | Thomas Yanuziello | Skillshare
Search

Playback Speed


1.0x


  • 0.5x
  • 0.75x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

Dialogue & Events in Godot!

teacher avatar Thomas Yanuziello, Indie Game Developer

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.

      Intro

      1:40

    • 2.

      Dialog Box

      11:53

    • 3.

      Monologue

      13:12

    • 4.

      Event Manager

      10:31

    • 5.

      Interact

      11:21

    • 6.

      Choice

      12:33

    • 7.

      Flags

      12:11

    • 8.

      Camera

      15:00

    • 9.

      Animation

      11:49

    • 10.

      Direction

      11:53

    • 11.

      Modularity

      10:56

    • 12.

      What's Next?

      1:38

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

24

Students

--

Project

About This Class

This course is a continuation of Game Development Essentials in Godot.

Click on my website link in my profile to join our discord server!

In this course, we will cover the creation of dialog boxes, events & interactions, then tie them together with progress flags, camera controls, and character animations.

You'll also learn useful skills for working with the Godot game engine, organizing, and designing your projects to be more scalable.  You will be learning how to code with GDscript, with everything explained in detail.  Our scripts will be written to be highly customizable and reusable.  All of the project files will also be available on GitHub if you need to review the project as it was after completing each lesson.  These videos were recorded using Godot version 4.2.2.

This course will be part of a series designed to teach bite-sized pieces of game development that can all be used interchangeably with each other.  So let me know what types of games you're interesting in learning how to make and I'll try to include them in future courses in this series.

Meet Your Teacher

Teacher Profile Image

Thomas Yanuziello

Indie Game Developer

Teacher
Level: Beginner

Class Ratings

Expectations Met?
    Exceeded!
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Intro: Welcome to my course on Dialogue and event Systems in Gado. This course is a continuation of game development essentials in Gado but can be followed and applied to any project that involves a character that the player can control to interact with NPCs or objects by pressing a button and displaying dialogue. You're welcome to join our discord server to work on this course alongside your peers. In this course, we will cover the creation of dialogue boxes, events, and interactions. Then tie them together with progress flags, camera controls, and character animations. When you're done, you'll have a comprehensive dialogue and events system for your game that can be used to produce high quality cut scenes and complex branching narratives that react to players choices and allow them to progress through the game. You'll also learn useful skills for working with the Gada game engine, organizing and designing your projects to be more scalable. You will be learning how to code with GD script with everything explained in detail. We will apply object oriented design principles in our scripts, inheritance, encapsulation and abstraction, alongside other software engineering design patterns like Singleton and Composite pattern, to keep them organized, customizable, and reusable for a project of any size. All of the project files are also available on Github, if you need to review the project as it was after completing each lesson. These videos were recorded using Gadot version 4.2 0.2. The project starts with assets from K Kits Character and Dungeon remastered packs made by K L auberg and basic Guy bundle made by Penzla. All are available to download for free on dot IO. 2. Dialog Box: Hello, friends. Before we get started, you should have a game seen in your project that contains a character that the player can control and a level they can walk around in. You should have other characters to act as NPCs or objects for the character to interact with. Throughout this course, we will add the ability for the player to interact with the environment by pressing a button, either to initiate a dialogue with another character or interact with an object. These interactions will include the player progressing through the game, making decisions, and creating basic cut scenes with custom camera behaviors and animations. You can use your own project to follow along with this course or download a starter project from my Github titled Godo Dialog Course Main Branch. Your project can be two D or three D and almost any game genre that involves the player interacting with the environment or NBCs. Let's start by adding a dialog box to the game scene. Depending on the assets you're using, you may want to make the root node of the dialog box a panel or a panel container. But these assets are so large that I want to scale them down, which will require the panel node to not have any children, since the scale would be inherited, and I don't want that. You may want the dialog box to be visible over the FAD for artistic reasons. It really depends on how you want to use it. I'll have mine drawn first behind everything else on the UI. With my dialogues root node, just a simple control node that doesn't actually represent anything drawn on screen. I'll anchor the dialog to the bottom of the screen, covering the entire width and set the top anchor to two thirds down from the top of the screen. The dialog box will cover the bottom third of the screen, which with my window size is around 240 pixels. Then I'll add a child node, a panel to draw the actual box on screen, and set its anchors to full wreck, so it matches its parent. Giving the dialogue group node a new theme. We can add a panel to the theme and give it a new style box texture to draw it. I'll use box orange rounded as the texture from my dialog box with texture margins of 256 pixels on all sides. This image is huge at 1522 pixels. I'm going to scale the node down to half size. Then use grid snapping and set its dimensions to match the dialog parent node. Most dialog boxes will contain at least the name of the person who is speaking and a line of dialogue arranged vertically. Let's add a vertical box container and inside that, also add two labels. One for the speaker's name, and another for a line of dialogue. But for the line of dialogue, you might want to use a rich text label instead of a normal label. Rich text labels allow the use of BB code to do all sorts of formatting tricks to your dialogue text, such as bold, italics, or even colors and animations. You can express the tone of the speaker, associate related themes with unique colors, or highlight important keywords to help the player remember them. I'll resize the vertical box to fit inside the boundaries of the dialogue box. I tend to use the American spelling of dialogue when referring to a UI interface displaying text. But I'll use the traditional spelling of dialogue when referring to a conversation between characters in a scene. Let's fill these in with some placeholder text so we can see how they look. Somebody is speaking, and they're saying some oram Ipsum. Turning on fit content will force the rich text label node to expand to fit its contents like a normal label node would. Let's set the default font and font size for the theme, using the more legible font as the default with a reasonable size that's easy to read. I'll use 32. Adding rich text label to the theme, we can set the default color for the dialogue line and not the speaker's name since they are different node types. I'll use black to stand out better against the orange box. Then we can add the label node to the theme and use a different font to display the speaker's name. I'll also give the name labels a different color and a shadow, so it sticks out even more. I'll use a fancier font for the speaker's name label. Before writing any dialogue for your game, it's a good idea to get a rough estimate of how much text comfortably fits on screen in your game. Make sure your dialogue box is capable of displaying a reasonable amount of text while still being easily legible. Using fake placeholder texts like this, we can come up with a worst case scenario of how much text we can display and use that as a hard limit when writing dialogue for the game. There should also be some indication to the player that the dialogue has finished, and the game is waiting for their input to continue. Let's add a button to the dialog box. Since I want the buttons texture to match the scaling of the box texture, I'll have it as a child of the box and anchor it to the bottom right. This is a unique button, so instead of setting its style box in the theme, I'll use the theme override section to add a new texture style box that will only apply to this specific button. I'll use the arrow icon. Use the pixel dimensions of the image to define the button size, and position it where it looks good. I'll create more style boxes for the other button states using the same arrow icon. Then apply color modulation for each other state. Faded gray for disabled, green and yellow colors for the others. The theming for buttons is still available if we want to use it later to add themed buttons to the dialog box without affecting this button. Make sure you check how your dialog box appears in the context of the game and make any necessary adjustments you need until you're happy with how it looks. Once you're satisfied with your dialog box, we can hide it by default by clicking on the icon beside it in the scene tree. Next, let's add a script to it and save it in the UI scripts folder. Let's grab a reference to both the speaker label and the dialog rich text label, assigning them to private variables. Our script needs to be able to display any line of dialogue spoken by any speaker in our game, which means it will need a public function accepting two parameters, who is speaking and what they are saying. Both as strings. Both can be set by accessing the text property, assigning it to the parameter passed into the function. In older versions of Godot, the rich text label will have separate properties for text and BB code text. Make sure you're using the BB code text property if you want your BB code to work. We can make this more functional by having the speaker be optional, setting the visible property of the speaker label to be dependent on whether or not the speaker's name is an empty string. If we swap the order of the parameters, we can also make the parameter optional by assigning a default value of an empty string. Optional parameters must always be listed after mandatory ones when defining a function. We should also set the visible property of the dialogue box. Let's do that in a separate public function called open. And also a closed function to set it to false. Grabbing a reference to the continue button as well. After the text has been displayed, we should tell the continued button to grab focus so it can be accessed with a controller. Let's connect the button pressed signal to the script, and use it to close the dialogue box for now. In the game manager script, we can grab a reference to the dialogue box. When the game first starts, we can tell the dialogue box to display a message. Hello World. Since the speaker's name is optional, let's see how it works without it. This time, let's add a speaker name, ghost. The ghost will say, Boo, I'm a scary ghost, but it will have some BB code applied to it, so the player will get a better sense of how the line is meant to be read. If your labels leave the confines of the area, they will be cut off. But we can change that behavior in the control properties unchecking clip contents. There are a lot of different BB code tags that provide a wide variety of effects, and you can even create your own using a custom script that inherits from rich text effect. For a full list of BB code tags, refer to the Google Docs. We can now display a dialogue box to give the player information. In the next lesson, we'll add a typing animation and display sequences of dialogue. I'll see you in the next lesson. 3. Monologue: Hello, friends. In the previous lesson, we added a dialogue box to the game scene. In this lesson, we'll animate the typing, so the text appears gradually and allow the box to display sequences of dialogue. For demonstration purposes, I've referred it back to using Guram Ipsum for this lesson and I'll make the dialogue box visible. With the dialogue rich text label node selected, expand the displayed text section. We can see that the number of visible characters is an easily editable property with a default value of negative one. Negative one visible characters is obviously impossible and is being used to mean unlimited. But if we adjust this number by clicking and dragging it slowly to the right, we can see the text disappear and fill in gradually as the number of visible characters increases from zero to the length of the string. In our dialogue script, when we set the text being displayed, we can also set the visible characters to zero. We then want to increase the number of visible characters gradually over time until all of the characters are visible. Since we don't know how many characters there are, we can use a wile loop to keep repeating the same code as long as the condition is met. The condition, in this case will be as long as the number of visible characters is less than the total number of characters. Checking the documentation for the rich text label node. We can see that there is a function called get total character count, which conveniently returns the total number of characters excluding BB code tags. While the number of visible characters is less than the total character count, we can add one to the visible characters. But this loop and the entire function are going to run in the span of a single frame. The entire text will still just be displayed instantly. We need to tell the loop to pause for a brief time each iteration. We can tell the loop to await a signal being emitted by the scene tree, a signal that is emitted once every process frame, 60 times per second with the default project settings. This loop will now display the characters at a rate of 60 characters per second and will automatically adjust with the frame rate setting of the project. But what if we don't want to use the projects frame rate? L et's instead add a typing speed variable to the settings resource and give it a default value of 90 characters per second. In the settings menu scene, we can add a label and a slider to allow the player to change the typing speed. And give it a reasonable range. I'll set it to be 30-120 characters per second with a step value of ten. Connect its value changed signal to the script. And change the settings resource to match and initialize the value in the ready function. See the game development essentials course in this series for more information on game settings. Back in the dialog box script, we'll need a variable to hold how much time has passed since the typing started and set it to zero before the typing loop starts. Then in each loop iteration, we can add the amount of time that has passed by accessing the function, get process Delta T. Since we know how many seconds have passed since the typing started and how many characters we want typed per second, we only need to multiply these two numbers together to get the number of characters visible. Let's see how it looks. We can even change the typing speed in the game settings. The debug log tells us that we are narrowing precision, converting a float to an integer. We could either make the typing speed into a float or ignore this warning since we know that the slider was configured to only use integers anyway. So I'll add a warning ignore tag before this line. Godo knows to ignore the narrowing precision warning. This also happens in the settings menu script for the same reason. Now the typing speed is a bit faster, but some players may still want to skip the typing animation. So let's change the behavior of the continued button. If the player presses the continued button and the number of visible characters is less than the total character count, we can instantly set the number of visible characters equal to the total character count. This will display the entire text and force the wile loop to break since its condition is no longer met. If the number of visible characters is not less than the total character count, that means that the typing has already finished. In that case, we should close the dialog box. For this to work with a controller, the continue button will need to grab focus before the typing starts. When the typing animation starts and the player presses the continue button, either it will skip the typing animation or it will close the dialogue box, not both. Skipping the typing animation will still give the player the opportunity to read it before moving on. But what if we want a character to say more than one line of dialogue and keep the dialogue box open? For that, we can add another public function for displaying dialogue. This time, accepting an array of strings as the first argument. Let's start by just calling our other function to display the first line, using Index zero on the array, passing it and the speaker's name along to have it typed out into the dialog box. In order to then continue onto the second line when the continued button is pressed, we will need the script to remember what the lines are and which line is currently displayed. Since the scope of this array is only within the context of this function call, it cannot be accessed from another function. So declaring a new lines variable, an array of strings, we can assign its value at the top of the new function, and another variable for remembering which line is currently displayed as an integer. We can set it to zero at the top of the function call as well, and use it to index the array. Then when the continued button is pressed, we can add one to it, then check if it is still less than the size of the array. If it is, then there is more dialogue, so we can call the first function again. Changing this to be the private variable, which we declared above and used to store an array of strings passed into the public multi line function. If you want the speaker's name to continue to be displayed, you'll need to store that in a variable as well. And if not, then the dialogue is finished, and we can hide the dialogue box. But now the continued button will not work as expected if we only display a single line of dialogue. We will need to separate the public functionality of displaying one line of dialogue from the details of how it is accomplished by creating a private method that does the. The public functions, either accepting one line or multiple lines, can now both call the private function to achieve their goals internally. The first function can store the single line into the array as its only contents and set the current line to zero, and now it behaves exactly as the multi line function. But whether it is you or someone else writing the dialogue scripts for your game, there now exists multiple options for displaying a single line or multiple lines of dialogue. Opening the dialogue box and grabbing focus only needs to be done once for each set of dialogue to be displayed. And the continued button only needs to call the next line function as well. I typically like to organize functions in my scripts to have public at the top and private at the bottom and keep related functions together. This follows two object oriented principles, Abstraction, hiding the details of how things are done privately inside the script, and encapsulation, only exposing publicly the functions you want others to see and use. Let's try simulating a character speaking multiple lines of dialogue. First, we'll declare a variable to hold a sequence of dialogue on a array of strings and pass that into the function call as the first argument, which we will populate by opening a square bracket. But since the sequence of multiple long strings will take up quite a lot of space, I prefer to have each of them on their own line, and it helps to use indentation to keep them organized. Then the closing square bracket can be used to clearly mark where this array ends. Running the scene, we can see that the first line is displayed. Clicking on the continued button displays the next line, clicking on the continued button while the line is still typing, we'll skip the typing animation and display the complete line. When all four lines have been displayed, clicking on the continued button closes the dialogue box. But I don't want to have to declare a variable, populate it, and call a function every time. I would prefer to do it all in one line of code. Following a similar format, we can replace the variable declaration with the function call The parentheses add another layer of indentation. Our array is declared and populated within the function call along with specifying the speaker's name. However, the array now doesn't have a type specified, but since Godo Script is a loosely typed language, this is fine. We can actually remove the type requirements from our dialogue script function parameters. But be careful to only pass valid arguments or you will get errors. We can now display longer sequences of dialogue with animated typing and skipping. In the next lesson, we'll create custom event scripts that can display complex sequences of dialogue. I'll see you in the next lesson. 4. Event Manager: Hello, friends. In the previous lesson, we improved the dialogue system with typing, skipping, and multiple lines. In this lesson, we'll create an event manager to handle our games dialogue and synchronize it with other systems. Starting in the game scene, let's add a new basic node, and name it event Manager. The game manager script can grab a reference to this node using add on ready, along with the player node. Thinking of the scene tree like an organization, typically, if different branches want to request something from one another, they need to send the request through a manager above both of them. In this case, something in the level is going straight to the top of the game scene manager to request permission to use the event manager to run an event. If anything in our game scene wants to trigger an event to happen, they need only ask the game manager to start an event, passing the event as an argument, and we may also want to wrap up events with a common function too. The game manager can disable the player node, so the player can't control the character during an event. And when the event is over, we can restore the player's control. Once all preparations have been made, tell the event that they are good to run, passing the event manager reference. Can make this more flexible by also adding an optional Boolean parameter to the function call with the default value of true. Now it's possible to write some events that do not remove player control. In the player script, let's add a public variable for enabling and disabling player control. In the same lines where we are checking if the game is paused before processing input, we can add a condition using the or logic operator represented by two pipes. If the game is paused or the player has been disabled, we return and ignore the input. But if the character is moving when the player node is disabled, they will continue running in the same direction until control is re enabled. We can define how a variable is set, as well as add automatic functionality with a setter. Following a variable declaration with a colon, we can write a block of code for better restricting how this variable is accessed. Creating a set function, accepting a new value as a parameter, we can assign the new value to our variable. But we also want to ensure that if the player node is not enabled, that we tell the character to stop moving. This behavior becomes automatic anytime the enabled variable is set to false. Similar to a scene manager, the event manager's job will be to facilitate communication between different parts of the scene. But this script doesn't have the same responsibilities as a scene manager, so it won't inherit from that class. It can simply inherit from node. I will still save it in the manager's folder, however, since it is still considered a manager. At the top of the script, we will need references to anything that might be commonly involved in an event. The character, the dialogue box, and the FAD, all is public variables. You may also want to add extra functions to the event manager to bundle commonly used behaviors together, build an event queue, or even nest events inside other events. Now, any script in our game scene will only need to reference the game manager in order to be able to start an event, present dialogue, fade in and out, and even control the character. L et's switch to the level scene and set up a basic scenario where when the player enters this room, they are presented with a message. For that, we will need an area three D node with a collision shape. I'll make it a box that covers the entire doorway into the room, so there's no way the player can enter the room without colliding with this area. Keep in mind that these types of proximity triggered events can be disruptive to some players and should be used sparingly. Most players prefer to enter into events that remove their control voluntarily by pressing a button. It doesn't need to exist on any collision layer, masking for collisions with the player only. It only needs to be monitoring for the player and not monitorable by anything. We can attach a script And make a new folder restoring all of our event scripts. T's name it enter room, connecting the body entered signal to the script, We can write what will happen when the player character enters this area. We can find the game manager using dollar sign a notation, starting from the root node. We can tell the game manager to start an event, passing itself as the argument as the event that is to be started. You might want to do this using ad on ready, but I would recommend doing it when the event is triggered. Since not all events will be triggered every time a level is loaded into your game, so there's no need to have them searching the scene tree all at once, when it would be more efficient to do it only if they need it. Since we don't need the body reference, let's prefix it with an underscore to market for the engine. Before starting the event, we can also double check to make sure that what was passed as an argument is not null. And if it is not null, also make sure that it has a method named run event. Reversing the logic, we can check if this is null or if it doesn't have the run event method. If either of these conditions are true, we can return ignoring the request. I prefer to use if not event here, which means the same thing. Let's also print out a warning message to the developer about this, telling them that they have made a mistake that they need to fix. Push warning will display a yellow warning message in the log without disrupting the game. The event script can now define this run event function as a series of instructions passed through the event manager to any of the other scene components as required, and everything can be synchronized through the use of signals. To create our scenario, we only need to go through the event manager to access the dialogue box and tell it to display a message. Then tell the game manager that the event is over. Adding a weight to the front of any function, we can return a signal from it, so the event knows when this task is complete. Switching over to the dialogue box script, let's have the display line function, return a signal for when the dialogue is finished. And also do the same for the multi line function. At the top of the script, we can give the signal a definition, and emit it when closing the dialogue box. The main objective of all this infrastructure is to create scripts that read somewhat like a screenplay, giving clear instructions of who is saying, how they are acting, where they are standing, what the camera is looking at, et cetera. If the player enters this room, they are now presented with a message. Control has been temporarily taken away from the player while the message is displayed. Upon pressing the continued button, the dialogue box emits the signal that the dialogue is complete, allowing the event script to move forward, ending the event, Restoring control back to the player. We now have an event manager that disables player control while an event plays out involving dialogue. In the next lesson, we'll create an interactable component that the player can interact with to trigger events. I'll see you in the next lesson. 5. Interact: Hello, friends. In the previous lesson, we added an event manager that can run an event script. In this lesson we'll add interactions for the player to initiate. A more popular way to initiate events is when the player presses a button to interact with something or someone. Opening the project settings, switching to the input map tab. Let's add an interact button. I'll use E on keyboard or Y on my Controller. Then in the player script, We can check if the interact button was pressed, and if so, tell the character to interact with whatever or whoever is in front of them. To figure out who or what that would be, we can use a. In the character scene, let's add a child node to the rig of type three D. We want the ray to point in front of the character, a reasonable distance to be able to interact with something, I'll set it to 2 meters. Since the rig is the thing that's actually rotating, as a child, the raycast will rotate with it. The collision mask determines what the ray can collide with. We want this ray to find interactions. We should give interactions their own collision layer. Opening up the project settings in the general tab, under layer names, three D physics layers, I'll set layer five to be specifically for interactions. The ray cast will collide with layer five, but also be obstructed by Layer one, preventing the player from interacting with something on the other side of a wall. Expanding the collides width section, we will be looking for both areas, our interactions, and bodies like terrain. Positioning the height of the ray cast, it makes the most logical sense to put it somewhere around shoulder level, or at least somewhere between chest and eye level. But we can also use this as an opportunity to implement certain range restrictions on our interactions. Let's position the CSG box in front of the character and imagine that our interactable object or NPC is on top of the box. How tall can the box be before the person or thing on top of it is no longer interactable. Since the ray would collide with the box and not the thing on top of it. I think it's reasonable to allow interacting with something that is 1 meter higher than the character, but not 1.5 meters. So I'll position it somewhere in between. 1.3 meter seems good, which is half the height of the character's collider. In the character script, we can grab a reference to the cast node using add on ready. But note that doing it this way will require every character to have the cast node. By changing the dollar sign annotation to a function, get node or null. We can pass the node path as a string argument, which we can copy by right clicking on the node and selecting copy path, or click and drag the node directly into the quotation marks. Now for any character without a cast node, this variable will just be set to null instead of generating an error. L et's add a function to the character script named interact, which we called from the player script when the interact button was pressed. First, checking if the cast node exists, we can then check if it is colliding with anything. We can then check if what the ray cast collided with has a method called interact, and if so, call it. If the ray cast doesn't exist, nothing will happen. If the ray cast isn't hitting anything, nothing will happen. If the ray cast is hitting terrain like a wall, the terrain will not have a method named interact, so nothing will happen. If the raycast is hitting something on layer five, that has a method called interact, it will call that method, triggering an interaction event. But thinking about all the many different things in a game that the player might interact with, an NPC and a door are both interactable by pressing a button, but they don't share much else in common. An NPC might want to inherit the same properties and behaviors used by a playable character to walk around and such. While a door needs properties like whether or not it is locked and behaviors like opening or closing. Using an inheritance structure. You could make all doors into NPCs that just don't move or make all characters into doors with their otherwise door like behaviors and properties ignored. Using the interactable behavior as an abstract parent of both doors and NPCs is better, but still implies that all NPCs and all doors should be interactable, which is often not the case in most games. You might want to have an NPC class that inherits from character, but also inherits from another class to gain additional functions of being interactable. But Godot does not allow multiple inheritance. It would be better to have the interactable behavior of doors, NPCs, and anything else be independent of the nature of the thing you're interacting with. We can instead apply the composite pattern to accomplish this more efficiently. Creating a child node to represent the interaction, we can then attach an interaction behavior to anything we want. In our level, we have a few different characters. Let's pick one and attach a child node to it to represent the interaction component of this NPC as an area three D node. This area will not be monitoring for anything, but will be monitorable by the ray cast we created earlier. Therefore, its collision layer will be five, so it can be detected by the ray cast and its mask can be zero since it's not looking for anything. It will need a collision shape. I'll use a capsule and have it larger than the character. The ray doesn't need to be exactly pointing directly at them. As for the height of the capsule, we can apply the same logic as we did before in reverse. This time, imagine our character is standing on the box. How tall can the box be before our character is no longer allowed to interact with the NPC standing on the floor? I'll set the same restrictions and let the player interact with something 1 meter below them, but not 1.5 meters. Knowing that the ray is being cast 1.3 meters above the ground the player character is standing on, then the interactions collision shape be at that t plus 1 meter. Making it 2.6 meters tall seems reasonable, which also happens to be the same height as the character's collider. But note that even a tiny object like a key sitting on the floor, will need to have a collider, this tall to be hit by the recast. We can attach a script to this interaction. Let's call it mage dialog. And save it in the event scripts folder. This script will need two functions, interact and run event. The interact function will be much the same as the previous lesson, asking the game manager to run this script as an event. For the run event function, we can have the mage say anything we want. And end the event after they finish talking. Now let's duplicate this interaction node and attach it to the door. Then also duplicate the Mage dialogue script and call it cell door. Replacing the script attached to this interaction, then edit it to say something like the door is locked. In just a few simple steps, we are able to create any kind of interaction that player can initiate by pressing the interact button with any object or NPC. Since I modified the scale of an ordinary wall to create the door, the collision shape is inheriting its scale property. But after centering it within the door, it still does a good job of encompassing the door. Let's try it out. Pressing the interact button when we aren't facing anything doesn't do. But if we walk up to the mage, we can press the interact button to initiate a dialogue. Likewise, we can also walk up to the door and interact with it. We now have a door, the player can't yet open, and in NPC, the player can talk to by pressing a button. In the next lesson, we'll add dialogue choices and conversation trees to the dialogue system. I'll see you in the next lesson. 6. Choice: Hello, friends. In the previous lesson, we created an interaction component that can be attached to anything in our game. In this lesson, we'll add branching dialogue choices to the dialog box system. First, we'll need to add more buttons to the dialogue box. It's up to you how and where you want to position your dialogue option buttons. Whether they are in the same dialogue box, a separate box, floating in the center of the screen, accompanied by dialogue or not. For my game, I'll have my buttons inside the dialogue box. Adding another vertical box container to the dialogue box. L et's name it options, and it will contain three buttons named option zero, option one and option two. I'll give each of them some default text and set their text to be aligned to the left. If I hide the speaker's name and restrict the dialogue to a maximum of two lines, everything should fit inside the box. I'll just anchor it to the bottom right of the dialogue box and move it to a position where it looks good. Holding down the alt or options key while clicking and dragging will move only the selected node and its children. Editing the dialog box them, we can add for buttons. I'll have the default font color black. Disabled, faded gray. Focused covered and pressed all white, and add a black outline, so the white text is still legible. In the Constance tab, I'll set the outline to two points. Next, I'll replace all of the style boxes from the buttons with empty style boxes, so they just display as normal text. Selecting the Options vertical box container, I'll use the theme override section to add 12 pixels of separation between each of the option buttons to space them out. Take some time to organize your dialog box layout and use fake placeholder text to test the limits of how much information can be displayed at once, including how many options you would like to present to the player. Once you're happy with how everything is displayed, remember to hide the dialog box. We'll need to alter the script to be able to display everything appropriately. L et's grab a reference to the options as an array of its child nodes using the Get children method. And at a public function called Display Options, accepting a line of dialogue as a string and an array of strings for the options that the player will choose from. Like the display line function, we'll assign the array of lines to be just the single line that was passed as a parameter and setting it inside square brackets and set the current line to zero. When displaying dialog options, I want the speaker's name to be hidden, so I'll set it to be an empty string. We can then tell the dialog box to open it if it isn't already and start typing up the next line. Where this differs from displaying a simple line of dialogue, however, is displaying the option buttons, which I don't want to happen until after the line has finished typing. For that, we'll need another signal, emitted by the next line function. L et's call it finished typing. And await this signal after the next line call. We will also need to define this signal at the top of the script. Once the typing has been completed, the continued button is no longer relevant. So we should set its visible property to falls to hide it. We can then use a four loop to iterate through the array of option buttons. But since we need to match the indices of the button array with the indices of the string array, we can instead use a different kind of four loop. Using the alias I, which in programming is universally used for index, followed by the keyword, we can iterate through the arrays size. The value of I will start at zero and increase by one until it reaches the size of the button array, in this case, three and stop. We can now use the value of i to check if it is smaller than the size of the string array that was passed as a parameter to this function. In case the dialogue has less than the maximum number of options. If this is true, then we can set the text property of the button at index to the string value of the same index, and also set the button to be visible. If the button index is larger than the number of choices, then the button should not be visible. Finally, we can tell the top button to grab focus so it can be accessed with a controller. To make this work seamlessly with our other dialog display functions, we'll need to toggle the visibility of the continued button and the option buttons when typing dialog lines. I'll also move the line telling the continued button to grab focus to be here as well. With everything being displayed correctly, there's just a matter of telling the event script, which option the player selected. The return type of this function will be an integer. The index of the button the player pressed, which matches the index of the choice they selected. Each of our buttons, one clicked, will call a function. Let's call it on option pressed, with an integer parameter named index. This function, just like pressing the continued button, can close the dialog box and emit a signal. Let's call the signal selected, and pass the index along through the signal as an argument. At the top of the script, we can give this signal a definition, and this time, also give the signal an integer argument named choice. The display options function can now return a weight selected, which will cause it to wait until the player presses one of the buttons, then receive the integer index of the button that was pressed and return it. The last thing to do is connect the button pressed signals to this function. Make sure your script has been saved so the engine can find this new function. Selecting each of the buttons, connecting the pressed signal, select the dialog box node from the list. Ignore the suggested receiver method and click on the pick button. This presents a list of functions in the dialog script whose parameters match the signals arguments. Since the pressed signal has no arguments, only functions with no parameters are compatible. Click on the Compatible methods only toggle to see a full list of functions and select on options pressed. To fix the compatibility issue, we need to add our own arguments to the signal in the advanced section. Using the drop down, we can select any built in type for the argument. The one we want to use is an integer. Adding an integer argument to the signal, we can set its value as the array index of the button. Option button zero is index zero. Click and Connect. We can then repeat this process with the rest of the option buttons, changing the argument value for each to match its index in the array. The argument will now be passed to the on option pressed function, emitted by the selected signal and returned by the display options function back to the event script, which called it. To test out the dialogue options, let's modify the Maj dialogue event script. Similar to displaying a single line of dialogue. We can await em dialog Display Options, providing a short line of dialogue, followed by an array of options. This statement returns an integer between zero and the number of options in the array exclusively. Assigning the returned integer to a variable, we can then branch our dialogue based on its value. You can use an if statement for this, but I find match statements much cleaner for this purpose. The keyword match functions similarly to switch in other languages. In the block of code following the match statement, we can provide an option followed by a colon, then inside that block of code, proceed with what will happen in that case. And provide different cases for each of our dialogue options. Let's try it out. I have the text typing speed set to the minimum to test the continue button. If we talk to the mage, they will say a line of dialogue, which we can skip. After skipping, we are presented with three options, which have grabbed focus, and the continue button is no longer available. Selecting any of the options, the dialogue continues based on the selection we. If we do it again, we can select a different option. The mage says something different. We can simplify this script even more by replacing the variable declaration with the match statement. We now have dialogue options for the player to choose from, leading to branching dialogue trees. In the next lesson, we can allow the player to progress through the game during these events. I'll see you in the next lesson. 7. Flags: Hello, friends. In the previous lesson, we allowed the player to choose from a list of options and used their selection to create a branching dialogue. In this lesson, we'll use progression markers to change how events play out. Let's start by having an NPC's dialogue change each time the player interacts with them. In whatever script you're using for your games save file, in this project, we're using a custom resource named progress. We can add an exported variable for tracking how many times the player has interacted with the maj as an integer, which will naturally default to a value of zero. In my project, actual file saving and loading has been removed. Keep in mind that my demonstrations will always start with the default values. For more information on saving progress information and custom resources, see the previous course in this series, Game Development essentials. In the Mage Dialogue script, we can start our run event function by first matching the value of this variable. Then based on the number of times the player has spoken to the Mage, we can provide different lines of dialogue. It would make sense that if the player has never spoken to the Mage, that the first might start with an introduction. We can then access the same variable and increment its value by one. And the next time the player speaks with the mage, they will say something different and also increase the value by one. Adding a default option, in Gado script, this is identified with an underscore instead of a value. We can provide a repeating line of dialogue for after the player has exhausted all the previous options. The third time, speaking with the mage, and every time after that, we'll produce this line of dialogue. Let's try it out. The first time we speak with the mage, they introduce themselves. The second time they go on to being a hospitable host. The third time and every time after that, they will simply tell us that they are too busy to talk right now. Alternatively, instead of having repeated dialogue, we could this interaction after the dialogue has been exhausted, so this NPC will no longer be interactable. We can also create cycles of repeating dialogue by resetting the value to a lower number after the last option. So now the made will say something different every time repeating the same three lines in sequential order. Instead of creating a separate variable for each MPC, you might want to consider using an array of integers. We will need to initialize this array to contain a zero for each MPC. Since I have three NPCs, I'll add three zeros to the array. Then each MPC in your game can be assigned a number starting from zero used to index the array. We can make this easier and less prone error using an enumeration, a numbered list. I like to keep all of my enumerations from my project in a single script called enums. Starting with giving it a class name Enums. It doesn't need to inherit any of the properties of node since it won't be attached to one. The only contents of this script will be declaring enumerations to be used throughout the project. Our first num will be a list of all NPCs in the game, night, Mage, and Rogue. The convention for Enums is to use upper snake case, sometimes referred to as screaming snake case. The Mage dialogue script can now access the array and index it with the enumeration. F. These are common practices for NPC dialogue in games, but they don't really allow the player to make progress through the game. Let's use the door as an example of progress and have the player able to open the door during an interaction event, but only if they know the password. Knowledge of the password must be given to the player character by the mage through a separate dialogue event before they will be allowed to use it to open the door. In the progress resource, we can add an exported Boolean variable for whether or not the player knows the password, which will naturally default to false. Boolean variables used to control game progression are commonly referred to as flags. When speaking with the mage, the character will have the option to ask to see the prisoner. Upon selecting this option, the major will tell the player the password. At which point we can set the variable in the progress resource to true, since the player character now knows the password. Attaching a script to the door, We can add an exported variable stating whether the door is currently open and add three public functions, checking if the door is open, opening the door, and closing the door. Returning a Boolean. We'll just return the value of the is open variable, so other scripts can easily get its value, but not set it. Applying encapsulation. I'll also declare a variable to hold the y position of the door when it is closed as a float. And set its value during the ready function to be either its current y position or its current y position plus 4 meters, based on the value of is open. Each of these functions can set the is open variable then call a private function to move the door. To open a door, I'll move its y position down 4 meters and to close it, reset its position back to the closed value. With a tween variable. After checking to make sure the tween doesn't already exist and isn't currently running, create a new tween. I want the door node to remain interactable while the door is open. I will remain in the same position, and I will instead twe just the doors me. Opening this scene, ignoring the warning since I'm not making any edits. We can see that the mesh node of this door is n Wall Half. I'll use that as the node that is being tweed and tween its position y value. Since the position is the property, we can access the y property of the vector three structure with a colon in the property path to move to the new position over a duration of 1 second. Returning the tween finished signal up through the function calls in case any other script wants to await it. This is a little overkill for this demonstration, but this script is now flexible enough to work with both opening and closing similar doors anywhere in my game. In the doors interaction event script, we will need a reference to the doors root node. For simplicity, I'll just use get parent. We can first check if the door is currently open. In this case, we can end the event to give control back to the player, then tell the door to close and return to prevent any more of this script from running. The player won't have to wait while the door closes and can continue playing. If the door is closed, then we can check if the player knows the password or not. If the player doesn't know the password yet. We can present some simple dialogue about the door being sealed shut and end the event. But if the player does know the password, I'll have the barbarians say the password in line of dialogue, end the event and tell the door to open. I would like to prevent the player from interacting with the door while it is opening or closing in case the matching the button. In both cases, I'll set the collision layer of the interaction node to zero, preventing the interaction rec from detecting it. Then add a weights and set it back to its original value after the door has finished opening or closing. We can see the value by covering the mouse over the bit mask. Each layer is represented by a single bit, either a zero or a one in memory. And each bit is numbered starting from zero. Layer five is bit number four. Its value represented as an integer is then two to the power of four, which is 16. So we can reactivate this collider by setting its collision layer back to 16. If using multiple collision layers, adding their bit values together will generate the integer value of the bitmask. Any integer can be used as a bitmask in this way and is an extremely efficient way of storing up to 32 Boolean flags inside a single integer. The door needs to be set using at on ready. Let's try it out. When attempting to interact with the door first, the door is sealed shut. But after speaking with the mage and gaining knowledge of the password, interacting with the door again, the interaction plays out differently and the door opens. Once the door is completely open, we can interact with it again to close it. We now have progress flags that allow the player to make progress through the game using events. In the next lesson, we'll change what the camera is looking at during events. I'll see you in the next lesson. 8. Camera: Hello, friends. In the previous lesson, we allowed the player to progress through the game by setting and reading flags during events. In this lesson, we'll add a cinematic camera to the event management system. Let's start in the game scene by adding a camera three D node to the event manager. And set a reference to it in the event manager script. Then in the level sn, using the maje dialog event as an example, we can add another camera three D node as a child to the interaction event. Then move the camera around and use the camera preview toggle to position or rotate the camera to where we think it will look good as the perspective for our dialog event. Once you're satisfied with how it looks, change the type of the node to Marker three D and rename it to something more appropriate. Retaining its transformed properties of position and rotation within the scene. We can now use this marker to position the cinematic camera during the dialogue event. Adding a script to the cinematic camera, I'll put it in the events folder. We will need a public function to move the camera to a marker, setting the camera's position and rotation to match that of the marker and making this the current camera. But since positions and rotations of children are relative to their parents, we'll need to use global position and global rotation to make sure we're getting the correct values. Then in the Maj dialogue event script, we can grab a reference to this marker node and tell the event manager to move the cinematic camera to it. When the event ends, we will need the game manager to switch back to the player's camera. In the game manager script, we'll need a reference to the player's main camera so that when events are ended, we can make the player camera the current camera before allowing the player to start playing again. Let's try it out. When speaking with the mage, the cinematic camera moves to the marker and takes over. Then when the event ends, the player's camera resumes control. We can also fade the transition between cameras quite easily. Adding a function to the event manager script, let's call it fade to marker, accepting of Marker three D, and returning a signal. We can await fade to black, then send the marker onto the cinematic camera. Then return fade to c. The game manager can also fade in and out when switching back to the player camera. But it would only make sense to do this if the event was using the cinematic camera. We can also make this behavior optional by adding an optional Boolean parameter to the function and only use the fade if requested by the event specifically. Adding true to the end event function call, and awaiting Fay to marker at the start of the script. The camera now fades in and out as it switches perspective at the start and end of the event. If your events have more than one marker, you might want to sort them under a parent node. Let's call it camera markers. And in the event script, grab a reference to it as an array of its children. The event script can request to the camera to move to camera marker zero, one, two, et c, more like a film set. But what if we want the camera to pan? Let's add some more functions to the camera script, Pan direction, to move the camera using a vector and pan to marker to gradually move the camera directly to a specific location. Both will accept a duration of time for the pan and return a signal to tell the event when they are finished in case we want to await it. To gradually change something over time, we'll need a tween variable. Both of these functions can be simplified into one private function, Let's just call it tween camera, accepting the target destination, the time duration, and returning the same signal. Since pan to marker already knows its target destination, we simply pass that information along to the tween camera function, accessing the global position of the marker. And the Pan direction function can calculate its target destination by adding the direction to its current global position. After checking if the tween already exists and is running, if so, kill it, then create a new one. Then we can tween the cameras global position to be the target destination over the requested duration. In the pan to marker function, it would also make sense to match the markers rotation as well as position, but maybe not every time. Let's add an optional Boolean parameter to allow this behavior. We could duplicate the tween camera function to add another with rotation, but I would prefer to add the rotation in the Boolean parameter to this function, giving the rotation a default value of vector 30. If the event wants the camera to match the markers rotation, Then also tween the camera's global rotation to match the target rotation over the duration. By default, multiple tweens will happen in sequence, but we want to sap in simultaneously. We can add another function call here parallel to achieve this. If we change these argument types from a marker three D to a generic node three D, then any node three D can be used as a marker, including the player's camera. They will also need to set this as the active camera if it isn't already. In the event manager script, we can also change this Marker three D type to a node three D. Grabbing a reference to the player's camera node will mark it as private, so other scripts won't see it. We can then add a function that moves the cinematic camera to the player camera's position. In the Mage dialogue script. Instead of using the Fade, let's try setting the cinematic camera to the player camera's position and panning the camera to the marker during our event before the first line of dialogue. Then pan the camera upward and fade back to the player camera at the end. Let's try it out. This dialogue now looks more like a cut scene with the camera moving around. But we can take this even further by creating camera tracks that will guide the camera around the scene in a smooth sweeping motion. Start by adding a path three D node to the interaction event node. This node has one property, a curve three D resource. Clicking on it to expand it, it contains an array of points. Adding a point, each point contains a vector three position. Starting with the first point at the original markers location, we can add more points to the array. To move the camera through the doorways, Then to the guards window into the prison cell. The other point properties are anchors that add weight to the calculation of points before and after them along the curve. The first point only has an out anchor, the last point only has an in anchor, but every point in between has both. Adjusting these anchors will smooth out the curve following a bezier curve formula. Now that we have a path we would like the camera to follow, we need to add another node to the path. The path follow three D node. The second property of the path follow node, the progress ratio will move it along the path by a percentage. Adding a camera three D node to the path follow node and giving its original rotation looking towards the mage. We can then click the preview toggle to view from the camera's perspective. Then adjust the progress value and watch the camera sep through the scene. We can now delete the camera three D node. To do this in our scripts, we only need to grab a reference to the path follow node in the event script. I'm going to break the dialogue up into separate lines to better synchronize with the camera movements. We can also use this as a marker to position the camera if we want before initiating the movement. We can then tell the camera to follow a path, passing the path follow node as an argument, along with the duration of time. Let's define this function in the camera script. Accepting a path to follow and a duration of time as parameters. The first thing we will need to do is re parent the camera to the path follow node, then reset its position and rotation to match it. Doing all the usual tween things and returning the signal when finished. We can then tween the progress property of the follow node to reach a value of one over the duration of time. And make this the current camera. Finally, the game manager can re parent the cinematic camera to the event manager while ending the event if necessary. L et's try it out. After mentioning the prisoner, the camera sweeps through the scene to look at them through the window. If you want to restrict how the camera rotates while following the path, it's easy to limit it to either only rotating about the y axis or not rotating at all. We now have a cinematic camera that can take our simple dialogue events and turn them into cut scenes. In the next lesson, we'll also add character animations. I'll see you in the next lesson. 9. Animation: Hello, friends. In the previous lesson, we added a cinematic camera to the event system. And this lesson will animate the characters during our events. Let's start in a character scene. Selecting the animation tree node, the animation tree panel opens at the bottom of the editor, Displaying the state machine that controls the characters movement mechanics. For more information on the basics of character animation and state machines, see the introduction to three D game development course. To prevent confusing tangled webs of state transitions, we can keep different types of animations separated into different state machines. Right clicking in empty space, let's add a new state machine to this one. For lack of a better name, I'll call it miscellaneous. Using the Connect tool, add connections from locomotion to the new state machine and back again. Using the select tool, select the transition from locomotion to the new state machine. Expand the advance section and change the advance mode from auto to enabled. This will allow us to trigger the animations through script instead of expecting the animation tree to automatically handle the transition, like what is happening with the movement mechanics. Selecting the other transition, expand the switch section, and change its switch mode to at end, allowing any of the animations inside the new state machine to finish before returning to locomotion. Selecting the new state machine itself, change the state machine type from root to grouped. The movement mechanics are currently the root state machine, and this new state machine will contain similar animations which will be grouped together to help keep things organized. Then click on the Pencil icon to edit this new state machine. We can return to the root of the state machine by using the bread crumb buttons at the top of the animation tree panel. In this state machine, we can add any animations that both start and end with the idle pose, which are meant to be played once and not loop. In this asset pack, the cheer, interact, and pick up animations fit this description. For the state machine to work as expected, the number of transitions from start must match the number of transitions entering the state machine. Likewise, the number of transitions exiting the state machine must match the number of transitions leading to the end state. Since there is only one transition into and out of the state machine, there can be only one transition connecting from start to end. We need an intermediate state in between, the state which both starts and ends all of these animations, the idle pose. Unfortunately, this asset pack does not contain an idle pose, but we can make one easily. Switch to the animation panel at the bottom of the editor. And select the idle animation from the dropdown. Click on the animation button beside the dropdown, then select duplicate. Rename the new animation idle pose. Change the duration of the idle pose animation to zero and turn off looping. Back in the animation tree panel, inside the miscellaneous state machine, we can add two copies of the idle pose animation. L et's name them Idle Post start and Idle Pose end. Then connect from start to idle pose and then to each other state from there. And each of these to the second idle pose, and finally, the end state. Selecting each of the transitions from idle pose start, change their advanced mode to enabled. And the transitions to idle pose end can switch at end. Now the number of transitions into and out of the state machine match the number of transitions from start and end to end. There is no possibility of ambiguity for the animation tree to navigate it smoothly. We can preview how these animations look by clicking on the play button beside them. And we can add a short duration of cross fade to the transitions in and out of the state machine to better blend them with the locomotion states. When you're satisfied with the state machine, we can save it as a resource within the project. After creating the idle pose animations for the other characters, this state machine can be loaded into their animation tree nodes since they all have the same animations. In the character script, we can add a public function to trigger the transition to any of these animations by simply passing the name of the animation as a string. We just need to tell the state machine playback to travel to the animation by adding the name of the grouped state machine which contains it to the front, along with a separator. But we will need to manually restrict only allowing this to happen from the locomotion state by first checking the state machines current node. If the current node is not locomotion, we should ignore this request. This will prevent starting these animations while in mid jump. We also might not want the character to be able to move while these animations are playing. Let's add an optional boolean parameter named locked with a default value of true. And also declare a private variable for the script indicating whether the character concurrently move or not. Adding a setter to this variable after setting its value, If the character can't move, we should also set the value of input direction to zero to stop the character if they are already moving. Then in any function that moves or jumps, we can add a conditional statement checking if the character can move before allowing it to happen. When told to perform an animation, if the animation locks movement, then we can set the can move variable to false and set it back to true after the animation finishes. But we can't just await the animation finished signal, since it will be emitted when the current animation finishes, which is idle, not the one we want to wait for. Placing the wait command inside a while loop and just continuing the loop, we can await every animation finished signal repeatedly until the specific one we want is emitted. Holding control or command and clicking on the signal. We can see that it passes an argument of the animation name that just finished. O. We only need to compare this value to the animation name, which when received, will now break the loop of awaiting and set the can move variable back to true. Lastly, to use this as part of the event management system, we should have a return a signal, just in case we want to await it. Let's call this signal animation finished. We can emit and return the signal if the animation was canceled and give it a Boolean parameter for whether or not the animation request was successful. Passing false is the argument. And at the end of the function with true. Defining the signal and its parameter at the top of the script. Since our event manager already has a reference to the player character, telling them to perform these animations is now trivial. In the cell door interaction script, let's await and interact animation before any dialogue happens. In the Maj dialogue script, we will need a reference to the Maj character in order to be able to request animations from them, which can easily be done using add on ready and get parent, since this interaction note is childhood to the mage. If you want to include multiple characters, simply export a variable for each one that is included in the dialogue. I'll have the mage cheer while talking by excluding the await keyword, but pretend that the animation is actually angry. Note that we can also give the characters other commands such as start jump or face direction. Let's try it out. When speaking with the mage, they angrily flail about while shouting at the player character in dialogue. If we interact with the door, the player character will reach out and touch it before initiating the rest of the event. We now have the ability to animate characters during our events. In the next lesson, we'll move the characters around to. I'll see you in the next lesson. 10. Direction: Hello, friends. In the previous lesson, we added character animations to the event system. In this lesson, we'll tell the characters to walk and run around two. Let's set up a basic cut scene. When speaking with the mage, let's have them walk back to the wall, grab something from the shelf, return to the player and hand it to them. Like we did with the camera, we can add marker three D nodes that tell the character where to move. Much like stage directions. We can add one as a child of the mage to easily copy their exact current position. Then duplicate it and move the new one to be over by the shelves on the wall. Making sure to leave enough room so the character's collider doesn't collide with the wall before reaching the destination. If you have multiple markers in an event, it would help to have them sorted under a parent node. Clicking and dragging them to be re parented. But if the character walks around, since all of these nodes are children of the character node, they will all move to. We can select the parent nodes holding our markers, expand the transform section, and click the top level toggle, which will prevent the transform properties from being inherited. The markers will remain stationary while the made walks around. Then in the event script, we can grab a reference to this node as an array of its children. After a line of dialogue, we can tell the mage to walk to character marker one, interact, then return to character marker zero and interact again before displaying another line of dialogue. In the character script, we can add the function that tells the player to move to a marker. Accepting a three D as a marker, and an allowable distance to the destination as a float for parameters. This will allow the function to end when the character gets close enough to the marker without having to stand directly on top of it. Let's give it a default value of a quarter meter. Then return a signal when it's done. Let's add a signal named destination reached then emit it and return it at the end of the function. Subtracting the character's current global position from the destination will give us a vector pointing to it, a vector with both a direction and a distance. Our character controls assume a vector length of one, as that is what is provided by the input singleton. When using this direction to move a character, we will have to normalize it first, setting the vector's length to one. Simply by setting the direction variable, the physics process will then move the character. If we wrap all of this in a wile loop, we can keep running this code while the character is farther from the destination than the allowable distance. Awaiting the next physics frame This code will run alongside the physics process to move the character from where they are to where they're going. When the destination is reached, we can set their direction to vector 30 to stop their movement, but the character will still decelerate forward from here. If we want the character to more accurately stop on the marker, they will need to start decelerating before reaching it. So we can split this into two phases before and after the point where the character starts decelerating, called the stopping distance. We can calculate the stopping distance by taking their current x z velocity, averaging it with a velocity of zero, which can be simplified into dividing it by two, then also dividing it by their deceleration. The stopping distance will be longer the faster the character is moving, and also be dependent on the rate of deceleration. Up until the character reaches the stopping distance, they will accelerate up to their maximum speed. When reaching the stopping distance, they will start to decelerate and come to a complete stop more or less directly at their destination. If the allowable distance is achieved before the stopping distance, then the character will still stop. All of this can be done using a turn rey statement in one line. As long as the player character interacts with the mage from the front, this works fine. But if the player character is standing in the way of where the mage wants to walk, this may cause the mage to be unable to reach their destination. While navigation around a static environment is fairly simple, dynamic path finding around moving obstacles is a difficult problem to solve. Many older games often had characters walk through each other or could get blocked for a limited duration of time and then would snap to their destination as a last resort. Neither of these look very good. Most modern games, including AA, will instead opt to have their cut scenes operate in an entirely controlled environment and eliminate any possibility of path obstruction. To achieve this, any cut scene that evolves characters walking around will start by fading to black. Then every character in the scene can be repositioned, if necessary before starting the scene. Then when the scene fades back in, every movement is carefully choreographed. We can add another character marker to specify where the player character should stand during this interaction. But with the top level toggled on, the character markers appear to have moved to the scene origin, but they still retain their global positions as before. This does make adding more markers difficult. Let's turn off the top level toggle and edit the transform position to force it to update, then reset it to zero. Now it appears in the correct position. Let's add one more and move it to where the barbarian should be standing. Setting its rotation to face toward the age. Then turn top level back on. In the character script, we'll need a function that will instead of wocking the character to a marker, simply set their position and possibly rotation directly to it. Accepting a node three D as a parameter and an optional parameter to match the y rotation with a default value of true. We don't need to return a signal since this will happen instantaneously. Setting the character's global position to match and if matching the rotation, also set the y rotation of the rig to match that. When the cut scene starts, we first fade to black. Then position the player character on character Marker two, matching its rotation as well, before fading back in and resuming the rest of this event. Now, even if we deliberately stand in the major's path, the cut scene will fade to black to correct this before playing the event in a more controlled fashion to prevent potential problems. T L et's make the character walking function even more flexible by adding the option to have the character run. Adding a Boolean parameter named running with the default value false. We can tell the character to run at the start of the function and walk at the end of the function if this parameter is set to true. We can also add another function which will allow the character to follow a path with multiple points instead of just one. Starting with the same function definition as move to maker, let's rename it follow path. Change the first parameter to path and remove the type. We'll just assume it's an array of node three D without enforcing it. Then emit and return the destination reached signal. Declaring a new variable for the current point in the path we are heading towards, which will start at zero. As long as this number is less than the size of the array, we should await moving to that marker. And we can pass along the same allowable distance and running values. In the dungeon scene, I've gone ahead and positioned a marker for the barbarian to walk to after opening the door, and three markers which form a path for the roge to run along. Since the door interaction node doesn't move, there's no need to worry about the markers moving either. The cell door interaction then needs a reference to these markers, as well as the rogue character. While the door opens, the barbarian can move out of the way to marker zero. Then the rogue will show thanks while exiting out of the prison cell. I'll add more allowable distance leniency and have the character running. I'll let the player have control back as soon as they're done with the dialogue box, so they don't have to sit idle while the rogue runs. Let's try it out. Interacting with the cell, the door opens and the barbarian walks off to the side. Then the rogue shots thanks and escapes. We now have characters able to walk and run during events. And the next lesson will improve the modular design of our events. I'll see you in the next lesson. 11. Modularity: Hello, friends. In the previous lesson, we had characters walk and run around during events. In this lesson, we'll wrap up with some improvements to the event scripts. Throughout this course, we have created a variety of different events. Some triggered when the player enters a specific area, others when pressing a button. Looking at these two scripts, we can start to see the similarity of the run event function with something like a screenplay, giving directions to characters and cameras on a film set. You can imagine in making an entire game. There are going to be hundreds or possibly thousands of these scripts, possibly written by multiple people. We want them to be as easy to write and edit as they possibly can be to reduce the amount of time required to produce them, as well as standardized to reduce potential bugs. Let's start by adding a class name declaration to the event manager script. Then in our event scripts, specifying the type of the event manager, the engine will now produce autocomplete suggestions when accessing variables or functions inside the event manager. A useful feature to reduce bugs in any script that uses it. Next, it would be useful to separate how an event is triggered from how it has run. Let's create a new script inside the event folder. While Godot doesn't actually have an implementation of an abstract class, we can still pretend this is an abstract class, one that is never meant to be instantiated in any way, only inherited. Inherited from Aa three D, and give it a class name interaction event. Moving the interact method into this class, Any interaction event script in the game can then inherit from interaction event to hide the interact function through inheritance. We can also clean up the collision layer code by calling inherited methods with appropriate names. Like set interactable, passing true or false as an argument. Then the interaction event class can hide the details of how this is achieved and standardize it for all interactable events in the game. Setting the collision layer to 16 if it is interactable or else zero to become uninteractable. It would be a good idea to further inherit from interaction event to create a common class that can be applied to every similar interaction in your game, such as having one script for all doors or one script for all pickups, for example. The only differences between their similar interactions being exported variables or references to other nodes. Many events in games are only triggered under certain conditions, and many become irrelevant after they have happened once. Any of our interaction event scripts can easily override the interact method to add conditions, checking the player's progress for flags if necessary, before calling the superclass definition of interact to start the event. Okay. Setting the same flag inside the event. We can also make this event uninteractable to prevent the same event from happening twice. Another popular option is to use the ready method to check if this events conditions are met and set it to be interactable or not when first loaded into the game. This is often the case anytime triggering an event requires zoning out and back into the level to reset it before talking to an NBC, for example. The conditions for the event are being checked only when first loaded. Let's duplicate the interaction event script and also create a proximity event script. Changing the class name to match. We can swap out the interact method with on body entered, accepting the body as a three D parameter. This collision will trigger the event, but the body entered signal isn't inherently connected. In the ready function, we can manually connect the signal to the callable method. Let's also set the collision mask to detect the player, which is layer nine, bit eight, two to the power of eight is 256. Now any event script can inherit from proximity event and its triggering conditions as well as collision masking are all handled automatically. In this project, I have set all the characters to the same collision layer, so any of them will trigger proximity events. Opening the project settings, Under layer names, three D physics. Let's add a separate layer for NPCs. Then in each NPC scene, we can change their collision layer from player to NPC and have them both mask each other. Now only the player character will be detected by proximity events. Let's also make another type of event, one that is not triggered by the player at all, but can be triggered through other scripts. The game manager will likely start an intro event the first time the game is loaded. A level might also want to trigger events when the player enters it, or events might want to trigger other events. Creating a new script in the events folder, let's call it scripted event. This time it will inherit from node, since it doesn't need any behaviors of the Aa three D node class. Since a scripted event doesn't need to trigger, the only purpose of the script will be to ensure that there is a run event function, and also provide a signal that will be emitted when the event is finished, which can be emitted and returned. Any script which inherits from this class can now return super dot run event to automate this behavior. Adding a variable to the player's safe file, let's call it intro played Ablean, which will naturally default to falls. Let's create an intro event that will only be triggered once when the game is loaded for the very first time. For simplicity, I'll just have it fade in since the current state of the fate is black when the game first loads. Then say hello world and set the flag to true. Then return Spot Run event, which emits and returns the finished signal. And also tell the game manager to end the event. Adding a new note to the game scene to hold de scripted event. The game manager can grab a reference to it using add on ready. In the ready function. We can now check this flag. Then start the intro event and await its finished signal. If the flag has been set to true, then it will be skipped. Running the scene, the intro event ps. If the player saves and reloads the game, the intro scripted event will not trigger a second time. I'll demonstrate by defaulting the variable in the sa file to True. Let's add another node to contain a secondary event, one which will be triggered by a primary event. Adding a script to this node inheriting from scripted event It will just say secondary event, and return Super dot run event. Any event script in the game can now await running this secondary event and it will be executed before returning to the event that called it. This can help divide extra long events into more sizable chunks, split branching narratives into separate scripts, or recycle the same event to be used in multiple contexts. We now have a very comprehensive dialogue and event management system in place that can be used to create complex branching narratives, compelling cut scenes and give the player choices of how they want to progress through the game. The system is also optimized to make producing the event scripts fairly simple. 12. What's Next?: Welcome to my course on inventory and shops in Gado. This course is a continuation of dialogue and events in Gado but can be followed and applied to any project that involves picking up items, adding them to an inventory, dropping, using or equipping items from an inventory menu, and trading items with an MPC. You're welcome to join our discord server to work on this course alongside your peers. In this course, we will cover different methods of picking up items, managing the players inventory data, displaying their inventory on the UI in a menu, allowing the player to interact with their inventory in different ways, and trading items with NPCs. When you're done, you'll have a simple inventory system for your game that is easily customizable in its presentation and function. You'll also learn useful skills for working with the Gada game engine, organizing and designing your projects to be more scalable. You'll be learning how to code with GD script with everything explained in detail. We will apply object oriented design principles in our scripts, inheritance, encapsulation, abstraction, and polymorphism, to keep them organized, customizable, and reusable for a project of any size. All of the project files are also available on GitHub, if you need to review the project as it was after completing each lesson. These videos were recorded using Gdoersion 4.2 0.2. The project starts with assets from K Kits, character and Dungeon remastered packs made by K Lauberg. Basic Guy bundle made by Penzilla, and icons made by Shakahi. All are available to download for free on it. Sound effects made by Valensbyer are also included and are available on freesound.org.