UE5 Blueprints for Complete Beginners: Build a Shoot 'Em Up from Scratch | Dev Enabled | Skillshare

Playback Speed


1.0x


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

UE5 Blueprints for Complete Beginners: Build a Shoot 'Em Up from Scratch

teacher avatar Dev Enabled

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.

      00 Intro

      1:47

    • 2.

      01 - Setup

      9:24

    • 3.

      02 - PlayerPawn

      7:24

    • 4.

      03 - Gameplay Framework

      10:56

    • 5.

      04 - LegacyInput

      8:23

    • 6.

      05 - PlayerMovement

      10:58

    • 7.

      06 - SmoothMovement

      18:33

    • 8.

      07 - EnhancedInput

      10:22

    • 9.

      08 - Boundaries

      13:16

    • 10.

      09 - Materials Instances

      13:16

    • 11.

      10 - EnemyMovement

      14:41

    • 12.

      11 - EnemyCollision

      12:31

    • 13.

      12 - Inheritance BaseClass

      9:59

    • 14.

      13 - RefactoringPlayer

      10:33

    • 15.

      14 - Refactoring Enemy

      7:49

    • 16.

      15 - HealthSystem

      12:35

    • 17.

      16 - ImprovedMovement

      14:08

    • 18.

      17 - ImprovedRotation

      13:04

    • 19.

      18 - EnemySpawner

      15:16

    • 20.

      19 - ProjectileBase

      22:13

    • 21.

      20 - ProjectileFire Enemy

      13:30

    • 22.

      21 - ProjectileFire Player

      7:07

    • 23.

      22 - ScrollingBackgronds

      18:23

    • 24.

      23 - NiagaraParticles

      13:34

    • 25.

      24 - AudioEffects

      10:08

    • 26.

      25 - SpawnAnimations

      23:57

    • 27.

      26 - CameraShakeKnockback

      18:56

    • 28.

      27 - UI

      16:54

  • --
  • 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.

7

Students

--

Projects

About This Class

Class Overview

Build a complete shoot 'em up game in Unreal Engine 5 using Blueprints. Go from zero code to a fully playable game with player movement, enemies, projectiles, particle effects, audio, UI, and a working game loop. This 27 topic series focuses on programming best practices, clean project architecture, and understanding why things work, not just clicking through steps.

Every system is built properly: frame-rate independent movement, inheritance-based class hierarchies, reusable spawner architecture, and component-driven design. You'll write the same kind of code you'd write on a real project. No shortcuts that fall apart the moment you try to extend them.

What You Will Learn

  • Project setup and organization that scales (folder structure, naming conventions, project settings)
  • Player and enemy movement with frame-rate independence using Delta Time
  • Enhanced Input System — Unreal's modern input framework
  • Collision detection and overlap events
  • Inheritance and refactoring: building a shared base class, then converting existing Blueprints to use it
  • A health and damage system that works for any actor
  • Spawner architecture with randomized timing and positioning (reused for enemies and backgrounds)
  • Custom projectile systems — why we skip the built-in Projectile Movement Component and build something better
  • Materials and Material Instances: understanding master materials, texture channels, and creating color variations
  • Scrolling backgrounds with parallax-style depth
  • Niagara particle effects (thrusters, explosions, impacts)
  • Audio integration and game feel polish
  • Spawn animations using timelines and curves
  • Camera shake and knockback
  • UI creation with Widget Blueprints, including a full game loop (play → die → restart or quit)

Why You Should Take This Class

Most Unreal Engine tutorials show you what to click. This one explains why. Every decision. From choosing overlap events over blocking collision, to building custom movement instead of using built-in components. Everything is explained so you understand the tradeoff, not just the result.

The series is structured so each video builds directly on the last. Concepts introduced early (like frame-rate independence and component hierarchies) reappear throughout, reinforcing them through practical use rather than simple repetition. By the end, you'll have built a complete, extensible game. More importantly, you'll understand the architecture well enough to modify it, expand it, or apply the same patterns to a completely different project.

No prior Unreal Engine experience is required, but basic familiarity with the editor interface is helpful. If you've opened Unreal and felt lost about where to start building something real, this is that starting point.

Meet Your Teacher

Teacher Profile Image

Dev Enabled

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. 00 Intro: This is what you'll be building a complete shoot them up in the Unreal engine five entirely in blueprint. I'm Rob, and over these 27 topics, this series will take you from an empty code base to a finished playable game. We'll be creating our own fully customed player movement with framerate independence, using the enhanced input system and covering things like collision detection in depth. We'll be implementing the pawn classes for the enemies and the player planes with a focus on inheritance, so one base class, which powers every ship type, adding a projectile system that will work for both sides, a single class, which is fully customizable. Even the spawning classes that we create are set up with an architecture which will handle enemies and backgrounds, and you can extend this however you like. We'll also be taking a small look at things like design and just juicing up the game with particle effects, audio, camera shake, and even knock backck. We'll be adding a complete game loop, so you have a playable game by the end of this, including your user interface. You can play, die, restart, or quit. The focus is going to be on doing things properly. We're going to be looking at clean architecture, reusable systems, and actually understanding why something works or doesn't instead of just copying the notes. Every decision gets explained. I'll purposely be skipping built in features inside of Unreal, and then tell you why we take that choice. When something is counterintuitive, I call it out and all of the assets that you need to follow along and get this final results are provided, meaning that you can just focus on the programming and getting that good project structure down. I've handled the art for you. Your class project is going to be this finished game fully polished, juiced up and ready to expand into a portfolio piece, along with any new features that you decide to add. There could be new enemies, weapons, a scoring system, whatever you wanted. The architecture is going to be laid out in a way for you to build and extend. For example, I've taken the project to look something like this over the space of a couple of months and adding a bunch of new features and assets to an extended project. The first video is going to be covering the project setup. I look forward to seeing you there. 2. 01 - Setup: Most unreal projects become impossible to navigate within a month. Assets are going to be scattered everywhere, no naming conventions, and you end up spending most of your time searching rather than building anything. I've created this series with an intention to fix that from the start. I'll be helping you build a complete shoot them up from scratch, where every topic will be focusing on getting things done properly. This is good structure, good naming, good architecture, skills that will transfer into any project that you work on. We'll be working towards building the game that you can see in the background here based on official content created by Epic in their content examples package. If this is something that grabs your interest, then let's get going and set up that foundation. First, I'd recommend that you navigate to the download link in the description. You'll see two options here. We have shoot them up underscore assets. This contains the rule meshes and textures in case you wanted to modify anything in blender or Photoshop. Then you have the shoot them up, underscore tutorial underscore start, and this is the complete project. And this will be the one that you want to follow along with the topics that we run through. By default, this will be provided as a zipped folder for you. So you need to make sure to extract this somewhere sensible, and starting from the same project simply means that we're working with identical settings, the same assets, working at the same scale and so on, which will make troubleshooting a little bit easier if you do run into issues. The first thing to look at inside of the extracted folder, go into the shoot them up underscore tutorial folder. By default, the project is in Unreal Engine 4.27 for maximum compatibility if you wanted to work with a slightly older engine version. If like me, you're going to be upgrading this to work with the Unreal Engine five. The upgrade takes about 10 seconds. On Windows, you can simply right click on this dot U project just here, and you have the option to select switch Unreal Engine version. From the drop down, pick the installed version that you want to work with and then click Okay. You're ready to go. I'm currently working on Linux, so I'll show you an alternative approach. This is useful, anyway. This will work on Windows, and it's actually a really useful thing to know about because this can help you fix projects that wouldn't otherwise open due to things like missing plugins, plug in issues. So to do this, I'm just going to right click on the dot U project. I'm going to go to open W, and you can pick any text editor. This could be Notepad VS code. I'll be working with Zed, and we can see here on the left hand side, if we look at the Engine association, it's telling us that this was made with 4.27. We can just change this. I'm going to be working with 5.6, which is probably outdated by the time that you're watching this, but it will be close enough. With that done, like with any text editor, we can just save this. We can close the window, and that's now ready to go. To get the project started, we simply double click the dot U project, and this will begin loading in the background ,atic on Linux, double clicking a U project by default doesn't actually launch the Unrel engine. If you wanted that kind of integration along with my custom UnreLEngine installer, this will do things like finding all of the locally downloaded or installed versions of Unreal, adding them to an automatic launcher here, and we can just go and Impress Okay, and we can load the fairly standard launcher that you'd normally get with the Unreal engine. It's not really the main topic of what we're going to be going through, so I'll provide an additional link to this. You can check that in the description below. I'll be providing the scripts completely free of charge, so you can download the scripts as well. And that will just go more in depth than what I needed to do to get Linux up and running, ready and working pretty seamlessly with the UnreL engine. Now that we're inside of Unreal, your interface may look a little bit different to mine depending on which version you've decided to work with. Epic tends to reorganize things roughly every time someone gets a little bit bored. But we can just focus on the concepts, not the exact buttons and the locations. They definitely will exist, and you'll be up and running quickly enough. The first thing to check out is the content browser or the content drawer, which is in the bottom left hand side. This is where everything in your project lives. We can click this and we can see our project structure. Inside of the content folder, there's a subfolder that matches the project name, which is shoot them up underscore tutorial, and this is Epic's recommended naming convention for projects and the folder structures. Inside then, we have an assets folder which holds things like our meshes, textures, and materials. We have a blueprint folder for our code and logic, and we have a Maps folder for our levels. This is a simple structure, but it scales to any project size. The other thing you may notice if you're browsing through some of the folders are things like the naming conventions which have been chosen. We have things like MM underscore texture, which stands for master material. We then have MI underscore, whatever the material is for, and that is material instance. If we go to the textures, we have things like our T underscore plane hero, and that's T for texture. The place I'm guessing all of these naming conventions from are not random. These are all based on the AA style. You want to Google that, you can find that here, so we can just search for Style Guide, and we're looking for the Github page, the first one just here. A link for this will be in the description below as well. Naming things is supposedly one of the too hard problems in computer science, and this solves one of those problems for you. I'd recommend bookmarking this for reference. And then whenever you're creating a new type of asset, if you're not sure what to call it, check here and through repetition, this will become automated. It's a really good resource, though. It provides pretty much everything you could want. You don't need to remember everything at once. There's quite a lot of things in here. But if we look down at the general naming conventions, we've got things like skeleton meshes, would be SK Bob. The material for Bob would be M nscore Bob, texture for Bob would be T underscore Bob, and so on. If you look at blueprints, we've got things like BP underscore, and then we can also check things like the file and folder structure. So exactly as I've mentioned here, the name of the project, and then all of the folders in a somewhat logical, meaningful setup. So this really is the holy grow of how to structure your projects, how to keep things tidy, and how to organize as you go. To get started, we need a main level to work with him. Before I dive into that, there's something I want to do and introduce you to just to make things a little bit easier for you to see my screen, but this may be useful for you to know about, as well. So I'm going to go up to the edit settings just here at the top of the screen and go to editor preferences. This is where we can change different features about how the editor is presented, the shortcuts, key bindings, and things like that can all be found in here. The thing that I want to change is the application scale here. I'm just going to change this to 1.25. This is purely because I've got quite a large screen set up, so it may look a little bit stretched and the buttons may look a little bit small for you to follow along with. So as I've said, this is optional, but it's useful to know this exists. And there are many more things that you can do inside of the editor preferences to tweak the general look and feel of your editor if something isn't quite right for you to work with. This saves automatically though, so I'm just going to close the window, and we can now get into creating our first level. This is nice and simple. We can go to the top left hand side. We have our file button, and we've got the option here to create a new level. One thing to look out for are the shortcuts. We can see all of the shortcut keys. New levels are something we tend to play with quite a lot. So this might be one that's worth remembering early on to save you a little bit of time. Inside of the new window, we're just going to select the basic level, not the open world. That's going to be far too complex for what we need right now. And it also takes longer to load in the editor every time you start backup. So we'll double click on Basic, and we're now inside of our new basic level. Then let's make sure that we've saved this correctly, as well. By default, this is called Untitled, meaning that we haven't yet saved it. This isn't stored. If we close the project and come back, we'll lose our progress. So we're going to press Control in S inside of the Viewport here, Controlling S for save. We're going to drop down into the Shootm up tutorial folder, go to the Maps folder, and we'll call this one main. We can give the name just down here. Hit Save or Enter, and that's automatically saved. Inside of the content drawer, we can go to our Maps folder, and we can see our map is just here. Quick thing to note. UnreL uses the word map and level interchangeably. Same thing, two different names because of course, it does. We can see that here, in fact, this is called a level, but when we make use of this later, Unreal will ask for us to set a specific map. Another thing to note and something else we'll be working with quite often is the content drawer. So rather than pressing the button down here on the bottom left hand side constantly, we can press Control in space, and this will toggle the content drawer open close. And this bit is really important. This is where beginners always lose their work, or at least they think they do until they come back and get some help. Without this step, you'll close your project, you'll reopen it, and you'll find yourself staring at an empty, open world map or something you weren't expecting to see. Your main label does still exist, but Unreal doesn't know that it should be opening by default. So to change this behavior, we're going to go to the project settings. We'll go to edit and then project settings. On the left hand side, in the top category, we want to find the maps and modes option. And then in the right hand side, you see the editor startup map. Change this dropdown from template default to our main map that we've just created, and this tells the Unrel engine which level to open when you first launch the project. We could also set the game default map here to be main as well. This determines what the player sees if you actually package the game and hand this to somebody else to play. These settings all autosave, so we can close this. We can reopen the project, and this means that you'll return exactly where you left off as long as you've had a chance to save the map you're working in. So this may seem like a lot of extra work and a lot of talking just for the first topic. But these fundamentals really compound. An organized project will stay manageable at the 10 hours of work, 100 hours, even 1,000 hours, whereas a messy project just doesn't. And that's really the goal and focus of this entire project based approach. Things may take a little bit longer, but by the end, you'll understand what you're doing and hopefully take away some good practices and make your future projects much more intuitive and easy to work with. But with that said, the foundations are nice set. The project structure, naming convention, and settings are all configured correctly. The next topic will be creating our player pawn, which is something you can actually control. And as I've alluded to, I want you to actually learn what you're doing as you go through. One of the best ways to learn isn't just by following along step by step. Once you feel that you understand something, try extending it. Even for something as simple as we've done now, add your own folders, rename some things, and just prove to yourself that you understand the pattern, not just the steps. I'll be guiding you on that throughout the course of what we're doing anyway, but there's only so much I can do, and a lot of the work will come from self learning. If you make changes that work well, drop those in the comments and discussions below. And if you think you understand something and see somebody posting a question that you think you can answer, try answering it. Explaining something that you've just learned is one of the quickest and best ways to actually lock the new information in and ensure that you don't forget that in the future. 3. 02 - PlayerPawn: The projects now set up, and we need something to actually begin controlling. By the end of this topic, we'll have a player pawn with vigils. It won't move yet. That will be coming next, but we will understand exactly how and why Unreal decides to spawn things and where. One of the core things in development is planning ahead and really thinking about what your current goal is and how to achieve that. So quite simply, from the videos, we know that we want to be controlling this small character in a little blue plane, to ensure that nobody's getting left behind. If you're completely new to everything, I don't want to throw complete jargon out there. So I'm going to show some really basic things. The plane that we're looking at is something called a static mesh. So if we go to the assets meshes, we can see here this is a static mesh of our little character in the blue plane. So we want to be controlling that. If we threw that straight into the level, we wouldn't be able to control a static mesh. We need to make this code based and use a type of blueprint class at the very least to get this going. Inside of Unreal, this is referred to as a pawn, something that we can possess or take control of, essentially, just a special type of actor. So let's go ahead and create our pawn properly with our good folder structure. So I'm going to go into our Blueprints folder. Inside of here, we want to create a new folder. We can right click and search for the new folder option here. I tend to use the Control Shift and N shortcut to create a new folder there. So N for new. I'm going to rename this folder to CR. This will be our core gameplay mechanics or gameplay features. And then inside of this big folder structure, if we right click, we want to go up to the blueprint class option just here, and this will allow us to create a new blueprint, and we can see a range of different options here. And this is where understanding Unreal's class hierarchy can begin to help. In short, everything inside of UnreL inherits from something called an object. This is just a data container, the most simplified class that we have access to. We quite often then see something called an actor. Everything in the level at the moment is at the very root an actor class. We'll have static mesh actors, camera actors, light actors, but they're all actors at their root. Adds a physical presence on top of the object data, which is a location, rotation and scale referred to as a transform. And then the pawns that we're going to be working with are just an actor with the functionality to be possessed. You'll see in some tutorials, they will use something called a character class instead, and that would be overkill for what we're looking to make with this shoot them up. Characters include things like walking mechanics, gravity, capsule collision, stuff really designed for humanoid characters walking around on foot. We're going to be making a spaceship, so we don't really need any of that. And it comes with a lot of extra overhead that we'd need to actively work. So you may see other tutorials creating things like a tank controller using a character class, and this is just where being aware of things like this would allow you to consider that that may not be the best solution for what you're trying to create. Because pawns are quite a commonly used actor or type of class, we can see that's actually up here in the common section, so we can click on this and this will create a new pawn class for us. And we want to call this one BP underscore pawn base, because this will be the base class for any pawn related classes that we create in the future, allowing us to reuse some of the logic for our enemies, as well, naming it for what it is in general, not just what it's meant to do first of all, which is going to be working as the player class. Before we go any further, another quick aside here, quick, quality of life fix. UnreLEngine has some strong opinions about window management, and they're mostly wrong. So to fix one of the things which is about to happen, if we double click to open our blueprint now, this will just create a floating window pretty much anywhere on the screen. This can be quite annoying. It means that windows can get lost behind other applications and things like that. So if we go back up to edit and then editor preferences. Same place that I showed you previously where I changed the application scale. As I've mentioned, we can do a lot of quality of life fixes and changes. I want to find is under asset editor open location. We want to change this from default, which is open wherever, and we're going to change this to main window. You can try different things and see which may work best for you. I quite like main Window. It's kind of like working in a web browser, then, where each new file, blueprint, asset, whatever it is you're working with. Whenever you open a new window, it will create a new tab and dock them kind of like bookmarks at the top. So again, this automatically saves, we can close this, go back into the content drawer, and then double click again to open the pan base. You can see that opens up here, which is exactly what I wanted. If you've done the same as me, where you may have opened this twice, you may be greeted with this simplified data only view. And that's perfectly fine. We don't need to panic. This is all of the class details presented to us essentially in text or drop down form. To get back into the full blueprint editor, we just want this blue line of texture, the open four Blueprint editor, and that will bring us back to where we want to be. Okay, so there's a lot of information that I could front load you with right now, but you'd probably forget most of it. So we're going to take a kind of more organic and natural way to learn the unreal engine. We're just going to work with new features, and I'll introduce them as we go through and as they become relevant. The first thing that we can see at the moment is we're in the event graph in this tab just here. This is where we place our logic and code. We're going to come back here later. For now, if we click on over to the viewpoint, this is just a similar thing where we can look around and see what our blueprint class will look like. Blueprint classes are normally made up of different components, each with their own features and functionality. In the left hand side, we can see the components panel, and we want to add our visual representation, that static mesh. So if we press ad and search for static mesh, we can see that just here. That's going to be the first part of the puzzle that we're trying to solve. With the static mesh selected, in the right hand side of the window, we can see our details panel. This tells us everything about the component that we currently have selected. We can see we have a drop down here for the static mesh, and this is the visual representation that we want to set. We know that we want this to be the blue plane, so we'll find the plane hero and we'll select that one. Inside of this viewpot, I'm just using the mouse wheel to scroll in and out. If we press Alt and left click, we can drag around and view the model, and if we press right click and drag around the mouse, we can change what we're looking at. Middle mouse click and move Will Dolly or Pan, I think that's calling. And this is the basics of kind of navigating around this viewpot and also the world viewport later when we come to level building and things like that. Something else which is useful to know is if you press F when you have something selected, you focus in. So that's F for focus. Now, what we've just created is our static mesh, and we can see back in the components panel, we have something called a default scene root. This is showing the actor's structure, and every actor needs at least a default scene root, a point in space where everything else is attached. The default scene root that we have is just a placeholder, so we can grab our static mesh, and we can drag our static mesh onto the default scene root and let go. So left leg and we've now made our static mesh what is referred to as the root component, so the base element of our player class or our pawn. One other thing, when we get to a stage where we think we're quite happy with the progress, we need to make sure we get into a good habit of hitting compile and save. This just make sure that if anything crashes, then we don't lose our work. And compiling we just check for any errors and catch things early. So we've now got our pawn with visuals. But of course, if we go and press play, we're not going to be seeing this just yet because we haven't told the Unreal engine to use it. And we can use some standard first person game input here. So WAS and D will fly you around. Q and E will move you up and down, and of course, the mouse will look around. But, of course, that's not quite the functionality we're looking for. So in the next topic, we'll be fixing this by looking at the Unreal engine gameplay framework. We'll be telling the Unreal engine exactly what to spawn and where we want it to spawn. Once again, if you're experimenting on your own, which I'd always recommend, try adding in other components to the pawn class. Something as simple as a point light, a default particle system, just to see what happens when you attach things to different parts of the hierarchy. That kind of experimentation is really how you'll get this stuff to stick. 4. 03 - Gameplay Framework: With the pawn built, we've pressed play, and we've already seen that in play mode, we actually end up flying around what is called the default pawn. So we need to change this. Unreal spawns is by default and has completely ignored the class that we've made. To fix this properly, we need to actually understand how Unreal'sGame framework decides what to spawn, where, and then we can tell it what to do instead. So to begin, a little bit of a concept overview, again, let me show you what's actually happening behind the scenes. First of all, I'm going to go back into the main window and press play. Inside of Playode, this is where we can fly around. Something which is really useful to know is you can press F eight to eject from play. So I'm still in play mode. But I'm no longer controlling the default porn. I'm now essentially back in editor mode so I can use WAS and D, Left Mouse click to look around, and I can see what's happening during play. If we look at the outliner on the right hand side, we can see something called default porn zero. That's what we've actually been controlling in the level. And in fact, everything highlighted in yellow over here has been spawned by UnreLEngine gameplay framework. We didn't place any of these in the level. So when we press Play, UnreL follows a specific chain. First, it will check for a game mode, and if you haven't set one, it will use a default option. The game mode will then decide what pawn to spawn. And again, if you haven't specified one, it uses the default, which is literally called default pawn. And that's currently what we're controlling instead of our plane because we've never told Unreal to use that plane class. So let's press Escape to exit Play Mode, and we can begin taking control of our project. Back in the core folder. So if you're not really here, blueprints core inside of the content drawer, we want to create a new Blueprint class. Same way as before, right click and go up to Blueprint class. And this time we want to create one off type game mode base. I'll give this one the name of BP Underscore Game Mode base. And a game mode is essentially something we can think of as a rule holder. It tells nrLEngine what classes to use for everything, what pan to spawn, which controller to use, which Hud to display, and so on. If we double click to open our game mode, we can see this looks somewhat like a normal blueprint. We could go into the code Event graph section if we wanted, but everything that we want is actually on the right hand side in the details panel. We can see here those things I've just mentioned, the default Hud type of controller are all exposed just here. The thing that we're looking for is our default porn class just here. We can drop this down and change this to BP Underscore porn base. That's pretty much all we need to do right here. We can hit Compile and save to make sure all of that's updated. And we can actually close out of the game mode for now. I will keep this open to give a bit of a visual example of something in just a moment, but that's pretty much all we need to do inside of our game mode. As you'd expect, just creating the game mode isn't enough. We now need to tell the project to actually make use of it. So if we go back into the edit option in the top left hand side, we're going to go to project settings again. And we're actually looking back to where we were previously, maps and mode section. You can see on the right hand side, we have the default game mode is just one called Game Mode base. And if we drop this down here, we can actually see what it was using. So it was using a default Hod class, the default pawn class, the thing which we've literally seen ourselves flying around, the player controller by default, and so on. We can't change these, which is why we needed a custom class to work with. If we drop this option down, we can change this to BP underscore Game Mode base. We can see this is now freed up. And the important thing is it's now changed this to be our BP underscore pawn base. The one thing I wanted to show you very quickly is that if I change this here, I could drop this one down. I'll change it to something completely unique. We changed this to the Arc Vz character. We can actually see that the game mode class that we've created in the background, so we've got our BP Underscore Game Mode base was updated in the background to be Arc Vz as well. If we click this in here and change this one back to BP underscore pawn base, I just wanted to highlight that these are completely connected now. So if we change something here, that will update in our Blueprint class. And if we change something in our Blueprint class, that will change in our project settings if we have it hooked up just here. With that done, we can close the project settings, make sure that we compile and save the game mode. If you're playing around with things, go back into the main level, press play, which is O and P, the shortcut to enter play mode. And we're kind of in the plane now. In fact, we're quite literally in the plane. So another problem to solve, but we're getting closer. If you've been placing things manually earlier in between, if you're playing around with things, you may have other planes in the level as. Make sure you get rid of those just so that there isn't any confusion, but we can kind of see what's happening. We press play we go into play mode, and then if I press F eight to jump back out, you can see it has now actually spawned a plane for us to control. The main problem is that we're inside of it. I just wanted to highlight another thing which is happening with the gameplay framework, and that is the use of our player start. So you can see that just here in the level. And in the outliner, we can see it's called here the player Start. This is what the framework will be looking for to confirm where you want to actually spawn the pawn that you want to control. So if I move this somewhere really far back, I move this all the way into the open space over here. If I press play, we're still spawning the plane, but we're now spawning out into the open. So again, something useful to know about. If we don't have one of these, we can delete these, but Unreal now needs to kind of guess where we want to spawn our thing. And it's not really guessing just to kind of show what happens with this scenario. If I move around in the viewpot, I'm just again using Right Mice click to look around and then W AS and D to fly based on where I'm looking. If I press play, we can see that the plane is now just going to spawn the last position my camera was in the viewpot. So again, something I've just seen trip up new developers because it may not be super clear what the player start was doing, especially if you're working in a project where you might have something like a third person character or a first person character with gravity, you'll be working in the level, you'll design a few things, you'll press play, and then you'll suddenly fall through space, completely avoiding the level and be a little bit confused. And that's just because you've probably gotten rid of your player start. Unreal didn't know where to spawn your default character, so it's chosen the last place you were in the editor. If you followed along here, we can go to this Add button in the top. We can search for a player start just here, and we can just drop one of these back in the level. Nice and easy, we're back to where we were. So that just leaves one problem is that we're looking through the wrong place. This just means that we need a camera and we need to tell Unreal which one to use. To do this, we need to go back into the BP underscore porn base. If you don't really have this open, remember that in the content drawer, so control in space and double click the porn base class. As I've mentioned, most classes are just made up of numerous amounts of components, each component trying to fill a specific role or use case. In this case, we want a camera component to look at our static match. Before we add a camera, I'm going to add something called a Spring arm just to introduce those. So if we press ad over here, we can search for spring arm. We'll click to add this. And this is basically a stick which will hold the camera at a fixed distance. It can also handle things like collision, pushing the camera closer when we hit walls and trying to avoid things. We won't need that level of complexity for our game, but it's useful to know that these features exist. Another really handy use of the spring arm is it avoids us needing to directly affect the offset, the position, and rotation of the camera. So we're going to keep it just for those reasons. With the spring arm still selected, we're going to go back to the Add button. We're going to search for a camera. And we want to select the default camera here at the top. You can see that this is just placed at the camera at the end of the spring um. This gives us a little bit of time to focus on Unreal engine and the way that it treats hierarchy. We've already looked at the fact that the static mesh is now classed as the seam root. So wherever the seam root goes, everything else follows, including rotation, location, and scale. The spring um is then classed as a child of the static mesh, and the camera is a child of the spring um. So what this means is if I grab the spring um component and then change something in the rotation, for example, I know that I want the camera to look 90 degrees down on the static mesh. That will be -90 degrees. We can see that the camera inherits that rotation that I've given the spring um. I make the spring um bigger, so I'm going to lock the location, and I'll make this the size of ten, we can see that the camera also gets ten times bigger. Now, we don't scale the camera and spring um this way, but I just wanted to show how hierarchy works inside of unreal with the components. We do want the camera rotation, though, so I'd recommend a rotation of something like -90, so we have the camera looking straight down on the player. If we press play, we can see what this will look like. So maybe a little bit too close, that could be somewhat difficult to see what's happening with the enemies and projectiles during gameplay. The Spring hum has another feature, which will help us with this. It's called the target arm length, and you can see that again in the details panel. So, as I've mentioned, with the static mesh, when we've got something selected, the details panel is just all of the information about the components you currently have selected. The Spring hum has this value called target arm length, which is this red line, and we can set this to something like 1,200, and that would be roughly where we want this to be. So we can press Play, and we can see we can see a little bit more of the screen. We'll be coming back and refining things like this a little bit later anyway. But for now, that will be perfectly fine. Once again, when you've made changes you're happy with, make sure that we hit Save, and we have this looking somewhat more like a top down shooter. So if you've been following along and paying attention, we've now seen that if we don't tell the Unreal engine which map to use, it uses a somewhat random map. If we don't tell it which game mode to use, it uses a default game mode. And if you don't tell that which pawn to use, it uses a default pawn. But you've probably noticed that we didn't tell it which camera to use, but it knows to use the camera that we've just added. So it kind of breaks that rule that I've been highlighting. In this works, though, is it still comes down to the gameplay framework behavior. When the framework first possesses a pawn, it will look inside of that class and check for any camera components. I'm simplifying this a little bit, but it's essentially the process which is taken. If it finds a camera, then it will use it by default. If it doesn't find a camera in your hierarchy, then it will create a default camera, and that position it puts it at just happens to be zero, which in our case, is directly inside of the plane mesh, which is why we were looking at the inside of the body. So again, we can now see why we were staring at the inside of the plane and how we've overridden that to now look down on the plane. Same kind of pattern as everything else, though. The framework has its default processes, and the important thing is that we are now more aware that we either have the option to override them or live with them. With that done, we now control what spawns, where it spawns, and which camera we look through. The framework is working for us instead of against us. And again, you may be surprised how many people end up working against the framework for quite a long time. Something else I've become quite familiar with when students just dive headfirst into projects. By the time I get to see the project after they've worked on it for a while, they'll have workarounds for viewing through specific cameras, forcing pawns to be triggered at certain points, basically breaking the framework apart and fighting it rather than working. Where possible, we can use a lot of these framework basics, and they actually help us more than they hinder us. Of course, we still don't have movement. We currently just have a pawn that sits there and a camera to look at it. So that would be the next thing, adding movement. I'm actually going to show you the wrong way to add movement first because it's what you'll see in a lot of different tutorials. After we've seen that, I'm going to show you how to fix those problems, so you know what to look out for. If you wanted to experiment again, the small recommendation, whilst we're still quite early into the topics would be to try adjusting the camera distance or angle, maybe go for a slightly different setup for the angle at which you're looking down at the plane. Can add a second camera and see what happens. Which one does it pick? And if you are doing things like that, start trying to describe what you're noticing the framework is doing, what it's doing that you don't expect, or what you're kind of understanding about it. 5. 04 - LegacyInput: Before the player can move, Unreal needs to know what the players pressing. That could be keyboard keys, controller sticks, triggers. All of this flows through the input system. Unre takes those bindings and converts them into input values, and we read that for our movement system. So the first step will be understanding how those values actually work. To get started, we'll be doing this through the legacy input system going kind of old school. To find these, we want to go back to our project settings. So we're going to go up to edit and project settings. We're going to go just a little bit further down than we had before. And we can see here we have the input section. As I've mentioned, this is kind of old school, and there's even a warning here about the legacy input system now being deprecated. This is fine. It's still useful to know because you're likely to come across projects which still use this. Many examples and projects out there are still using this system. Have two different types of input here. We have our action mappings, which are binary. These are either pressed or not pressed, perfect for things like fire, interact, jump, and so on. We then have our axis mappings, and these convert the player input into continuous floating point values. Perfect for movement. So for that reason, we're going to go and add a new axis mapping just here, and we'll name this one move right. Because we have this scaling value here that we can change for the different inputs that we provide, we'll call this one move right. It will also control moving left by making the other input a negative value. From this dropdown, we can find any input that we want. This could be your keyboard presses, mouse, gamepad, whatever the case may be. Something that I didn't know for far too many years when I was using the unrel engine is this. Is actually a button. If we click on this and press the D key, for example, that will track the next input as D, and that would be perfect for moving to the right. If we press the plus button, we can add another binding. I'm going to press this again and then press A, so we now have our leftwards movement. The only thing as I've mentioned is that by default, this is a positive value. If we change this to a negative value, so minus one, that will allow the input to move us in a leftwards direction. If you wanted to bind different keys to the same input binding, we can just add two more as an example. We can add one for the right arrow and one for the left arrow. Again, when we're moving left, we just want to make sure that we add the negative value to this one. Simple system and flexible enough for what we need to do right now. Like with everything before, this automatically saves so we can close the project settings, go back into the content drawer, using control in space again as a recap on the shortcut and go back into the porn base. If you're still inside of the viewpot, we're going to want to move on over to the event graph tab now. So that one that I mentioned, this is where our logic is going to be going, and that's what we'll be doing straightaway. In here, we can see a few different nodes already in the graph, and I'm just right clicking and dragging to move around. First, we can see we have our begin play node, and this fis just once when we press Start. Below, we have our event tick at the very bottom, and this event fires continuously while the game is running. You may hear people say things like we avoid using event tick at all costs, but that is slightly oversimplifying things. For functionality like movement, where we want that to be smooth and responsive or camera movement, physics, things like that, this is exactly where the logic needs to lie. We just need to make sure that we keep it clean and efficient. Then finally, we have the event act to begin overlap. This is an overlap check, a collision check for the entire class. In our case, that could be anything from the static mesh, the spring um component or the camera component. It's a little bit vague, so I tend to not use this and we'll handle collisions on a per component basis later. So we can grab this one, press delete, and we'll get rid of that. To demonstrate what the axis mapping is doing, we're going to move down a little bit, so I'm right click and dragging to move the graph. Want to right, click in here, and we're going to search for that input binding we made just a moment ago. I've called mine Move Right. We have two different options here. We have the axis event and the axis value. The event would create something that looks a lot like the eventic providing this execution pin so we can drive future logic from this. The difference with the axis value is if we click this one. This provides just this green pin. That green pin is a float value or a decimal point value, and this will provide a value depending on what we're pressing. If we're not pressing anything, we get zero. If we're pressing left, we get minus one, and if we're pressing right, we get positive one. I can actually show you that in a really simple demonstration, and I recommend this to anyone learning new parts of the Unreal engine. We're going to pull from this execution pin here, drag this into the graph and let go, and then we're going to search for the word print string. So this is just going to provide a debug message on our screen, on the left hand side if we press play. So we can see that over here is currently spamming the window with the word hello. Now, to avoid that being spammed because this is running 120 times per second, we want to drop this down, change the duration to 2 seconds. This is currently printing for 2 seconds, and we'll turn this to zero. This then becomes more of a constant print over here, so it's now just printing the word hello. Obviously, we don't want this to say hello. We want this to show us what is being pressed. So I'm going to take the float value here, pull from this pin, and drop this into the instring. The Unreal engine is smart enough to automatically convert our float to a string of text so this is readable bias. So if we hit compile and play again, you can see now this is printing zero, which is because I'm not pressing anything at all. If I press the left arrow, it prints minus one, the right arrow, I get positive one and the same for A and D. So we can see that's working. We have something happening here. So printing the value shows the data, but it's still just a number as far as we're concerned. So let's just do something very quickly to make this effect something visibly. We're not going to add movement just yet. We're going to get there on a separate topic. If we pull from our print string, though, so this execution pin here and search for set Act scale three D. We want this node just here, and all we're going to do is we're going to affect the overall scale of our actor based on the direction we're pressing. To do this, we're just going to need a tiny bit of space in between. So I'm just moving some nodes around here. We're going to keep the maths really, really simple. This is completely throwaway and just to visualize parts of the concept here. We can take our set actor scale three D, though, and we can control this by the direction we're pressing. So we can make the scale of the actor zero minus one or one. And if we add some analog input, we could make that value this is really simple. We're going to take our get move we value here, so I'm going to grab this, press Control in D to duplicate. And we're just going to plug this in here. So as I said, really, really simple. This isn't going to be useful for any kind of real use case scenario. I just wanted to get you somewhat familiar with a little bit of blueprinting before we dive in with movement. But if we hit compile, this just ensures that everything's been checked for errors and working, hit save so we don't lose any of the progress. And one thing I think I will do is go back into edit and then project settings. I've connected my gamepad, and if we scroll back down to the input section, just going to add a new input here, click this button, and I'm just moving the analog stick to the left. So the left analog stick, I've moved on the X axis, which is left and right. We can leave this as a positive number if we wanted to invert the analog movement. So, for example, if I pull left, then I'd move right. Then that's why we may want to add a negative value. But because the analog has a full range of movement, and real smart enough to know to take the right direction as positive, and the left direction will automatically be given this negative value. So we can close this. We can go into play moode. We've kind of broken the plane at the moment. So at the moment, we're printing zero at the top. Which means we're passing the number zero into the X, Y, and Z of the scale of our plane, which is why we can't see it. If I press A, then we're inverting the size, so we're seeing the bottom because we're now making plane the scale of this negative one. And if I press D, we're making the scale of the plane positive one. The reason I want to use the analog stick is that we can kind of visualize this a little bit more so I can get some smooth movements by just moving the analog stick. If you see the number at the top, this is going between zero and minus one because I'm moving to the left. And if I move it to the right, we're going between zero and positive one. So just a kind of visualization of how we can make use of these values and plug this into some really simple logic. Again, we're getting another conversion. This is taking our movement direction, so minus one, zero or one, multiplying that against all of these scale properties. So the scale properties and unreal. You've got the X, Y, and Z. Just to visualize this for something simple like a cube. I'll come in here just to show you this. If we wanted to change the properties or the scale of this object, we could take the X axis alone, and you can see that's getting longer. We could scale on the y axis to make it wider. And again, we can change the z axis to make it taller. So nice and simple, and that's really all we were doing here in the code, but we're just normalizing this. So all of them are either zero, either minus one or one, based on the input. So as I said, completely useless code, complete throw away, but hopefully that makes the input and how we can utilize that value a little bit more understandable. With that done, we've confirmed that our input is working. Porn itself still doesn't move. That part's going to be next to come. And in the next video, we'll turn this input into some actual player movement. 6. 05 - PlayerMovement: But set up and tested, it's time to make the plane actually move. Fair warning again, we're still going to be doing things the wrong way first framework dependent movement with a kind of snappy movement, fill, but it will work. And you may be wondering why I'm knowingly teaching the wrong thing. This is because this exact code will show up in a lot of places. This is from the epic official content examples, which means you'll see it in other tutorials that reference that older projects you may inherit, and I'm sure in many other places. When I realized that, I decided this would be a perfect opportunity to get you to a point by the end of this video where you'll see the bug, you'll understand why it happens and you'll know how to fix it. So we need to still be inside of our BP underscore pawn base. We can actually get rid of all of the code that we have at the moment. We'll get rid of this scaling and print string. That was just for demonstration and debugging. For the movement, this is really simple. We just need to consider this as taking our move direction. We'll add in some kind of variable speed a little bit later, and we want to offset the location of our plane every single tick. We can do this by pulling from the eventi, the execution pin just here. We're going to search for something called set actor location. And this will be our node, which is directly driving and controlling where we move. To get this to work, we need to calculate an offset, taking into account the plane's current location and then where we want it to be. We can do that using another built in function. We can right click in the event graph, and I'm going to search for Get actor location. If we hover over these when we're finding them as well, in case you weren't aware, you can see tool tips on what these functions do. So this returns to the location of the root components, which we know is our static mesh, essentially telling us where we are at any given frame. To the side of this, we're going to pull from this vector pin the yellow pin just here, and we're going to search for the plus symbol, or you can type add and we want the AD node. We can do some really simple vector maths. So what we have here is our current position plus an offset will equal where we want to be so we can plug this straight in. One problem we're faced with at the moment is we want to directly control the Y axis. So when you're working with Unreal, I'm going to come back into the level just for a quick example here. When it comes to directions and movement, if you're ever not sure which way you want to move, we can just do something like grabbing our mesh. Drop this in the level, and we get this widget here, which will show us the directions that you can move inside of Unreal. The main thing to try and remember early on, red, which is the X axis is forward in the world. Y, which is green is right and left in the world or sideways, and Z, which is blue is up and down. That's quite handy because Unreal Engine does color code these. So if we look in the details panel to the right, we can see here we have X, Y, and Z, and that has a small pill icon showing red, green, and blue. We have the same for rotation and scale. And as I've mentioned briefly before, these three variables, the location rotation and scale are just a structure that make up the trans and each of these variables like the location, and it's just a structure of individual floating point values. In the example that I've shown that this project is based on, the plane obviously moves side to side and only side to side, kind of like old space invaders, and we're trying to recreate that, so we know that we need to move on the Y axis. So back inside of our code, that means we need to somehow expose this variable here. Super simple to do. We can right click on the yellow pin, and we've got this option to split the structure pin. And that's why I mentioned that these are referred to as structures. They come in many different forms, but they're normally just a combination of different variables, making one specific type of variable with a different use case. In this case, it's the location for the three dimensional movement. If we split the structure pin, you can see that this is then reverted into its individual floating point values, the X, the Y and the z, and we already have our movement set up. That is our move right, which we can just plug in to the Y axis. So what we're doing now is we're taking our current location, and every frame, we're either adding one, zero or minus one to the direction. So if we press play, we can see we now have some kind of movement. So A and D will make me move left and right, as do the left and right arokis. So we're a step closer to where we want it to be. Now this does put us in a small situation here where we now have an issue where this is a framerate dependent game. So I'm going to show you a few things on the repercussions of this and how you can kind of test and see this for yourself. So at the moment, remember we're moving one unit per frame if we're pressing a button. So if I'm running at 120 frames per second, that's 120 units per second. An rL engine calculates units in centimeters. So this is currently moving around about 120 centimeters/second. Now, if I go back into play mode, I'm going to go into the main window here, and I'm going to introduce you to something called the console commands, where we can run simple commands to test things. I'm also going to quickly get rid of this test plane here. So back inside a Playmode, click into the viewpot to make sure you have full control of Playmode. And you want to find the Tilda key. On most keyboards, this is next to the row of numbers. If you press the Tilda key, you can see a small console appears at the bottom of the screen over here. I'm going to enable a really simple console command, which is stated space FPS. If I press Enter, we can see to the right hand side of the screen. I'm now displaying the number of frames I'm running per second. I'm going to press Tilda key again, and I'm going to type T dot max FPS or One Word and then space, and then I'm going to set this to 30. We can now see on the right hand side that's capped my frames down to 30 frames per second. Now just considering what I mentioned a moment ago, if at 120 frames per second, I was moving 120 units per second, this now means I'm obviously moving at 30 units per second. So we're playing the same game, we've got the same movement code, but the movement is drastically different. If I put this back up I'll uncap this to 300. I'm running at about 300 FPS. We can see we're now actually moving considerably faster because I'm now moving 300 units per second. I'll put this back to 30 and you can see the contrast. Now, you can probably begin seeing why this isn't ideal. And this is what we refer to as frame rate dependency when a game is running specifically based on the hardware. So players with faster systems will move faster and players with slower systems will move slower. It's a bug that ships in a lot more games than you'd expect. So before we get to moving and looking at making what is called frame rate independence, we're going to tweak the rest of our movement code for now so that we have the full thing to work with. At the moment, moving just one unit per frame is obviously not too notable, so we're going to want to multiply our speed by a value. We can do that really easily. Again, working with maths inside of blueprints is relatively simple. We can pull from our float in here what we want to multiply against. Drag that into the graph. Search for the asterisk, the multiply symbol or type multiply, and we can plug this result into the Y. This means that will now take our move direction multiplied by a movement speed, which we can set to something random for now like ten, and this will be our new offset that will apply every single frame. So we're now moving faster, but we're still going to have that frame rate independence issue where if I change the frame rate I'm running at, I'm now moving a lot faster. So that's becoming even more noticeable. I just wanted to focus in here as well. What I've just created here is what we refer to as a magic number. This number that context, when I come back to this in a month or two's time, you're quite likely to forget exactly what this number was representing without looking back through the code. Give you the question, why was this ten? Why was it not 100? Why was it not 1,000 and so on. To alleviate this a little bit, and again, focusing on that good programming practice and trying to keep our code tidy right from the very beginning. We're going to pull from this green pin here, and I'm going to do something called promoting this to a variable, and you can see the option just here to promote this to a variable. So we'll click on this. On the left hand side is where the variables live. We can grab this name, and I'll rename this one to movement speed. So now at a glance, at the very least, this gives us some idea what this value is doing. So this is the speed at which we're expecting to move, multiplied by a direction being added to our active location. It begins to read a little bit more like a novel on a page, and you get an idea of what the code will be doing, even without fully investigating it, which is what we want our code to do, become human readable. So we have our movement speed variable. We can hit compile up here, just to double check that this has the number ten filled in because we already had that value as a magic number. Back to the frame rate bug, then. We need to ensure that regardless of the speed our system is running, that we get the same type of movement. This isn't just for the player's outcome, but as a designer, when we're making our game, we're fine tuning everything. We want everything to feel as smooth and reactive and responsive as we'd expect. We fine tune the whole game to play in a specific way. And obviously, that's going to be completely ruined if somebody who's playing it on a system which is twice as fast as ours gets a different result. What we need for this is something called Delta seconds, sometimes referred to as Delta simply the number of seconds that have passed since the last frame. Rather than using this pin here for our calculation, because, of course, it would have overlapping wires and begin to introduce spaghetti code. We're going to right click in the graph, and we're going to search for Delta seconds. You can see here we get the option, get World Delta seconds, and we can plug this in and make use of this. Quite simply, we want to take all of the values that we have here and multiply this against our Delta seconds. So same again. We'll search for multiply, plug this in, plug the calculation into the result, and we're getting closer to what we want. Now, allow me to quickly demonstrate what Delta seconds is actually doing and what the value represents. A really quick way to do this. I want to throw in a print string like we've seen before. I'll set this to zero for the duration, grab the Delta seconds node, duplicate this, plug this in just so we can see what's printing. In my 300 FPS, we're not going to use this number, so I'm going to cap this back down to 120. At 120 FPS, we're getting a value around about 0.008 seconds in between each frame. If I cap this back down to 60, we'll see this drops down to 0.016. And then if we drop this down further to 30, we're going to get a number of 0.033. So roughly, not exactly, but roughly half the number each time we drop the frame rate by half. And here's how this is useful and how this fixes our frame rate dependent issues. If you're running at 30 FPS, each frame moves you further, but these are bigger steps, but you're taking fewer steps per second. Whereas at 120 FPS, each frame is moving you less. These are smaller steps, but you're taking four times as many steps within that. And what we end up with is the total distance per second stays roughly the same. And this is whether you're running at 30 FPS or 120 FPS. We get frame rate independence, and this is the difference between a game that feels fair on all hardware, compared against people having different experiences on higher or lower end systems. One thing to note, because we're now multiplying our values by this incredibly small number, the speed we're moving is obviously going to feel a lot slower, very sluggish, almost not noticeable. So we need to come in and account for this in the movement speed. I'm going to increase this to something like maybe 500 rather than ten, hit compile and play, and it will feel roughly the same. The main thing, again, is what we're looking at is a I jump 30-120 FBS. It looks smoother, but we're not noticeably going faster or slower. The only difference we're seeing is how smooth the movement feels due to the frame rate that we're looking at. So that's really the goal here. So with that, the movement works. We have the framework problem fixed, but it's really not quite polished. We can notice that it's quite snappy when we move it. If we press a direction, we're instantly at full speed. Once we release, we are instantly stopped. There's no acceleration, no momentum. Real vehicles don't work this way. Even our gady ones will have some weight to them, so we'll need to consider adding some smooth acceleration. And this whole approach that we're taking at the moment, the set actor location. Doing this every frame has no physics integration, no proper collision response, and it's perfectly fine. This is why it's provided in Epics content examples. The pattern works because it's simple, not because it's best practice. So we have movements. We understand the framework bug that we had and how to fix it, and we know why this approach is common and why it's not ideal. Next, we'll move the sites. We'll add some acceleration, rotational banking, just generally aiming for something that feels more like an aircraft. If you wanted to try experimenting, you could always try adding vertical movement. It's the same pattern. Just use the different axis, the X axis instead of the Y or add to it. You'll likely need to introduce a new input binding, as well, a small tip there. 7. 06 - SmoothMovement: Moment, our movement technically works, but it feels very robotic. Instant start, instant stop. We'll address that in this video by adding some acceleration and banking rotation, so it feels like you're actually flying some kind of Arcade aircraft. We'll be using something called interpolation, which is the smoothing between two points over time. This will also be the next step to inheriting the bugs inside of the epic official learning content. We'll find it, we'll acknowledge it, and we'll fix it a little bit later. So right now, the problem is that our speed is being directly taken from the input. Just here, we have move right multiplied by movement speed. So if we're pressing right, that's one multiplied by 500. We're going straight to the speed of 500 units per frame. If we're pressing left, that's negative one multiplied by 500, so we're getting negative 500 being applied straight to our movement speed. That's why we get that instant start instant stop. Of course, as soon as we release a key, then we're multiplying the value by zero, so we go straight 500-0 movement speed. For smooth movement, we're going to need two different values. We want a target speed, which is where we want to be, and we need our current speed, which is where we are at the moment. Or the use these to gradually move between our current speed and our target speed. To get started, we can very easily create a new float or the current speed that we're going to be tracking. We can grab our movement speed over here, right click on this or press Control in D to duplicate. We'll call this one current speed. And what we'll do is we'll take this value that we're calculating just here, essentially our movement speed, and that will be the target that we'll use a little bit later on. If we wanted to tidy this up, we could definitely make this more visually readable. If I move this over, we can grab this calculation just here. I'm going to press Control in X to cut this, control in V to paste it above, we'll pull from this here and we'll promote this to a new variable. We'll promote this one to something which we'll call target speed. And we can plug this in just here. You'll see why in a moment, this isn't really changing anything right now, but it will make things just a little bit more readable. Now, as well as just hiding up things so we've got a little bit more space to work with, do remember that we need to rehok our target speed that we're now calculating. The only difference is that we've essentially given this pin a name. So again, it's just a little bit more readable. We're going to grab our target speed. And if we just drop this straight onto the Y flow pin which is now exposed, that will automatically hook that up for us. Something you'll see me do quite often, as well, is what I've done just there. So these pins we kind of the wires were floating around a little bit. If you grab a bunch of nodes, press a cue that will automatically align the wires for you. So we can keep things a little bit tidier as we go. Another thing which is useful to know is that if you hover over the pin just here, for example, and press a cue without actually grabbing the notes, it will take the node which is connected, and it will line those up as well. So not something that you absolutely have to do, but I'm just mentioning it because I realize that I do this kind of with muscle memory, so I'll do this whilst I'm talking sometimes. And I just wanted you to know the shortcuts that I'm using. Okay, so back on to the main topic. We want to get this movement smoothed out with a float interpolation. If we right click in the event graph over here, we're going to search for F interp two. This is a float based interpolation to a certain value. We can see here a few things that were given. So we start with our current value, which is, as I mentioned, where we are now. The target is where we want to be to fill these straight in. We already have these values and they're named accordingly. So we can grab our current speed and drop this over here on current. So we're going to go from our current and we're going to reach our target speed. So we just drag and drop that on, as well. You can see that we then need to plug in the Delta time. So we have the Delta seconds here. I'm going to select that one, press control, indeed, duplicate with my mouse down here, and we can plug this into the Delta seconds. The Delta time here keeps the E interpolation frame rate independent kind of with some quirks. But again, we'll come back to that when we see that problem, and we'll address it later. And then finally, we have the interp this is controlling how quickly we accelerate toward the target speed that we're given. Higher values here mean a snappier response. Lower values mean it will feel a little bit more floaty. I'm going to start with the value of four, which has worked perfectly fine in testing. And then, of course, we don't want any magic numbers in our code. So we're going to pull from here. We're going to promote this to a variable. I'll give this one a sensible name something like movement interpret speed. So we now have the four F interp two. The result of this interpollation will become our new current speed value. Very important here. We're going to need to give ourselves a little bit more space. I'll remove the print string over here. We're not going to need that anymore, and we'll move this right on over here for nine. We're going to avoid any spaghetti code, and we're going to keep things tidy as we go. This will need to be calculated up here. We need our target speed first, so the value that we know that we're going to be hitting. And we also need to now set. So using a node like this setting the value of our current speed. Something I don't think I've covered just yet is that we have alternative ways to bring our variables into the graph. If we hold the control key on the keyboard and grab, for example, the current speed and drag this into the graph, this provides a node. This slim node is called a getter. It basically provides the value. We can read this. It's getting the value for us. If we hold Alt and do the same thing again, grab current speed and drag this into the graph, but now holding Alt. This is providing something called a setter. So this is the opposite. This allows us to write to the value that we have here. So this allows us to read the value. This allows us to write to the value. We want to keep the writable version here at the current speed, so I'm going to get rid of the getter. And we want to take our value here. As I've just said, this is what our current speed will be tracked as. So we're going to plug this into current speed, and we just need to make sure that we hook up the execution pins just here. And just to try and wrap this up and ensure this makes sense, because I appreciate this could look a little bit confusing. We need to know, first of all, what we're pressing and what speed we're trying to get to, which is why we're currently setting the target speed first. Then if we imagine we're calculating this from the very first frame, we already now know that our target speed is movement multiplied by or more accurately our direction by movement speed, multiplied by Delta, so we'll get a value of 500 multiplied by whatever our small value is based on the frame rate. So we have a clear target to reach. The first time we run this, obviously, current speed will likely be zero. So we're going to go from our current speed of zero, so we're not moving at all to let's just say 100 units of movement to provide a nice clear number. We're going to take Dw time into account again to keep this frame independent, kind of, and we have our movement speed, the interpllation movement speed to provide different level of smoothing here. Then the next frame, obviously, current speed would have been calculated based on this to be a little bit higher. So we're going to go from that slightly higher speed. Let's just say two or three, and then we're going to go from that two or three value, still trying to get to that 100 units of speed that we need to get to. And we're going to be doing this continually over time, which will provide that smoothing. So the final thing is we don't want to use target speed directly. That's just our we want to use whatever the speed is at the moment. So we'll delete this one. We're going to grab our current speed. And again, we're just going to drag this onto our green pin here, and that will automatically hook that up for us. And that's it. That's pretty much ready to go and test the smooth movement. So you can see here, as I'm moving back and forwards, the moment I release the key, we have a little bit of smoothing happening as we go down to no speed at all. So we have some deceleration and acceleration coming in here. Now because we're doing that over time, the movement may feel a little bit more sluggish. So one of the other really useful things from promoting our magic numbers to variables is I can just grab the movement speed. I can maybe double this, so we'll just set this to 1,000 because it's feeling a little bit slow. Didn't need to go through the code. I didn't need to find out where this was happening. This is really well named, so I know that this is controlling the speed at which I'm going to be moving. I can press play. I can test this again, and we've got a completely different feeling plane going on. So this is feeling much better. This really kind of exaggerates the acceleration and deceleration a little bit more. And again, all of that was done changing a complete property of the overall movement system simply by grabbing the movement speed and plugging this in here. Of course, we've added the smooth movement, which is why we needed to make that amendment. If you wanted to play around with things you could do a similar thing, grab the movement interpolation speed and see if you wanted that to take more or less time to reach that final value, that current speed that we're targeting. So these are just kind of variable tweaks here based on how you want your game, your plane to fill. One design principle that I heard very early on and has always stuck with me is always try to exaggerate the variable. So if you're not sure what they're doing, or you're not sure whether you wanted it to be much faster or much slower is double or triple the value that you're working. I could set this 4-12 because you want to hit an extreme first, find out if that works, if it's too high, too low, and then refine it in between. So I can see that is almost feeling quite snappy again. There is some acceleration and deceleration, but not very much. So what I might want to do is tone this back down to six because 12 is definitely too high, and we're feeling a little bit better here. Now, as I said, for me, I quite like the feeling of this at four, but just a general design principle is to take the value to an extreme, double, triple, half something, whatever the case might be, and you'll quite quickly be able to hone in on what you wanted your property to feel like. For my purposes, I'm going to keep the movement interp speed at four, and we can move on to the next feature. So now adding the banking rotation. This is actually going to be very simple. It's essentially a repeat of what we've just done with some slightly different functions. Essentially, as we're moving side to side, we want the plane to tilt a little bit to make it feel a little bit more like an actual aircraft. A quick refresher on the axis here. I don't think I've touched this in fall just yet. I'm going to grab a static mesh very quickly. I don't need to follow along. This is just to visalize what we're doing. I'm going to press E inside of the viewpoint to go into rotation mode, and we can see our axes here. So I've mentioned before, we have this transform, the location, rotation and scale that makes up the position in worldspace for our actor. The locations very simple. We have X, Y, and Z, which is forward, sideways, and up and down. Rotation, it's relatively simple. Same thing. We have three colors. We have the red, the green, and the blue, so the X, the Y, and the z. But if we hover over these, we can see here they actually have their own unique names. We have the roll, the pitch, and the le. Now, this doesn't matter too much, but if you ever trying to tweak these features, add your own functionality, and you're not even sure which axes should I be rotating or something around. I just wanted to show you a slight visual here that's actually really simple to find out. So, in this case, I want the plane to tilt in a direction based on how it's moving. So if I try just a few things at random, if I try the green one and see what this does, we can grab the green value over here and I'll just slide this to get this rotating. And we can see this gives us a kind of up and down rotation of the plane here. This would probably be good if you're moving forward and backward. You could maybe add a little bit of this to exaggerate the movement of going forwards and backwards. If I grab the z and do the same thing, I just grabbing the ones I know don't work to begin with, get these out of the way. We can see that this is rotating around the plane's axes. So again, not going to be very useful unless we're doing four tones or something. So the one that we want to use is going to be the X axis here. So as we're moving left and right, we're going to want to slightly bank and roll just like this, based on the direction we're going. So this is how you could very easily come in, play around to the different widgets here, rotate in a certain direction, and see which axis you wanted to apply the rotation to. As I said, that was just for visual demonstration. I'll delete the static mesh. We'll go back into the pan base. Move this over a little bit and we can get back on with the rotation. For the rotation here, we have a couple of very similar functions we've already used. We're going to pull from the set actor location, and you can probably guess we're going to search for something called set actor rotation. That's a very similar thing, updates to the rotation, and this will be every single frame because we're throwing it on the eventi. Now, we're going to want to use a very similar function as well, we want to use an interpolation to. Now for rotation, we don't use the F interpret, we're going to use something called interpret two. So we're right clicking here. Search for interp two, y for rotation, and we'll take this one just here. The other reason we're doing it this way, we could potentially grab just a single floating value because we only want to do this on the X. We could do a very similar thing here and use an F interpret. Now, I'm keeping this on parity line for line with the Unreal Engine content examples project. So this is exactly how they've done it. They've used an F interpret for the location and an R interpret for the rotation. So I wanted to show again, the code that exists and how we can fix it just in case questions were arising, why are we doing this slightly differently. But the main things here, this is very useful still because we can see we get the same properties that we need to feed in. We have a current rotation, so where we are at the moment, we have a target rotation where we want to rotate towards. And we have the Delta time to make this frame rate independent, again, in kind of. And then we have the interp speed, which is the smoothing between. So current is actually really simple. We have another very similar helper to what we've used before. If we right click in the event graph and search for Get actor rotation. This will provide, like the get actor location we've used, the rotation of the actor at this given frame. The target is kind of a random arbitrary number, and I say that just because all the epic example has done is it's taken the current speed, so I'm going to control drag the current speed into the graph to get the current speed that we're moving and multiply this by a value. So we're going to pull from here, select multiply. And I think theirs was something like 1.5. So we're going to take whatever speed we're moving, whether that's a positive or a negative value, multiply that by 1.5. And that will be the target that we want to rotate towards on the x axis. Something I wanted to introduce you to. We've already seen here. The use of the split structure pin to take our three point vector into its individual floating points. The rotator is exactly the same. We could just right click split structure pin and take this into the X Y and Z. This can get somewhat messy when you have so many different floats altogether. So one thing that can be quite useful, and more than anything, this is really just for code tidiness is we'll pull from the target here and we're going to search for something called make rotator. We want this bottom option just here, and it does just as the name suggests. It's making a rotator from an individual X Y and Z. It keeps these two floats kind of by themselves, so easier to read, and it allows us to just plug our value here into the X, just a different way to get to the same goal as we've done before. Neither of them are less or more efficient, and it can actually come down to what is more readable in a certain instance. This I wouldn't say is too unreadable, because it's just one vector that we've split. Where's this? There are already a bunch of floats, and splitting more floats into that just became a little bit unwelding. Like we've seen previously, we want our final result to be the new rotation we have here, so I'm going to take that interpolation and plug that into the new result. And that just leaves us with a couple of things to fill. So we want our Delta time, so I'm going to come over here, grab a get world delta seconds, press Control ID over here to duplicate that and plug that in. And then we just need our interpolation speed. So again, that's moving in between. I'm testing, I think, a value of around about ten worked very well, and this is one of those values you'll just come back and tweak to get the rotation feeling exactly as you want. Again, no magic numbers, so we'll pull from here, promote a variable. Give this one the name of rotation interp speed. And with that we're pretty much ready to go and get this tested. Although one things just stuck out to me. This is essentially acting as a tilt factor or tilt strength, rather than having this magic number as well we'll pull from here, remote to variable. I'll just go with Tilt factor, and that is now actually ready to test. So we can hit compile, press play, and we can see we do have some banking. Now we do have another issue, which I completely forgot was going to happen. So we will fix that in a moment, but the banking, the rotation is indeed working. So now that I've potentially given everybody motion sickness, the camera is banking with the plane. We've confirmed that, at the very least. The reason that camera issues happening is the camera inherits rotation from the parent. We've really seen this in the hierarchy. The cameras attached to the spring um, the spring arm to the static mesh, and these are following all of the rotation and location applied to the static mash. This is really simple to fix. And one of the reasons I wanted to use the spring um, I just forgot to tick this on with the spring um selected in the details panel to the right hand side, this unique component provides us with a solution. Epic knew this kind of thing would be a potential issue. So they've given us some tick boxes here. We can untick the inherit pitch, U and rotation. Remember the names for the X Y and Z of rotation. So if we untick those, this is basically saying, don't allow the spring arm to follow the rotation of the parent component. And because the cameras a child of the spring arm, that will apply to the child component of the camera as well. So if we hit compile, press play, we can go in and we can see we can see the rotation nicely. And we're not getting that potentially nauseating camera movement that we had just a moment ago. One other thing I just realized with the spring um selected, I still had this ticked, as well. Do collision test. If you've had any problem where the camera seems to be moving around or getting shoved into the floor or something, that's potentially because you have the collision test happening. What's happening here just to visualize this? If anything is detected between the camera and the thing it's looking at here, so something with collision on it at this point in the red spring arm, that's where the collision test is happening. If anything passes through that, that's where the camera's going to try to automatically move out of the way or duck behind something to avoid that collision. Really useful for things like third person games where you may have the camera brushing against a wall, and it will get a little bit closer to the player. So that's quite a nice feature. In a game like a shoot them up like we're creating, though, we can have issues where the camera looks twitchy if projectiles or traces and things like that are being passed through the camera, the line of the camera. And again, this is something else you see in beginner projects where the camera's kind of twitchy or glitching at. It's probably because they've got some kind of collision test happening between the camera and what you're looking at. So we're going to turn this off. We won't need that for this type of game. With that done, the camera stays level. The plane banks naturally, feels much better. So now we finally get to look at where Epic's example breaks down a little bit. Just wanted to mention this is no shade being thrown at the example project or Epic or anything like that. I'm purely bringing this up because I realize this is a perfect teaching opportunity, something which is so widely covered used by so many different tutorial channels, myself included, you're going to see code which is broken even from official sources. So knowing that and being able to look out for that is going to be super valuable. So the problem we're getting at the moment is if we're going to play mode. At high frame rate, I'm going to put my framerate stats back on. So 120 FPS, this is looking kind of okay. If I put this back down to 30 FPS, though, focus on how much the rotation is being applied. So again, you can see this isn't just a case of one of them looking slightly smoother and one of them looking a little bit less smooth due to the frame rate. This is literally a difference in the mechanic, same code, same input, completely different rotation. At 30 FPS, we're almost rotating the wing through the floor, depending on how far away the plane is from the floor. And this is one of those things. It's not even gameplay breaking. But if you've set up a system to look super smooth, and it looks exactly as you want it to look to be the most appealing to the player at but then someone on a lower spec system plays and is greeted with the second result. You're obviously going to be disappointed, they're not getting the experience that you intended. And this is why this is really important to focus on. And this is what I meant by this bug exists in Epics official content examples, same code, same problem, shipped in the actual documentation. And this is why blindly trusting even official examples, even first piety documentation can have bugs. So always test edge cases and look out for things like this. That's what I'm really trying to get across. Touched on previously, it's going to be a little bit too long for this one topic. So this is something we're going to fix a little bit later, and we'll be rebuilding the movement system entirely. But for now, we're just going to acknowledge that the bug exists. This is still perfectly fine to work with. It's a perfect foundation for building further. We'll come back and we're going to rework the movement system entirely, and we'll fix all of the bugs when we go through. And now that we've seen the payoff for not using magic numbers, we have our movement speed, controlling how fast you can go, movement interp speed, which is how quickly you reach that speed, our tilt factor on how much the plane banks at the different speeds, and then even the rotation interpolation speed, as well, which is how quickly we get to those tilting values. All of this is tunable without touching the logic. If you want snappy arcade action, we can increase the interp speed. If you want a slow and floaty space, you can lower it. The feel of the game is then all coming from the numbers. So we can leave that there. We have a smooth, somewhat satisfying movement with some nice banking. And yes, there's the frame rate bug that we're going to come back to, but at least we know about it, and we'll fix that later. Next, we can upgrade to the enhanced input system. This is Unreal's modern replacement for the legacy input system that we're currently using. If you wanted to experiment, as I've said, you now have all of those values ready to play with. Try adding some more pitch to the rotation and potentially adding that with the forward movements. The same pattern, different axis for both the rotation and the movement if you still only have this going in one direction. 8. 07 - EnhancedInput: Movement setup and working to a testable point. Remember the legacy input system that we've been using that as I've mentioned, is deprecated. The enhanced input is the modern replacement. There's a little bit more upfront setup, but it's significantly more flexible. We get things like hot swappable control schemes, built in dead zones, runtime rebinding, all the things that you'd have to code yourself using the legacy system. So let's begin migrating over. The first main difference is the enhanced input system actually has its own set of classes. So navigate back to the blueprints folder, right click in here or press Control Shift in n to create a new folder, and we'll call this one input. Enhanced input splits things into two parts. We have our input actions, which defines what can happen things like fire, move, restart. And then we have something called input mapping contexts, which define which buttons trigger the previously mentioned actions, providing a separation of concerns. And the reason this separation proves useful, this allows the control schemes to change without needing to rewire the entire gameplay logic. In code, we can simply call the input system to switch between menu controls, gameplay controls, vehicle controls, whatever it may be. They can share the same action mappings to the specific buttons, but they can run under different contexts. One line of code, and we have everything switched. Inside of the input folder, we're going to right click, and I'm going to go to the Input category just here. We can see we have the input action section, so we'll create one of these. Give this the name of IA Underscore Fire. We'll select this one Breast control, indeed, to duplicate. We need two more of these, so we'll duplicate this twice, and we'll call one IA underscore move, and the other IA underscore restart. Next, we want to open up our IA underscore move class. We'll double click to open this. And the main thing we want to look at is just here, we have the value type. By default, this is set to digital Boolean or ball, and this is very similar to what we set up in the project settings with the action mappings. We need to delete these anyway. We're going to go back into Edit, project settings. We'll go down to input again. And just for context, the digital boolean that either on or off would have been if we created an action mapping here. For example, we could have created something called fire, bound this to a button, and had this firing every time we wanted to call a projectile. Whilst we're in here, I'm going to also get rid of the action mappings just so that we don't have anything leftover. This will also cause a little bit of a bug in our code, but this will make things easy to find and hone in on when we're doing our code update. So we'll just press Delete to get rid of our access mappings, as well. Again, that automatically saves we can close this and go back into the IA underscore. Both our fire and restart input actions. These both need to digital bullions, which is why I haven't opened these. They're perfectly fine by default. We're either pressing the fire button or we're not. It's the same for the restart button. For the movement, though, we want to drop this down, and we want to use this axis one D float, essentially providing the same thing as an axis mapping, providing a value from negative one to positive one or zero if nothing is being pressed. So that's the only change that we need to make inside of our input action. We can press Save. We can close this one, and we can go back into our input folder. So now we want to get to our mapping context. This is going to be which buttons trigger, which actions. If we right clicking the folder here, we're going to go back into the input section, and this time we'll create an input mapping context. Naming convention is going to be IMC. Underscore, and then the name of the thing this will be responsible for. We'll only have one type of input in this more simple project. So I'll call this one main for our main input. Double click to open this, and we can see the way it works. Now, this kind of is representing the project settings that we were editing earlier. In the top left hand side, we want to click this plus button and drop this down. We need to make each of these entries unique, which is why this is currently being graded out, so we can't add any more mappings until we have some unique properties in this one. If we drop this down, we can set this to Ia underscore fire. And then if we drop down the category just here, this is where we can begin doing the same type of mapping that we saw earlier. So if I wanted the fire functionality to be called on Spacebar, I can press the button here, hit Spacebar, and we have one binding. If I also wanted it to react to the left mouse click, I can click here and do left mouse click. I'll also do the same thing one final time for the gamepad. I'm going to press the button again and press the lower face button, the A button on the gamepad, and we have that bound. So we now have three different buttons or three different inputs, which can all call the fire function when we get to implementing that. We want to do the same thing again for our other input actions. So we'll press the plus. We want to bind something to the restart. This will be nice and simple, so I'll do this one first. Same thing again, we could have potentially R for restart, and perhaps I'll add one for the gamepad, as well, and I'll make this the start button. So that is the special right face button. Just to note, I'm using the left shift and pressing the arrows, and that will collapse everything back up. If you wanted some shortcuts here, hold Shift, press left click, and you can see that unfolds every individual element. So a nice quick way to have things either unfolded or collapsed neatly when we're going through these different mappings. Final mapping that I wanted to create is going to be the movement. So we'll add one more of these. We'll bind this to I A underscore move. We'll drop this down. And again, we're going to want to add some keys here. If we get started the same way we did previously, we're going to click the button here, press D to have a default right movement, and then click another one, press A to get our left movement. Now this is where things change a little bit, and this is unique to the enhanced input settings. Even though we have these set as a one directional axis, a floating point value for our IA underscore move, you can see we don't actually have any floating values to change here to invert the A to make things move left. Instead, this is where modifiers come in with the enhanced input system. So if we click on the plus sign on the modifier, can drop this down, and you can see a bunch of useful functionality pre built in. We can add things like dead zones for analog sticks, automatic scalars and things like that, which can be applied to the input. The One we want is just here, so we want to negate the value. It's going to default to one. If we negate that. This is what gives us a negative one. So nice and simple to use just a slightly different approach. I'm going to do the same thing again. I'm just going to add two more. One for the right arrow. One for the left arrow. We'll add that same modifier onto the left arrow. And then finally, I'm going to add one final input per move, and I'll move the left analog stick again. Now, the same thing happens here. We don't need to add in negates here because Unreal knows that an analog stick has a left and a right potential on the x axis, it will automatically return negative one when I push this to the left. That's everything set up and ready to go inside of the IMC underscore move. So we'll hit Save and we can close this one. We won't need to edit anything in there again. So, this may have looked a little bit tedious. As I've mentioned, it does have a little bit more of an upfront setup, but you only need to set this up once, and these assets can then be copied to two other projects very easily if you find you're using similar patterns. And the main thing is that in larger projects, this will begin saving you a lot more time as it's much more flexible than the legacy input system. Now we want to jump back into our porn based class. This is another one I really need you to focus on because this is the step that everybody often forgets with the enhanced input system. The mapping context exists, but nothing is using it just yet. And unlike the legacy input system, our enhanced input system needs to be explicitly bound to a class. We can do this really easily, and this is the step that everybody quite often forgets to do. From the begin play, we can right click somewhere near here, and we're going to search for the G player controller built in function. The player controller is what is receiving the input directly from our physical devices, feeding that into unreal and deciding how to manage that across the pan that it may be controlling. This can also apply to AI, so this would be the input that AI is giving to the controlled AI pawn. From the controller, we're going to pull from the pin here and we're going to search for something called G enhanced input local player subsystem. Really snappy name there. We want this one just here with the pink F, so this is indicating this is a function. It's just going to be returning a certain value, which is that enhanced input, local player subsystem. And from here, this is the function we really need. We're going to call the Ad mapping context. Nice and clear. We can see exactly what this will be doing. This will be adding a specific mapping context, the one that we've just created to be bound to the class that we're currently in. So we'll make sure that we hook these up with the execution pin, drop this down here the mapping context that we want to use, and we only have the one we have IMC underscore Man. The way that you may be using this, though, as I've mentioned, is you may have a more advanced specific controller, which is tracking what you might be doing during the play through of your game. During gameplay moments, this would be making sure that the mapping context is using the gameplay IMC, and you may have something like a main menu IMC, as well, so you can navigate with the keyboard in your main menu. When the player presses pause or escape, the player controller would be responsible for changing the mapping context to go from gameplay to menu. And this is where this becomes really, really flexible. As I've mentioned, a little bit overkill for the kind of project we're creating, but it's useful to know, and you can really build upon this into any of your projects. And the thing I really want to drive home again, this step is critical. I've seen so many people configure everything perfectly. They have all of their input set up. All of the movement functionality would be working correctly. They press play and nothing happens. You spend an hour debugging, and then you finally realize that you forgot to bind the mapping context. So don't be that person. So the final thing we want to do here is replacing the legacy input that we had. If we hit Compile, because we've removed the legacy input from the project settings, this is what I meant that we'll actually get a very useful error or warning down here. It's now telling us that it's trying to reference something which is an unknown axis. If we press this underlined text here, that will actually drop us straight down to where that problems occurring. So nice and easy. And all we need to do is we'll grab this node that no longer exists in the project settings. Press delete. We can right click in here, and we want to search for our IA underscore move. Same thing, very similar to the legacy input. We get two options. We get the action events, which would create a node like this where we have an execution pin and the float variable, or we can use the axis action here, the action value. The same thing as the axis value we had previously. So we'll select this one. This will give us just the floating point value, and we can plug that back in. So the code itself is pretty much identical. Nothing's really changed. It's just the way that we're going to be setting up the enhanced input to feed that information to us. That makes the transition relatively seamless, and all of our movement logic will stay exactly the same. If we press play, we can test that. So A and D are doing the same thing, left and right, and this is the analog stick again. So all working exactly as we would expect. All we've had to do is upgrade the input layer underneath our core code. And that's it. We now have the enhanced input system in our project, fully setup, which did take a little bit more than the legacy input setup, but it's much more flexible and definitely worthwhile. We'll be using more of the features later, such as the fire events for shooting, triggering held inputs on a loop, and then also the single fire restart button. The next topic we want to address, though is going to be our gameplay boundaries. Right now, we can fly up into affinity, which is not really great for any shooting up. And, of course, if you wanted to experiment, you can try adding in things like dead zone modifiers for the thumbstick input, see how timers or pulses might work using the built in functionality. No code required and just something to play around with. 9. 08 - Boundaries: New input system, we still have this issue where we can currently fly off into infinity. Eventually, you'd hit some kind of floating point precision issue, but long before that, the game would become unplayable and more importantly, quite boring. For this type of game, we're going to want a fixed camera and some boundary walls, some invisible walls. The camera we have is currently attached to the pawn, and for a shoot them up, a level camera would just make more sense. So if we go back into the BP underscore pan base, we can grab the spring arm control select, the camera, and we'll just delete these. If we press play, we're now back inside of the ship, so we have that problem back, but we're about to fix that pretty much straight away. The main thing is that you're now aware of the gameplay framework and you know why we are looking at the inside of our ship. If not, then go back a few topics. If we go back into the main level, we just want to add a camera. To the top, just above the viewpot there's a small cube, which is our add button. If we drop this down, we want to search for the option camera. Many of the options here tend to move categories every few unreal versions, so searching can be a little bit faster because life's just too short to be looking for cameras. One that we want is our camera actor. This will be dropped directly into the world if we select this. The camera is going to need to look at where the player will be spawning, and we already know again from the gameplay framework how we're managing that and where that's going to be. So a nice quick, shortcut here for you. If you grab the player start, which is where the player spawns, we're going to go to the details panel on the right hand side, right click on the location property, and we can copy the actual location of the player start. If we grab the camera, we'll do the same thing, but we're going to right click here and we're going to paste the property that we've just provided. So we now have the camera directly at the point of the player start. We also want to give this some rotation. So inside of the viewpot we can press F to focus in on the camera. If you wanted, you can go into rotation mode and you can use the widgets just here. Now I know for our needs, we just want to rotate the camera -90 degrees on the Y axis. So I'll do this from the details panel, and we're now looking directly at the floor. This may be a little bit too close. So the next thing we're going to want to do is move the camera up in the Z axis. So again, if we grab the Z value here, I'll place a value of 2,400. I think this is similar to what we had previously. We can see now we're going to be looking directly down at the player start and where the plane will be spawning. You may not be tempted to press play and see what happens. The reason being quite simply, what if we had five, seven, 20 cameras in the scene? How would it know which one to pick? Like with the other rules that we are setting, we want to be in control of this and set this manually. So with the camera actor selected, we just want to search in this panel just here, in the details panel, we want to search for something called Autoplayer activation. We can see we have this option here. Auto activate for player is currently set to disable. If we drop this down and select player zero, this will be the first and only player in our single player game. So now the framework knows to use the camera that we've just selected. If we press play, we can see that. We'll need to do some adjustment to get everything centered properly, but we're now looking roughly where we want to be. So with the camera placement, we're going to want the player to be closer to the bottom of the screen, kind of like space invaders and again, somewhat recreating the setup in the content examples that we're going from. The problem with the editor and usability here is that the player start doesn't have any visualization. So it will be a little bit difficult to see where the player ships going to actually be spawning and position the camera accordingly. So a quick trick here that I wanted to introduce, we can add a temporary sphere or shape, something like that to show where the player start is. If we go to the Add button up here, we can go to our shapes option. I know that I still have the location stored from the player start earlier when I copied that. I'll undo the search for Autoplayer here, right click and paste the location of the player starting. And I looking at this sphere, which is directly on the players start. If we grab the camera actor again, this has helped because we can now see exactly what the camera will be looking at. Pressing F, I'll snap back up to look exactly at the camera and we can un drag this into place. So if we bring the camera forward or we could bring the players start forward, either one will be perfectly fine. The main thing is that we want the player ship to be roughly down here. So that's a nice, easy way to get things in position nice and quickly. If I grab the sphere, I can delete this. Obviously, we don't want that to stay in the lab. Can press play and the players closer to where we wanted it to be. Another tip of the camera that I just wanted to show very quickly is if we grab the camera again, whilst you're playing around of things. If you didn't want this demonstration window to automatically toggle off when you select something else, we can choose this little pin icon down here, and that will pin this window to your viewpot. So if you needed to come in, move the players start to something around to see what's happening through the view of the camera, another quick tip there that we can keep this on screen temporarily. When you want this to go away, we can just press the pin again, and that will unpin from the viewpot. Just a couple of things to begin getting comfortable with and familiar with. I'll grab another shape just here. I think I've mentioned it a couple of times, but if you press F with an object selected, you're focusing on the object you want to look at. You can click to then rotate directly around the object. And another thing which can be quite useful is if we're placing something like this, we can hold a shift, left click on the widget direction, and then drag this. So if we begin dragging an object here and then press Shift, was this moving, we can actually have the camera in the viewpot keep tracking the object that we're moving. Quite handy if you're trying to play something and you didn't want to keep moving it, moving the camera, moving it again, and so on. So just some quick tips there, getting familiar with the viewpot. Now we're going to want to begin looking at the boundaries. Once again, from the Add button, we're going to search for something here called a blocking volume. We can see how we have the option for blocking volume. We'll select that one, and what we get given is this invisible cube. This is going to represent our invisible wall, and we just want to lay this out across the level to block where the player or the enemies may hit. To get this in place, I'm going to do the same thing again. I'm going to right click and paste the location of the player start. That will put us down here. For this one, we can put this on the left side of our play zone, which I know from testing would be a good spot of negative 1,240. That will place this just over. Ready perfectly fine on the Z axis. This is high enough that the plane will indeed collide with this. If we put this at the X location of zero, that will put this kind of middle of the level on the Ford axis. And then what we want to do here is scale the box to cover the entire extent. Now, one way that you could do this is to make the X axis here much longer. That will change the box. That is kind of stretching the actual actor. When we're working with things like volumes, we have another option. So I'm going to set this back to the ideal way that we'll be working with these is actually changing the brush settings here. So this is the size of the box that we currently have at the moment, which is 200 centimeters around. So if we set this to something much larger instead, I'll just add a couple of zeros here. So we'll have a 20,000 unit box just to ensure that we're encapsulating the entire play zone and a little bit more, probably a lot more. But that's perfectly fine. The reason that we want to do this is that this is changing the scale of the actual component inside, this brush box component. Whereas this value up here is the scale of the actual entire actor. So if we can keep this uniform and clean at one, then if we ever needed to check things in code, it's a little bit easier to work with. And again, just keeps our structure a little bit tidier. Whilst we're down here, I'll just make the height a little bit bigger asll. So we may want to set this to something like 1,000 on the Z just to make sure that we're definitely blocking absolutely everything. Something else that may get a little bit confusing is, if it's a little bit hard to see, we do have options to change the opacity, we can add a display shaded volume, and then we can start seeing where the actual cube is. Again, this doesn't show in game mode, but it does show through the camera, which makes it a little bit easier to see. And the main thing is being able to gauge where the blocking volume will actually occur. With that done, I'm just going to press Control in D after selecting the blocking volume we've set up. I'll set this to 1,240, so we have this on the right side of the playfield. So on the YX is here. And just to make it visible, like the left side bound, I'm just going to scroll back down. Turn on the display shaded volume, and these will both and look the same. Do be aware as well. I think when we duplicate things, Unreal tries to move the duplicated actor several units either way so they're not fully overlapping. So you can see that on the right side volume, we have some offsets on the X. This actually wouldn't cause a problem. But just to soothe the voices and my brain, I'm going to set this back to zero and everything again, nice and tidy and well maintained. With a number of changes like that made, you may want to press Control Shift and S to save everything that we've been working on. So now we can go into playode. We're going to be looking through our camera. It's going to be a little bit difficult to see because we can't see the actual bonding volume here. So if we did want to test things and make sure we know exactly when we might be going through a wall, another option, a little bit lengthy here is we could go back into shapes. We could add a cube. Want to go down to the collision settings in the details panel on the right hand side, and just to ensure that we're only going to be checking against our blocking volume, we can select the collision preset just here and turn this to no collision. We'll look more at collision a little bit later, but this is just to get something visual. Then again, I'm going to go back to details, paste this in the location, and put this -1,240. For speed and simplicity, I'll just keep this roughly the same size. It doesn't need to be exact, so we're going to grab the cube. On the Y axis, we need to make this two units on the Y to get roughly the size of our blocking volume and again, we'll make this a lot larger on the X so that it spans everything and larger again on the Z. We don't need to be quite so careful. Here. The main thing, it's just something visual so we can see if and when the player would be going through the wall, so we can see that here. The main reason is that I knew that the collision wouldn't be working, and I just wanted to immediately eliminate the fact that we are indeed going through the bonding area, which should be colliding with us. The reason I wanted to introduce this, as I knew this was going to happen, this gives us a bit of an example on how we may go about debugging. This is really important in development, actually understanding how to problem solve, work things back, and find out what's going on. Already kind of answered one of the problems. The first thing is that we wouldn't have known if we're just flying around whether we had actually gone through the wall or not. So we've already discarded the fact that we're not reaching the wall. We can get to the point of the wall and go further and no collision is happening. So that's one option from the checklist completely ticked up. If I grab the blocking volume on the left here, we'll try and fix this one first. The first suspect might be the collision properties that I've just shown you. So on the right hand side in the details panel, we'll go back down into the collision properties. Under collision, we can see that this preset is set to invisible wall. If we drop this down, we can have a look at what this means. And this is going to be a very brief overview of collision. Again, there will be much more depth on this coming in future topics. But for now, all we need to know we have three different types. We have Ignore, overlap, and block. Ignore does what you would think. It ignores all other colliders and interaction. Overlap is looking for two things which are in the same physical space, but not physically touching with things like physics and blocking each other. And then we have block, which would be two physical objects impacting each other and trying to push each other away so that they don't inhabit the same physical space. In short, invisible wall is set up exactly as we want it. It's set to block absolutely everything in the world. So that's our second suspect, by the way. We know that we're reaching the wall and we know that the wall has collision. The next thing is we're going to look at the pans collision. So if we're going to the pawn class, we'll go to the static mesh, and the collision at the moment is being handled on the static mesh. So in the details panel, again, we're going to want to scroll down and find the collision. We'll drop down the collision presets, and we can see that this is set to block all dynamic. Now, this actually doesn't matter. Again, the important thing of these tick boxes here. This is ticked to block absolutely everything as well. And if we go back into the world very quickly, we'll get a very quick crash course on collision here, we can see that the invisible wall is set to be its object type is set to world static. Porn itself is set to world dynamic. So this is looking to block world static objects, and this is looking to block world dynamic objects. So as long as those two are looking to interact, we should see some physical blocking. So that's the third suspect, by the way. The porn collision is actually set up correctly. And that really leaves the only thing is that we've probably done something wrong in our code. And spoiler alert, I already know that's the problem. So if we go down into the event graph on the event, we're going to move over to our set actor location function here. And this is one of those things where it would be quite difficult to debug back and actually know this is the issue without really having experienced this. Two things to look out for here are the sweep and the teleport booleans on the function call. If we hover over the sweep, we can see here the description that we get is whether we sweep the destination location triggering overlaps along the way. This is really important. Also controlling whether we stop short of the target if something is blocked by something. This is the real important part here is that if we don't have this ticked enabled, then we won't be checking for having our position blocked, which means we're not going to hit anything. In short, having sweep unticked is technically kind of teleporting us to the location that we want to be. So if we tick this on, hit compile and save, we'll go back and test again. We can now see when we've flying over, we're now being blocked. So we haven't needed to change any of the collision presets. We'll get into those a little bit later. That means, as well, that the wall on the right hand side will be working because it's set up in exactly the same way, so that's perfectly fine. It was just a really simple thing, very easy to overlook in our code. So I wanted to include that just because, like I've mentioned, things don't always go perfectly, and you do need to be comfortable just somewhat debugging and trying to work back through the things that you logically think could be part of the problem. This is one of those things that ends up being a really common gotcha. It's really easy to miss. Really hard to debug if you don't know what you're looking for. But had that failed, even myself as an experienced developer, I would have been kind of out of potential thought paths I could have gone through, and I would have had to have just gone over to Google or RDI or something like that and tried finding there if anyone had a similar issue. So just a quick tidy up with the boundaries now. I'm going to go back in. I'm going to get rid of this white cube here. That was just for visualization. We now know that we are spawning in the right place, and we're getting blocked by both of the walls over here. So with that done. The camera is fixed. It's now in place where we want it. The boundaries are set, and when we begin adding enemies, they'll detect the walls and bounce between them. Obviously, once we've implemented that code. Before that, though, we're going to start looking at materials, the more kind of visual elements of our project. We'll be able to customize the look of our ships so that all of the enemies don't look identical to the player. As always, if you wanted to experiment, at the moment, just try something simple. Try adding a top and bottom boundary, especially if you already have some forward and backwards movement. Once again, same process, different axes, just to get you familiar and trying out different things. 10. 09 - Materials Instances: Moment, if we jump straight into creating our enemies, they would look exactly like our player. Same blue plane, no way to really tell them apart. That's why our materials come in, allowing us to wrap different colors and images around our three D models to differentiate them and add some variety. We won't be creating materials from scratch, as that would be its own rabbit hole. And as I've mentioned, we're focused primarily on programming, practices and projects, the three Ps. But I will show you how we can create color variations from what I've really provided. Whilst we won't be creating new materials from scratch, I do want to show you the materials that we already have. If we go into the assets folder materials, as I've touched on previously with the naming conventions, we have a few things here to look at. We have two MM underscores the water and the texture, and we have four MI underscores, which are the material instances. Mm, which is a prefix for master material. This is where the actual shader logic happens. We open the texture base, we won't be editing anything here, but I just wanted to show you some of the terminology and details to be familiar with. If we move over to the left hand side of the graph here, this is very much like the blueprint graph we can just right click and drag around to move. The main thing that we have are three texture sample inputs. We've got the base color, which is our color map, the blues, the wood grain, the paint. And this is all taken from the Ubi map on the three D model. We then have our normal texture or the normal map which adds surface detail without geometry, those bumps, ribbts, panel lines, and so on, all faked by manipulating how light bounces off of the. The final important one is the ORM texture. In this case, this is something I've created personally, and I have a slightly different workflow. I've created this as an ORME specifically this time. This is essentially four grayscale images crammed into one texture files, RGBA channels. The main reason for this is that all of the data within them can be extrapolated through grayscale. This saves memory, and it's just a little bit easier to work with. In this case, the red channel is the ambient occlusion. This is where the light will struggle to reach. The green channel is roughness, creating a mirror smooth versus matte surface in different areas. The blue channel is metallic. This is binary. This is either metallic or not metallic, and the Alpha channel is emissive, showing which parts would glow. Something which is useful to be aware of when you're working with materials is that rather than just having this spherical representation here, we can change what shows in this viewpot with a handy editor trick. If we open the content draw and go into the meshes folder, grab a mesh that you'd like to visualize with your material. In this case, it's going to be the plain hero. We don't need to do anything with this. We just need this to be selected. We can go back into the material and unil remembering what we currently have selected inside of the content drawer. Can change the representation, the visualization of our material here to be things like a cylinder, a plane, a cube. And then specifically this brick icon here. If you have a static mesh, selected, and we click that, it will give us a representation, visualization of our plane or the mesh that we currently have selected in the content draw. So this can be quite handy to visualize things and see exactly what is responsible. But what when you're changing different properties and getting that immediate feedback? Next up, I want you to focus on these named nodes everywhere. These are called named reroot nodes. Intead of wires stretching across our graph, somewhat like a conspiracy theory evidence board. We can instead choose to create named connections just once, and then we can reference these absolutely anywhere inside of our Shader. Completely optional. You won't always see these used in other examples, but it does again, allow me to keep all of my Shader logic on the left hand side and then just call things when I need them on the right hand side. One of those things where the tool exists, so why not use it. As I've mentioned, though, I wouldn't recommend changing anything inside of the master material unless you know what you're doing, or at the very least make a duplicate and make some tweaks in that one so you always have something to fall back on. The main reason being the next topic we'll look at are the material instances and the way that the instance works is driving the information directly from this master material. So if something breaks in here, then you're potentially going to break all of the instances that rely on it. So back inside of the materials folder, if you've moved around, to save a little bit of time, we can use a fairly logical starting point. We already have one plane. It has most of the textures and everything set up exactly as we need it, and that is the MI underscore plane player. So go ahead and grab this one, press control, indeed, to duplicate and rename this one to plane enemy, keeping the MI at the front. If we double click and open this, we can now start looking at material instances. We can see that this is much different from our master material. One thing to keep an eye on here is on the right hand side in the details panel. Something I should mention, you're probably noticing patterns here. Every time we're in a panel, whether that is materials, blueprints, components, view pot, when you grab something, there's normally a details panel lingering around somewhere to give you information on what you're looking at, which is definitely one of the nice things about Unreal. When you begin learning parts of the feature sets, that transfers over to different features inside of the engine. But what I was about to say is if we look at the details panel, we have the parent being denoted here, and that is showing that this is the MM underscore texture base. So we can double click into this, and this just takes us back into the material I've just been showing you around. So, as I've mentioned, all of the information is coming directly from this master material. One other thing, it'd be nice to visualize the plane again. So just to recap, go back into the meshes folder, grab the plane and press the brick icon over here inside of the material instance. The main things that we want to change are the textures. We can see that this is very different. We don't have any of the graph or that shader information here. All of that is specifically used in the master material. Inside of the instance, what we have instead are things that I've specifically chosen to expose. All of these named big notes here, the textures, the texture color. These are parameterized variables that I've exposed so that we can see those in the instance. So what we want to look out for if we drop Dan's categories, these are all of the things I've just been showing the exposed properties. By exposing these inside of instances, we get a really pleasant work environment where we can just make a change and we can see that update immediately. So we can do that to get our first enemy type. If we drop down this texture base color, so the color information, and we're going to search for the green enemy. I think I'll make the enemies green. I have provided a lot of different texture colors here if you wanted to expand upon this and make your own enemy types. I think I've included yellow, purple, maybe orange or red. I'm going to go for the green base color texture, and we can see that will immediately change our information of the plain. Most of the normals and the OIME textures are probably about the same, but I did export them individually per texture when I was creating them. So we may as well go ahead and grab the normal. So we're going to search for green again, and we're going to find the plain green hero. But this time, we want the normal texture. And then finally, we're going to drop the OIME slot down, and we're going to search for green again, and we'll just replace this to be the hero green OI ME. Okay. So just to make sure that that lines up exactly the models I've made inside of blender in case anyone was curious, and I've textured them and baked all of this information in substance painter. So as I mentioned, you may see similar but slightly different workflows. It comes down to the artist and the person who's making them. The main thing to note there, though, is that we got that immediate feedback. As soon as we change something, there's no recompiling, we saw the new texture applied to the material, which is one of the key benefits to material instances. Definitely not exclusive to that benefit, and we should always try and use material instances where possible, where things are sharing similar properties, features and values that we can expose. So beyond textures, this would be a perfectly fine ending point if you're happy with this looking as it is. I just wanted to provide a little bit more information about material instances, what they do and how we can use them. So some of the other properties that I've exposed here are floating point values or colors that we can directly change and override the general color or tint being applied to the material or things like how specular the value is. So specularity is really easy to look at. If I tick on specularity here, this allows us to edit this. And at the moment, we're at 0.5, roughly average specularity. This is how much light is bounced back from a surface, making things look a little bit more plasticy or more like rubber the higher the specularity goes. Generally, we don't have anything that we interact with on a daily basis that has zero specularity. So starting somewhere around about 0.3 or 0.5 is somewhat realistic. If we turn this down to zero, we get that immediate feedback. I mean, I have a completely matted, completely non reflective surface. If we put this all the way up to one, then the light is bouncing back around this much, much more. So again, you can tweak features like this and try and get an idea of what you'd want yours to look like. You could also play around things like a color mask or a color tint. So if we take on the color mask here, essentially a percentage normalized 0-1. So zero being no masking and one being full masking. If we turn on the texture color, these two work together. So the color mask is masking the texture color override here. If I make this something really obvious, like a red color and then turn the mask off by making it 100% effective, so allowing 100% of the color to come through. So it changes to the value one. We now have a fully pink plane. So we can control this very easily. And the really nice thing about this is we get that immediate feedback and the immediate update when we're making these changes. If you wanted this to be a little bit more subtle we could change the color mask to something like 0.5, and we're only allowing half of that color to come through. Take it back down to 0.2, and we're getting just a very light blue tint on top of our green plane. Turn it all the way down to zero, and we're going to have no color coming through at all. So just another thing for you to play end with, along with the different textures that you can use, and then the emissive properties work the same way. Now, I didn't have anything on here to actually make glow, like headlamps or anything like that. So the emission was more of just a way to give you different properties to play with. If I turn on emissive strength, we need to give this a value more than zero. And then if we turn on the emissive color, at the moment, it's black. Now obviously, black is just zero, zero, zero in the RGB value. So if we multiply black by one or 100, we're still going to get black, which is nothing. So we need to give this a color if we change this to something like a yellow or an orange, and we maybe need to increase the strength a little bit more. And this is one of those things actually where as I was exploiting different textures, some of them, I realized wouldn't have any use for a texture packed emissive channel. So if you wanted to play around the emissive property, you can want to go into the OME texture here and we'll change this to the blue plane again. Plane here are blue. We're going to have to tone this down just a little bit. Maybe but there's something more like one or 0.5, so it's not completely overriding the color. And you can make the whole plane glow a little bit here. So, you do have control over the emissive property as well. As I've said, unfortunately, I didn't think of modeling in some headlights ahead of time, but I wanted to shove the emissive properties so you can make the whole plane glow and have fun with that. Might be useful if you wanted to animate it blowing up or something. You could have it flashing between its normal color and, like, a red before the explosion happens, an idea. I just to kind of help visualize what's happening here and how these textures, especially the OIME is working. I realize that could be quite confusing. I'm just going to turn this off, and I'll set these back because I don't want to override any of these. And I'm going to turn the texture back to green for the OIME as I know, that one's been baked specifically for this color property. I just wanted to very quickly show you through the textures and what's happening. So when I say these have been baked into different channels, if we go into the texture folder, and we'll find doesn't really matter which one to be honest. We can just find the plain blue hero and I'm going to open up the OIME channel. So normally, in most textures, colors and everything that we're seeing here are coming from the RGBA channels all being combined together at once. If you take some away, all you're going to end up with is kind of muddy weird colors like this where red and blue added together will obviously give us purple. Red and green will give us a kind of orange color and so on. So we wouldn't use it this way. With the ORME process, it's a little bit different. And this is because what I've done, in the red channel, this is our ambient occlusion. So anything which is white is essentially not blocked by any other elements of the model. So the light is impacting us fully, and then anything which is going towards gray or black has some kind of occlusion from another part of the model, kind of like the underweel bearing and things like that. And it will apply a little bit of fake shadowing there to emphasize the shadows. In the green channel, this was our roughness. So again, going from essentially a value of zero to one, different areas appear to be more rough like these areas here are providing some kind of rough detail, whereas these are going to be much more smooth. The blue was the metallic, so again, that was either metallic or not. So these would be the front of the propeller. There are some small metal parts on the wheels and other small features like that. And then the Alpha channel has been baked, and as you can see here, this is essentially the entire body of the plane has been baked into the emissive channel. So by taking this information out and multiplying this against our color properties and things like that in the material, we can manipulate specific elements of the model through our texture channels. So it's a clever way to do things. It saves on memory, and it gives us a little bit more flexibility within our materials. Just a couple of other things before wrapping on this topic. If you wanted to play around with more materials, just to show you a few different ways to create different material instances. I've shown you how to duplicate our original player material. Control in D will duplicate that. If you know that you just want to make something completely new, a brand new material to play around with those, we just go to material and we'll select this. If you already have a master material to work with and you wanted to make a fresh instance, you could right click on the master material here, navigate up to the top here and create a new material instance. It would give you something very similar to the player plane or whatever I had previously, and it would just have some default pre filled values, essentially the same result. So I'll get rid of this one. I won't need to keep that. Just wanted to show you the way that you could work with these. Saying that because it's probably one of the easier topics to get on board with, and we're not going to go much more in depth than we have here. I definitely recommend taking some time between topics to pause the videos and take a step back. Maybe create a few material instances with different colors. This would give you the option later to have a green enemy, a purple enemy, a red enemy, whatever fits your vision for the game. You could also try giving different chips, different secularity values, adding a little bit of a missive property to some of them, or giving them a color tint. So if you really wanted to start learning more about the engine and really understand what's happening, take this as a small piece of homework between this topic and the next and try to create a few material instances in between. But with that done, that is our material stuff kind of covered, and we're ready to go with an enemy material so we can start creating our new classes. Using material instances, we've been able to create that new variation without touching any coat, using the one parent material class I've provided with a whole range of potential for material instances to come from that. 11. 10 - EnemyMovement: Time to make some enemies. Fair warning, we'll be doing this the sub optimal way first. You might question some of the choices if you know unreal. Once again, this is completely intentional, and this time it's to allow us to refactor our work. Refactoring is part of any real development. It's taking the code base or project you already have, improving the code which is working that you want to keep and removing that which you don't need. I want to make sure I have the chance to run through this with you so that I can show you how to make the process a little bit more painless. But of course, first, we're going to need something to allow us to refactor. To do this, we're going to create our enemy pawn. So if we navigate to the content drawer inside of the blueprint and the core folder, we're going to right click here and go to the blueprint class option and create a new pawn class. We'll give this one the name of BP Underscore pawn Enemy. Enter to open this, and this is going to be almost identical to what we've already set up for our player. And that's essentially part of the initial problem here. We're going to be duplicating a bunch of work that we've already done. We'll be going through the better solution later, but for now, let's just build up to what we had before with our player. So in the components panel, we know we want to add a new static mesh. We're going to grab our static mesh and drop this onto the default scene root. With the static mesh selected, we're going to go to the right hand side in the Details panel, and we'll select to use the SM underscore plane hero. Of course, we don't want this to be the same color. And if you can't see your materials in the details panel at the moment, we simply need to click off of the static mesh, so find the other component. Select the static mesh again, and you can see we now have our material selected. Element zero is the body of the plane. This is the main color and the detail. Element one is a separate material slot created exclusively for the wooden propeller. This allows us to do things like vertex based animation through the shader to make the propeller look as though it's moving. A little bit complex for this project, but the features there if you wanted to extend it later. If we drop down element zero, we want to search for the enemy material. And of course, if you took those extra recommended steps between the topics, you may have a few different materials to choose from. We'll select this one, and you can see that my plane has turned green, so this should stand out against the default player plane model and material. Next, we're going to want to add some movement to our enemy. So if we go over to the event graph, we do the same thing as before, remove the Acta begin overlap. We won't be using that, and we can focus in on the event ti. Something which is always useful getting into the habits of is really planning ahead with the features that you're going to implement. We could do that, for example, by looking at the content examples project we're basing this off of and break down the core functionality and features the enemies will have. The first is that the movement isn't the same as the players. It doesn't have that fake momentum or kind of pseudophysics going on. It's more snappy, I just moves left, right, and down the screen. So we have diagonal movement. This allows us to plan ahead a little bit because we can also see things when the enemy hits the wall, it changes direction. So we know that we need to do some sideways movement and forward movement, and when they hit walls, we need to do something to account for a directional switch. Structuring our projects this way allows us to somewhat logically break down a larger problem into sub problems which are going to be easier to tackle, and we can hit those one step at a time. So to get started, the first thing we're going to want to do is actually add the actor offset, the same kind of positional offset we had before. So from the eventicEecution pin here, we're going to search for set actor location. So again, very similar to what we've done in the player. We'll also want to use the same approach to find out where the actor is at the moment and then add an offset to this. So we'll use the get actor location, and we'll do a very similar things before. We'll pull from here and we'll search for add or just press the plus button. We'll figure out the calculation first before plugging anything in, and then once we have this finished and ready to go, we can hook that up and test to make sure we're starting on the right foot. We already know that we don't want magic numbers, so we may as well create a couple of variables that I can already see that we're going to need. The first one will be a floating point value for movement speed. Just a quick tip here. If you create a new variable over here, by default, this will be a boolean. We can keep this one in fact. The way that we're driving movement will be checking whether we're moving left or right. So whilst we have this? Let's go ahead and call this one B move left. So this was just to say that whenever you create a new variable, you will be given a boolean. A shortcut that I like to take, knowing that I need a floating point value for the movement speed, similar to what we've used before. We can pull from this green pin here. The green indicates that the Delta seconds is a floating value. Drop this into the graph and then click Promote to variable. We'll give this one the name of movement speed. And that's just a quick way to get a variable of the type that you're looking for without needing to use the dropdown and finding it in the graph. You can do it either way, but it's just nice if we needed a vector, for example, we can find a random vector, drop this into the graph, promote the variable, and then give it a name like so. The only thing is we need to do some cleanup, so we'll delete the nodes. We won't need those just yet. But I just wanted to introduce different ways you can go about working with the Unreal engine graph editor. The vector was just an example, so select this and delete that variable as well. And we're ready to carry on. When it comes to the movement, we've already covered the frame rate independence and why we need it, so we can build that in from the start. If we right click and search for Get Wild Delta seconds we can use this in our calculations coming up. For the movement, we're going to want to take our movement speed. And if we hit Compile and just make sure that this has a variable, we'll set this to something like 250. That would be perfectly fine for testing. And like I've mentioned, we're going to need two separate calculations. We're going to want one for sideways movement and one for the forward movement down the screen. For the forward movement, we can take our movement speed. We can multiply this by a value to invert that, so we'll take this as negative one because down the screen, as I've mentioned, is a negative value, and if you wanted to move up the screen, that would be a positive value. So the forward movement for the enemy is actually relatively simple. And, of course, we want to multiply this result by Delta seconds to keep things framerate independent. So that would essentially be our forward movement ready to go and test. For the sideways movement, we're going to want to do something with our Boolean value. So I'm going to grab the movement speed and duplicate this. We're going to want to do something very similar here where we're going to take a multiplication node, but we're obviously not going to want to multiply either only one or negative one. We need to turn this into some kind of conditional operation. The way we're going to use the boolean is a true or false stick. We would say we're either moving left or we're not moving left, so we're moving right. Now, if you tried this, nr will convert a Boolean to a floating point multiplication. Unfortunately, booleians only read as zero or one, zero when it's false, one when it's true. So obviously, that would mean this won't work for our current use case, because if we're multiplying by zero rather than moving left, that would just take the movement speed, multiply that by zero, which would cancel out any movement whatsoever. What we could do though, if we press Alt and left click here, we can use something called a select node. So we're going to pull from here, we'll search for Select. And we want this option at the bottom. This gives us some options. If we're working with things like integers as the wildcard here, you can see we add any number of integer. Whatever the integer that you place in here, so any whole number, if you place in a whole number of one, for example, we could give this a negative value here. What we want to do instead is we're going to take our boolean. We're going to plug this into the index here, the wildcard, and Unreal will switch that to be a boolean for us. And we can see that this is now set to either be true or false. So the way that we can read this to make this more kind of intuitive and easy to understand, is we can say that if we want to move left, are we moving left? If that's true, then the value needs to be negative one because we want to move in a negative value. If moving left is equal to false, so we're not moving left, then we want the value to be one. So essentially, this is moving right. This is moving left, and this is going to be positive and this will be negative. So a nice simple way that we can use a Boolean value to essentially toggle on move left or not. And then when you do the same thing again, we'll take our Delta seconds. We'll take our calculation here, multiply that against Delta seconds to make sure that both directions are frame rate independent, and we could essentially begin plugging this in. So just to keep things tidy, I'm going to pull from the vector pin here and search for make vector. So we've seen these before. This will just give us the three floating point values exposed. Forward in unreil is on the X axis. Sideways is on the y axis, and that's pretty much it. We can hook this in. We could test this, and we would have a moving enemy. So if we hit compile, save, we'll go back in into the main level, we can just drop our enemy. Doesn't really matter where this is going to be as long as it's just slightly up the screen from the player. I'm going to give this a 180 degree rotation on the z axis, so it's facing towards the player. And then we can hit play, and we should see this moving down the screen. We have this moving in a diagonal direction, which is the initial part of the problem. So that's one of those steps solved. I think I noticed one small issue here. It looks as though we're going to be going through the invisible wall. So yes, we've definitely had the enemy pass the invisible wall. I think it's just within the bounds. As I mentioned, something which is really easy to forget, and I've just done it again is we want to ensure that we hit the sweep toggle, so make sure that we're sweeping, and we shouldn't have the same functionality as the player, but we'll just hit the invisible wall and stop. Perfect. So that's the next part of the problem. That's the next step that we need to solve. When the enemy hits the wall, we want to take that information and toggle the movement direction. And we already kind of have that set up. So we have our move left. We'll change that to true and have them moving left relative to the enemy's direction. Right before we go through that, I wanted to go through a little bit of information about general kind of code tidiness, things that we could improve here, even before doing a big refactor. I've purposely laid this out to make it more readable and understandable. But this is actually some pretty bad code already before we even get into what we need to refactor later. Now, the first thing is I just want to introduce people to commenting, which is something I haven't mentioned yet. If we grab a node, for example, and press C, we can make a comment. Kind of a self documentation thing intended to remind us what something was, why calculation is being done, how it was. And it also makes things kind of more readable at a distance. You don't need to comment everything. You may have seen certain developers share some of their code base, and there may have just been comments littered absolutely everywhere. You may have seen things like this where they have a variable or something really simple like this called movement speed, and they've given it a comment just saying movement speed. This is completely arbitrary. It doesn't do anything. It's not useful. And as long as you're naming your variables correctly or with some good intent, we already know that this is movement speed because that's the name of the variable. So we don't need to comment like this. However, if we grab this and expand it to cover all of these nodes, what we could do is we could change this to say something like move direction, move forward. And we could even make this more useful because if we press F two to go back into renaming this, we could also explain why we're using negative one as a multiply because this doesn't have a value. It doesn't have a real use of having a variable. So it is kind of a valid point to have a magic number here, but at a glance, we might forget why negative one is being applied, so we could leave a comment just describing what's happening. So we can say here, we're multiplying this by negative one to move enemies specifically down the screen. Then do a very similar thing. We could grab these nodes, press C, and we'll give this one a similar comment. This one doesn't really require any explanation. I think this is quite clear. If we're moving left, then take the value related to which direction we want to move. The main thing we get from doing this is that if we start zooming out of the graph, we can now see that this section without actually looking at the code. This section is for forward movement. This is for sideways movement. If you ever needed to quickly hone in and find a specific chunk of code to fix something or double check it, we know exactly where these this is just the use of comments. These can be useful. You don't have to use them everywhere, but definitely places where you think you might forget what you're doing if you come back in a month or two's time. Now, the main issue that we want to fix now though, I'm going to delete these is that we're currently doing a bunch of duplication. We have G World Delta seconds multiplied twice here individually. So one thing that we could do, and this may not have been completely obvious to begin with, but rather than multiplying the individual float point values here, we can multiply against the result. So if we take the results of this make vector that we've created, pull from here and search for multiply. We can grab one of our Get world to Delta seconds, press Control X to cut this and control B to paste and then plug this in here. And RL converts that to a vector multiplied by a float. We can plug that calculation in here. And then we can get rid of this node here, so we don't need this multiple anymore and plug that in. And we can get rid of these two nodes. We don't need these at all, and then plug this back into the Y. So we now have something a little bit tidier. We don't have quite so many multiplications going on. We're taking the entire vector now, which will take the multiplication for the X and the Y at the same time against our Delta seconds, ensuring that everything is framewright independent for the entire calculation. So that was just a really quick code tidy there. So if we hit compile and save, we should see we get exactly the same response so nothing's changed. It looks and moves the same as it did before. Final thing before leaving this topic for night before doing the Ru factor is that every enemy is going to be starting moving in the same direction and the same speed, which will be a little bit boring. If we add it in a couple of enemies, so we can just hold Alt and drag on a widget here to duplicate immediately. So I'm just holding the lt key, grabbing the widget direction and dragging in a direction which will duplicate what we have. If we press play, it's going to look like some kind of synchronized swimming. They're all going at exactly the same direction, the same speed. It's going to be a little bit dull. So if we back up to the begin play, I'm going to show you how to randomize a boolean first to at least change the direction that the individual enemies might be moving. This is actually really simple. We're going to hold Alt and drag the boom move left next to the event begin play. Or you could just put it on the execution pin just here. And then from the variable, we want to drag from the pin here. I'm going to search for something called random ball. So we're going to get a random boolean and we'll set this on the bignPlay. So super simple. We have a 50 50 chance of moving left or right. So if we press play, they all chose to go the same direction and they've done it again. We can see there's some randomization, so that's good. They're not always going to go exactly the same direction. So a small learning exercise for you. The approach to doing a random float is slightly different, but take a few minutes, pause the video and see if you can implement something similar with the movement speed. So bring the movement speed in and see if you can find some built in functionality that UNR might have to allow us to pull a random value into the floating point. You could randomize this between something like 250, which is currently our default and maybe 500, so that each enemy will move at a slightly different speed. Pause the video, give that go and begin testing how comfortable you're getting with the blueprint system. Okay, so I'll run through the way that I would have done this. If we plug this in and hopefully you have tried that out because again, these will be really valuable to actually learning how to use blueprints rather than just following my steps. But so that we have the same code, at the very least or something to go from. What I would do is I'd grab my flight variable here. We're going to get a random flat. This is the only thing which may have thrown you off, and you may have wanted to come through and try a few different options. But rather than just using random float, which would just give you any number, this could be really fast or really, really slow. So not too usable. You could have also tried things like random float from stream. But the one that I would use would be this random float in range. So if we select this one, it just gives us two points to pick from our minimum, which I would say would be the 250, and we'll double that to 500 as the maximum. So some planes might get that minimum 250, some might get 500 or a value in between. And there we go. We can see they all start at completely different speeds, which again, will just make things a little bit more interesting when we get to actually adding these and they're working properly in the level, bouncing off the walls and things like that. With that, though, the enemies are moving and they're colliding with the walls. They don't bounce yet, and they also wouldn't interact with the player. So before the big Ruf factor, we're going to next look at collision detection, making things actually happen when objects touch each other. 12. 11 - EnemyCollision: Have enemies that move, but they don't react to anything. Ideally, when an enemy hits something, we need to check what it is. Is it a wall, and if so, then toggle direction. If it's the player, we can actually see from the example project again, it applied damage to itself and gets destroyed. So what we'll do is we'll automatically destroy the enemy if they touch the player. Projectiles are going to handle themselves, so we'll think about that a little bit later. This isn't going to be a problem to consider here. One thing to mention is that the unreal collision system is flexible and powerful, which unfortunately means it's somewhat complex. At least when you start working with it after trying a few different iterations of different types of things you want to do with it, I think it clicks quite quickly, and it's quite a comfortable system to work with. So let's navigate back into the pan enemy. We can use this for testing. If we grab the static mesh and open the collision section on the right hand side, just scroll down here and find our collision category. If we drop this down, we can see the collision preset here. We'll drop this one down. We can see it set to block or dynamic. So this is our current collision setup. The object type is also set to world dynamic here, so that's the collision channel that this object identifies as. That's why when we press play, the enemy stops at the walls. This is a physics or physical collision where they're both blocking each other and trying not to inhabit the same physical space. Now, if we drop this one down and change this to overlap or dynamic, so the equivalent, but for overlaps, we'll go and hit compile and quickly play. What we'll see is that they now pass back through the walls. Now, under the hood, something is being fired off. There's a message being sent saying that these three enemies have just overlapped the wall. But because it's not a physical interaction, it's just a notification and essentially an observation that they were passing through the same space. You can kind of think of blocking versus overlap like a racing game. We want the walls and surroundings to block the vehicles. You crash into them, you get stopped. We want the checkpoints to overlap. We obviously want to know if a vehicle has passed through a checkpoint and what time that happened, but we wouldn't want those checkpoints to start physically blocking the cars from proceeding. That would be just a little bit annoying. So if we go back into the pawn enemy, that was just to show how they work different. We have the third option as well. We have the ignore option. Now, that one's probably quite obvious. That will set this to ignore all types of collision. Could do that, for example, by going to the no collision option, and even though it looks as though this is saying it's set to block, this is actually in the code, reading the Ignore option. Now, there won't be any messages fired. Nothing really knows that the plane is going through it. And this is good for objects that don't need to be added to the call stack and the collision checking stack. So good for performance. But generally, we're going to be working with overlap or block depending on what we need them to do. In this case, we can set this back to its default, which is block dynamic. I've done that by just pressing here. You can see the small arrow. If you ever wanted to reset properties to their default, we can just press this, and this goes back to what it was before we were tweaking things. With the static mesh still selected, we want to scroll down even further in the details panel right to the bottom. And we have these options here, the events. Now, we're not going to go through all of them. The things that we're again, most often going to be interested in will be the on component hit. This happens when two things physically block each other and on component begin overlap, and sometimes on component end overlap. These are called when two overlapping objects, either enter or leave each. So we can get the end overlap because as we've seen, overlapping collisions allow the objects to inhabit the same physical space. We want to bind our wall check to the on component begin hit function here. This creates a new customer event for us, so function, which called every single time a collision is detected on our static mesh. So if you remember, every time we've created our class and we had that default function that we really didn't need the actor hit function check that we've always deleted. That was essentially a similar thing, but like I've mentioned, for the entire class. Whereas now we can hone in and specifically check I just the static mesh has been hit, then we can do something afterwards. These nodes actually provide quite a lot of information. For example, if we right click on the hit structure pin here and select the option to split the structure we can see all of the information we can get back every time something hits us or we hit something. We can find out the time. We can find out if the blocking was successful. We can see the actual hit location and the impact point. We can get things like the rotation from this, the type of physical material, and so on. We don't need all of this right now, but it's useful, again, as you start getting more advanced and comfortable with unreal, if you want to do fancy things with things like bullet penetration or the type of results that happen when two things collide, you can definitely dive in here and see what's available. Just right, click on any of these pins, though, recombine the structure pin just to tidy this node back. The main things that we tend to work with when working with collisions, we're going to want to know a little bit of information about the other actor, because at the moment, we don't know what's hit us. We just know that we have been hit when this gets called. It could be the wall, it could be a player, it could be another enemy. So this is going to become useful to read later, so we may as well promote this to a variable now, and we'll call this one hit actor. And this is why I just wanted to take a quick moment to go through different ways that you could even just consider approaching solving this problem, finding out what hit us. In a lot of student projects that I review and work with, I see a lot of casting because a lot of very basic tutorials that you may find online tend to just fall back to showing how to cast to other objects because it's a quick and simple way to get information. In our project, this is relatively small and simple what we're doing, so it wouldn't actually look too bad. But if you were to go that route, you might have something that ends up looking like this. I'm just going to do a quick cutaway. Quick cutaway. As I said, this is essentially what you'd end up with. The way that a cast works, and I want to introduce you to casting anyway, the way that casting works is that when we're working with our other actor, if we highlight over the pin here, we can actually see what's being returned. So I'll hit compile. And I hover over the other actor information. This is showing us that this is providing an actor reference. The reason being, if we remember back to UnrelEngins hierarchy of inheritance, everything that we can interact with in the world that has a physical presence, that transform needs to be of actor or higher. So by providing a pin here, which is the most basic object that we can find in the world, an actor, we can ensure that we can check against everything and then hone in specifically on what type of actor, what extra functionality and features that holds. Now, the way that you might do this, or I've seen this a lot of time in many tutorials is that they'll show you to take something like the actor that you've hit and we cast against that. And what this is this is essentially a question. It's saying this generic actor, I can see the generic actor exists. Is it specifically the type of pawn base, which for us is the player. If it is, so this is the true, the yes, it is a pan base, then do this thing here. So we'd call the function specific to the player class, which as I've mentioned, is applying damage to the player and then self distracting. If it's not, so if we've done that check, and it comes back as not being the player that we've hit, then we've hit something else. So it would go to cast f. And then you'll see people do a second cast. So we get like a cascading waterfall of casting, which is messy wildy. And if we get into much larger projects, you can see we've only got three things to consider. But if we've got different things like pickups and obstacles and many other things that we might need to consider, we're going to get a much larger cascading waterfall. But the next thing you might do again is we're going to come back in here and say that generic object, the generic actor, is it specifically an enemy? If it is, then we'll do the enemy stuff, which at the moment, I haven't decided how we'll handle enemies. I think they'll just phase through each other, but we need to do that function. If not, then we'll check again. Then we might cast to something like the blocking volume if the things are blocking volume, if that's true, then yeah, we've hit a wall, and we'll change direction. So this is one way that you may see it done. It may have been how you consider doing it if you're aware of what casting does. And I just wanted to run through a general kind of rundown of why we won't be doing this and why we should try to avoid this where possible. It's another thing which gets repeated very easily like the general blanket statement of never use tick. You'll hear people say never use casting. Again, that's actually really simplified. There's a bunch of reasons that we would want to use casting where it can be completely safe and very cheap to do. It's a little bit outside of the context for this topic. So at the moment, we're just going to say, I'm going to delete these. We won't be doing this purely because we don't want this cascading waterfall effect. Where we're checking per object. So to keep things simplified, I'm going to break this into two different things for this project that we pretty much want to check for. We want to check for the wall, and we want to check for the human player. I think, just for game design and to make it feel better, I think I'm going to let the enemies phase through each other so that the player only has to consider which way they're going between the impact with the walls. So this actually becomes them really, really simple. We can do a really simple check to begin with. We can right click in the event graph, and we can search for Get player pawn. So this is a built in function. The Unreal engine is always tracking what's classed as the player pawn, which is the thing that the human player is controlling. So when we press play, that would be the blue ship. So what we can do here? This avoids casting, which is one useful thing. We don't need to get all of the information out of that other class. This also means that if we had more features like letting the player pick between five different blueprint classes with completely unique ships, again, we wouldn't need to check against each unique ship. We just need to know that the thing that we've hit is a player ship, and we would know that from this check here. So we can pull from this bin, and we can search for equal two, so just two equal signs. And I'm just going to unhook this to make it a little bit more readable. Hook this back up. We can just say, Is the hit actor? So the thing that we've just collided with, is that also specifically the thing the player is controlling? We can then pull from our boolean here. We can search for a branch. We can plug this into our execution pin and a nice simplifying the code that we need to check against. So if the thing that we've hit is the player, if that's true, then we're going to do our damage thing. So we may just want a print string here to remind us to come back. So I'll throw on a print string because we don't have damage functions and stuff like that just yet, but I'll make this big and bold so that every time this happens, this will remind us to come back and change the code. So this would be where we apply damage to the player. And then the destruction of ourself is actually really simple. We just need to call the function destroy actor. We can throw that in here. And that's actually part of the feature set, already done. So now if I did let the enemy planes fly down, they would be ready to apply damage, although we don't have health and everything yet anyway, and then they'll destroy themselves. Now, off of the false branch, again, actually really quite simple, that now tells us that if we're going to allow the enemies to phase through each other, then we're not worried about that, which means if we hit something else, that's going to have to be a wall. That's the only other thing that we can collide with in this project. So we can do that from the false branch here. And what we want to do is we're going to set the move let. So I'm going to plug this into the execution pin. Move this down here. And again, just to consider the way that you might go about this. When you're getting used to the flow of code, you might be tempted to throw a Banchon so I'm just grabbing from the execution pin. This will connect these up automatically, and you might do something like this. So check the value of move left. Are we currently moving left? If yes. So if left is true, then we want this to be false, and if left is not true, then we want this to be flipped and turned to true. So that's definitely one way you could do it. But again, I just want to how a nice kind of clean way that we can do this. So if we hook this back up here, we can move these out the way. I'm going to delete these, in fact, from our boolean here, I'm going to pull, and I'm going to search for something called a knot ball or a not boolean. The way that this works is basically, if this is true, this returns false. If this is false, this returns true. So we can take this value, and it's essentially always going to flip whatever the current boolean is on. So if we're moving left, it will make us move right, and if we're moving right, it will make us move left. So a nice, simple, one liner version of what could have been an extra branch structure that we didn't need. And that's pretty much it. That is our health being accounted for when we get to that topic a little bit later, the destruction on contact with the player, and then flipping the direction when we hit a wall. So if we go back in, press play, we can see they're currently bouncing off each other, which I guess would be a perfectly fine implementation, completely up to you. And then if they hit the player, we can see on the top left hand side, the print string was being called, and they're being removed. So it's going to come down to again, design. If you wanted them to bounce off each other, we can leave that functionality in. If you want them to phase through each other, I'll show that when we get more into the collision topics a little bit later. But that's it. We now have a working implementation. And as I mentioned, the reason that this is working is every single time a collision is detected in this class now, this will automatically be fired off. It's tracking all of that information about what's been hit and what's happened, and then we're extrapolating from that information what's relevant just to our project. We're going to do something quite clever with the projectiles, so we won't need to account for projectiles when they hit the enemy either. That's going to be handled in the projectile code. So all the enemy needs to know about is the player and the walls, which is actually another programming principle, which is really important, especially in languages like C plus plus, and blueprint because it's essentially still based on that workflow in the real engem. The general concept is that we try to make a class as specific and small as possible. So it's meant to do one thing and one thing well. An example of bad code in an enemy class would be tracking for what pickups the player may have held, checking for things like what projectile has just hit it, how much damage that projectile should apply, and things like that, because that is the logic in reverse. All of the stuff to do with damage from a projectile should be stored in the projectile and that information should be passed along. The enemy should only be considering and responsible for enemy related stuff. And in this case, that is moving and hitting walls, very simple entity. So just another quick look at what would be classed as clean coding is that encapsulation of making sure that your class, in general, the rule is, as I've said, to do one thing and do one thing well. With that set up and ready, though, we now have a full class ready to restructure. So next, we're going to look at the topic of inheritance. This will be a better way to handle a lot of shared code and features that we're currently seeing in the enemy and the player. We've been duplicating a lot of work, so it's going to be time to fix that. 13. 12 - Inheritance BaseClass: Time to fix the mess that we've made. We've duplicated the mesh setup. We're handling movement differently, but following the same structural logic twice. This seems small at the moment. But when we get to adding health, destruction, effects, this will all begin to compound. Every shared feature means twice the work for us, but also potentially means twice the bugs. Inside of the pawn class as well, we're currently using the entire mesh as a collider, which really isn't ideal for this type of game. Most kind of ICD action games like this actually use a trick to make the player feel a little bit smarter than they may actually be. When you think that you've dodged something skillfully, but you actually just scraped by that feels good, but that's because your collider is probably a little bit smaller than the enemy's collider. We're going to want to implement something like that so that our game starts to actually feel interesting to interact with. Just a minor form of psychological manipulation, but it makes the game feel fair, even when the math would say otherwise. The way that we'll solve this is by using a sphere collider as the new route instead of the mesh. The mesh will just act as completely collision free decoration. Both classes will need this, so we may as well build this just once and share components and features like that across all of the classes. The way that we do this is with something called inheritance. I've mentioned before, the UnrelEngine is a very heavily inheritance based piece of software, so we'll be following the same kind of conventions. It basically means that we can code something once in what's called a parent or a base class. We can then make child classes of that, which will inherit all of the components, the features, the systems that we input. For example, we've seen the actors provide a transform, a location rotation scale in the world, and then pawns take that information and build upon it with being given the mechanics to be possessed and many other things. That's enough talk. Let's see this in action. So let's get to creating our new base class that will help us see the system in practice. If we go back into the blueprints and the core folder, we're going to create something completely fresh. A right click in the window, we'll go to Blueprint class again and create a brand new pawn. I'm going to switch this from being called Pawn underscore something to BP underscore plane base instead of pawn. They're all going to be planes, so we may as well be clear with our naming convention here. Inside of our new class, we'll go up to the components, and we're going to add that sphere component I mentioned. We're not looking for this one here. This is a solid three D visual sphere. We just want a colliding bond. If we search for the word collision, we can see here we have a few different options. We want our sphere collider, which will apply this just here, and we can do the same thing we've done before. We can drag this onto the default scene root to make this our new root component. We can then go back up to the Add button. We still want our static mesh. This just won't be handling collision anymore. We'll do the same thing again. We're going to go to our static mesh over here and we'll select our SM underscore plane hero. With the static mesh still selected, we can go down to the collision section over here, and we can actually just use this drop down and tell it to do no collision detection whatsoever. We can also turn off the overlap generate events. This just make sure that nothing is being checked against the static mesh at any point. Perfect because we want this to be visual. It means it doesn't need to get added to the collision stack. It's just kind of sat there being rendered. If we select our sphere collider, as well, we can make a couple of changes here. If we drop this down in the right hand side in the collision section again, we want to change this from overlap or dynamic, and we can actually make this more distinct than that, and we'll tell this to be classed as a pawn collision. So we'll see that this changes the collision preset to pawn. The object type is now tracked as a pawn, and it's done a few things over here. Not going to leave it at this if we wanted full flexibility. We'll take this as the default preset, but we can customize this by dropping this down again and selecting custom. So we still have this set as a pawn. We still have the collision enabled settings as we want, and we have all of this set by default. We may find that we want to come back in though and make this not overlap other pawns or block other pawns, do something different with world static, and we now have the option to change these if we wanted. We won't do that right now. We'll do that based on testing and seeing how the project unfolds. With that done, this is putting us in a really good position, though. Every child class will now inherit this set. So this means we've configured this once, and we never really need to think about it again. So now we can start looking at our shared functionality as well in the event grab, the kind of things that we want to share between the two classes. As always, we're going to grab the event to begin overlap and delete this one. And for the movement, there are a few things to consider. Once again, purely just based on the recreation of the example project we're trying to hit some level of parity there's a small feature if you really look at what's happening where the planes and the player don't move or align movement at the start of them spawning him. They play a small animation. They animate up the screen for maybe a second or two, and then they allow movement to go sideways. So we're going to do something very similar. We're going to freeze or pause any movement input until that animation is rolled out. We'll get to the animations in the polish section, but we just need to again, think ahead of what we're going to need to account for and we can program that in now. So let's start with that. We're going to want to do some very simple, logical if and check. So if we pull from our execution pin, we're going to search for a branch. And from the branch, we can take the condition here. We'll promote this to a variable straightaway. Call this one B, movement enabled. We'll hit Compile and make sure that this is defaulting to true. So if movement is enabled, which we're going to want to do whilst we're just debugging before we have that animation set up, I movement is enabled, then we'll allow the enemy to fly side to side and move down the screen, and we'll allow the player to fly side to side based on the input. If not, then that means we're probably in the animation, and we'll call the false function here and stop any movement from happening. So this just sets us up. Later on, when we get to those features, by thinking ahead, we don't need to completely refactor the code again. Next thing is both classes will need some type of movement. But like I mentioned, we are programming the movement slightly differently, so we can't share the actual movement code because there's not the same type of movement being used. But what we can do is share logic flow and the concepts of the functionality that we will need. We can do this by having a look at functions now. So on the left hand side, we have this function category just here. Kind of like events, we can create our own custom functions, and then we'll just have logic called when that function is called the same way that we've been doing with events in the past. So if we press plus here, and I'm going to call this new function we've made handle movement. And you can see one of the differences here and actually one of the core benefits of using functions over events is that they do very much the same thing. They respond to being called, but here we get our own function graph. So it's kind of tucked away, and again, keeping things nice and tidy. Now, we're not going to actually handle any of the movement function in here. The way that this will be used is we're going to override this function in our child classes later, but we need this to exist to have something to call. What we do want to do is we're going to go back into the event graph. We want to take our handle movement function. And we're just going to drop that on the true pin. So if a movement is enabled, then we're going to call the handle movement function. So again, it becomes nice and readable. And when we do get to actually implementing this, we can get an idea of exactly what's going to be happening. So this is kind of simple for now. You'll see this unfold and make a lot more sense when we actually begin implementing things. This is one of those things where I could try and spend five to 10 minutes describing what we'll do later, but I think it'll just make much more sense simply implementing it as we go. Another thing that both plain classes are going to need the enemy and the player and bosses or whatever you want to start inheriting from this class, they're all going to need to be destroyed or killed off at some point. So whilst we're here, we may as well go and add another function, and I'll call this one handle death. Again, we're not going to put anything in here just now, but one thing that we can do with this function, the generic things like everything is going to need to play a sound effect or a particle effect when they die. We can do that in this class here because that is a universal function. That's something that regardless of the type of plane, they all need to show some kind of feedback when that happens. So we can do the universal functionality here. And then if there's something unique to the individual plane, then we can override again the functionality that's happening here and then do something on top of that. So just a kind of rough idea of how inheritance works and how we can make use of that reusable code, saving us time and effort as we go. Now, the final thing that I can imagine that both of the plane classes will need is the movement speed. And we kind of already know that. If we look at our own code, one of the few things that both of them have is if we open both their event graphs, pan base, which is the player has a lot more functionality. There's a lot more variables here, but it does have movement speed. And then the pan is slightly different, simplified. Whereas we're not doing the move left check in the player version, we are still using movement speed. So things like this are what we're looking for in our base classes. Things that we've had to implement more than once is something that we could just dump straight in to the base class. So in here, what I'll do is I'll create a new variable called this one movement speed. And we changed this one to a float, it and pile, save. And any time that we might need to use those between the classes, that's available and ready to go. Again, we don't actually need to make use of it yet, but this is just setting the groundwork for us to have a more smooth and pleasant transition. And really, that is our base class fleshed out as much as we need right now. We can come back and add things later, but we have the shared components that we need the unified collision, the movement ready to implement in the individual child classes. Same for the death and then the speed variable ready to use as well. So just to have a very quick look at how we can make use of this base class, we'll add the full functionality a little bit later, but a very quick demonstration. If we go back into our content draw, we can right click on the plain Base. And up here, we have the option to create child Blueprint class. If we click this one, this will create a new blueprint for us. This will inherit everything from plain Base. I'll call this one BP underscore plane player. And then if we double click to open this one, we can see that this now comes pre built with our sphere and our static mesh. We also have a few other things here as well. If we click the override option, we can see actually the functions that we've created manually, handle death and handle movement, specifically from our base class. If we click these, this will give us a function that we can then start using the parents version of the function, plus, as I've mentioned, doing our own thing on top of that. Our press controls to get rid of that one for nine. We don't want to do any overriding just yet, and we don't need to change anything just yet, but I wanted to show you another way as well that we can go about adding classes based on those that we've. If you go back into the content draw one more time, right click here, and this time, we're going to go to a blueprint class, something that may not be super obvious. But a lot of people think that this is restricted to unreal engine classes and those created by the unrelengin developers. But in fact, as soon as we've created a class, we can actually find those here. So if I look for plain base, we can see here plain Base is a child of a pawn and a pawn as I've said, is a child of an actor. And our plain player is on a child of plain base. So we can actually now select our own classes from our class list here in the editor. So we'll select plain base again. Select this one. And, of course, we're going to call this one BP Underscore plane enemy. So both of these are now ready to go as we start fleshing things out. Just wanted to mention, as well, that there are multiple ways that you can make use of your base classes when you have them ready and set up. With that done, the base class is ready, though. As I've said, we have the shared components, the shared functionality, and the unified collision setup. Next, we're going to refactor the player first. We'll take the big one, go through the chunky code that we're going to move across, and we'll focus on the enemy afterwards. 14. 13 - RefactoringPlayer: Class is now ready to go. So now we're going to need to convert our player to make use of this. This is what's referred to as refactoring, essentially restructuring existing code without changing what it does. We're aiming for the same behavior but better organization and reducing overhead or reused variables components and things like that where possible. Realized in the previous topic we've just gone through, I gave you instructions to create two new plane classes. There's one other thing which I realized in between that could be useful to be aware of and actually would save us a little bit more time. So what I'm going to recommend is we actually delete plain enemy and plain player. So we can grab these Shift Select and press Delete. We haven't actually done anything in those anyway, so we're not losing anything, and at least you're aware that those options are available. What I would suggest instead is if we go to our plane base, and I'll just close these to not confuse things, we want to double click and open the plane base class that we have been working on to make sure that you haven't deleted the plane base class. So with plane Base still here, we didn't want to get rid of that one. What we want to do is we'll actually make use of the existing classes and we'll just move things around where we need. So the first thing is to focus on our naming. If we focus on the player first of all, I'm going to rename this one to BP, underscore plain player. And that's why we needed to delete those other classes so we don't have naming conflicts. The class name was fine, but we can just do something quite smart here to make use of what we already have in this class. And we'll do the same for our enemy. So we'll grab this one and call this one BP underscore plain enemy. And then if we double click to open the BP underscore plane player, we can start making our changes in here. So the thing that I thought would be useful to know about is there's actually a really cool way that we can change what this class is classing as its parent. So at the moment, we can see the parent class as pawn. If we come over here to the class settings and then back in the details panel, we can see we have the parent class here. The dropdown is set to pawn. If we search for plane, we can change that to now be inheriting from BP underscore plane base. And this is actually quite a nice way to refactor things. One reason is that it will give us errors and warnings about code that may be duplicated. And we can kind of already see that. So if we look to the left hand side, we can see we have two static messhes. Anything here which says Edit in Blueprint or Edit in C plus plus, if you're working in a C plus plus project is an indication that this is inherited from a base class. So when you see this, we cannot come in. We can't grab the inherited ones and delete those, so they're stuck here. So what we want to do is we want to grab our static mesh underscore zero. We don't need two different meshes for our plane, and we can delete this one, so we'll just delete that here. The other thing that we can see here is we have something called movement speed underscore zero in the variables. If we write, click on this, we can go to the find references, and we can find all of the current class members of this. If we double click this one, that will take us directly to where this is. So as I've mentioned, it's a nice quick way to be able to tidy things up because we can just use these shortcuts to jump around and find the remaining things that we might need to get rid of. And again, the reason this has been given an underscore zero is because we're aware that we have something called movement speed in the base class and we can't have the same variable, even if it's in a child version of that class. So we'll do the same thing again. We'll delete this one. We'll just delete movement speed. This is just telling us that we're currently using this, but that's perfectly fine because we know where that is. And then what we want to do here is we're going to pull from here and we'll search for movement speed. And we can see here we want to get the movement speed variable, and this is the one in our base class, the pairing class. And there we go. So that is pretty much it. That's the class kind of fixed up and ready to go. So we could definitely leave it here, but there's a few more things that we can make use of from the parent class. So the first thing we always want to account for is on the begin play, and we need to do on all of our functions essentially. But to begin on our begin play, we're going to right click on the node, and we want to find this option here to add call to parent function. And we want to hook this up in between our current execution and the functionality we're already calling. This just ensures that the parents begin play logic is also called when we start playing, meaning that if we had any universal logic again that we want to do on the parent class, that will also get called. Otherwise, this will only be called by itself, and we'll ignore the parents version. We want to do the same thing for the eventic. We're going to write click on this, add call to parent function, and then just hook everything up. Including the Delta seconds, we might need to move some things around here. And then we can just plug this in over here. And again, tidy things up if you wanted. Try to help visualize this for you, the parent functionality will be called first. So, for example, when our player first gets created, it will automatically have its event begin play get called once, and then we're going to immediately send a message to actually look into the parent version and see if anything's happening on that begin play. Navigate over here. We can open the parent blueprint with this shortcut here, and we can go to the begin play over here, and we can see there's no actual code at the moment. If we were to do something like pull a print string off of here, though, we would just say parent begin and keep that on screen for a moment. What would happen now is when we press Play. We're going to see that parent begin is printed at the top. So we can confirm that our parent logic, the universal logic is, in fact, being called. Now the problem, of course, is if we added this to the enemies, so if we go into the plain enemy class, we come into the begin play. We're going to want to do the same thing. We're going to right click, add a parent call, and we'll plug this in here. Now, because we've got three enemies and one player, we're going to see that message four times because they're all calling that same function. Of course, we need to make the same change in the enemy class class settings. Change this from pawn to plain base. Okay. So we're doing the same thing. We're just reparenting what this class is inheriting. Hit compile. So all four of them are taking that same process. Now, this is fine, in this case, but this is where you want to be careful and start making sure that you're considering what you're implementing. So this and wouldn't make sense if this message was saying something like, if this was specific to what the player should be doing, like shouting, I'm the player. If we hit compile, that means the plane player, we'll call that, which is perfectly fine. But so all of the enemies. So all three enemies are also saying, I'm the player, which is a little bit confusing. So this is where you need to be careful with inheritance. And this is why I keep saying that we want our universal logic in here. Anything which is just related to being a general plane is perfectly fine. So if this was every plane had to just announce itself and just say, I'm a plane, this would be perfectly fine because all four of them are indeed planes. As we can see, but we wouldn't want any code in the base class that was related specifically to the player. So hopefully, that makes sense, and we can kind of start seeing how we can use inheritance and how we can share our features, components, and logic between multiple classes. And this is how we'll be handling the things like destruction. We can have the particle effects, the sound effects all play in the parent class because that's not specific to any one plane. They all need to be destroyed in some way. But the things like the movement we just slightly unique, we're going to handle that in the child classes instead. Back into plain player. We're going to finish this one off first of all. A really quick way to take the code, which we know is already working. Like I've mentioned, all we really want to do is make sure that we don't have this littered around, and we can make use of that function that we created a small while ago. So if we grab everything related to movement, so that's going to be these nodes here, calculation here, I'm just holding Shift and dragging and then the actor location here. This is only related to rotation, so we're going to leave this one for now. What we want to do is we're going to press Control and X to cut, and we're going to click here to override the handle movement function. So we want the function that we made earlier, that empty space. And then we're going to press control V in here and we're going to hook everything back up. So we can ensure our movement function will always be called because we're calling our eventic. And if movement is enabled, then handle movement will also be called from that eventic. So again, we only need to call this function once here, and we can make use of that function call in our child classes. We've moved this, and I'm just going to come back into the event graph. We'll move this over for now, the rotation, and we'll hook this back up. And just testing as we go through, like I've mentioned, this is more to show you how you might refactor code on your own if you needed to. And this is a very common process. We just want to test these little steps as we go, and we should still have the movement being enabled. So if something's broken, let me just double check what that could be. If movement's enabled, if that's true, we're calling our eventi. We have our handle movement here, movement speed. So I just realized we've got our new movement speed variable in the base class, but we haven't given that a value. So if we grab our class settings just here, this is actually going to give us access to the class settings when we select this topmost element here or the class default. We can see the class defaults have the variables here, and we can see our movement speed, and I think I was setting this to 1,000 in the player. So we'll set that back. And then we should be able to go and press play and move around. So we get the same movement, we get the same rotation. It all feels the same. We haven't changed the functionality. We've just tidied up our graph and where things are handled. So we're ensuring that this is called if and when we want to and another really cool benefit of dis now is we could come in. We could disable movement enabled, so movement are no longer enabled. We can press play, and now we can't move the plane. But of course, it breaks the rotation because we're now multiplying against a value that doesn't really do anything. But you can see the logic is that we're kind of handling things as we want this to work. And all of this is now actually handled in the base class regarding the movement enabled and disabled states, and we're just leveraging that inside of the player version of that class. So make sure that we click this. We're going to make sure that movement is enabled for the moment. We'll be toggling that through the code a little bit later when we get to the animation. Remember that I've said that we won't be adding things like the rotation or faking physics and things in the enemy class, which is why we're not re using the movement code exactly. Another thing the enemies won't have is the actual rotation logic we have here for that reason. Something we could do, though, to keep the naming conventions and the general styling the same. There's another tip I wanted to show. We can grab all of these nodes in a press shift select to grab these nodes. And then if we right click on any one of the nodes we have selected here, we can choose to collapse this to a function of its own. And we're just going to call this one. You can probably guess. We're going to call this one handle rotation. Nice and obvious. If we have to dive through this and start reading through our code, we'd come from the plane base festival, and we can see that if the event ticks running, if we have movement enabled, handle movement, and then this will jump out of the base class. So once this is done, this will then return us to our child class. So this has now been handled. The execution pin will be called, and then we'll start calling this. So we're handling movement if movement's enabled, and then we're handling rotation, again, if movement's enabled. Exactly the same results if we go and press play, nothing's changed. We're just making this much more kind of readable, maintainable. We're getting rid of that spaghetti code or even the potential of that happening here because we're in full control of our code base and how we wanted this to run. We're making use of inheritance, so we're saving a lot of time when we get to adding new features. At the moment, this might just feel like busy work, but it would definitely save you a lot of time in the long run, understanding what this is doing and how to make use of it. Oce we're here, if you're inside of the plane base, if you're following along exactly, we can get rid of this message describing what the class is. We'll just get rid of that one. It compile in. Enemy will leave for now and just double check that everything is saved in the player class, and that is the player refactor done. So you can see it wasn't really that painful. I took a little bit longer just because I wanted to explain these concepts and make them clear as we go. If I was doing that by myself, that probably would have been a 1 minute task. So even refactoring and completely reconsidering how the structure of classes are handled really doesn't need to take that long, especially if you're thinking early in development, which is why I'm trying to emphasize this as we go. Try and make these choices upfront, and it's the difference of having a 1 minute refactor to a 1 hour refactor. If you're testing your code and something's broken or not working, just double check that all of your parent calls are connected. So these parent calls here, the orange nodes are connected up and being called on the begin play, the event tick, and so on. Likewise, make sure that things are the movement enabled is ticked in both the base class here and more importantly, through this value up here, like I've just shown, just a couple of gotchas there, which I can imagine some people might fall into. So just double check the things that we have been changing. For the next topic, of course, we're going to jump into the same thing, but for the enemy and it should take a little bit less time. Now we've seen how to do it here. 15. 14 - Refactoring Enemy: Refactoring the enemy. We've already done this once. Should go a little bit faster. Obviously, if you don't already have this open, we're going to make sure we open the plain enemy class. Double check up here if you haven't made the change already that we have this reset the parent class here to BP underscore plane base. Remember, we can do that through the class settings and the drop down just here. Same things again as with the player. We're going to delete the static mesh, which will give us a little bit of a problem here. We'll just say, This is fine, so we'll click yes. This has dropped me straight down to where the problem is, and that is that we've bound the previous component hit function, that collision check to the original static mesh. That's actually not a problem because we wanted to fix this anyway. We don't want to use the mesh as the collider instead we're going to take the sphere component here, so we'll grab this one, and we want to do the same thing again. So in the right hand side in the details panel, we just drop this. We're going to find the on component hip function and create a binding to this. And that's all this is doing. It's just binding whenever action occurs, it's binding this function to then be called. So we can delete this one, hook the same things up, so execution pin and actor pin, and that's the initial part of that problem solved. Then I'm just going to jump into the viewport quickly. We're going to grab the static mesh, and we want to make sure that we're updating the material. So again, in element zero here, this is the one controlling the body of the plane. We'll drop this down, and we can now find the other material that you may have made for your enemy, which for me is just MI underscore plane enemy. Select that one, and we're pretty much back where we were. Same thing with the movement speed as well. We want to right click on this, find the references by the class member just to make sure get rid of all of these. We can double click on one of these and see where they're being used. We can see that we have a few of these to ensure that we don't actually miss any when we're trying to hook things back up. I'm just going to right click in the event graph down here, search for movement speed, and we're going to get the movement speed variable here, the one which was created in our parent class. Hook this in here, duplicate this, hook this down here, remove these just to tidy things up a little bit, and double click on the final one, which is over here. And again, we're just going to right click and search for movement speed. We're going to set the movement speed here, and then we'll plug this in. Get rid of that, and we are back to where we are. Now, there is an option, which I didn't want to mention because I found it to be a little bit fiddlly. In fact, on the current version, it just doesn't seem to work. But something that might be useful if they fix this in the future, is that in the past, we've been able to right click on the variable. We can select here to replace references. What I'm missing at the moment, I don't know where that features gone. You can normally drop this down, and we could have found the movement speed variable created in the parent class. For whatever reason, I can only access variables from the actor and the pawn directly, not the actual BP underscore claim base, even though I should be able to. But just to say, for those reasons, we may just need to go through and do it the slightly more manual way that we've just those removed, remember to get rid of the movement speed underscore zero. We shouldn't be using that at all anymore, and then just hit compile to make sure that everything's gone through. Same with the player. Now that we have these in place in the enemy class, we just wanted to double check. I've already taken this path whilst we were doing the player content if you decided not to jump into the enemy class I'm focused just on the plain player class. Then just double check that you have the begin play, parent call and the eventI override of parent call as well. So we're going to R click on here. We're going to do the add call to parent function, and again, just make sure that we're hooking these up as we go. For the movement logic, we're going to do the same thing again. So we actually want the same movement logic to happen. We can grab all of this. Nothing's really going to change. We can grab these, so just Shift select to grab these. We want to press control in X to cut all of the nodes out of here. We're going to go to override, and we'll override the handle movement function. After we do anything that might need to happen in the parent function, we're going to paste these here and then make sure that our movement is called. With that done, that's just reminded me that we could also improve another thing over here. So we want to make sure that with our overlap that we were doing, we were previously just calling destroy actor and leaving a message here. We can bring in our handle death function, drop this in here and replace that with the destroy function. So we'll get rid of Destroy. We've got our handled. What we may want to do is actually come in here. We will leave ourself a comment. So this is taken. If we double click on this, that will take us into the plane base. So we're now in the parent class. We can pull from here, and something we're always going to want to do at the end is called the destroy actor. One thing that we might want to do another use of comments, I can make things easier to track things down. So if we press C to place a comment here, I like to leave myself these kind of to do postic notes, almost. We can say something like implement the pre death effect. So what happens before we destroy our actor? We're going to do that just here. This makes it really easy because you can press Control in F in any class and you can search for Do. Double click on this. And wherever you are inside of your class, if you have a bunch of to do comments left, you can quickly jump to them and see what you can work on. We can also come to the right hand side. We can give this a different color just to make this stand out a little bit. Completely up to you how you wanted to handle this. But as I mentioned, it's just a nice way for me to quickly find the features that may not fully be ready to implement yet because we're not touching those parts of the system, but I know that I will need to come back here and do that at some point. So when we come back to update this later, and again, this is how you should really be focusing on your workflow. This hasn't really done anything for us right now. What this means is that when we do come back to add effects and things like that later, we don't need to come back and change our enemy code. This is now ready to go. We can keep developing around the entire project, and this will just automatically be accounted for when we've updated the features in here. The rest of the collision is still the same. This is still going to be pretty much what we need to do here. You could replace this with a comment, another to do comment if you wanted to, but again, we haven't got to the health system, so we need something to remind us to come back here, and that print string is fine. We just want to double check that all of this is working, so we'll go back into play mode, make sure that the enemies and the player are still moving around. Intended, and that's all pretty good. They're still bouncing off the walls. They're still taking their random direction, random speed, bouncing off each other, and we can again come back and change that depending on how we feel the game should play out. But I think with that done, we now have the parity of behavior. Everything works as it did before, but making use of proper inheritance. It's just a quick recap now that we've finished our refactor, moving over to using inheritance, find out what this has brought us. The core things are going to be that we have shared collision setup. We can configure that sphere once, and it will be working in both of the classes that make use of that. We have the shared death handling. So again, when we get to adding things like explosives, sounds, both of the classes will be able to use that. We can override the effect if needed, but we don't need to make any changes to our base code now. We have the shared movement logic toggle, so when we get to adding the animations, that movement enabled will be valid for cut scenes for both the player and the enemy. We've got the shared variables like the movement speed. It exists once we don't have duplication or multiple versions of those across class. Might see my overkill at the moment for just these two different classes, but we're about to add things like health, projectiles, spornos effects, and everything. We'll now have a shared feature pool to pull from where things are relevant to make use of the same thing rather than duplicating it. One thing that did just come to mind is that we could make those sphere updates now. Like I've mentioned, the general kind of approach that people take with this type of game is for the player. We want the sphere to maybe be a little bit smaller than what you'd expect. So this might actually be fine. We could potentially increase the sphere radius a little bit. We can see at the moment it's actually inside of the body. It's so small, we wouldn't be able to see it. So projectile is going to completely fly through the entire wingspan and most of the body. This, as I mentioned, is just to make the player feel like they're a little bit better than they are. If they think in their eyes that they a bullet just about missed them, but in fact, it hit them. They're going to feel like they've dodged it. And what we don't want is for it to go the other way, where they feel like they should have dodged something, even though they clearly wouldn't have done, and they feel cheated by taking that extra damage in their mind. With the sphere selected, though, we want to find the sphere radius here, and we can set this to something like 50, and you'll see where that comes into play. So now, this is the area that the player would be taking damage. So maybe I'll just reduce that a little bit, so it's just purely the body, something like 45, and I think that's probably as much leeway as we want to give them. If we hit compile and then go back over to the enemy class, same thing. We're going to go into the viewpot, grab the sphere, but I think this time, what I might do is make this something like 64 or double it. And in fact, that might be maybe even that's a little bit too small, so let's make something like 70. So again, it's just going to feel like the player will feel as though they're making their shots much better that every time they take a shot, they're actually hitting the enemy exactly as they planned, where, in fact, they're going to be missing the body, but we're going to pick that up for them and make them just feel as though they're doing better at the game than they might be. Obviously, we want to balance this. We don't want to make it too easy, but you can see it's nice and easy to tweak these things anyway. So that's how inheritance setup. Both classes are working, both inherit from the plane base. Next, we can move on to adding the health and damage. And this will be where you really start seeing the benefits of taking this time ahead of the implementation to get inheritance set up. 16. 15 - HealthSystem: Time to add health and damage. We already have our base class set up to share the important data, so let's jump straight into BP underscore plane base. In here, we're going to need two different variables. We want to track our current health, the health that we currently have, and our maximum health. We can do this with the use of floats, so I'll take the movement speed, press control, indeed, duplicate this once and call this one current health. Duplicate it a second time, and we'll name this one Max Health. If we hit compile, I'm going to grab the maximum health, and I'll set this to a default of 100. We can leave current health empty. And what we're doing instead is on the begin play, drop our current health onto the execution pin just here, and we want to take our current health and set this to be our maximum health. No human errors. Every time we start playing, we will always make sure that we start at full health. So a nice, simple way to just ensure that we don't get anything wrong with our health system. Once again, just a reminder. This is why it's really important. When we hit compile and save, go back into the plain player and just double check that you have the override ce to begin play, and the same for the enemy. So this is where it becomes important, because we want to make sure that both of these classes initialize their health to whatever their maximum health is meant to be. With that confirmed, we can implement our damage handler as well. This is really simple. RL Engine has a built in messaging system specifically for handling damage. If we we click in the event graph, we need to search for an event called any damage. So it's this one here, the event, any damage. And this will be called whenever we send a message from another actor, whether that be a projectile, an explosive impacting the plane or the enemy class, we can send a message to say, Hey, you've been damaged. When that message is sent and this is received here, this event or function call will be made. And when that happens, all we want to do is we want to set our current health, so we're going to drag this onto the execution pentagon. We'll make that our current hell, so we'll take the current health a second time, control drag that into the graph to read the value, and that will be negative our damage. So current hell minus a value here, which will be our damage that we're passing in, so this will be something we place in the call from the other class. The result of that calculation will be our health value. One thing which is normally recommended for this type of calculation is to avoid things from getting a little bit hacky or weird. We don't want to allow damage or if we implemented healing, as well, we don't want to allow those values to take us past our maximum or minimum threshold. So we can use something called a clamp. We'll pull from this pin here and we'll search for clamp. We want the clamp float option, which is just up here a little bit. So clamp float. And we're going to plug the result of this clamping into our pin just here. So we can see what this allows us to do. We can take our minimum value. So the minimum amount of health that we should be zero. And the maximum amounts of health that we can have will be our max health value. So nice and simple. This just means if we add in something like health kits, when you pick one of those up, you cannot go past the maximum health that you started with. So it will always keep the results of that calculation within that range. Once we've confirmed the setting of the health is all done correctly and we can't get any of this wrong, we're going to do our check to see if the damage we've just taken is enough to destroy us. So we'll pull from our execution pin just here. We're going to search for a branch. Our true false flow control here. And all we want to do is we're going to get our current health value again, so control drag that in. See if this is less than. So we're going to look for the less than operator here. And what I'll do is I'll actually check whether this is less than or equal to a certain number. Plug this into the branch just here, the Boolean, and we now have a check saying that if our current health is less than or equal to zero, then we can handle our calling of the death function or the handle death function. Be asking why we're using less down at equal to, especially since we've just clamped this to make sure that it never goes below zero. And this is just kind of a habit of safety. If something applies more damage than the remaining hell, or we get some floating point weirdness, or even if we just refactor the code again later and decide not to clamp this, I want to make sure that this check will always be relevant to ensuring that we're dead. So, for example, if we do take this clamp, if we're at five health and we take another ten damage, obviously, we're going to be at minus five, which means that if we just checked only zero, then something which has taken more damage than they should would avoid being killed off. With that done, though, from our branch here, we can take our handle death. And if this is true, we can just plug this in just this is what will now start handling all of the death functionality for any of the planes that you may be tempted to make, and, of course, the two that we already have. Handle Death already has the destroyed function in it, and like I've mentioned, we're going to return here later to add things like explosions, sound effects, and all of the fun juicy things that we want to put into our game. So to actually start making use of the damage system, we already have something in place to test at least part of how this structure is going to work inside of our enemy. So if we go back into the enemy class, this bit here, which has just been doing our print string for the longest time, we can finally remove this. What we want instead is we're going to take our hit actor, so we'll control drag this into the graph. We'll pull from the other actor, and we're looking for that global message that I mentioned a little bit earlier, the apply damage. So we can see here we have apply damage. This just makes a call to any actor that it can communicate with. In this case, is going to be the one that we've just bounced into. We're going to hook this up just here, so we'll take our execution pins, make sure they're all hooked up, move this out the way, and we want to apply a certain amount of damage to the player. I think for the enemies, I'm going to give this half of the players health. I know the player is going to have 100 heal. We'll set this to 50, and this means the player can take two hits from an enemy so they don't want to bounce into them too often. It's not going to be a quick and clean way to gain extra points by just flying into all of the enemies. Now, that does remind me. Another thing that we can do with our inherited variables is if we go and select the enemy, for example, and get the class details just here, in the right hand side, we want to make sure that the enemy health is probably going to be different to the player's health. So we could set the enemy health to 50, and in the player, we'll just double check, there should be defaulting to 100. Make sure that the player health is set to 100. So again, a nice easy way we can set the health value just once and handle this in the parent class for both the damage taken and the health given here because they're based on variables which will exist in all of the classes. And then in the child classes, all we need to do are just tweak these variables in the editor if we wanted to change the general kind of markup of our game. And like I've mentioned, because we've already set this up in a certain way, it now means that we've got the handle death. We don't need to come back and touch this. We needed to make one small change side of the enemy class didn't need to be accounted for and everything will keep working as it was. One thing I want to show you really quickly, just because this is another one of those really clear places where you might have been tempted to take a different approach to handling death for the enemy is we could unplug this just for now. Another thing that you could do is we could duplicate our apply damage function. We could plug this in here because the main thing is that when we hit the player plane, we know that the enemy class needs to damage itself, essentially. What we could do is we could take our MAX health value. So we can search for MaxEL and plug this in here. And then the damaged actor, we could basically just apply damage to our self. So if we pull from the damaged actor in here and search for a cell, we get this option built in functionality that I wanted to introduce, get a reference to self. So this will basically it's a bit of a weird one. This will basically find a reference to itself, which is found here. It will call the apply damage message on itself, which will obviously be picked up in the base class because that's where we have the event any damage being fired off. And because we're passing in a maximum health, and this is just a safety thing. I later, I decided that, in fact, the enemy health at 50 was too low, and I came in and changed this to 75. Obviously, what we don't want to do is have this hard coded 50 because any changes that we make on a wider base than just this one node here would be kind of forgotten and missed. Just another way, again, just trying to get to think about how you structure your code, the use of variables, mainly just kind of future proofing so you don't have to do more work in the future. The main thing with programmers is that we're quite lazy. The least amount of work that we have to do the better, avoiding coming back to our code and changing variables everywhere. If we can set something up like this, I can change the maximum health to anything. I could lower it, I could increase it. This function will always. Like I mentioned, though, it just doesn't read very cleanly. It takes a little bit longer to work out why we've got one applied damage, another one here, and what these different actors are. I think it makes more sense if we're just going to do a full kind of kamikaze style enemy, we can just plug these in, and it makes it much clearer. So we're going to apply damage to the thing that we've just once we've done that, we're going to apply damage to our self. And remember, this is really safe because we're looking down here, we're checking if the hit actor is the thing that the human player is currently controlling, and only if that's true, are we going to do this. So this won't be applying damage to the walls, the projectiles, other enemies. All of that will be accounted for down here. So this is a nice, easy, safe way to structure impact damage. Something I haven't really mentioned, but also keep in mind that order of operation is really important. As soon as you think about it, if you haven't thought about it before, it becomes really obvious. It is something that people miss. If we just handle death first, so we could throw this the other way as well. If we plug this in here, unhook these, move this. Just a very quick example, plug this back in. And this is just because I've seen this happen before. Students weren't quite sure why certain functions weren't being called when they expected them to. It can sometimes be down into the system as well. Some systems it might work, some systems it might not depending on how quick the ticket iteration and things like that will be happening. But basically, if you think about what's happening here, if we hit the player and call handle death first, this will be jumping into the parent function, calling destroy, and potentially if this gets immediately added to the garbage collection and is cleared from memory, this class no longer exists. That means it's not going to be stepping back out of here, back into this function, and applying damage to the player. So you might end up with a game where the player just never receives impact damage. So be careful with that type of thing. And again, it's very common for people to have already handled the death side of functionalities, and then they'll come back and they'll add the particle effects and the sound afterwards and then not be immediately clear on why the particles and the sound effects aren't playing. It's because your class is probably being cleared out of memory and doesn't exist. That function being called. So order of operation is also important. Flow control is another word for that. This was a really simple kind of example, but it's an easy trap to fall into if you're not paying enough attention. Even if you do understand the concept, it's a simple mistake to make. So with that, if we just hit compile and save, we can go back in and test or hit play. The thing that we're looking for here is making sure that they still destroy themselves when they hit the player. And most importantly, after two hits like we saw there, they're also destroying the player. So neither player and the enemy have a health system, keeping in mind that we're not implementing that health system into each class. We're doing it just once in the base class, and all of this functionality to calculate the health, check if their class is dead, and then called the function is all being handled there. As I look at this, I've just realized I've probably made a bit of a coding faux par my this is actually going to cause us a bit of a problem because we are calling handle death when we've calculated that death would be appropriate, and we're calling it here. There's a good chance we're going to get two sets of particles playing off. We're going to get one from dysfunction call, and then one from dysfunction call, which is happening based on health. So now that I'm looking back through my own code, what I think we should be doing is actually the way that I was trying to use as just a learning example. But if we duplicate this again, I think we will have to do it the other way. So we're going to apply damage to ourselves. So again, we'll pull from the damage actor. We'll say sell, and we'll plug this in here. This just ensures really easy to overlook things like this. Lucky that we went back and had a quick look at the code. This just ensures that damage is applied to ourself. We're going to use that same trick I mentioned a moment ago, so Maxel. And again, I realize I could edit this out of the video. But since I've created this whole project with the intention to try to teach how to think about coding, I think this is a pretty good learning example of the kind of things that you can overlook and how you can easily get yourself into a little bit of a situation that you need to come back and fix later. So this will ensure that we definitely kill ourself. We're always passing in the maximum amount of health we could possibly have. That will trigger this to be called, which ensures that our handle death is only ever called once, meaning that when we do get to the particle effects, the sound effects, going to have it called once here and then also played once here. So that's the main thing we want to avoid, so I can delete that one. It doesn't read quite as well. This is where you might want to bring in a comment just to make it clear. This is for the player. This is for yourself to make sure that we always remove ourselves from play after hitting the player. Really simple thing, really small thing. Turned it into a bigger topic just because it's more of a learning moment that just accidentally cropped up there. Final small thing we can do whilst we're still in the enemy class. We can remove the use of magic numbers. So at the moment, we're just applying 50 damage to the player. We can promote this to a variable, and we'll call this one impact damage. Just double check for any others that we might have missed. I think it was just that one. We do have these, in fact, if you wanted to create a minimum and the maximum movement speed because the enemy has the chance of that randomization. It compile save, go back to the player, double check here. I don't think we've got as many variables that we've been creating recently, so that's fine in the player class and then in the base class as well. Something else that you might want to do if you're really trying to keep all of these variables tidy as you start getting a lot more variables, it can become a little bit harder to find what they're related to. So you can give variables categories, which may help. So, for example, we've got here current speed, target speed, interp speed. So these three are all related to movement, and then these two are related to rotation. So what we can do is if we select one of these in the details panel, we get this option over here for a category, and we can set this to something like movement. And then we can just simply drag and drop the other movements into the movement category. We can grab the rotation, and we could maybe give this a category of rotation. Same again, drop these in, and then we can collapse these. Makes it a little bit easier especially when you've got a long list of variables that start cropping up in larger classes. That means you can just navigate very quickly to find all of the movement related stuff if you're looking for one very specific value to tweak and the same for rotation. You can have combat. You can have a category of Booleans, vectors, whatever you want to set up here. Very much how your work process is going to best fit your project, but just a quick idea. If you wanted to do something in between topics, feel free to pause, have a little break from following along and maybe go back through the other classes and see how you might want to categorize things in the base class and maybe even the enemy class. I won't do that on screen, but it's just an idea to get those concepts going and let you know that those features exist. With that done though, that is our health system complete. We've implemented that once, and it's working everywhere. It means we can move on to the projectiles. And again, we can now just build upon the foundation that we've already created. That's going to be when the combat gets more interesting and when we can actually fire back. 17. 16 - ImprovedMovement: Remember that frame rate bug that we acknowledged and just moved past. It's time to return to that and fix it. We'll be replacing the problematic movement code with something production ready. Proper frame rate independence, cleaner structure, and a better fill. I realize that before we start adding fancy things like actor spawners, projectiles and fleshing out the damage system, whilst we're doing this factor and improvement of the base code, we should probably return and make sure we don't forget about the movement and the bug that we're already aware of. The offending code that we need to fix is in our player class. So if you don't already have that open, go ahead and open BP Underscore plain player. In my case I'm just going to pres middle mouse button, close the tabs that we won't be using. So base class and the enemy class and navigate into the handle movement function just over here. So this is the code that we want to replace and improve. Now, the first thing is after the parent handle movement, we want to click and unhook this code. Some people have different opinions on this type of thing. Again, I want to keep focus on code structure, good practices, and things like that. What you may see, especially in C plus plus code is some developers might keep the old code that we're trying to replace. Maybe put this in a comment just in case you wanted to see it later in case you think you might need to fall back on this. That ends up with a cluttered workspace. You have maybe a lot more code in your C plus plus than you need. In Blueprints obviously we can have a lot more stuff just in the graph than we need. And ideally we'd be using something like Github for source control anyway. So if we ever did need to look at old code, ideally, we'd just roll back our repository and view that through Git or Perforce or something. Now, in this case I'm going to keep this just for a moment because I think there's a few things we can simply copy and paste for quickness, but we will be getting rid of this code entirely. So we're going to start this one fresh. The first thing we want to do is we're going to pull from our execution pin up here and we're going to create something called a sequence. We want this option just here. And if you haven't seen these before, sequences are heavily underutilized. They make our blueprint read like traditional code, meaning from top to bottom, left to right. Each of the pins handles a specific task, or at least that's the way that we should think about using these. It's much cleaner than logic stretching across all three monitors, just because you have this long line of logic. Just to point out, I'm not showing off. That was an exaggeration. I do not actually have three monitors. There are other benefits and reasons that you might want to use this, but the main thing, going back to sequences is that they are just a really nice way to help structure our code and help us think logically breaking things down essentially into steps. You're saying, do this, then do this, then do this, and you try to keep everything somewhat unique to a given task. So the first thing we actually want in our given task is that we need to calculate our target movement speed. We're already doing that down here, so this is one of the things that we can just grab. If we grab the code up to the target movement speed node, press Control in X, and then come on over and paste this over. So this will be the very first task that we give ourselves on our sequence. One other thing that can help us keep the code a little bit readable is if we double click on a wire, this will give us a reroot node. We can move this up here because we want to make sure we're going to have enough space, essentially for the different then path that we're following. Now one thing we are going to change is we're going to be creating our own interpllation. We won't be using F interp two. For that reason, we're going to change a little bit of the Get World Delta seconds. Man get rid of this node here, the multiply. Hook this straight up into set target speed, and we're going to make this frame rate independent a little bit later. We'll keep this around. We will need this in just a moment. The get world Delta seconds. But that's pretty much it. That's our first step. Our first thing we need to do is have a target to reach. That then allies us. We can go down to then one. And what we're going to do here is calculate our own custom interpellation Alpha. So this is going to get a little bit mathematical, but it's going to be the key to proper famerate independence. We'll go through we'll add this in, and then I'll step back through and try to explain why we end up with a calculation we have. So the very first thing that we want is our movement interp speed here, so we're going to control drag this in and get the movement interp speed. We'll multiply this by our Delta seconds, which is why it's available just a moment ago, so we're going to have our Get world Delta seconds to start making our calculation frame rate independence again. We then want to negate or invert the calculation that we create, so we're going to multiply here. We'll get to multiply node and multiply this by negative one. From our calculation here, we're going to pull again. I'm going to search for something called EXP. This is the exponential value, so it says here, this will return the exponential E to the power of the value. So we'll use this. And then finally, after our exponential node, we're going to pull from here, and we're going to subtract, so just press the minus key, find subtract and subtract one from the final calculation here. The important thing before we forget we obviously don't want any magic numbers, we're going to pull from the final pin. We're going to promote this to a variable, and we'll call this one MV Alpha. If you've been creating categories, you may want to move this into the movement category. As, of course, this is going to be movement specific. We're going to hook this up to our execution pin, then one, and we now have our Alpha code ready to go. So this is our smooth interpolation that we'll be using in a more kind of manual and intentional way in our code. So just to focus back in, what this is doing, this is giving us one minus E to the negative speed times Delta. Essentially, this is providing a smooth decay curve. That's the main reason that we need to use the subtract one at the end as we want this to decay over time. And the reason that we need to use a formula like this is going back to the main source of the problem we're having between different frame rates and keeping things frame rate independent. The F interp function is actually providing a linear approximation, and that's what's breaking at low frame rates. In comparison, if we create a function like this that we can use with a standard larp function, this exponential approach gives us mathematically correct interpolation regardless of frame. So that was the main goal here is we actually just want to completely stop using this. Finally, keeping in mind that we want to try and do very unique specific things per line. We're going to add another pin, and we want the then two pin just here. We're going to pull from here, and we want to do that final thing, which is actually using the current speed, providing interpolation to it. We still want to use interpolation, but just the more rudimentary basic version with our own calculation on top. So we can pull from the execution pin. We can find the set current speed. So setting the value that we've been tracking, and this is where we want to pull from this pin here, and we're going to search for loop. And this time, we just want to use the standard float loop. So you can see it's slightly more simplified. Instead of having the current to the target at Delta time and an interp speed, if we wanted to change the interp speed, we need to add some kind of offset here. All we're doing is providing that exponential curve, which is going to be our Alpha. So if we pull in the move Alpha, we can drop this in here. But besides that we're essentially still doing the same thing. We're going to be moving from A to B. And I mentioned this when we first looked at the F in top two, we kind of have 0.1 in 0.2 or point A and point B. So we're still doing the same thing. We're going from A, which is current speed to B, which is our target speed over a duration of time, and we're now basing this off of our own custom moving Alpha. So that's the main change here is the way that we're setting current speed is being updated. So if we go through, we can actually delete this now. So we can see we're sort of most of the way through updating our code base. I'm going to double click here again to make another reroot node just to keep this somewhat tidy, move things out the way, just give ourselves enough space. So the final thing is we want to set our location again. We're going to do something slightly different here as well. So after we've set the current speed, once we're now tracking the speed, we want to pull from our execution pin, and we want to search for something called add actor world offset. So we can see the option just here at the top, at Actor world offset. So for our use case, this is actually the better solution compared to set actor location for physics aware movement. The only thing that we really need to change is we're going to right click on the Delta location vector here, split the structure pin so we have the same results as before. We want to take our current speed, so we can plug that in if you wanted, but in this case, I'm just going to Control drag current speedom. We're going to multiply this by Delta seconds. So we're going to get another G world Delta seconds, and we'll plug this in to our result here. And then, of course, we're going to make this the Y because we're moving side to side. So we want that on the Y axis. That's pretty much it. So we've now updated. We can get rid of this as well. We've now updated our movement code. We are tracking the target speed a little bit differently. We've got our completely custom movement alpha based on a decay curve, and we've changed a little bit the way that we're handling the current speed and how we're plugging that into the world location. Now one thing that we could do here, just to improve things a little bit, is we could go back into commenting things. So again, this is a nice use of comments when we've got these nice big splits of specific logic in our so what we could do is I'll grab these nodes up here, press C. And if I wanted to give this a comment, I'd maybe say something like set speed for the plane to reach. And so that I didn't need to dive into the code later, I could just specify that this is specifically the target speed that we're looking at. Again, I might give these kind of more unique colors just so that the comments stand out against other things like to do that we created earlier. We don't need to do this and make everything look really pretty. You can definitely get carried away with that. But when I change the color of things, it's more so that at a glance, I know that I use a specific gray for comments. I use a specific red for potentially broken code or something that needs to move all together. I normally use, like, a green for to do. So again, just at a glance, I can see as I'm going through my code. These are just comments where I need a refresh this is something I really need to get rid of later, and things like that can become useful. So I give these like a very blue, grayish color just to remind myself. Just going to press Control C and Control V to get another comment over here, kind of duplicate this one, and we'll move this roughly in place just so I didn't need to set the color again. And this is one of those things. Once you get used to shortcuts and things like that, the tiny bit of extra time it takes to give yourself a custom color and sort of theme things a little bit doesn't actually add that much overhead to your development time. So this, if I was leaving myself a comment for later, is helping us track Alpha for use in our interpllation function. Once again, just reminding myself that specifically, this is updating the move Alpha function. And then we want maybe one more comment. I've mentioned, I won't do this for every line of code, and I won't do this for the rest of the project and the videos as the videos would get too long. But just to show the concept, as we go through some of these bigger refactors and changes, which might be a little bit more confusing, I think this is definitely valuable to see. So we're just going to move these into place. And what I might call this one is we're updating our current speed and then setting the location of our plane. And as I say that, this is actually another really useful thing is I'm actually now questioning my own code because we're doing two things per line. And if you remember, I said, the general rule is with a sequence, we kind of treat them a little bit like functions where functions should be doing one thing specifically and one thing really well. So at the moment, we're mostly following that. This is setting specifically the target speed. This is calculating and setting specifically the move Alpha, but this is now actually kind of doing two things. It's setting the current speed, and it's then moving the plane. So what I might be tempted to do is we'll add another then pin down here, breast control in X, control in V. Hook this up. And again, more than anything, I could have left it, but just to show how easy it is to keep your code clean and sort of follow specific rules and structures. So it's now we're actually sticking to the rules that we gave ourself, and I just need to move things around just a little bit and maybe add another reroot node here. Tidy things a little bit. Again, we're not trying to make this artwork, but the easier it is to work with, our future selves will be grateful. What we're doing now is if we kind of think of the code. We can come and give this its final comment. So this is setting the current speed based on a simple interpllation, so simple alert. So that's the one thing that this line is doing specifically. And then, again, we need one final comment, so Control C control V put this in place. I mean, I have a really nice logical brick, so this is now simply updating the plane location. So then if we were to read this, we can see specifically what's happening. We're setting the target speed. We're updating the move Alpha, which is used for the interpret function later. We're using that interpret function and setting the current speed based on that. And then we're taking those variables, and we're updating the plane's final location. So this is now probably better than what I had just a moment ago. It's only one small change, but again, just sticking to those rules, we kind of think of these as small functions, essentially, and they're all doing that one job very specifically, and they're doing it well. Keep things tidy, keep things easy to read. And if we ever needed to come back and remove something, add to it, then it's going to be much easier to update and fix our code in the future. So it's like side tangent there. I wasn't expecting to go that much in depth on code cleanliness, but I just again saw the opportunity because I kind of went against what I mentioned at the beginning of the topic. That done. That is the movement now ready to go. What we need to look at next is the rotation system. It's going to be the same problem because we're making use of the same type of thing. If we go to handle rotation. We have a really simple function, which is, again, the really nice thing that Unreal does for us. It provides a lot of these helper functions, this built in R interp two, nice and simple, one node, a few nodes next to it for the calculation, and then set the active rotation, and we're kind of good to go for test. But something that people need to be aware of is that generally, it's only kind of good for testing. We probably want to start rolling our own code when we get more serious and indeed with things. Before moving on to fixing the rotation, just a couple of things to check in the handle movement. Because we're going to be driving the current speed a little bit more manually this way, we probably want to make sure I just noticed when I clicked it earlier, current speed has a default value. Default value here was essentially when this was originally our general movement speed. So we're going to want to reset this and make sure current speed, target speed movement interp speed is going to have a value, but mob target speed and current speed are all set to zero by default, and these will only be calculated at runtime. The other thing, same problem, really easy to make again, is I almost forgot to tick the sweep function, so this has the same function as the set actual location function we used previously, so we need to make sure that the sweep is enabled. That should be pretty much good to go. This won't work fully at the moment. And I just realized we did this the wrong way around. I think I was telling you to subtract the right thing earlier, but during implementation, I hook this up the wrong way. So if we alt click after the exponential, really important here, we want to subtract our calculation from one. So we're working back from one. We're making that decay curve from one based on this update over time. So yeah, it's just testing quickly there to make sure this still works. As I mentioned, the rotation is going to be a little bit worked, but we still have the same movement, so we have some nice smooth movement, the rotation because it's based on the speed that we have, that's completely broken, but this is something we can work with, and we kind of improve that as we go through and refactor the rotation. So just a couple of final recaps there. Make sure that we're doing our calculation for our exponential value, and then we're taking that away from one. So it's one minus our exponential calculation here, and that is the result that we want for our move Alpha. Current speed, target speed, ideally you're going to be set to zero. We're calculating that here, multiplying those back together, and we get a nice frame rate independent movement here. So this is still kind of feeling and working very similar to what we had previously. You may find you need to tweak the speed a little bit now, but we're very close to what we had. Rotations broken, which as I mentioned, is expected because we're using a very simple calculation here based on target speed and just multiplying that against tilt factor, and now the target speed or current speed, sorry, is being handled in a very different way. With that done, though, we have the movement improved. It's a really good groundwork to get started, and then we can jump straight into rotation. I appreciate these quite heavy topics as we go through this, but hope taking some time in between the topics, pause again whenever you feel you need to and really try to understand what we've been doing and why these changes are going to be working. I tried explaining the math and stuff behind them. Some of the stuff is really hard to get across without actually having hands on experience with the source code. So that's another thing is you could jump into Unreal Engine source code, find the Ler function in C plus plus. And again, even if you don't fully understand C plus plus, that's fine. Most of it is very much human readable. The comments are all there. It's explaining what's happening on the F interp function step by step. And sometimes just diving into the source code that way, since it's all readily available. Be really useful and you'll start understanding some of the limitations of just using the rebuilt, predefined functionality provided by the unreal engine. Not to say that it's bad, as I mentioned, it has its use cases, but we do find these fringe cases where it just doesn't work, and we need to roll our own code. 18. 17 - ImprovedRotation: Movement set up. We now need to do the same thing for our rotation. We still want to be inside of the plane player. We're going to go into the handle rotation function. I'm going to make a very similar update here. This is another nice thing that we can start making the code a little bit more uniform because the logic behind it will be very similar. So, in fact, we can take our initial steps here. We're going to move this down. We'll unhook this, and we'll keep anything that we might come back to a little bit later, but like you said, previously, we will just be completely getting rid of that by the end. Now, the first thing is we want to get our sequence. We can do the same thing again. So we'll take a sequence node, and we'll start keeping this nice and tidy because we're going to need to do quite a lot of calculation for this. So we'll make sure that we keep our code lined out nice and neat. Now, there is a little bit extra we need to do for the rotation because like I said, previously, it was somewhat of an arbitrary value just being multiplied against current speed with nothing really else accounted for. So we're going to do a little bit of clamping and tidying to make this a little bit more refined as well. What we want is we're going to take our current speed. We'll get to the current speed that we have, so we're still going to base it roughly off the same thing, but with some modifications. The way we're going to do this is from current speed. We want to make use of something called a map range clamped. This is going to map one range of values to another, perfect for converting speed to a specific tilt angle. In range A is going to be our movement speed negated. So we'll right click, we'll search for movement speed because this is being accessed from our parent class, the actual target speed, the maximum speed we can move, so we're going to get the movement speed. We're going to multiply this by negative one. Feel free to give yourself space as you're going through. So that's going to be our in range A. In range B is simply going to be the movement speed, so unaltered there. And again, just giving myself a little bit more space, as we're going to need some more stuff down here. Then we're going to need a new value for our maximum and minimum tilt that we want to reach. So this is where we actually not getting a little bit of control. So we can say we either want this to be able to tilt a maximum of 15 degrees at any one time or 25 or whatever you wanted in the rotation category. And again, if you've got categories, that's fine. If not, just grab any float that you have. I'm going to grab my tilt factor, duplicate this, and I'll call this one Max tilt. Hit compile, and I think by default, I will give this a maximum value of 15, so this will clampas to rotating 15 degrees either way. You could create a minimum tilt, which would be negative 15, but similar to what we've done here, instead, we're just going to grab our Max tilt and we'll multiply this by negative one. And that will be our out range A, and you'll start seeing how this all works. Hopefully, as you see these come together. And I'm going to grab our max tilt and then this will be our out range B. So we're going to take our current speed, whatever our current speed is, as this has been calculated, if it's somewhere between zero and negative our maximum movement speed. I think that was 1,000. Then our maximum tilt is going to be ranged from zero to negative 15 based on a kind of a percentage, a uniform value that would match roughly where that speed is. And then vice versa, if we're moving right, then we're going to take our maximum movement speed, zero to positive 1,000, and I'm going to pick a tilting value 0-15 degrees. So this is how we can actually clamp rather than just having completely arbitrary rotation either way. We're actually basing that specifically between whatever our current speed is and then mapped between the actual speeds we can. This makes it really easy to update. If we ever wanted to say have more of a tilt, but keep the speed the same, then we can just increase this. I could say you can now tilt up to 25 degrees instead of just 15. For testing, I think I'll keep it at 15, but we can play around with that very easily. We don't want to promote this to a variable. So whatever this calculates, we're going to promote to another variable. I'll call this one target tilt. It's very similar to how the first thing we did in movement was calculate our target speed. And plug this into then zero, move this up. And we're probably going to have to do a fair amount of tiding here to keep this more readable just because there's a lot more kind of math going on in this function. Rotation is just always a little bit more intricate and fiddly than calculating movement or general kind of location or even scale offsets. Now, the target Alpha is actually so similar I'm going to be a little bit lazy. I'm going to go back into handle movement. I'm going to grab all of the code here, press control C, so I'm going to get all of these nodes here. The only thing we don't want is the move Alpha. Going to come back in here into the rotation, and we're going to plug this in to a value in a moment. The only thing that we may want to do is I'm going to move our target tilt into the rotation category. I'm actually going to rename this because we've got some slightly different naming conventions going on. So I'm going to grab the rotation interp speed and change this to tilt because we've decided that everything else is kind of being classified as tilting rather than rotating. I'm going to control drag the tilt interp speed. And plug that in there. So we can get rid of movement interp speed at the beginning. But other than that, we're doing exactly the same thing. We're taking our tilt interp speed multiplied by the G world Delta seconds, multiplied by negative one, getting the exponential return, and then we're creating that again, our decaying curve over time. So we're taking all of these values away from one. So the results of this will then be another new variable, so we'll promote this to another variable, and we'll call this one Tilt Alpha. Because we're going to want to do the same thing again. We'll be plugging this into our own interpolation or lop node that we're going to be in full control of. So we'll just move this up again trying to keep things somewhat tidy. I think just for cleanliness and space, I'm just going to delete this now. We don't need to go through step by step like we did earlier. We just won't be using anything from the old code here. So we're going to create another sequence pin. We want to do our next stage of logic. This one is very simple. So very similar again to the third stage in our previous code. We're going to set our current tilt. And I think just looking at it now, we're probably going to start mixing up values here. So Max til we want and we're currently using Tilt factor, I think we're going to miss. So I'm going to rename this one tilt factor to current tilt. And that word is slowly losing all meaning. I've said that a few too many times. We're going to drag this in and plug the execution pin. And again, the value that we want to fill in here, making sure if we hit compile that this doesn't have a default value anymore, so we'll set that to zero. This value will be calculated based on another loop. So same thing again, a floating loop. And same thing again. So we're going to take our current tilt, our target tilt, and our Alpha. So nice and simple. Pretty much, as I mentioned, like the movement, but now all rotation or tilt related. So you can see the pattern. We've actually been able to use basically the same code. So aren't you understand this once. Very easy to reuse it and remake it. We needed to get a little bit more intricate with the initial part of the rotation up here, but otherwise, very similar. So we can do the same thing again. We want one more pin, and this is going to be actually using these values and setting the rotation. So same thing again. This time we do want to use the set actor rotation function call. So we'll plug this down here, and we'll come back up and do a little bit of tidy with some reroots a bit later. We'll split the structure pin like we did previously. We're going to take our current tilt value, and we'll plug that into the we're doing this on the roll. And that's pretty much it, but we've got the same steps again. So set the target, set the Alpha, set the current, and then use the current as the value. Now, I'm just going to come in, as I mentioned, we will do a little bit tidy up because this is getting somewhat cramped and difficult to read. I think that's fine if I'm not going to spend too much time pritting up the nodes, but that's enough just to keep things separated. And like I mentioned, I don't want to use huge chunks of time just commenting my code. Not the most useful thing to see on stream or on a video. But if you wanted to do a similar thing, just a small thing to take away in between topics, maybe again, pause the content and just say it's off a task to make sure you understand what's happening. Read the code and the rotation and see if you can give yourself some good comments to describe exactly what's happening here, why we're using the range clamp and things like that. And the core thing is that if you needed to come back and look at the code in a month or two's time, at a glance, this is much easier to see what's happening than this, currently, as I'd need to come back through and read in depth. But let's just go ahead and check that everything's working. I think the main thing we've got our Max tilt is set to 15, so we should only see a 15 degree tilt either way. And obviously, we're just negating that based on the movement speed. Movement speed is still 1,000, which I think is perfectly fine. If it does feel a bit more sluggish, we can come back and increase that later. The interp speed, I think we want to slow this down a little bit nice. I think we're going to set this down to three rather than ten because our decay curve is going to work a little bit differently than what we're doing with F interp two and then target current and Alpha should all be getting set at runtime, so they should start at zero, which is perfect. And then we're just using that here. So this should, I think, now work. So if we come back in, press play. Yeah, there we go. It looks I think it looks different, but we can see it's very much the same. I think I kind of prefer the way this looks. It's a lot more sort of it just feels a bit more refined. So that's pretty good. And like I've mentioned, this is now where the flexibility comes in. If you wanted to change the speed, so maybe that was interplating too slow. We can increase this. So we'll go back into play mode over here. So we're getting back into straighten you out a little bit faster. So that's fine. And then if you wanted, like I've mentioned before, if you're not sure what something is doing or which way a value should go, if you make it really drastic, to make a really big change. And you can see that's almost like flicking strike back when we let go. So decay is happening much faster and we're flicking strike back. So that might look a bit nicer, actually. So maybe I was wrong, maybe going to a higher value rather than lower would have been better for that nice and easy to test, but this is the main thing. Try to instill into the way that you think about your own code base, making things easy for you to update and tweak things later will make it more likely that you'll refine the game and make it better. If your project is really hard to work with, you're much less likely to want to make refinement. Spend that time to improve the game. So just more of a design thing there, which is always fun to think about. The final thing is maximum tilt. So if we grab Max tilt, if I make something much bigger, like 45 degrees just to show off the difference we can get, if we ne float, you can see that when we start reaching the maximum speed, we're hitting that higher maximum tilt angle. So this is how that comes into play. Probably don't want it to rotate quite that much, but you may want to up something maybe more like 25 to make it more visually noticeable. But the main thing that we have over the previous implementation is, as you saw. It wasn't really doing any specific calculation to reach a certain angle of rotation based on speed or anything. It was just going as far as possible within a given amount of time. Whereas now, we've got a bit of control. Depending on our current speed, we can control exactly how much we rotate. And then the really important thing to check is I'm going to throw in some console commands to get rid of the enemies. We don't need those. Just keep flying into us. I'm going to go into play mode. I'm going to give myself the stat FPS console command, so we can see I'm running at 120 FPS. I put myself straight down to 30. And again, we should see we're getting a much less pleasant framework to look at, but we're still getting only 25 degrees rotation, and we're hitting it at what we should hopefully see in a moment, roughly the same speed. So if I now increase that to 60, we're only reaching the same rotation, the same sort of pitch tilt or roll tilt, sorry. I'm doing that in roughly the same amount of time. And then if we go up to 120 or actually, we'll just uncap this. I'll say like 600, so I can get around about 300 FPS. Look smoother, but the movements happening at the same speed, and the rotation is taking the same number of frames or seconds to get there. So we've now completely fixed the frame rate independence issue that we had previously. So the movement, remember, wasn't really that big of an issue, but the rotation had an issue where even though we were multiplying by down to seconds because of that, just the type of logic the F inter functions are using, remember that we were able to essentially rotate the wing into the floor because the rotation wasn't clamped. The different frame rates were affecting it differently. So we've now completely solved that problem. We had to get a little bit more kind of knee deep in some of the mathematics, made use of the more rudimentary simplified lurk function. But by taking the rains a little bit with our own code, we've now solved a whole bunch of problems which have been shipped in you'll probably find a lot of games because a lot of games are released based on example content example, product code, and stuff like that. That's the test complete. Same movement speed. We have the same rotation limits. There's no wing clipping through the floor. The exponential formula is what gives us the mathematically correct interpretation at any frame rate whatsoever. I hope that more in depth content like this hasn't completely frozen your brain and stopped you from keeping up. That's really been the goal of the topics when I was creating these is to try and get people thinking more about their code base and what's happening under the hood and not just following along. So hopefully this is now getting to the point where you can see the benefits of what we've been I mentioned right from the beginning, this is taking a lot longer than it could have done, and I could have just edited out a lot of the trivial information or what may have seemed like trivial information, but I think you would have missed a lot of the learning potential and actually understanding what you're doing. Goal here is that hopefully you can watch something like this, save yourself watching another two or three other tutorial series or courses or whatever the case may be because you're actually getting much more hands on with the systems and fixing things that even exist in the official learning content by Epic. So hopefully, there's value in that. So if you wanted to really start refining your project now, this is where you have probably got the general kind of understanding of what we're going to be doing. We've got our project organized to a pretty rigid and solid place. So if you wanted to start experimenting between topics, this is where you may want to put a little bit of the brakes on. Again, this isn't a race. We're not aiming to get the project finished as quickly as possible. We're aiming to get you leaving, understanding what you're doing, and how to expand upon what you've already learned. So maybe do something simple, even if it's just going into your own code, seeing if you can now comment and describe what your code is doing back to yourself using some good comments or categorize the variables. If you've noticed you've got this long list of variables, and you can see a way to improve that then start adding categories to your variables, simple things to keep your project structured, tidy, easy to work with. Remember, the main goal is sequences are your flow control, making it more readable, like normal scripted code or written code. Good comments, explain the intent rather than just describing the variable. Things like you could just have a comment saying interpolation alpha, but a good comment would be, calculate the frame rate independence values based on interpolation alpha, things like that to kind of give you a descriptor, at a glance of what you're doing, and why. With that done, though, the movement system is production ready. We've got smooth, controlled frame rate independent movement and rotation for the player, which is the heaviest and sort of most complex class we're going to have in the project. Next, we'll be looking at spawning enemies dynamically during gameplay, rather than just placing them manually, which is obviously going to be a little bit restrictive for a full game. 19. 18 - EnemySpawner: Time to look at automating our enemy spawning. We'll be building this in a flexible way from the start. This will work for enemies to begin with, but we're going to future proof it just a little bit, so this will work with things like background objects or anything else that may need to appear over time. No more manually placing enemies, which will be the first important step. So to get started, we're going to go into our Blueprint folder, and we'll just create this inside of a new folder structure. So I'm going to press Control shift and N for a new folder, call this one spawners. Right click, we'll go to Blueprint class, and we want to create our bog standard actor class. As I've mentioned, this is just a container. It has a transform, a place in the world, and then we can drop our logic in here. We'll give this one the name of BP Underscore Spawner base. So as I've mentioned, forward planning a little bit. We're not going to specify that this will be just for enemies, and we may have other spawners that will be a child of this class with inheritance. Inside of this, we just want to go straight to our event graph. There's nothing that we need to visually add to represent this. I think in this case, we can get rid of the actor being an overlap, and most things in here should be responsive, so we shouldn't be doing any checks and updates constantly on the eventi, so we can get rid of both of these function calls. We can drive our core logic from a custom function. I just move this over. We'll create a new function here, and I'll call this one spawn actor. So again, everything that we're doing as we go through and plan is trying to keep this very generic. We're not saying spawn background piece, spawn pickup, spawn enemy. All we need to pass in is an argument of the type of actor that we want to spawn, and we'll probably need maybe some random rotation, an offset, location, and things like that. We can make this nice and generic and make everything very reusable. So always trying to plan ahead. Remember, think lazy, plan program once, and use as many times as possible. Inside of this function, we'll pull from the execution pin, and we actually have a built in function inside of Unreal, named spawn actor from class. When we're using this function, the main thing that we want, as I've mentioned, is a transform, so where we want this to spawn and specifically what type of class we want to spawn in. So at the moment, that could be things like our enemy, but again, that would be too specific. So if we highlight and hover over the purple pin here, we can see that this is just specifically referencing the type of actor. So these purple pins are the actual class references themselves, not an instance in the world. When we have these blue pins, this is an actual instance that exists in the world. We can kind of think of the purple pins as a reference to the location inside of our folder structure. So we're going to make use of this generic typing. We're going to pull from here and we'll actually promote this to a variable. We'll give this one the name of actor type. Make use of this now that we can fill this with any type of actor in our project, whether that is an enemy, a pickup, a background piece when we add them, whatever we wanted, we can now tell this function to spawn that at a given point. The main things that we're going to want to expose are the spawn transform details. So if we write, click on this, we'll split the structure pi. We're going to leave the spawn transform scale to 11 and one. Generally, we try not to override these. If everything's been set up correctly, as with this project, all of the assets should be the correct scale to begin with. So we'll keep this and just assume that the assets are imported and working as intended. You do have the option to randomize or offset the rotation, but I'm going to keep this nice and clean for now, and we can always come back and rework this later. What I'll do instead, I'm going to right click and search for Get actor rotation. The reason for this is this means that we can plug this straight in. And wherever we rotate our spawner, we have full control in the editor, so this is more of an editor tool now, where if I want this to spawn things sideways, I just need to remember to rotate the actual spawner sideways. Alternative would be to expose this to a variable and manually plug in the rotation, but I find this can be quite a nice way to work. It's just a little bit more intuitive for me, as long as it's not too restrictive, which I think, in this case, we should be fine. For the location, we're going to pull from here and we'll add a plus pin. This one may need a little bit of randomization, some variance and offset supplied. If we right click, we can search for G actor location. So same thing again, we're going to use the general properties of wherever the spawner is, so that will be our base starting point. Now for this type of game, because we know that we're only going from a top down perspective, we don't need any randomization on the Z, and we probably want to do all of our spawning off screen, which means we also will not need any randomization on the X axis. So what we could do is we could right click on this second vector pin and split the structure. We'll pull from our Y Pin just here, and we want to use that select node that we've seen previously. We want not select float. We want this one here with the unique icon. This gives us the wildcard that we've used previously. We'll change this to a Boolean, so we're either going to randomize or not, and I'll promote the index here to a new variable, and we'll name this one add random offset. Again, remembering to give that a lowercase B. If we decide to add a random offset, we want to provide some kind of floating value within a range. And again, we've seen how to do this, so we're now just layering upon the functionalities and approaches that we've already taken in the past. So if this is true, if we're adding a random offset, we'll pull from here and we're going to use that random float in range function that we've seen before. And from this, we want just a distance, so a minimum and a maximum sideways distance that we can apply. So if we get our maximum float here, and we'll just promote this one to a variable, and we'll just give this one the name of offset distance. The minimum is as we've seen before, with speeds and things like that, is just going to be the inverse negative, so we can control drag in the offset distance, multiply this by negative one, and then plug the result in here. So if we set a distance of let's say 100 units, and we want that to happen either side from the center of the screen, we'd place this spawning actor in the center of the screen, plug in the value 100 in the offset distance. That means it can either spawn it -100, positive 100 or some value in between. If we decide not to expose the variable and we don't have the random offset enabled, then we're just going to add false. So we're going to add zero to our overall transform, which means the actor will spawn exactly where we've placed it. So relatively simple, but it's still kind of a developer tool, something we can build upon if more complexity is needed, but it gives us some flexibility and variation in our project, which will just generally make things look more interesting and fun to play. Thing I haven't shown yet, and I want to introduce here because we are kind of making a very rudimentary developer. This is something which is really useful inside of Unreal. Other engines do similar things, and that is allowing people to see the variables outside of the class. At the moment, for example, if we grab one of our spawner classes and just drop this into the world, making sure that we've compiled and saved everything, we can see here that the objects selected, none of those variables that we've just created are actually exposed. Wouldn't be able to manually come in on a per instance basis. So if we had three or four of these, we wouldn't be able to pick that this one had some randomization and a unique distance, and this one had no randomization, for example. We currently only have one place we can change that, and that is just here in the graph. So it would either get this boolean and turn it off, and then that's on or off for all of them. And likewise, for the distance. The really useful way that we can override this are these icons here. You may not have touched them just yet, but if we click these on, it provides a small eye icon. By default, they start closed, and we can open the eye. In other languages, this is referred to as making something public. It's exposed to other classes and readable and writable outside of just this class. Out of unreal blueprint, this is just making the variable editable essentially outside of the class here. So if we go into our viewpoint again with the same objects selected, you can see that in the default dropdown, we have access to the type of actor that we want this to spawn. So this is where we could start doing things like this could be an enemy spawner. We're not going to do this, but this could be a player spawner. So we already have some flexibility in what these are going to spawn. And then because the enemies might want to be more random, we could turn this one on to be true to add a random offset in either direction, and we could set that to be, as I've said, maybe 100 units to the left and to the right of the spawner. So nice and simple, as you've seen, it's not a very complex setup, but we already have a relative amount of flexibility and freedom with how we use this. So at the moment, if we were to add this on begin play and get this to be called once, one enemy isn't going to be a whole lot of a challenge for our player. So what we want to do is add some continuous spawning. We're going to avoid using things like delays and loops as this becomes messy and inflexible. So you may have seen something like this before, where on begin play, again, I just want to visualize some of the things that I've seen in plenty of projects. So you may have something like, we'll come in the first time and we'll spawn an actor once. We'll then add a delay. So a delay is just a timed kind of waiting process. We'll wait, let's say, 3 seconds. Before we do the next thing, and then we'll spawn actor again. And you may be tempted to then come into here, and maybe after each time this is done, try and find a way to recall itself. It's going to become messy. You end up seeing people do things like this, we'll loop background, and you end up with this infinite loop where people say that after we've spawned this, we're going to wait another 3 seconds and call the spawn actor again. So we're going to avoid this beginner style code. What we want to use instead is something called timers. These are much cleaner, and it gives us a lot more control over if and when they get called. So, off of our begin play, the execution pin, we want to find a function called I valid. We want this option with the question mark at the bottom. This is checking if another object or actor is currently valid, meaning, does it exist, and is it not being sent to garbage collection for cleaning if it's being destroyed or something like that? From the input object, so the thing we want to check if it exists, we're going to pull from here and use that built in function, the get player pawn. So this is, again, just one of those things to consider. It happens in a lot of new developers games where you've made a game like this, and the enemies have destroyed the player. But even when you're at the game over screen or you're just waiting for the credits or whatever's happening afterwards, you can see the enemy still spawning in or shooting at the player, kind of dog piling on a dead body. It just looks a little bit intense, and it's usually because there's no checking going on midgame to see if the players actually classed as dead or no longer available. What we're going to do is if the player has been removed from play, there's no more need for enemies to spawn in, so we're going to stop. If this is true, so if the player is still valid, we're going to call a function here, which is the set timer by function name. We want this option here, and the first thing that we can fill in is the time here. So this is how long we're going to wait until this function that we'll create in a moment is called. So for the time itself, we can pull from here, and we can search for another random float in range. And a small trick, which can be quite useful when you're not looking to just have the minimum and maximum or something just be the inverse of each other. We can make use of something called a vector two D. So if we create a new variable, we'll name this one spawn intervals. And we're going to drop this down, and we're going to search here rather than a vector, which is a three point floating value, we're going to look for vector, too. We want this one down here, the vector two D. And if we hit Compile, we can see all this is just two floating point values instead, where the X value is going to be our minimum and the Y value will be a maximum. So we could set this to something like a minimum of 3 seconds before something spawns in and a maximum of six. And we can come back and tweak this later. We can control drag this into the graph, right click on structure pin, split the structure pin here, and we can say minimum, maximum. So a nice kind of clean, tidy way to set up the timing function. For the function name, we have our spawn actor over here. One thing I always tend to do, again, partly because I'm lazy and don't want to type things, but also to avoid making spelling errors or typing error. I'm going to press F two to rename this, press Control N C to copy and then control NV over here to paste this in ensuring that we have the same spelling, the same capitalization, avoiding any errors in the name because this is case sensitive and needs to be spelled exactly the same. We're not going to set this to looping, but if you did have a function where you want it to keep going constantly, you could set this to looping, and it would just keep going round and round and essentially calling it. To want a little bit more manual control. I'll make sure that this isn't ticked on. And what we can do is if we grab all of this code, including the is valid, because if you think about this, when we first begin play, there's no reason that the player pawn wouldn't actually be a valid object. It's always going to exist when we press play. So what we're going to do instead is we'll right click on any of these nodes, and we'll collapse this into a function. We'll give this the name of call spawn Aca timer. So we're just kind of obfuscating out the logic a little bit here. So, again, the steps are a little bit more clear and easy to understand as we go through our code. And the way that we actually want to handle this is we're probably going to want to create some enemies straightaway. So rather than spawning based on a timer, we can bypass this first of all. So we'll start by spawning an actor straight in. We'll drop this onto our execution pin. And again, just thinking about the order of logic in our code. So we can always rework things like. We're going to begin play, and we're going to spawn an actor straightaway. What this will do, it will drop in here. We're going to then find pick our actor type if we have one set, pick a location and an offset, if there's an offset to be applied, and select the rotation and spawn that in at a point. And after this, this is where we can start doing an almost automatic looping here. So if we wanted this to keep going, we could take our core spawn actor timer, drop this on just here. And what this will now do is this will go in, and this is more important here because at runtime, this is where something after the first or second spawn, we may have had the player get destroyed. So this one I do our is valid check, a really simple, cheap check to make sure that the player is still in the game. And if they are, we're going to allow it to spawn another actor, so using the spawn actor function, basically, calling this function again after a set period or interval of time. Now, it's something that I quite like doing, and this is completely optional. But because when we're using the set time by function name function, we can't double click and find what this is actually calling, we need to look through our function list and find the name which matches. What I quite like to do is I'll the function over here and just drop this below the set timer by function name. So this means if I'm ever looking through my code in the future, if I get to this point and realizing to fix or change something in this function here, I can just double click this node, and that will take me to what that function is calling. So just a really small tip there, but I find that can be really useful, especially when you're coming back to tweak things if this ended up not being flexible enough, we needed to remove something, just a nice way to navigate around. But that's pretty much everything that we needed. That is a nice kind of self encapsulated, self controlled spawner with some flexibility on the different actor types that we can spawn in, where they're going to spawn and if there's any randomization applied. We could also come down and expose the spawn intervals as well. So we could have each different actor, each different spawner, having different timed intervals on when things should spawn him. So make sure that we compile and save. One thing that I do want to add before we actually go and test this is if we go to the component section, we're going to add something here, I'm going to search for the arrow component. So there's actually a component here we can use which is just a visual arrow. We can change the color if you wanted to change the color for different reasons. But the main thing is, again, at a glance, this will just allow us to know which way is forward, so it's always defaulting to point forward. So if we hit compile and save go back into the main level, we can see, in fact, here, the enemies will be facing the wrong way. So I'm going to get rid of some of these we'll just test with one at a time. Make sure this isn't overlapping with the floor, otherwise the enemies won't be able to move. And then we can just rotate this 180 degrees on the Z. And you can see that that is now pointing towards our player start, which means if we set this to our enemy class, we could give this a random distance to either side, and a spawn intervals fine there. We can press play, and that will spawn one in straightaway. Then within three to 6 seconds, we should see another one spawn in, and there we go. So we have our random spawning actor working correctly. By providing the rotation that I mentioned, we can see exactly where this is going to spawn in which direction you'll be facing. The main issue that we have now is, of course, that we can see them popping in, so we want to move this back a little bit so that they're spawning off screen. They'll do that animation, and then they'll spawn in with that given interval. If you wanted to mit slightly different position, or if you wanted more of them, you could stagger it this way, so we could have multiple spawners. You could provide different offset distances, different spawn intervals, and you could kind of stagger them that way a little bit. Again, to make the gameplay and interaction that much more interesting. I think at the moment, we could probably get away with just one spawner for our enemies. So if we find out or decide there's not quite enough being spawned at runtime, we can come back and add more or play around with these spawn intervals. So another little bit of experimentation or homework you can give yourself. You can do some simple things here like duplicating the spawner and try different configurations, as I've just mentioned. Try different spawn intervals, different offset distances, enable or disable the randomization. And ideally, if you've gone outside of the content so far and created your own different enemy types, you may have enemies that only move forward or backwards or side to side, whatever the case might be. Maybe try adding some different spawners for different enemies just to see how you can really easily make use of this somewhat flexible system. And that's the main thing here. We're looking at and thinking about how we engineer our system. Could have created one actor class specifically for spawning only enemies. We could have came into the class and hard coded the logic here to specifically spawn enemies. We would have then needed to have done the same thing for a different enemy type or the backgrounds when we get to that. Essentially, a lot of duplicate work again, more things to go wrong and more bugs to fix. Whereas with this approach, we can reuse this for the backgrounds later, different enemy types, bosses, whatever you wanted to make use of this for. 20. 19 - ProjectileBase: Going into one of the fun parts now, it's time to add some fire power. This is going to be one projectile class again that will work for the players and the enemies. As with before, we're going to be thinking ahead earlier, making this customizable for the colors, the damage, focusing on that smart design up front, saving on duplication later. So we can leave the BP underscore Sponerbse if you're still here. We're going to go into our blueprints folder. I'm going to go back into the blueprints main folder here. We'll create a new folder, so control shift in N, and we'll call this one projectiles. Now at the moment, there's a good chance that this project will only ever end up with one type of projectile. Like I've mentioned, we're going to try and make this as flexible as possible. But again, in more advanced or flashed light versions of this project, you may want things like home missiles, lasers, shotgun style, shell blasts and things like that. So we may not necessarily always be able to reuse the projectile that we have. So we're still going to assume that we might want to build on this later, even if we don't for this smaller project. So we're going to right clicking here again inside of the new folder. We'll create a new blueprint class. Once again, this will be a simple actor, and we'll give this one the name of BP Underscore projectile. We'll throw the word base on the end just again in case we wanted to expand this a little bit later with similar but slightly unique child versions. Inside of our new actor class, we just want to, first of all, create a new static mesh. So we're going to add a new component, static mesh, and we can drag this directly onto the default scene root. So again, making this the new root component. With the static mesh selected, we're going to go to the right hand side. I provided here the SM underscore projectile, so we'll select this one. And this will be our visual of the projectile. On the left hand side, we go back into the components. We can drop this line again, and we can see here if we search for the word projectile movement. We have our projectile movement component. Unreal has a built in system for this to simplify some of the setup. Now, we won't actually be using this because again, it's nice to be in full control of our own logic. Let me just show you how this works. So if we hit compile and save with this set on, we want to go into the right hand side here in the Details panel with the projectile movement selected. We want to turn the gravity scale to zero. Otherwise we're going to get some fall off over time. And we'll give this the initial movement speed of 300. If we come in, hit compile, we're going to go into our viewpot and we'll just drop this into the level. Now, I'm not going to press play because we want to actually see this moving around. But the way that this basically works is we can hit simulate, and we'll see that fires off at roughly 300 units. So it works. It's nice and simple to set up. But as I mentioned, there's a reason we won't be using this. And that's mainly because the component includes network replication, things that we really don't need. A lot of physics calculations, again, that aren't needed for what we're going to be doing, handling of gravity, which we've explicitly disabled, but it's still there. It's also a little bit inflexible. We wanted to add things like acceleration, over time, homing, all of this is kind of locked into Epix implementation. For the longest time, I think homing didn't work properly. There are some work around you can force on that, and acceleration definitely doesn't work. It's very difficult if you wanted to emphasize speed and make something look a little bit cartoony and go from 100 units of speed and then ramp up to 600 over time, and then ramp back down. It's very difficult to control the speed of these components at runtime. Essentially, if you spawn this in at moving 100 units, that's going to be the acceleration, the value of speed for that projectile until you reset it or destroy it. So for that reason, we're going to get rid of this actor and for that reason alone. Rather show you how we can implement some very similar and much more simplistic logic for our own projectile movement. So just to let you know that I'm aware that this exists, we're going to delete it though. We're going to get rid of that component, and we're essentially going to roll our own custom built version specifically to do what we need in our project. So if we jump on over to the event graph, we're going to do the standard here. We're going to get rid of our Act to begin overlap. And remember what I've said before, we can use venti, and this is one of those instances again, where we want this to move smoothly over time with some simple movement logic. So eventk is where we want this to run. Get interested and wanted to look into the Unreal engine source code, that's even how the projectile movement component is working there. That is running on venti to keep things updated constantly, framework independent and nice and smooth. The logic that we need for this is really simple. So if we pull from our execution pin, we're going to search for the add actor local offset. This ensures that the location of the projectile will be moving forward relative to its own rotation. So this is another really nice thing is that we just need to plug in which way to face when we start spawning it. And by using this functionality, it ensures it will always be moving in the correct direction. We'll split the structure in here, purely because we know that we only want this to move on the X axis, forwards and backwards. But you definitely could come back in and do some sideways movement if you wanted to make it a homing projectile or something like that. All from the Delta X. We'll promote this to a variable so we get a float, and we'll call this one movement speed. Hit compile, and we'll give this a default value of something like 2000 units, or maybe 2,500. We can come back and change that later if that turns out to be too fast. Then, of course, we just need to come in here. We'll take our movement speed. We'll multiply this by our Delta seconds, making as with the planes movement, everything frame rate independent, and then plug that result in here. And that's pretty much it. We have a framerate independent projectile movement, which will move in the direction that it's spawned, making it nice and easy to use for either the player or the enemy, regardless of the way that the initial plane is facing a simple, fast implementation, but it's customizable if we needed to extend on this later. So in the previous topics on Looking at enemies, I mentioned that the projectile would be handling their own collision and damage system, and that's exactly what we'll implement now. So with the static mesh selected, we're going to go down into the collision presets. And we want to change the preset here from block all to overlap or dynamic. So we'll drop this down. We'll find the overlap or dynamic, and we can see what that does to the collision presets. The main reason is that projectile shouldn't physically push things around. That can end up in situations where things look glitchy, where all we really need to do is be aware that they've touched something. And if you start being pushed back around or the enemies start getting moved with projectiles, it can look a little bit strange to the player. If we want to add knock backck as a feature, that's perfectly fine, but we'll do that through code so that we're in full control. Generally, when two things physically overlap with the blocking function, it doesn't look like knock back anyway. It looks like two things are fighting to inhabit the same space, and it's just not pleasant to look at. All we need the projectile to do is detect and hit something, and when that happens, apply damage. No physics simulation or things like that. We'll do the same thing as we've done before. We're going to scroll down to the bottom. We'll find the event section, and we want to add the on component to begin overlap function call here. So when this overlap something else, same as we've done before, we need to find out some information and see if damage will be applied. We'll take the actor here, so the other actor, again, same as before, we'll promote this to a variable, and we'll call this one overlap to actor. And this is because the same reason as with the enemies. We're going to want to check some information against this. We don't want to keep pulling from this pin here, so we'll use a variable that we've stored for reference. So the first thing I think we want to account for is if the thing that we've overlapped is referenced as the thing that's sponda so the owning actor. So, for example, if the enemy fires, and this is classed as an enemy projectile, we don't want it to apply damage to that enemy's plane. And likewise, if the player fires a projectile, this is classed as the layers projectile. We obviously don't want to apply damage to our self. So the first thing we can do is we can get our overlapped actor, and we'll check to see whether this is equal to a specific type of actor again. So if this is equal to, and we can search for another built in tracked variable provided and tracked by the Unreal engine by default, so we may as well make use of it. We're going to search for G owner. So whenever something is spawned into the world, it's immediately given the concept of what is owning it, what spawned it into existence. So we can use this. So we can say that if the thing that we've overlapped is also the owner, then we're just going to skip applying damage. The main reason for this is that we may want to pick our spawn point maybe directly in the middle of the plane. So there's going to be a very brief moment where the projectile that the player spawns is actually inside of the plane for just a moment. If that's the case, then we're just going to ignore the first overlap here, we won't do anything. So we'll pull a branch here, which is our conditional flow, so we'll check if we should do something based on this result. And if that's true, then we can essentially just skip we won't do anything at all. Now, the way that I generally like to read these, though, is I like to do things off of the troop in where possible. This is a personal preference. Again, different programmers will always have different preferences for how they approach things. But I would rather my actual finalized code, the thing that goes ahead to do the main functionality come off true. So what I'm going to do is actually do a slightly different checkout I just wanted to show the more readable way first of all, but with one very small change, we can actually search for isn't equal to. So an exclamation mark and equal will give us a not equal check to the overlap factor. We'll plug this in here, and this now does the inverted check. So this is checking if the thing is the owner, and this is now checking that the thing isn't then. So if we plug this in instead, we'll get this result here. So we're saying if the overlap dactor isn't the owner, and this is how I like to run my code, is that if that's not the owning object, then we can do our damage application. So once again, some of this is really just to show the different way that you can think about your code and the different way that you can have multiple approaches to solve the same problem, even if it just comes down to how the code is read. And this is nice and simple now. We can get our overlap actor again, Control drag this in, pull it from this pin, and we've done this before. We're going to call the apply damage that global universal function. The one that can be applied to any type of actor. So we can pour that into the true pin. If the thing that we hit isn't our owner, then we can apply damage to it. I think by default, I'll give this a default value of 20, so we'll plug 20 in here. We'll promote this to a variable. Again, remember, if you place a value in here before you promote it to a variable, that variable will already have that stored. And the main thing is that we don't want magic numbers. Just going to rename this one to projectile damage. Hit compile and just double check that we should have the number 20 filled in here. Now, ideally, once we've had this done, we may want to start playing some sound effects and particle effects specific to the projectile. I provided some really small kind of sparks rather than a full explosion, just to indicate that something has hit a surface rather than actually causing an explosion. So to do that, I'm going to use the same concept that I mentioned previously. I'm going to press C inside of the event graph with nothing selected. I'm just going to press C to create a new comment, and I'm going to give this a big to do sign here, and then we'll just say something like implement effects. This will be something that we do in our polish pass a bit later. That's fine. Ni this will just remind us to come back here. As I've mentioned, I normally give this a kind of yellow green color just to remind me and really standite against other things that might appear in the graph. But the thing that we definitely want to do is once we've played our effects, we want to make sure that the projectile is removed from play. So we're going to pull from the execution pin, we'll search for the destroy actor and we'll get rid of ourselves. The projectile's done its job so it can go away. And that's pretty much it. So we're going to check what we've overlapped. If it isn't the owner, then we're going to apply damage. Now, this is a really safe call. You may be wondering what if I hit the side bounds or something like a floor that I may place in or a pickup or something like that. Now, if that happens and they don't have the on any damage function that we saw earlier, so we can see here the event any damage. They don't have this implemented inside of their code base, then that's perfectly fine. They'll receive this information, but they don't have a health system or anything to deal with. So it's kind of like a very, very cheap throwaway message. We're not going to get performance issues unless we do something really drastically bad with this, and it's not going to do anything. I won't be destroying the floors or the walls or anything like that. So it's a nice safe function. We don't need to know anything about the other actor. As long as we've touched something, we can find out some generic information about it. And if we think that we may want to try applying damage, then that's perfectly fine. We can just send this message. So the final thing is to make this reusable. That is the damage implemented in a fairly universal way. We want to make sure that our projectiles match the shooter's color, so the player will fire blue projectiles and the enemies will fire green projectiles or whatever you want to customize it to. So we're going to go back up to our begin play function here. We just want to do this once when the projectile is spawned. We want to actually grab our component. We can do some code on the components that we already have, so we'll drag that into the event graph, and we're going to pull from here and search for create dynamic material instance. So we only have this one option here. And this is one of the benefits of using material instances over our master material, the base shader code is with the material instance, we can turn them into a dynamic version, and we can actually update their properties at runtime. We want to take the return value here, so that dynamic version which gets created, we want to promote this to a variable, and we'll call this one material R. So it's just a reference to our material. From this, we can do our code to change some of the core properties. The main thing if you wanted to do more on this is you just need to know the names of the variables that you can work with. And this is really simple. If we grab the static mesh, just very quickly show you some of the other things that you can do once you've learned one I'm about to run through with you. If we double click on the MI underscore projectile, we know that this is our material instance. This will open the asset for us. And basically, anything I've exposed for you over here, remember, I've said that I've created this big, somewhat customizable material. Anything which is named in our section here can be accessed in code. So you can change the specularity at runtime, if you wanted to make things look more glossy and potentially wet. You can change the texture at runtime, the colors, the override colors, emissive property, if you wanted to make it glow. You wanted to do. As long as you know the naming and the specific spelling, you can alter and affect any of these. So the way that we can do something really simple to show this kind of inaction is we want to get our material reference. And again, personal preference, I always get a new node to pull from here, but you could just find the information from this material reference you've just promoted to a variable. I'm going to grab our variable reference here, and from this, I want to find the set vector parameter value. So it's this option here. We see this comes under the material category, so we're looking for the right thing. This will let us set the color of a vector, in this case. So we're going to plug this into the execution pin. The vector that we want to alter is going to be the color, so the emissive color. And again, we can always just cs check here. This is called emissive color, so that's the one I'm looking for. And the new color that I want this to be will be based on whichever actor has spawned this. So if it's green enemy, I might turn this to be green. If it's our player, I turn it to be blue. If you have a red enemy, you may want to turn it to be red. So let's just check that this is working. If we change this to something quite obvious, we'll just make this yellow for now, hit compile, save. We're not going to see this happen until we begin play. So we can drop this into the graph and we can hit simulate here. We should see that begin glowing. Another one problem I think I have here is for the MI underscore projectile. I want to make sure that this is ticked on. But also, I need to make sure this has some emissive property. So I'm going to come in and make sure that the default value for the emissive property here is ten. Because again, we're multiplying it at the moment, any color by zero will make it not glow. So if we press Play again or simulate, in fact, we can see that has now turned yellow. So if I just zoom back in a little bit closer, I'll hit simulate so we can see the whole thing and we have a glowing yellow projectile. So the one change you want to make is open the material and just make sure you have this glowing as much as you want. You can make it a lot more exaggerated, so we can make this a very bright, glowing projectile completely up to you. So I'm going to set that to 100 just so it's very, very visible. And that's pretty much what we do here. So we're grabbing that property by its name, and we're setting it to a new value, nice and simple. Now something else you may want to do is have some glow more than others. So I've just needed to come in manually edit the emissive strength here. So what we could do instead is just to kind of show the way that we can use this system. We can duplicate the material reference, and this time, I'm going to set a scalar value. So the values that we're working with, although they're known as floats, when we're working with materials, unfortunately, they're called scalars. This is because they're made a scalable float. We can change them at runtime. So just try and remember that. It's a slightly new terminology, but the one that you need to learn for working with materials specifically. So we're going to take our scalar value. This one I've called emissive strength. So we're going to plug this in. We're going to find our emissive strength. And then you can change this to whatever value you wanted this to be. So we could turn this to something really silly by default like 1,000. So 1,000 may have been a little bit extreme, but it showed the general concept. I'm going to set this back to 100. I think that looks pretty cool as a somewhat emissive property there for a projectile that moves quite fast. We may need to emphasize that to make it more visible. As with everything, we don't want these to be magic variables again. We would only be able to change this here directly in the code. So we're going to promote our color to a variable that we can track and use later. We're naming this one projectile color for our value. So the floating point value here. We'll promote this to a variable and call this one projectile emissive strength. So that will give us some flexibility when we start spawning these in our parent classes, the enemy or the player. Now, some of you may have realized there's another problem we haven't really touched on yet. And that is that the projectiles can potentially fly in space forever if they don't hit anything at all. I've considered if things hit the floor and the walls, I've said they'll just clean themselves up. Fire off a very cheap throwaway function, which is perfectly fine to apply damage to the wall, which will essentially be ignored, but we haven't considered what happens if they don't hit anything at all. What they'll do at the moment is just fly into eternity, eating up memory. So there are two things that we could do. The first one is if we grab our main property over here, we can actually give any actor that we work with a lifetime. So I'm just going to find this in the details panel. We can search here for lifetime, and we can give this an initial life span. If we know that this definitely won't be on screen for more than, let's say, 10 seconds, then we can give this a ten second lifespan. And after that time, if it doesn't touch anything, if we don't call the destroy function here, it will just be destroyed automatically. The problem here is that we may end up with some projectiles which move slightly slower than others, or we're going to use them and maybe recycle them later, which means we may just hide them and bring them back into play later. And obviously, we don't want our projectiles to just pop off of screen randomly at 10 seconds. So I'm actually not going to use this approach. Another thing you might see is on the eventi, a constant check to see if it's currently out of camera or out of zone. And we're going to do something slightly similar, but I wanted to focus again on good programming practices. As I've said, some things definitely can and should be on Tick, but this isn't one of what we're going to do is we'll create a new function called out of bounds check. And this is just going to be some very, very simple math. So we're going to right click and search for the G actor location, the location of the projectile at its current point. We're going to pull from this node, and we're going to take away another vector. So we're going to subtract a node here, and we can use something called the player camera manager. So if we get player camera manager, we can find the premade always kind of referenced and in Unreal memory anyway, so we may as well make use of these values that already exist for us. And from the player manager, the thing which is currently tracking the current active camera, we can get the camera location. So this will be the current location of the camera that we're looking at. So we can take these two away, and this provides a new vector, a distance for us. From this, we can pull from the vector pin here, and we want to take the vector length. So if we search for length, we can get the vector length. This will convert this into a floating point value for us, essentially giving us a straight line distance. So we know exactly how many units in a straight line our current projectile is away from our camera. This works regardless of direction, which is another really useful thing of this kind of approach, meaning that this will work for projectiles flying in any direction, any angle. And then what we want to do is from our execution pin, we're going to pull from here and we're going to get a branch. Yourself enough room to work with this. And we're basically going to say, if this vector length is greater than or equal to a certain value, then we're going to destroy this actor. So I say more than or equal to to account for, again, if it goes over this value. If that's true, we're going to plug this in here, dy things up a little bit here. And I'm going to give this, I think a value of 3,500 units works. So that's roughly outside of the camera zones. We can be a little bit more scientific and try and test that later. If you find that things are popping off screen too early, then you just want to increase this value, and if things are taking too long, then you can decrease it. But I think that will definitely work. It doesn't matter if they exist for a few seconds more than they should. The main thing is we don't want to see them just magically popping off screen and disappearing. And we don't want hundreds and hundreds of projectiles all existing for minutes at a time. That's more of the problem we're trying to solve. If this is true, then I'm going to pull from here, I'm going to say destroy actor just so that we can clear that projectile out of memory. So nice and simple. As always, no magic numbers, so I'm going to come down and promote this to variable. We'll call this something like out of bounds distance. And again, this makes it nice and simple that we can always just come straight back here. And if we are having problems where we needed to change this, increase it or decrease it, we now have a nice easy to find a place to make that change. At the moment, we haven't called this function, so a very simple problem to make or easy thing to overlook is we can't just make the function. We need to call this as well. We don't want to just call this once. Obviously, we wouldn't want to do this on begin play. And as I've mentioned, we don't want to do it constantly on eventi. So instead, what we're going to do is from the execution pin, after our set scalar parameter here, we're going to once again, make use of timers. Tis are very, very flexible. So we'll search for that same function, which is set timer by function name. The function we want is obviously going to be our out of bounds check. So same again, I'm going to rename this actually rename it but press Control C, Control V to place this in, and we could set this to loop maybe once every second. So remember that if we did this on Tick, this would be checking potentially 120 times every second based on my current frame rate. So that's a little bit overkill. We don't need it to be that precise. Doing this once every second isn't going to add a massive overhead to the game's performance, and it means that we're still checking at a relatively quick iteration or interval for the projectiles that don't need to be on screen. As I've mentioned, having a projectile around for an extra several seconds isn't going to be a potentially having hundreds of projectiles checking 120 times every second, if they should still exist would be more of an issue. We want to make sure that this is called continuously, so more than once, so we will set this to looping. So once every second, this will now be called. We have a looping iteration of this timer, which will keep double checking the location of the projectile against the camera and whether it's technically out of view. So we can go and test this. If we plug one of these in, we'll make maybe a couple of these, and we'll hit simulate just so that we can see that firing off. And you can see one of them there actually hit the enemy plane, so that got destroyed by hitting the plane. But the other one fires off, and after it's a certain distance away, we no longer see it. So if I press the plane from here, the main thing is that we didn't see them just disappear through the camera, that's what we want to avoid. But we know from the previous tests that they are being wiped out when we're testing this in simulation mode. Of course, they're flying off and they're popping out outside of the camera view. So that's a nice kind of clean way to do this. It's not going to provide a big overhead. This definitely wouldn't cause any performance issues. It's a relatively simple and lightweight calculation here. I think it's much safer than using lifetime because we don't know how long or how fast all projectiles are going to move and how long they might be on screen. And this is cheap enough to run this calculation that it won't provide any performance issues. And I wanted to demonstrate just a simple comparison of where we might use tick and where we avoid using Tick. Remember, when we're playing with these things, always come back in and delete the actors that you may not be using anymore. We don't want to keep those around. We're going to spawn those in from some fire functionality a little bit later. So once again, if you are still playing around these things, feel free to try different speeds, different colors, see if you want to affect any of the other properties in the material instance. See if you can figure out how to change some of the other scalar values or vector parameters. Again, the main thing is you just need their name and the exact spelling, and you'll be perfectly fine. You can change the values as you see fit. Another cool challenge. No you're hopefully getting more comfortable with a little bit of the program. To add acceleration overtime with maybe some type of interpolation. You can see those approaches from the other code that we have. And it's one of the nice things as I mentioned about rolling our own code. It's just something that is almost impossible to do with the projectile movement component. So if you wanted it to go from a slower speed and speed up over time or in reverse, you could start it flying really fast and then slow down losing momentum. You could also have look at things like homing projectiles using similar techniques, interplating towards a specific goal overtime. I've mentioned, I would recommend keeping the current class as it is. If you're making these tests and trying some of this experimentation. I'd always recommend duplicating what you have and trying to roll your own version from that. It's a great way to learn though. I don't want to stop you from trying new things, but, of course, in the future topics, we're going to be using these projectiles exactly as they are. So I'd recommend having that version as well so that we can complete the project together. 21. 20 - ProjectileFire Enemy: To get things actually firing their projectiles. We're going to do this again, nice and tidy. One fire function in the base class customized by the children if needed. Same pattern that we've been using before, so we're going to end up with the same functionality as our example project that we're working to somewhat recreate, but we're taking a slightly different approach. So first, just a reminder, if you did have any of the projectiles lying around, I'd recommend deleting those. Before we get started, we'll be spawning all of these in dynamically at runtime when needed. We're going to get started in our base plane class. So if we go to the core plane base and we'll do our initial implementation just here, we'll create a function in here named fire. We'll do a very similar thing to what we've done with our enemy spawner class. We're going to pull from the execution pin and search for the spawn actor from class. We can once again, we'll pull from here and we'll promote this to a variable. And in case we did want to change this later at the moment, I think we're just going to have a single projectile class. But in case we did want different projectiles, we'll promote this to make this reusable. We'll rename this one to projectile class. Leave this as the default actor type hit compile, and that one's fine. Now, when we do hit compile, we get this error saying that we need to provide some extra information. We need the transponPin. We can do two things. We can split the structure pin. And if you wanted to leave this completely empty, we can hit compile and that will make the error go away. Now, I'm going to press Control in Dead. There's another thing that we can do here. For the spawn location, we need to know exactly where we want our projectiles to come into existence. And one way we can do this and make this somewhat visual is adding a new component. I'm going to go into the viewpot. I'm going to add a new component, and I'll select the scene component just here. One thing I should have done is had this already selecting the mesh, but that's perfectly fine. I want these scene components to always follow whatever's happening with the mesh. So I'm just going to grab this and drop this again focusing on hierarchy so that the new scene component is a child of the static mesh. To make this clear, if you really wanted to, we could rename this, and I'll call this one project our spawn location. So when we're looking back through our hierarchy, we have a rap idea on what these different components are responsible for. Now, we don't need this to be visual, but what we can do with the use of this is we can now move this around and have direct control of where we want the projectile to spawn. We could have that slightly in front of the plane. Well, like I've mentioned, we may want this to be just slightly inside of the plane, so it doesn't look as though it's spawning magically from nowhere. When you're happy with the position of your projectile spawn location, again, if we hit compile, we'll actually get this bad blueprint result in the viewpot but that's perfectly fine. We just want to go back into our fire function. We're going to control drag the projectile spawn location into the ground. I'm going to pull from here and I'm going to search for something called get world transform. So we want this option just here with the green function? Get World transform is going to provide the transform of this specific component in the world relative to the rotation and everything that's happened. So we can ensure that the projectile will be facing forward. We'll plug that in, we'll hit Compile, and we can see the issues are now gone away. I just wanted to show another thing, and I think it might actually be worthwhile changing this here from an actor to specifically the projectile based class. If we just go back into our projectile class here, we'll double click to open the base class and just ensure that we have two things exposed here. So we want the projectile color to be exposed, the projectile emissive strength, and maybe the projectile movement speed could be useful as well. Making sure that these three are public. You can come back and again, I'm trying to lay the foundation for you. You can come back and once you've seen what we're about to do, make any other changes that you wanted. Are completely optional, but I think these three will be good for testing. And if we hit compile and save, we'll go back into our plane base class and into the fire function. Now, by default, because this is set to a generic actor, there's no exposed data. Now what we can do by making use of specific typing is we can search for our BP underscore projectile. We want to use the base option, we need to drop this down and select the class reference type, not the object type. This will just tell us that some things are going to need to change. We might have some essentially broken references or links to different class types, and that's perfectly fine. We'll say change that variable. We'll let this do its thing. It's really not going to break anything because we're not using anything specific at this point anyway. We only had a generic actor. So we can close that. And what I wanted to show is that if we right click on the spawn actor and choose the refresh node option here or make sure that we hit compile first, what we should be seeing are those properties that we've just exposed, and I've just realized what the problem is. There's one other step which is really easy to overlook. And we're going to go back into the projectile base. As well as making these public, there's another tick box that we need. So just some of that stuff you need to know about Unreal, and you'll pick this up as you're going. So for the movement speed and the other two that we have exposed, we also want to choose this option here, expose on spawn in the details panel. So we need to do this for each of them, so grab your variable and then tick on the expose on span. I would suggest it just exposes this property whenever we spawn this class somewhere. It's also really important we need to make sure that we compile and save in the projectile class. Otherwise, if we go back into our plane base, we're not going to see this. And you can see now the main reason that we wanted this is by specifying that we're specifically going to be using the BP underscore projectile base rather than a generic actor. We now have these properties exposed, which is really useful. This gives us the power to change the color based on the class that we'll be spawning this in, as well as the movement speed, the emissive strength, and things like that, depending on how you wanted to. With your projectile classes. Now, this is all done once in the base class. We want this to be easily reusable without needing to dive into the code. So, of course, we're not going to leave these magic numbers. Instead, we're going to promote each of these to a variable. We'll promote the first one, and we'll call this one projectile movement speed. And then we'll of course, do the same thing. We're going to promote projectile color and projectile emissive strength to their own properties. And that's all good to go. That means that we can now have this one reusable function in both player plane and the player enemy, and all we need to tweak on a per class basis is how fast we want the projectiles to move. Because I remember in the example project, the enemy is fired slower than the player, and we can change the color depending on who is spawning the projectile. It means we don't need loads of different types of specific projectiles. We can just take our projectile base, and we can make some really simple rudimentary changes on a per plane basis. The one really important thing before we leave here, remember that functionality on the collision that we're doing where we're saying here, if we are overlapping at the moment, our own owner, then we don't want to apply damage to any. We need to make sure that the thing spawning the class, which we're now doing in the plane base is actually passing along the information of who the owner is. So one other thing that we can drop down here in the spawn actor in the plane base is the owner just here. So, nice and simple, we can pull from this pin, and we're going to search for G reference to sell. Or you can just type the word cell and you'll see get reference to self here. So this is now just ensuring that whichever plane spawns this projectile when this function is called, it's tagging itself as the owner of the projectile, ensuring that it won't cause damage to itself. So nice and clean, we can hit and pile, save, and that one's ready to go. So we're now at a good point where we can add the more simplistic version of firing, which is going to be our enemy class. Before doing that, I think there's one more variable that both classes will be able to make use of, and that is the fire rate, how often the plane is going to be able to fire. So we'll create a new variable down here. The first one is going to be of type of flight, so press Control ID on any of the other flights, and I'll call this one fire rate. It compile. I think just to make sure that we don't forget to set this later, I'm just going to set this down to zero, and we'll make sure that we modify this on a per plane basis. And then the other thing which I think will be quite useful in both of our classes later will be a vector two D for the minimum and maximum fire rate. So we have some randomization. So I'll create a new variable. We want this to be a vector two D again. But for now, we'll just call this one min max fire rate. We want that vector two D we had previously, and again, we can leave these at zero for now. We'll come back and modify these later. And this is one of those cases where we're getting a lot of similar variables for one system. So we may want to put these into a category, specifically for projectiles or combats or whatever you may want it to be. We could also have a health category, something like that. As I mentioned, I won't keep doing this on screen, as it's just some busy work in the background, but it could definitely help as we get many more variables as we flash the classes out individually. This is fine, though. This is good for a base working point, so we can now go into our enemy class, so we're going into core. Play enemy, and we want to create a sequence on our begin play. The reason for this is we're now getting some kind of muddy logic. We're doing a few different things on the initialization of our class, which is perfectly fine, but we can just keep things tidy again. So a quick trick here is we can pull off of the execution pin after the parent call and in between what we're already doing, and we'll call a sequence node here. And this will automatically hook up the logic that we have without breaking any of the so then zero is essentially our initialization for generic randomization. So we could have a comment saying, randomize values here. Then the next thing that we're going to want to do is set up our firing details. So, in fact, this is still going to be based on some level of randomization. So I'm going to right click, and I'm going to get the fire rate property. So we want to set the fire rate here, and this will be once again set on a random float in range. We'll plug this in, and that random float is, of course, going to be the minmax that we've just created. So we'll get the min max fire rate, split the structure pin like we've done before, and plug these in here. So minimum, maximum for the X and the Y. So another nice simple randomization so that every enemy isn't firing at exactly the same rate, and it's going to look very repetitive and dull, otherwise. If we hit compile, we just want to make sure, as well, that for the enemy class, we set a Minimax fire rate, which I think maybe somewhere between 0.4 and 0.8. Again, you can tweak all of these values as you like, and then the fire rate will be set specifically between that range. Then from the second output and now we've done our randomization, we can actually start setting up the fire functionality to be called. So we're going to make use of the set timer by function name again, we'll pull from here. We're going to use the set timer by function name. Of course, we're going to call the fire function. That we've created in our base class, and the time is going to be our fire rate. So we're going to get the fire rate that we've just randomized, and we'll make sure that this is set to looping. So this will keep being called for as long as the enemy is in play. The randomization ensures that each enemy fires at its own randomized rate. Some will be a bit more aggressive and some more leisurely. Another thing for the enemies is that when they fly off screen, if the player misses them, like with the projectiles, they should also be cleaning themselves up. So we're going to go into the projectile class, and I'm actually going to take the exact out of bounds check logic here. We can copy all of this, press control NC, go back into the enemy class. We'll create a new function. Again, we're going to just call this one out of bounds check. And we just paste this into the graph here. So a little bit of duplication. And again, this is where you could start considering things like the use of components would be a valuable thing here. You may see when you're getting more into programming the concept often referred to inheritance versus composition, making use of components like this. A little bit out of scope for this really introductory system. So some of the things we will be taking the slight shortcut, but this still isn't a terrible approach to doing this, just a tiny bit of duplication. But the out of band check variable here that we've copied from the other class obviously it doesn't exist here, so we're going to right click and we're going to create a variable. And this one we just hit Compile. We're going to check the enemy's location from the camera. For the out of bounds distance, we can use a similar value. I think 3,500 was working pretty well. We just want to make sure that we're keeping an eye on enemies that leave the play area, and if we notice them just popping out of existence, we may want to tweak this in some way. Now with that ready to go, we want to go back into our event graph. We might want to start tiding things up a little bit and just moving things around to give ourselves space. The main thing here, we could set up another time here, so this could be our logic to set up the reoccurring functions. So we'll create another TO by function name for this one. This is going to be our out of bounds check, and we might want to do this once every second. So we'll set a time here but once every second. We'll promote this to variable. And I'll set this one to be called out of bounds check duration. Like before, we'll hit Compile. We want to make sure we give this a value, so that's got the 1 second, and we'll set this to looping, so it's going continuously until it gets removed from play. Like I've done in the past, as well. I think I'm going to write click here and find the fire function. So we're just going to callFirefunction. We're not actually going to use it. It's just so that, again, if we wanted to double check what's happening inside of this function, we can just double click here. I'm going to get the out of bounds check, get the name, and make sure that we fill this in over here in our time and then just drag this in same reason again. Just in case I want to see what's happening, I can just double click in and see the code exactly. As I've mentioned, just a personal preference, but something I always tend to do in my own code. One final thing I think we've forgotten to do is because we had a little bit of a swapping of projectile classes, if we go back to the base class where we implemented the fire function, if we grab our projectile class here, we can see that the class is set to none. So if we tried this now, we wouldn't be getting any of the enemies actually firing anything because they're not given the specific type of class to fire. So at the moment, we only have one, which is the base class. But if we made some child classes from this, we could use those unique versions as well. So we sent this to be a default of projectile base because at the moment, it's the only thing we have to work with. Hit compile and save. And then in the enemy, what you'll see is this will automatically propagate dine. So we have the projectile class set here as well. Hit compile and save, and we can nest the enemy firing. So nothing super fancy, but they are indeed. Firing and we can see we get that a different rates, different speeds. So we have some slightly sort of unique gameplay from the different enemies. We should as well see that after a certain number of hits, the player should be removed from play, so the damage is working automatically. So we have everything kind of working as intended, at least for the enemy class, which is great. The final thing I notice there, that's probably going to be a little bit fast, and that's fine. This is why we've set all of these values to be exposed so they're easy to tweak and change based on play testing, feedback, and the general game field that you might be aiming for. So I think in the enemy, we're going to slow their projectiles down just a little bit because there's going to be a lot of them. The player probably wants to fire faster, and the enemies can be a little bit more relaxed. So we'll set this to something like 1,500. And if we feel this is too slow. So I'm just selecting up here in the enemy class, grabbing the projectile movement speed and setting this down to 1,500. We can also change the color. So my enemies are green. Going to get a color, which is a little bit closer to the enemy style here. You should be able to use the color picker so we can go into the color, get the color picker, and you can click on the part of the plane that you wanted to match the color too. For me, this isn't working. This is selecting the static match. So I'm just going to grab a color which is roughly similar to the plane body. That would be perfectly fine, I think. So now, again, if we hit compile, we have projectiles moving at different speeds, and they're actually representing the color of the ship, which is firing them. You can also change things like emissive strength if you didn't want them to glow or if you wanted them to glow more or less. But that's it. So that is the enemies set up and done. Just a small side task for you, pause the content again between the topics, and maybe start looking at different variables that you think might work for the minimum and maximum fire rate. Maybe you're not feeling enough difference between the different enemies as they spawn. Maybe change the color, the different types of movement speed, the damage provided, expose other variables, play around with all of the values that you now know that you have access to. Next, we'll be taking a look at the player fire functionality. It's going to be slightly different, a little bit more hands on, but you can see we already have a really good foundation to build upon. 22. 21 - ProjectileFire Player: Can now move straight into getting the player firing in a similar way to the enemy. So we're going to reuse some of that base logic, the exposed projectile properties, and have a little bit more refined control over if and when the player should be firing. So if we go in and open up the BP underscore plane player, we've got two different options here. Now, one is that a lot of people might just drop the core functionality outside of functions into their main event graph. Something else I wanted to introduce, though is that we can make our own custom graphs. So the default event graph comes with things like the begin play, the eventi, those things built in to the engine by default. Perfectly fine. One thing I quite like doing is we can press this plus button here and we can create an entire new graph. I'm going to give the name of this new graph input. And this will be where we put all of our logic specifically based on receiving input for the player. So again, we started adding things later, like a restart button or a menu button or different types of power ups and things, all of those inputs could be tracked here in one nice convenient place. To get the fire input registered, we're going to finally make use of an input event. So we've previously just been using the variables which are stored in things like the IA underscore in our handle movement we've seen over here. So this is our example of using just the float value returned. Inside of the input graph, we're going to right click and we're going to search for IA underscore Fire. And we see we have the option here, we actually want to make use of this event which is returned. So whenever the fire button is pressed, we can have something happen off of that triggered result of the input being received. Now, the first thing we always want to consider is we want that immediate feedback, something which is more of the game design and theory type side of things, is that when the player presses something, they have this expectation of seeing immediate feedback. There's no lag in between them pressing something and something happening. So for that reason, as soon as the input is received, we want to make sure that something begins happening. So we have a bunch of different events which get fired here, and we want to make use first of all of started. So we want to make sure that the fire function is called immediately on Start. We're going to call here the fire function from our parent class. We can see this is the base class. Started is called immediately as soon as that button press is recognized and then completed would be fired off as soon as the button is released. So we can make use of these different stages of the button press, but the important thing for now is that fire is called immediately. Then after that first firing function has been called, we're going to pull from the execution pin, and we're going to make another set timer by function name. The function, of course, is going to be fire. Mainly just out of habit here. I'm just going to duplicate the fire function we already have, and again, I'll just drop that below the timer because we know that's the one that we're using here. The amount of time between, we're going to pull from here and I'm going to find our fire rate. So we're going to get the fire rate value. And I think for the player, although I said, we may want to look at randomization, this is another one of those things now when thinking about things from more of a design principle. We probably want to set this rather than having a minimum and maximum, the player's going to have an expectation that every time they fire and every time they play the game, their fire rate should be the same. Unless you're making something like a roguec where you're specifically telling the player that based on your character or random stats, the fire rate may be different. In this type of game, they're going to have an expectation that it always feels the same so they can get used to the mechanics and learn how to play. So if we hit compile, we'll grab the fire rate, and we're going to set this to 0.2. We'll make sure that we set this to be looping. So essentially, what we're saying is we'll press fire, and then once you're holding the button, we're going to call this function so that when you're holding, you'll keep firing. Then we can go back over here and we can now make use of this completed function. So as I mentioned, this is called when you release the button that you are holding. So from completed, we can actually make use of another part of the built in time of functionality, and this is a really nice way of keeping your code again clean and tidy. You may have seen in other examples this thrown onto the eventic with some delays, which can be very confusing if you think about running an eventic and a delay together, essentially combining logic, which is meant to run constantly at a given interval, and then also trying to delay that whilst running it constantly. So we're going to avoid sloppy code like that. We're going to do instead is we're going to find a function named clear Tir by function name. So we'll use this function here, and this is really nice and simple. We're going to grab our fire name here, so we'll grab the text here, and then we'll paste that in here. So we now have complete control over when this has started and when this ends. So the first time that we press the fire button, we're going to call the fire function. We're then also going to make sure that we keep recalling this for as long as we're holding the button. But if we ever want this to stop, as soon as we recognize the button has been released, we're going to clear this function name, so this timer function. So it's going to find this because they share the same name. It's going to cancel the looping here, and it means that we won't be firing anymore. So we don't have wires going everywhere, things trying to cancel out on the event tick. It's just a single fire event, and we're only adding a stack of every 0.2 seconds. Call a really simple function. So again, very low overhead, very good for performance, very clean to read, and much easier to manage. And really for the player, that only leaves a couple of things here. So the firing functionality is relatively simple. We just want to go into the class details here and make some similar changes to what we've done before. So the projectile movement speed, I think, 2,500 for the player is fine. Leave the emissive is perfectly fine. And we're just going to change the color. My players are kind of bluish color, so we're going to go. With that just here. Maybe try and match that a little bit more. As I mentioned, on your system, you may be able to get away with the color picker. But for me, that's just selecting black. So I'm just going to set this in a more manual way. We can then hit compile, save, and then we can go in and play test this. There you go. So the player has continuous feedback on the button press. This is looping continually until we release the fire button. So as I mentioned, really nice and clean. If you haven't seen it before, I'm not going to find specific tutorials to call them out, but have a look on YouTube before something like Unreal engine, blueprint fire functionality or gunfire logic, something generic like that and see the result you get. Have a lookout for those specifically, which will bring you into the eventi, and have some delays thrown off based on a fire rate and see the way they'll use things like gates to cancel out of calling the fire function when a button is either pressed or released. And then compare that to what we've just done here with two really simple calls to start the function and end the function overtime. Again, as I mentioned, with the epic examples and stuff like that, this is never to call out other people or projects. Explicitly. Mcde is definitely not always the best, but it's just to get you thinking and actually critiquing what you're seeing as you're learning because one of the worst things that you can do is just follow everything blindly and just assume that it's always the best or only approach. There will always be different ways to approach a problem, and I just want to get you thinking about whether what you're doing in your projects or what you're seeing online is a way that you want to implement into your own projects to make them manageable and easy to work with. That's pretty much it for projectiles. Again, just make sure that you double checking everything has the precise values filled if you wanted those extra steps that you could take into improving your project. I'm going to leave this on parity with the example project, but we do have problems that may not be readily noticeable, but they're definitely there. For example, we can actually spam click the fire button faster than the fire rate. It's really hard to do because I've set the fire rate relatively quick anyway, but you can see I'm clicking here faster than if I would just hold. So maybe consider how you might fix that yourself. I'm not going to go through that process. It would essentially be a case of tracking a value on maybe a separate timer when you first press the button. The next time that you press the button if that is lower than whatever the set fire rate is, then you would just cancel of calling the fire function. In this case, it's not such a complex project that I really think that makes a big difference. And as I mentioned, this same functionality is in the example project we're trying to hit parity with anyway. But it's just a nice kind of thought practice and see if you can think about how you would incorporate similar logic, but stopping the player from clicking and firing faster than the given fire rate. Our main next steps, though, we have the ships are armed. The player and the enemies are both firing, both using different colors, different fire rates. You can tweak those values again as well. If you're finding that the game is too hard or too easy, change things like the damage, which is applied based on where the projectiles fired from, the fire rates, things like that. Next, we're going to go back to making use of our generic Actor spawner. And I'm going to start adding in some scrolling backgrounds to make things look us that a little bit more interesting. 23. 22 - ScrollingBackgronds: It's time to take this project a little bit further and actually make it look like a game. We're going to add some water, some scrolling islands, and we'll do this by reusing our Spanus system, which is why we've built that to have such a generic setup. We can close out of any of the classes we have at the moment. Right click on the main tab and just select to close other tabs, and that will get rid of everything that you're not currently in, and then we can just close the player. Back in the main view boot, we want to use the Add button just here, and we're going to add a simple plane. So if we go to shapes, and we can find a plane, we just want this to be a flat surface. We're going to reset the location, so we'll set this to zero by clicking the undo button just here. Press the lock button to make sure that this scales uniformly, and then we're just going to set the value to something like 80 to ensure that we cover the entire play zone, so everything that we can see. We can also get rid of the floor. We're previously using that just for context so that we can see exactly where and when we're moving. We're using that grid essentially to detect movement. We won't need that anymore, so we can get rid of the actual location of the plane itself. We want this to be negative 4,000 on the z. And again, this is just based on how I've got my camera set up, the plane and enemy locations. You may find your level has a slightly different setup, but if we're going from a roughly similar project for now, you can tweak this later. Having this far down keeps the shadows a little bit more subtle and gives us room for the floating islands because some of the objects I've provided are intentionally quite big. From the folder, we're going to go to our assets folder. We'll find the materials, and we can find the MI underscore water and just drag this onto the plane. So this will give us a nice, simple, basic animated water material. As I mentioned, some of the details and properties may need to be changed here, like the size of the plane or the location. We do a full kind of sweep and refine once we got to the end of the topic based on how things are looking, though. If you wanted to change up your water, again, we can double click the MI underscore water material. Drag this into the window here. This is a nice way to once again, make use of the material instance. If you find that flowing water was too fast or you didn't like the color, we can get that live feedback even whilst we're playing to see exactly what you wanted to do with things like the depth fade, the distance, the different color properties and things like that. So you have full control over how you want your water to look. This I'm going to keep this as it is, but just to say that these values are here if you wanted to tweak them. Next, we want to consider our islands. So we're going to go back into our Blueprints folder. We'll create a new folder in here. We'll name this one Islands. Inside of the islands folder, we're going to Right click, go to Blueprint class, create a new actor. We just need something with a presence in the world again, and we'll call this one BP Underscore Island. Inside of the class, very standard setup now. We're going to add a new static mesh. We'll set this as the default scene root, so we'll override the default scene root here. And with the static mesh selected, we're going to go to the details panel. We'll find the SM underscore island round, so the large one to get started with. We can press F in the viewpoint, so we can see what that island would look like. And then we just want to go down to the collision properties, and we're going to take the collision details here. We're going to drop this down, set this to no collision. We'll turn off the generate overlap events. Just making sure this has no physics, it has no overlap checks. This is going to be a very cheap throwaway object, which is just there for visuals. Now, before we even get into using our spawner to bring these into the world, we know that this is going to look very repetitive, very boring. We're going to get the same static mesh at the same size, the same rotation every time that one is spawned. Let's move to the begin play, and we'll solve that straightaway. Likaways, we're going to get rid of the Actor begin overlap function call. We want the begin play here, and we can do some simple things from the execution pin here. So first of all, we can randomize the scale of the actor a little bit. So we'll call the set Actor scale three D. And we're going to do this a slightly weird way. But if we right click and search for random float in range, we can set that range once again based on a vector two D. So we'll create a new variable. We'll call this one Min Max scale. Drop this down, find vector two D. It compile and provide some values here for maybe something like 0.8 to 1.2. Drop these in as we've done before. So you can see, again we're getting to the point where a lot of code is going to become very familiar. And this is that idea of repetition. We'll get you more comfortable with the project and working with Unreal. So we'll split the structure in. We'll plug that into the minimum and the maximum. And we can actually just take our float value here and convert that to a vector. The way that this work, it may look a little bit weird how we're using a float or a vector. Basically just going to ensure that the X, the Y, and the z take whatever this result is. So if this picks 1.1, it means that the X Y and Z will all be set to 1.1. Now if you wanted to stretch things out, have more refined control. Of course, you know that you can split the structure pin for the vector and maybe do a randomized float on the X, the Y, and the z individually. Problem I find with that is that you don't really have much refined control over stopping the mesh from being completely. You may end up with something like really weird and squashed or really odd and stretched. So for that reason, I'm just going to set this to be uniform, so it's just scaling all of the axes together. Then we can also grab our static mesh. We'll drop this into the event graph, and we're going to pull from our mesh reference here, so a reference to our component, we're going to search for the set static mesh function. We want this one here, the one which will give us the mesh component, just to make sure you saw that we want to go to components static mesh and set static mesh. Not this one here. This is actually updating the variable itself, whereas this is taking the component we have and it's accessing the details through our details panel here. So we want to maybe randomize the static mesh that we use, as well. So we can pull from here. You've probably got an idea where this is going because again, we've done this plenty of times before. We only have two different statics options for the islands. So I'm going to use a select node. And because we only have two, the easiest way I can see this is if we had more, we might want to use some random integer within a range, and if we had five, we would pick a range 0-4 or one, five, whatever. And depending on what was picked between that integer range, it would pick one of four or five different models. Now, because we only have two, I'm just going to use a boolean. I'm going to pick a random ball here. We don't even need this to be a variable. And then we basically have a 50 50 chance of spawning in the large island or the small island. So, nice and simple, we don't need to make this too complex, at least we're now getting some randomization. We might spawn the same big island three times in a row, but at least the scale will be slightly different on each one. If not, then we may be randomizing between the big and the small island. And again, they're still going to have some nice randomized scaling applied. And then finally, they're all still going to be looking the same way. So we can grab our rotation details, and we can randomize that as well. So we'll pull from the static mesh, and I'm going to find the set active rotation. This one, we only want to rotate around the z axis because remember, that's going to be up or down in the world. So we're going to right click and split the structure in. I'm actually just going to be a little bit lazy and grab the random float in range. Duplicate that over here, we're just going to rotate this 0-360 degrees. So this is one of those times I don't think there's any value in actually promoting this to a variable. I think that's pretty clear. When we look at something, we're setting the actors rotation, and we're going 0-360, so that's obviously we're allowing for a complete rotation. So I wouldn't actually recommend promoting this to a variable. Some things can be fine as a magic number, especially since we know we're not going to be updating or tweaking this. There's no reason that I can see that we're going to want to clamp that to a different. Was promoting this to a variable means that we don't need to come back into the code. And if we wanted to completely change the scale without finding the code itself, we could set this to 0.4 or 2.5 or whatever we might want this to be to make really exaggerate the scaling. I'm not going to do that. I'll keep that as 0.8 and 1.2. So just those three changes, we're going to have something which is now looking a lot more kind of random and a little bit more unique than just having the same two objects popping up exactly the same scale and rotation when they're brought into play. Now we can move on to the movement. So this is why we've kept the eventic. Again, we want a nice, consistent, smooth movement, and with some really simple logic, this isn't going to become too expensive to run. We'll pull from the execution pin, and we'll find the ad actor world offset. We want this one here? Add Actor world offset for the entire actor of the island. Give ourselves some room because we need to do some calculation here for the offset. We'll create a new variable. I'm going to promote the Delta seconds again just to be lazy here. Promote this to variable so we get the correct type, and I'll call this one movement speed. Hit Compile, and we set this to a default of something like 400 units of speed, but we're going to want to make sure we give this a negative value, so negative 400 to ensure that this is scrolling down the screen. We can get rid of our movement speed. We just want to take our movement speed multiplied by Delta seconds. And we'll plug this into our value here. I've just realized looking at this. We don't want to add a world offset here, so we'll delete this. We actually want to add the local offset. So we'll use the add actor local offset. Split the structure p for the Delta, and then we'll plug that into our Delta X location because again, we're only moving this on the X forwarding backward across the screen. We don't need to calculate with any physics or anything like that, or try and make any pseudo physics over time. So we're not using the world offset. We can use the local offset. I'll take where the actor currently is, and it'll just add this value to that X direction. In fact, the reason that came to mind is if we go back into the shoot 'em up tutorial, we'll go to our projectiles and open a projectile class. We can see the event here. This is actually the same logic that we're using to move our projectiles. The island is essentially just a very slow, very big projectile in the way that we have our code. Again, this is actually something which is making use of the same code. It's just something to keep in mind that we are doing a little bit of duplication again, and this could be in a larger project where you start looking at things like composition where we could have a custom movement component, which because it's so similar and all we really need is a direction or a speed, we can have those values exposed in a custom component, and we could drop those onto the different actors that need them. As I've mentioned, slightly out of scope for the content that we're going through at the moment, but just something to consider. This isn't the end of the world, so we're going to keep this as it is. And now we can actually start spawning these in. So this is where the simple generic actor class, the spawner class that we've created is going to become quite useful. So if we go into our level, first, I just want to very quickly test how the islands are going to look, so I'll grab one of the islands and drop this onto that water plane at the bottom. We want to get a rough idea of the height that we want to spawn this. Again, this will come down to a little bit of tweaking. I'll do some of that now, but I'll probably refine this offscreen. So I think, actually the first time I dropped one of these in, it was, actually, the same as the plane itself, so -4,000. So we can go with that as a baseline. We'll probably set the spawner to the same location. We want to make sure that this is further back out of camera. So this is something we can now test easily. We're going to grab our camera actor. We will pin this here, and we can start actually tweaking and making sure that everything spawns off screen. So for the big island here, plus maybe some accounting for we can up to a scale of 1.2, remember, is our randomized scaling. So if we get the biggest island spondm, we need it to be at least at the point of 11,360 on the X to be completely out of view when there is spondu. So that's our essential point that we want our island spawner to be placed in. So, in fact, I'm going to grab this here, turn that zero on the y, so we're going to start in the middle. What I'm going to do is with the selected, I'm going to come back into the blueprint folder here. I'm going to go to the spawners folder. Select this. And with this selected in the content draw, just to handy tip you're about to see here. We can right click on this island. We'll go down to replace Actor, and we can replace that with the thing that we currently have selected inside of our content drawer. So we're going to rename this. This object, we can see is now a BP underscore spaner. I'm going to rename this one to Island spawner. So that's clear what that's doing. And in fact, when it comes to naming, this is going to start getting a little bit sloppy as we. What I should actually have done is we'll call this spawner underscore island because we can see that this alphabetizes things. So we can also then rename this one to spawner underscore enemy. So it's now a little bit clear that this is our enemy spawner. This is our island spawner, and they're going to be clumped together. Now, another thing we can see as a problem is the static mesh is just the wrong size. I think I may have been considering a different asset I was working with before. So we just want to maybe increase this to something like 120 on all of the axes, maybe even 160. Perfectly fine. The main thing this is covering the entire camera. So we can see now in play mode, we have the water covering everything. The spawner will be just outside of the camera so we can ensure that the islands spawn off screen. Now, one really important thing I was about to completely miss is if we go back to the spawner island, when I was tweaking this, I set the scale to 1.2. Now we always want to make sure that we keep these uniform because in some of the spawning code, you may be taking in the actor's location rotation in scale as the point at which to spawn. So we don't want to add any default, kind of like preset offset to the scaling of anything, so just make sure that we turn that back to one. Then to get this working for the island spawning, we're going to want to drop this in and select our island class. So we want the BP underscore island. We're going to allow a random offset, so we'll tick this. We'll provide an offset distance of, let's say, 5,000 units either side. So remember, this is just using the built in functionality we've already implemented when we were planning ahead a little bit when creating our generic spawner class. And in the spawn intervals, we don't want too many islands cluttering up the scene. So we'll set this to something like 5 seconds and every 10 seconds. So 5-10 seconds, we should get a new island. I just have a very quick recap of the the reason we're using the offset here, remember, we've created in our spawner base, we have our branch on the spawn begun or a Boolean check here, sorry, on whether we should allow some random offset. If we say no, then we would just spawn the island at exactly the point of the spawner, which means we just have one straight line of constant island, which again, would be a little bit weird. If we say yes, then it's going to pick a random float within a range, which in our case, because we've set this to 5,000, which would between 5,000 on the Y and negative 5,000 on the Y. So that should give us a lot of variation in their start point, their scale, their rotation, and even the mesh. So we can test this now if we press we should see somewhere within 5 seconds. I'm going to get rid of the camera very quickly. In play mode, we can test this and just wait around a little bit, and we should see the islands start spawning in. We're looking out for things like making sure they're not just popping into existence, making sure that they're going the correct direction, which I think we may have an issue with here, and also that they are spawning correctly within the offset that we've provided. So I'm not seeing something. There's a very small island. That may have taken the 10 seconds, but we have a very small island there just outcropping from the bottom. It's kind of comical, but I think that still looks pretty good. And we should see, hopefully, some more islands coming within the next several seconds. Okay, so we actually have a lot of islands this is one problem we're having is that with their random rotation, they are moving forward based on the direction that they've been spawned. So let's go ahead. I think maybe I did mess up some of the logic when we're creating the movement logic. And I think, really, it's just going back to what we were looking at previously. So back in the island class, I think it was this here, the local offset versus world offset that I had in mind. So it would be nice if we could keep the code, similar to the projectile, and we could probably change the projectile code instead. But I think what we're doing just to keep this nice and simple, we're going to change this from our local offset because remember, when we're doing a local addition or an offset to the local position of the actor, that will take into account the current rotation that it has, because we want to rotate or if we're rotating 180 degrees the other direction, then forward locally has changed, so we're going to move in the other direction. Saying that, though, that's made me realize that we could probably do something a little bit cleaner here, focusing on hierarchy. So again, this is something where in pre development, I just overlooked this. And this is me, I thinking kind of on the spot, which is why I'm keeping this in just to show ways that we can solve different problems in various ways. So what I'm tempted to do is because I do want to keep this logic. I want to do the local offset movement. What I think we'll do instead is we're going to add a new component. We're going to create an empty scene component, and we'll make this the new route. So the scene component will be the way that we're facing, which is just always going to be forward in the world. And I think what we'll do instead is we're going to set our rotation, but we're not going to set the rotation of the entire actor. We're going to leave the scene component always facing forward. And this will just give you a nice example of different ways that we can use hierarchy, as well. We can grab our static match, and instead, we're going to set the rotation of this. This is also something that we haven't seen yet, so we can set the relative rotation of our components relative to their parent component that they're attached to. But exactly the same logic, though. We're going to plug this in. We're going to take this random float that we already have, split the structure pin like we've done before. We're just going to plug this into the Z here instead. That means that we can keep our movement logic exactly the same. And now we're not changing the rotation of the entire actor and the way it's facing. That's still facing the same direction. We're just changing the rotation of the visual element. Kind of similar to how we separated that out with the planes, where the visual element is just the plane body, but the actual collision and what everything's resting upon is that collideosphere. So if we go back in and play, we should see this is now working. One other useful console command here is we can hit the Tilda key, type slow mo, and then set this to how many times faster we want this to go. So if we set this to slow mo five, you can see that this is moving five times faster, much easier for testing and actually seeing if the islands are spawning properly. So they're now moving down the screen. That looks like it's working correctly. We've got that test in remember, where we're not going to keep spawning things. If the player is dead. Console command again, we can type a restart level, go straight back into Slom five, and we can do some testing just to make sure that the islands just making sure that we get several tests on the go, that the islands are working okay. So they're bunching a lot of bit, but I mean, again, this is where randomization comes in. We may have that happen, but it's providing some nice kind of variation, and I think that looks pretty good. May want to add a little bit more of a delay between the initial islands, but I think just for this type of project, that's looking pretty cool and having some overlap just gives it that extra variety. One other thing you may want to do is in the spawner base class, if we come in here, we're going to immediately spawn Actor. So we should have one. If we just hit simulates. We should just double checking. Yeah, we get one straightaway, which is perfectly fine. I was thinking that we might have already had an initial delay, but I think the only reason that we don't see it for a little while is just because it takes a tiny bit of time to come down the screen. So we actually spawn with an island straightaway. I'm just kind of wondering where that is right now, though. So, it's just it's taking especially if it spawns a small one, first of all. It's going to take a little bit longer to see it before it comes into frame. Not the end of the world. Again, randomization, you're going to have some different results there. Even with the bigger island, I think that's taking quite a while to get into screen though. So maybe what I would be tempted to do, and again, this will come down to a lot of traveling now playing around. Maybe we were a little bit too concerned with how far away this is this morning. So we'll just bring this back in a little bit, and just again, making sure that every time you press plate, keeping an eye to make sure nothing just pops in, but we want that island to maybe show on screen just a little bit earlier. So as I mentioned, trial and error, see how things go, keep an eye on the results that you're getting and making sure that the game is looking good as you're going through. So I think that's pretty cool there. This is where things should start getting a little bit more interesting, though. You're now in full control over the level design, where you wanted things to spawn, the speed you want them to move, really kind of just refining the play and the look of the game that you have. If things are popping in, then you can just move the spawner back a little bit further. If you want them to appear sooner, then we could bring them a little bit closer. If the water is not covering the whole view, then just increase the plane as you've just seen. And if the islands are too close, then just lower the spawning speed and things like that. And this is just pure iteration, play, observe, adjust, repeat. It's rare that you'll get this perfect first time. You've just seen me have to tweak and change a few things even for this demonstration here, but that should be where some of the fun of creating games comes in because this is where you're now in much more control of refining that final result. Now one thing I'd say is that it's going to be worth at this point as well, keeping an eye on the outliner, like I've mentioned before. We've got some things, got our blocking volumes are perfectly fine. Maybe putting things into folders like with the lighting that came by default. One thing I've realized we're not actually making use of is the sky sphere in the background. This is an old object. It's not doing anything. We can get rid of the sky sphere. You can see nothing changes, just to make sure we don't have more objects in the level than we need. Everything else is fine. This is providing the light and atmosphere in the background. But feel free to start playing around with these, changing the direction of the directional light, the strength of it to make things look brighter, darker. Again, really start getting creative and playing with the different properties and details to make the game look as you kind of intended. So that's it for our level. Things are now looking a little bit more live. We have water islands, parallax de you can play with. It's actually starting to look like a little bit of a game. Next, we can start getting into the polished pass, making things feel really interesting and much more kind of visually appealing to the player. 24. 23 - NiagaraParticles: Point where we can start improving the general feel and appeal for somebody playing our game. We're going to start with the polished pass of effects, adding things like thrusters, explosion, impact all by using Unreal engine built in Niagara system. So a quick overview again of the systems and emitters that we'll be looking at inside of our project. If we navigate to our project, go into the Assets folder and effects, we have our Niagara systems just here. But just open one of these. We'll go with the pixel engine for now just to show what we're working with. So what we're inside of at the moment is a Niagara system. Systems are generally built up of one and quite often more than one emitter. We can see here the orange tabbed panels are our emitters making up the different points of our overall system. This makes Niagara much more flexible to work with than the older cascade particle system. It's much easier in the new system to create some base emitters, and then we can start stitching them together to make custom and unique systems on. In a really simplistic way, you can think of this as something like a flame particle system. You may have many emitters that would go into this. You'd have the sparks coming from the top of the flame. You'd have some smoke. You could have the distortion effect, and you'd have the main body of the flame and the fire itself. All of these could be individual emitters building up that one big system. The important thing to keep in mind is that emitters are meant to be reusable. We don't place these directly in our blueprints or in the the systems that we'll be taking to use and actually make use of inside of our code or the level itself. If you wanted to start trying to play around with some of the systems I've already provided, you can see I've got several here. You can very easily come in and start playing around with the properties in the emitters. If you wanted to see which part is responsible for a certain effect going on here in the view pot, we can untick these here. We can see that this is just a light source, so nothing really changes. If we turn this back on, but then turn off the other emitter, we can see that this is clearly the one responsible for the body of our engine particle effect there. So if you then wanted to come and start trying to play around with things like the color, the size of this, you may not know exactly what you're looking for straightaway, which is perfectly fine. But just from having a quick look through the properties provided, we can see we could probably change the color from our scaling values here. This is making the yellow orange color here. If we give this more blue, then we're going to get a different color over the lifetime of the particle. It's another great way to get used to some of the systems inside of Unreal, just finding the things that instinctively makes sense to you and playing around and weaking with the values you can find. You can add different things like extra velocity to make this travel further, as well. And like I've mentioned before, one really great way to find out what things are responsible for is add a really silly large value to this, so we can see that if we make the velocity here 11,000 rather than 1,000, we can see exactly what that was responsible for, and then we can refine and tweak down to a value that we think we might want to work with. So like with some of the other systems inside of the project that we've looked at so far, the more artistic side of things really is not the focus of what we're trying to get through in these topics. So I'm not going to go any deeper into Niagara. I want to show you how to use it and how we can implement it into a project. Useful if you've got things like access to Asset PACs or some of the free examples from things like the content example project provided by Epic. The first thing, since we're already in this particle, we could make use of this for the thruster for both of our plane classes. Both of them will need this, both the enemy and the player. So if we go to our blueprint class, we'll go to CR, and we'll find the BP underscore plane base. So we can put this in the base class again, and this will be used for both of our different planes. We need to be very careful again where we're going to place this. We could place it directly on the sphere, but I think at the moment, in case we wanted to do any specific offset rotation, things like that to the static mesh, we'll place it on here instead. So we'll grab the static mesh. We're going to add a new component, and we want to search for Niagara. We can see here we have just the one option, the Niagara particle system component. And in the details panel, we'll see the drop down that we're probably looking out for and quite familiar with by now. We can drop this down and select the pixel engine. If we navigate to the viewpot, we can see this has kind of been docked in the middle of Amopla, so we're just going to need to move this back a little bit. And I think I'll just place this roughly by the metal circle that I've added on the plane just here. If we hit Compiling safe, there's a few things we might need to test with this. So the first one is the different rotation that we're going to be applying to the different child classes. So if we start off inside of the plain player and just check the viewpoint here, this one's looking perfectly fine. This is essentially the same setup as plane base. If we go into plain enemy, go into the viewpoard here, so we can see that the particle effect is looking fine here as well. However, if we go and press play, so if we go into our viewpot and play mode here, we can see that it's seeming fine on the player, but the enemies particles are looking just a little bit strange as they're this is going to be because remember, when we come into play mode, we are completely flipping the rotation of our enemy based on the spawners location. And even though this particle effect is nested, if we look at the setup in the P underscore pixel engine, we can see that the velocity is set to a specific direction, negative 1,200. So what we want to do, and this is just going to be a very simple tweak to an existing particle system. So you can start getting at least a little bit hands on, we need to make a duplicate of the pixel engine. So we're going to come back into our effects. We're going to go to our Niagara systems. We have our pixel engine just here. One other thing, these were copied from the content examples they don't have the best naming, so we're going to take this and we'll rename this one to Ns. So the proper naming convention would be Nagra system, underscore pixel engine, and we'll call this one underscore player. So we can leave this one exactly as it is. We can then press Control, indeed, to duplicate this, and we'll call this one underscore enemy. And if we double click to open the enemy version, what we want to do is we're going to find our velocity. We've already seen that this is controlling the direction that the particle effect is going, and we're just going to remove the negate option here. We could also do something whilst we're here, making this one maybe a little bit more green so we could drag the value up here to give that a slightly different tint. And if we take that one past the red value, we can see that's a little bit more of a green thruster here just to make it slightly more unique for the enemy version that we're going to place this on. So we can hit compile and save on that. We'll go back into the enemy class. We'll grab our Niagara particle system here, and all we want to do is change this from the player, which is the one we've just renamed and change this to the enemy version. So this is going to look broken in the blueprint. That's perfectly fine, because as soon as we go into playode, this is taking into account the world direction of the particle system movement rather than the entity that is on. So we can see that's now working perfectly fine. Slightly different because we've got different colours, so it also looks a little bit more interesting. A nice simple tweak just to get a little bit hands on with the particle system there, and we have both the player and the enemy system working. So that's one way that we can apply particles without code directly into components and existing features. Now if you remember back, we have a few things lined up ready to start implementing in our code as well. Remember some of those to do comments. So one of those that I remember having was actually in our blueprint class for our projectiles. So if we go into BP underscore projectile base, and exactly what I was mentioning earlier, I'm just going to type todo down here. We'll find that comment that I've left myself, and it's just down here. So if you didn't leave that comment, you just need to navigate to your component begin overlap, and this is where we want to put in some effects. And we're going to start with our simple particle system to show that two things have impacted. So between our apply damage, and again, really important before we destroy the projectile, we're going to pull from the execution pin and we're going to search for something called spawn system at location. So we want this option just here, span system at location. Now, there's a couple of different things if you start looking more into particles yourself to build upon what we're doing here. You may also come across the term spawn emitter. So if we search for Born emitter at location, it's a very similar terminology, and this is essentially the legacy version of the particle system. So it's not fully removed yet, but I've mentioned it very briefly a moment ago. There used to be something called the cascade particle system. It wasn't quite as flexible, doesn't have as many features. It's not as easy to program into, whereas we now use the Niagara system. So when you want to work with Niagara, which is ideally what you'll be focusing on, you should be looking for something called the word system will come up quite a lot. When you're working with cascade, if you're in a legacy project or an older project, you might see the word emitter come up. So cascade articles were referred to as emitters, whereas now, as I've mentioned, an emitter is just a single element that builds an entire Niagara system. So that's the easiest way to remember it. So we're looking for a system. You know you've got the correct one as well, because if we drop this down, we don't have anything valid to put into the emitter slot. So we'll get rid of this. If we drop this one down, we want to find our metal impact. So this is what we want to use when two projectiles or a projectile hits another surface. So that's the emitter that we want to spawn. And this allows us to bring these in at runtime, making the projects look a little bit more dynamic. The location is going to be really simple. We'll pull from here, and we're just going to search for get actor location. So this will be where we want the particle effect to spawn, the impact point, essentially, of where two things hit. The rotation in scale we'll leave as default. We don't need to change these. We'll come back and we'll do some sound effects and things later. So I'm actually going to leave this comment for myself later, as I will need to come back and add a few more things. But again, we can now come in and test, and what we should see is every time a projectile hit something, we're going to get some type of particle effect being played. So it's a little bit of an extreme, but we can definitely see that it's happening. And again, you can come in, refine the different scaling and things like that. But we've now got some feedback that two things have collided, which just makes the game feel that much more interesting and intuitive to play. I've also provided, as well as the metal impact. You could also potentially use pixel explosion, so completely up to you. I think they look slightly different. Just double check what they look like. That one might be a little bit more fitting actually for the smaller impact there. So I'm going to keep that with the pixel explosion. But that's the main thing we now have that visual feedback. And then we want to do the same thing for both of our planes. So again, we've flashed this out in the base class, so we're going to go into the BP underscore plane base. I think I've left myself a too here as well, and we'll just double click on this. So to do implement plane death effects. So these will be the effects I want to play when either of the planes are destroyed. So same thing again. We're going to pull from here. We'll search for the Born system option. Take the span system at location. Same property. So wherever the plane has just been destroyed, that will be the location. So we're going to find the get act to location. And we're going to do this a very slightly different way here. So like we've done in the past, we want to make this flexible because we're exposing this for use in the child classes of the parent where the core logic is. So we're going to take our system template here, and we're just going to promote this to a variable. We're renaming this one to explosion particle effect. And we'll need to change this on the child classes because the player and the enemy have just very slightly different particle effects. So we hit compile and save. We're going to go into the plane class, the player plane class. Make sure that we've got the top element selected, and we should be able to see somewhere down here, we now have an explosion particle effect. From this one, I'm going to find Pixel explosion player, and then we'll hit compile, save go into the enemy plane. Same again. Grab the top element. We're going to find our explosion particle, and we'll change this one to the enemy version. So nice and simple, compile and save again. And now we want to go and test and see how this is working when things actually blow each other. So we see there's a little bit of a lag the first time that comes in. In a fully flashed light game, you're going to want to run something to get the shaders to precompile, to make sure that all of the efects are essentially round at least on, so we don't get that lag. But now that we've got past that, we can see we've got the different effects. And we can just test, as well. When the player dies, we get the same thing. So it's slightly different if we double check and look at these. I think one is slightly more blue, so we've got kind of a blue explosion here, and the other one has that green tint. So you may want to come in and change the color properties. And you can see as things get a little bit more complex, we can see the complexity, the number of emitters is going. But once you start getting familiar with the things you're looking out for, these are relatively easy to tweak and change and set to work exactly as you want. That is the programming side of things. That's really all we need to do to get some particle efects playing. So it's a relatively simple step to add some nice polish into our project. And this is another one of those things where experimentation and playing around with things is really going to benefit you more than anything else. For example, I've taken the particle effects directly from the epic content examples, and I think just for the general setup that I the explosions look a little bit too thin, not quite as chunky as I might have wanted. So what I might be tempted to do on both of these would be to come in and find the things specifically related to the scale of the mesh. So these are three these static measures. I can see here we've got scale mesh size, and I could just increase this to something much bigger by default. I can see here exactly which element that's taking effect on, so we can see which ones have just gotten a lot bigger. I've made that four times larger than it was. Now, again, this is somewhat time consuming because I need to do that for each of the different elements. So I'd come into this one, for example, set this to four, four and four. But we have now immediately a much more kind of nice chunky explosion for the kind of star that we're going for. So really simple changes like this can really make a difference on emitters and all I've had to do is come in and find the mesh size. So if I wanted to test that on our enemy, I'd just do the same thing again, so we could grab the mesh scale, set this to four, four and four, so it matches the player. And we're just doing this for each of the different emitters so that everything stays somewhat uniform because they're meant to kind of represent different elements of the explosion system. So compile and save that, we can come in and play and see how this looks. A I think that's much better. I think it just shows a nice difference between the general projectile impact compared to the explosion of the ships. So really small things like that. You could find the colours as well, especially if you have different colored enemies to what I've chosen. You could come into the enemy explosion, find all of the color properties between all of the different emitters, see which ones are responsible for which color and then change them to your liking. Remember, quick tip is if you're not sure which element is the one you're looking to change, we can just untick the different emitters here and we can see what's responsible for which part. So you can go through. This would just be that kind of after smoke, so we can turn that one off. The middle one is for the main color. So if my my, my enemies are actually green. So maybe it would make more sense to have a more of a green color, so we can green value, bring this all the way up. And then we go. We have more of a green explosion for the enemy night. So we've got a nice kind of o yellow starting off with a heated explosion. But then we're showing that this is actually going back down to a green overtime, which is the body color of the enemy ship. So, again, a really, really quick change. As long as you know roughly what you're looking for, we can make these changes quite quickly on the fly. And see that kind of come together quite nicely. So play around to these types of things. These are kind of approaches that will get you understanding the systems much more quickly. You won't necessarily know exactly what you're looking for as you're getting started, but I think, especially with something like the Niagara system, the naming conventions, the stuff that you're looking for, and the immediate feedback you get in the viewpoint is a really handy way to learn this very quickly by just some trial error and tweaking with different values. So that's the first step of improving our visual feedback. It makes everything feel alive. The enemies are still kind of popping in from nowhere. Then the next topic, we want to add those really nice spawning animations that were provided in the example project. 25. 24 - AudioEffects: Might have said that we're going to jump into the animation Festival, but I realize that we're actually overlooking the missing piece of our polish here. And this is often something which gets left far too late in development in games, and that is audio. We want to start looking at the use of San cues to randomize a minimal amount of audio files to make things sound a little bit more varied and interesting. It's one of those things as well that if you're prototyping your own game, it's really, really easy to overlook and underemphasize the importance of audio. You might feel that your game is feeling flat or boring. You can add things like particle effects, camera shake, and all these other things, and it could just be that the project is missing some audio that would really help bring it to life. So we're going to focus on this festival. So if we go into our content draw, we're going to go into the audio folder. I've provided a few different sound files. We've got one for the explosion, so we'll be making use of these for our planes when they explode, and we have the gunshot effect, which will play whenever we're firing. We're probably not going to put this on for the enemy, but again, you can tweak and play around the things to see how you like the project to be set up. I think if there's too many things firing at once, it can very easily become an auditory overload. If you wanted to test the audio files and see how they're sound, we can hop it over the play button here, and you get immediate playback in the editor. If we focus on our gunshots, first of all, since we have two of these, we want to shift select both of them, right click on either one, and we've got this option here to create a single queue. So if we choose to create a multiple que, this will create file, one que file for each wave file that we have down here. Instead, we can take both of these wave files and compress them into a single queue. So we'll click that one. Rename this one to SC Underscore gunshot. You can see it's a slightly different asset type. And basically, SN cues are a very simple way if we double click to opens. They allow us to work with multiple audio files without needing to edit the source file. So we can keep the gunshot one and 71 exactly as they were when they're imported, and we can make overrides here individually to things like whether it should loop, the default volume, and things like that. One thing to note is that you can see the nrails clever enough. It knows that because we brought in more than one different signed file, we probably wanted to randomize the order in which these are played, which is, in fact, what we wanted. After that randomizations been made, though, we want to pull from here, and we're going to search for something called a modulator. Or, in fact, you can see it just down here. We're going to plug in the result of our modulator into the output. So this will toggle between which sound to play. It's going to randomize one of those. Going to throw in a modulator so we can tweak the audio slightly. And then whatever that output result is is what will be played to the end user. With the modulator still selected. You can see the main thing that I wanted to override here is the minimum and the maximum pitch. This is just going to help ensure that even though we only have two sound files, they're not going to sound exactly the same when we fire them potentially hundreds or however many times in a row. I give both of these quite a big pitch shift, so we'll set 12.5 and the other one to 1.5. And again, these might be quite extreme values, and if the sound is too distorted, then this is where we can come back and start just gradually refining it to sound exactly as we want. The same with the volume, we can set one to 0.9 and one to 1.1. So we've got a kind of similar offset to the minimum and maximum volume for both sound effects every time they play. So that was great. We could see that that was definitely taking effect exactly as we wanted with one small caveat. Common gotcha with the old Q system that we're using here. The randomization isn't really that random. You can see it's just simply flip flopping between the two options. It will play A then B, then A, then B. Quite predictable. So if we grab our random note, one thing we want to do is we want to untick this here, the randomize without replacement. Something that people often overlook. But now if we press play, we don't necessarily need to listen to them, so I'll just mute the volume quickly. But just double check that you can see which wire is firing off. And what we should see is that I had about four or five clicks there, whereas getting the same audio playback, which is getting much closer to true randomization rather than that flip flop between A and B. Obviously, you'd want to do this as well if you had multiple sound waves on top of this in your single que. So if you had five or six, you wouldn't want it to go one, two, three, four, five, one, two, three, 45, and so on. You'd want it to pick randomly between those five or six sound effects. So always remember to come in and untick randomize without replacement. With that done, we want to do one similar thing with the explosions. So I'll right click on the explosion wave. We're going to create a single sound queue for this one. We'll call this one SC underscore Explosion. Obviously we won't need any randomization between this one. We only have the single file, but we can still make use of things like throwing in a modulator again in between. So I'm just going to hook up another modulator here in the same way and maybe provide some slightly less intense differences here. So maybe 0.7 to 1.3. And again, we can come back and refine if that's too much of a pitch shift. I'm going to leave the volume minimum and maximum as they are. And it's just a really simple tweak that we're going to make inside of our sound queue. If you're coming from more of an audio background, there may be other terminologies and features that you can play around with in the right hand side, and you can simply just drag these into the graph, play around to the property, and tweak them as you see fit. For this simple demonstration, ough, I'll keep this as we have here. So I'm just going to go through very quickly and begin closing some of the windows that I don't need, especially the particle effects, so we'll get rid of those. And if we start in the play base, I'm already here, so I'm going to handle the death effect first. I'm going to leave the comment here, as we'll probably need a few more things a little bit later. So in between our spawn particle system and the destroy actor function call, I'm going to pull from the execution pin, and I'm going to search for play sound at location. So this is a really nice feature again inside of Unreal. We already have built in default spatial audio. So we can provide the initial point of where we want this audio to play from. So we can have things a little bit more believable if a plane is being destroyed to the kind of left upper side of the camera, then we're going to have a sound effect which should play a little bit more in the left side of your headset. So we're going to use the standard function here. We're going to use get act location to find out where the exploding plane was destroyed. And then we're going to use the sound drop down here and we'll pick our SC our sound cue that we've customized underscore explosion. Now, we don't really need to promote this because, again, they're both going to use the same explosion sound effects, and they both have their own randomization and things like that. Now, I'm not going to use it here, but I wanted to show you the features that we do have access to, as well. So if we use the drop down on this node, if you didn't want to play around with sound cues or you just wanted to use the rule wave file, we can still come in and do things like volume multiplication, pitch multiplication. We could throw in something like a random float, again, so we've seen this plenty of times or ideally a random float in range, as we've seen before. And that's very similar to what we've set up in the sound cue for the pitch multiplier. But because we've got a nice, customizable sound queue to work with, we'll just use that instead of the rural default sound wave files. There's a lot more in depth stuff we could go into with things like attenuation settings as well. So that's providing the three D sound system, a kind of a fall off, a range at which the audio should get louder and quieter, depending on where it's sport. For this, though, I'm just going to copy the functionality we have here because we want to add this onto our fire function. So we will just move the comment over. We're going to come back here and do a few more things when we do our final passes in the effects, so this is still relevant. But we're going to grab this press control in C. I'm going to go into the fire function. And after the projectile's been spawned, we can just drop this on here. Same thing again, we want the location at which the fire sound effect should be played. And we're going to grab our gunshot here, and this will automatically play a gunshot now whenever something fires. It's because I think if there's four or five enemies on screen and they're all firing it in quick succession, we may want to lower their volume. So I think what I'm going to do is rather than having separate fire functions in each class, again, we're going to make use of inheritance where we can. I'm going to promote the volume modifier and I'll promote this to a variable, and we'll call this one fire volume offset. So we can default this to a value of one, which it already was. So we're going to default to playing the full volume of the gunshot sound cue. So, of course, if we've set this to randomize a little bit here with the modulator, the default volume might be 0.9 towards 1.1. We're then going to take an override on this and take that either multiply that by a total of one or for the enemies, for example. The reason I want to use this is we'll hit compile and save. I'm going to go into the enemy class. We'll find that value here from the properties. I'll give this a default volume offset of 0.5. So we're going to have the default volume for enemy shots. So even if they do start stacking up a little bit, they shouldn't become completely unbearable and like I've said, completely overstimulating for the audio. Okay, so a really quick play test there. I found that the gunshots were probably about as loud or maybe louder than the explosions. So I think what I'm going to do, we're going to set a minimum volume, and this is just potentially based on the types of signs that I found. They were probably just exported, not with a good normalized volume. So maybe by default, actually, we're going to take this to 0.5 to maybe 0.7 or maybe 0.4 to 0.7. And we'll see how this sounds. Okay, so after a fair amount of tweaking there, I actually found a good value was 0.1 and 0.2 for a minimum and a maximum for the default, and then we're going to take that even lower in the enemy by adding this volume offset here. So this isn't the end of the world. As I said. Assets are perfectly workable, but these are just useful things to be aware of that you might get handed or you might find assets from marketplaces and things like that, and some may have just been exported with a completely different volume. So it's useful to know that we've got multiple places that we can apply different volume offsets and things like that. A project or a full production game. This is why it's also really important to have something like a settings menu with some really flexible audio sliders so that people can choose to make their sound effects either quieter or louder than their music and their voice over and stuff like that. And for the love of everybody's ears, including mine, please set your default master volume to somewhere around about 50%. Nobody likes turning the game on and getting their eardrums destroyed by 100% master volume when your game is a completely different volume level to anything else the system may have been playing. So audio really can be quite important and it can make a really good or bad first impression on your game, depending on how you use it. But again, this is just something else that you're going to want to tweak and play around with. Listen to what you think about the gunshots on your system versus the explosion. See where you might want to change the volume modifiers, play around with the different que options that I've shown you to try and get your game sounding well mixed and as interesting to listen to as possible, even if we only have a few different audio files to play with. You can also go to different websites, HIO, freesound and things like that to download some new sound effects and see what you can add into the project to really start making it your own. The main thing that you're looking for is just play the game for a few minutes. Does anything begin to get annoying after that 32nd to minute mark? And if so, can you just fix that by reducing the general volume in the sound queue? Or if needed, as I've said, we might want to even come in and remove the gunshot from the enemy because it's going to be happening so often. None of these are clear specific rules that we have to follow. As with many other things, it's just going to be a lot of trial and error to see what works and what best impacts your end result. With the audio feedback complete, the game now has presence, providing visual and auditory feedback. Next, we will actually move on to the spawn animations, which is just an optional extra on top of the visual flare that we've already started adding to our project. 26. 25 - SpawnAnimations: The final polish layer, let's give our ships a big intro. We'll have both the player and the enemy ships glide in and spin into position instead of just popping into existence. Once again, one animation in the base class that will work for all of the planes that we want to work with. So for that reason, we're going to go straight into our content drawer, blueprints folder, core, and we'll find the BP underscore plane base. We want to navigate back to our main event graph, and next to our begin play, we're going to right click and we're going to search for a handy feature here called Add timeline. So it's this option at the bottom. We can give this a name. It's kind of a function that has its own built in graph. We'll look at that in a little bit, but we can give these names. So we'll call this one anime, as in animate in. As with anything else, these will only be activated when an execution reaches them. We're going to pull from our execution pinhe and we want to plug this into play from start. So this is going to be a graph curve that we can animate across, and we just want to make sure that always starts playing from the zero frame in the curve all the way to the end. We have other options like to play from a certain point, stop the animation, play in reverse or whatever. We just want to play essentially from frame zero to the end. Way this works is kind of like a temporary update, so very handy if you don't need something to animate constant. This will fire every single frame for as long as the timeline is playing before it reaches its end. And then for as long as that is firing, it will call the update pin just here. And then once it's finished the animation, it will call the finished pin. So we know if and when it's still playing, and we also know when it's reached at the end of the animation. All of this is going to be very handy. So if we double click this yellow node here, and this is one thing to start noticing is a lot of the nodes that we've been working with have different colors. These blue ones are the functions that we're creating, so you always see a function is denoted by blue. Any custom event or an event in the graph like this is essentially a function still, but these are denoted by red. We've got things like the green variables, and we now have a slightly custom time based function here, our animation timeline, which are yellow. When we see these quite often, we can double click into these and we'll have a new feature here. So double click that node, and we'll be inside of the timeline. There's a few things we want to do. First of all, we need a track to work with an animation curve to begin creating. So if we press the plus track here, we have a few different options, we can create a float curve, a vector curve, an event track or color curves, essentially. We just want a really nice simple zero to one, essentially a normalized value that we can track. So we're going to create a float track here. And something which is really important, we can right click and drag to move around middle mouse to Zoom innit, like many of the other navigations inside of Unreal. But one really important thing before we start adding any keyframes or pins here, we want to set the length of our animation. If we start adding pins now, you can see we're going from a time frame of zero to 5 seconds. So we're going to take this length all the way down to one, and this just means we're immediately working with a curve timeline, which is the length that we want this to be. Now, the reason I do this, we'll see this a little bit more when we flesh things out, but we're not thinking of this as a time. We're not doing this across 1 second. We can essentially think of this as a percentage. This would be 0% of our animation, and this would be 100% of our animation. Show you handy little trick that I don't see very often a little bit later. And this is the way that we can actually adapt this then to be a five second animation, a ten second animation, half a second animation, whatever we wanted it to be. As long as we've normalized this to a range of zero to one, then we can treat this more as a percentage. Now if we zoom in just a little bit here, we've now made the curve a little bit smaller, we can hold left shift and left click and we'll create these keyframes here. So if you've worked with any other animation software, these are going to be quite familiar to you. This is going to be our time that this happens and the strength or the value at that point in time. If you've been clicking around randomly like me, we only actually need two points here. So I'm going to grab drag select a few of these points, these keyframes, and I'll get rid of these. First keyframe, I'm going to set this to a time of zero and a value of zero. So at this point, exactly none of the animation has happened. And then for the second keyframe, I'm just going to drag select this one, and I'll set this to a time of one and a value of one. So this is our 100% of the animation is now 100% complete. So you can start seeing how we're looking at this as a percentage rather than a specific value. One other thing that we want to do is we want to make this look nice and smooth straightaway. If we were to animate along this track, at the moment, it would just be a very flat, consistent and somewhat boring animation. So if we drag select both of these, right click on either of the keyframes and just set this to Auto, this is going to give us a nice smooth easing in and easing out. Now you can play around with these if you want it. You can have it exaggerated when it first comes in, or you can have it really slow when it first comes in. And that's one of the real cool benefits of working with these keyframes like this is we can play around with the animation to make it more stylized, more cartoony, whatever you're looking for. BdeFelt I'll keep it like this, though. The final thing is this track zero or new track zero is something that we can also tweak. So I'm going to click this. I'll press F two to rename this, and I'll just give this the name of Alpha. Because when we start actually animating our planes to fly in, this will essentially be the Alpha overtime that we're tracking rather than the specific rotational value. And that's one thing is that in a lot of examples, working with timelines, you might see people directly animating the location or rotation of actual vector values over a specific amount of time, and hopefully you'll see by the end of this how that can be quite inflexible, because, again, if animate something which takes exactly 7 seconds, and you've done that on all of the XY and Z of the location or rotation values, and then you decide actually that's rotating too much, and it needs to take half the amount of time. You'd need to come in, rechange the track length, move all of the key frames around, and it can become a little bit fiddly and time consuming. Where's this? We can just set this once, and we're now in full control of the animation in our actual code instead. That's it. That's pretty much done. So we have our curve ready to provide an Alpha value over time. So if we hit compile and then we'll go back into our event graph up here, all this has done is just dropped you into a tab inside of the animation timeline we've created. So we're going to go back into the event graph. So to actually set our animation first of all, from our Update pin, remember the one which is constantly running now until this animation has finished, we want to call the set actor Location function from here. This is going to be the result of a lp, again, so we're just going to use the larping function that we've seen before. So our linear interpolation between two vectors, not the interp two or anything fancy, just point A to point B with an alpha. So you can actually see the reason that we've called this Alpha is we can plug these straight and we already have part of the answer here just based on naming conventions. So then we just need to calculate or figure out what point A will be and what point B will be. To break this down as a concept, point A is simply going to be the spawning location. So wherever we start from when we spawn in will be point A. Then we want to get to a certain number of units ahead of that spawning point, which will be our point B, something which trips people up quite a lot when they work with lurping animations and it's confusing because it looks as though it's kind of working. But when you think about it, what I'm about to show you would actually be kind of confusing the animation a little bit. So what some people would do when they're first working with linear interpolations is we know that point A is our starting point. So we can right click and we can search Get actor location. This to a lot of people could make sense that we're going to go from our start point to an endpoint. And we could say that the endpoint, for example, would quite simply be where we are now plus a vector unit in a certain direction. So if we take where we are at the starting point and then project, let's say, 300 units ahead, that's where we want to end up. That would essentially be our endpoint. Now, this wouldn't work anyway, but just to very quickly put some code in to show that, we could say something like, we know that X is forward, so we could get x location plus 300 units forward on the X. That looks as though it could be our start and end. And I wanted to cover this and why we won't be using this precisely because I see this in so many student projects and new development projects, because as I mentioned, it does kind of make sense. We have our start point, and we have our endpoint based on that. But what people aren't considering when they're doing this is that this isn't just firing off once. I think that's where people get a little bit tripped up. I they imagine this just being fired once and this value staying the same. So if we start at 000 and we add 300 to that, we'd expect to have 30000 as the endpoint. But what's actually happening is every frame this is being called, and this is being rechecked. So if we start at zero, zero, zero, and then we move some units, say we've moved five units. On the next frame, when this gets cooled, we're now at 500. So that means the endpoint is going to be 30500 instead. So both of these values are then continuously moving. We're only going to be animating for that 1 second or however long it's going to be anyway. So that means we will get to an end, so it looks though the animations happening, but it will always and look just a little bit off, and maybe even a little bit unpredictable because these two values are constantly being rechecked and updated and applying a different lp result at the end of the calculation, if that makes sense. Was just to introduce why we're going to need to give ourselves some room, make some space over here. And what we want to do is we want to make sure that these values are stored and unchanging. So it's not a difficult thing, but I just wanted to highlight that this happens relatively often, and I can see, I think, why people would do that. And it's just something to look out for whenever you go to try and create your own animation or a customization of this later. Just remember that a loop needs a constant start and a constant target to animate smoothly between them. So what we're going to do instead is we'll take this, so I'm going to cut Control X and paste the get actor location. We'll promote this to a variable, and we'll call this one start location. So nice and clear, hook this up, and then we're going to do a very similar thing over here. And as I've mentioned, this wouldn't work anyway because we're just adding a unit. We're not actually taking into account the direction that the plane is looking, so we need to do a little bit extra than this anyway. But even if we did do the full process, this as it was still wouldn't work. So we're just going to move this over, grab this, so cut and paste this over here. We can still make use of a bunch of this. Now, the first thing is to remember that the enemies want to use this as well, and they're going to be getting flipped when they spawn in. So we don't want them being animated up the screen like the player. We want them to be animated based on the direction that they're facing. So what we're going to do is we're going to search for G actor forward vector. So this will get the direction that the actor is being rotated or currently facing. Remember that arrow that we applied to the projectile, I will essentially be checking which direction that arrow is facing. We then want to multiply this by a value. So we're going to take the forward vector, which is a direction, not a value, so this will either be something like 11 and one or 100 if it's facing forward or minus 100 if it's facing backwards, for example. We're going to multiply the direction, not the actual vector position, which is why we can multiply this. We're going to multiply this by a float, so we're going to right click on the pin here, turn this into a float, and we can still take that 300 unit value here. So we want to move 300 units in the forward direction. We can then make use of this. So we want to take this value and we'll add this to another vector. And in fact, we'll get rid of this one. So these notes won't be needed anymore, just to keep things nice and tidy, and we'll tidy these up a little bit. And what we want to take is wherever we started, we want our XY and Z of our start position. Plus 300 units in the forward direction. So multiplying our forward to get either a negative or a positive back on whether we're facing forward or backwards and the X, and we're going to take this and split some values here. So the first thing to make this a little bit easier to read is I'm going to promote this pin to a variable, and we'll name this one target location. We can alt and left click to drop this down. We'll split the structure pin here. And now we've got all of the nodes that we need. We can tidy this up a little bit, we actually want our first pin to be our start location. So again, just personal preference, I'm going to control and drag the start location in and replace this one. So we can take where we started plus an offset in the direction we're looking by X number of units. That's going to move us 300 units in the X is what we're looking at here because X is four. So if we could get the get actor up vector, and if we plugged this in instead, this would apply an offset on the z axis purely because Z is up inside of unreal. X is forward, Y is sideways. So we can get different vector positions with some built in functions here. We just want to consider moving forward. And then we can't use the full value here because we want to keep our Y and Z consistent regardless of where we're kind of rotating or moving here. So I'm going to grab the start location again, duplicate this down here, split the structure pin. And we can plug in the Y and the z to these values here, which is why I split this a moment ago. And then whatever this value is, so the calculation of how far we want to move in our local forward direction, we're going to split this structure in as well, and this will be our X value that we want. So remember, this is only calculating the X anyway, so we can take that X value. That will be how many units we want to fly towards in the X or forward value. And that will be our target location. So we'll plug these in. We might need to tidy things up and move things around just a little bit, but that's perfectly fine. We can just move this down and come back and neaten this up a little bit later. The main thing is we can now jump back over to our loop. And again, this is making things nice and clear now. We're going to give this our start location and our target location as the A and the B arguments. So if we go in, we could probably test this just to make sure that something is definitely happening here. And we can see the player plane is definitely sliding forward a little bit there, and so is the enemy plane. So it's sliding in and then it's moving side to side. So we can see that initial animation is working and the same for the player, which does mean that we probably want to grab our player start and start it just a little bit further back. In fact, we can just come here. Something else you may not be aware of. And this is quite handy is we can grab our location because we know this is where we want to end up at this point in the screen, and we can type in here -300. So if we type in -300, this actually works as a maths equation, so that gives us -190 -300, and we get exactly minus 490, which will be our start point, and then we'll fly in and we'll end up exactly where we were previously. So that's really looking a little bit more interesting. I think that looks pretty cool. So that is definitely a good start. And you can see, hopefully how this math is all coming together and working. And again, we've made this work by taking in the forward vector of the actor. So the way that it's been rotated and just to recap on why that's relevant is because when we spawn in the enemies, we're rotating them 180 degrees to match the pointing direction of our spawner class here. So they're moving forward relative to whether pointing, which means everything is animating in the correct way. So that's the first part of the puzzle. The second part will be making this even more interesting by providing a rotation offset as well. So after our set actor location, we want to effect just the visual element of the rotation because we don't want to mess with the sphere collider and change potentially the way that the actors facing. So we're just going to make this a visual change rather than an actual position or change. So to do this, we often get things like, we'll use the static mesh instead. We'll drag this into the ground, we'll pull from this here, and we're going to search for set world rotation. So similar thing, but we're now just affecting the mesh instead. And this is much better because we're not doing any collisions or anything important with the mesh anyway. This is here purely to do visual things, and this is a visual change. So we'll plug this in so this gets calledimmediately, and we're going to do something very similar. We're going to take our Alpha and we'll do a loop for the rotator. So we'll pull from here and search for loop again, p rotator, and again, get the same thing, A and B with an Alpha. This is completely optional. You could promote the Alpha to a variable just for use within this function. Something else I want to introduce, though is that we could just do this. I've double clicked on the wire. And again, just to keep things a little bit tidier, I'm just going to plug the Alphas in this way. Because this is the only two times that we're going to use it here, I think maybe making a tract variable just for this one time fire event, maybe a little bit overkill. So I'm just going to introduce the use of reroot nodes. This still keeps this somewhat tidy and easy to kind of visualize and work with. And then for the A&B, we're going to do a very similar thing over here again, but we're going to get the rotation values. So you may want to start tidying up some of the things here. This is where like I've mentioned sequences can be really good. So very quick cutaway here. We want to potential start tracking what we're doing to initialize values, essentially, when we're setting and fracking values on begin play. That's normally referred to as initialization. And I think I might separate that out from our actual timeline animation. So nice and simple, again, these ref factors really don't take long. If we pull from the execution pin here and search for a sequence, that will automatically hook that up for. So we're not really changing anything. Again, this is just organizing the order in which things are handled. I'll just unhook these nodes just here, and then I'll move the animation dine. And what we'll do is we'll make sure that we have all of the variables ready first of all, and then once we've tracked all of the variables that we're going to use on the then one, then we'll actually call our animation to be played. So again, really all this is doing is it's just keeping things a little bit tidier, easier to read and easier to work with. So that means we can now start moving some of these out because we want all of these location based checks first. So that's getting our start position still. And then after this, we're going to want to get our start rotation, as well. So from here, we can search for G world rotation on the static mesh, the same way we've done previously. I'll drag our static mesh we'll use the G world rotation. And like we did with the locations, we're just going to take this value and promote this to a variable. And this is for the exact same reason. When we're using this in an animation, we don't want this value changing over time. However, we begin our rotation when we're spawned into the world. That is our start point, and we're going to have a permanent offset against that as our permanent target point, as well. Now, this we can do a little bit more easily. We don't need an actual target for the B. You can promote a variable to target if you wanted, but this is a little bit more simple. So for our start rotation, we're just going to drop start rotation onto A, so that's where we're rotating from. And then our target. So B, we can just split the structure pin here because the only thing that we want to account for is we're doing a 720 degree rolling rotation. At least that's what they did in the example project. And again, we're just recreating that in a slightly more tidy way. So what we could do is we can drag in our start rotation here, split the structure pin. We know that we want the Y and the z to stay the same. We don't want to add any upwards or top down rotation. We're not rotating around ourself. We know that we're stating the static mesh is always starting at a rotation of zero because we've not changed that. And even with the enemies, we are rotating their entire actor rather than their static mesh to begin with. So that means that we can very safely just add a 720 degree rotation to the X, which is rolling around itself. Remember what we're doing to get it kind of rolling around itself that way. We're doing a very similar thing here. So if we hit compile and safe, we can test this, and again, this should work. So Nice, easy. You can see that's working perfectly fine. So that's what we wanted there, a 720 degree or two times rotation around itself. If you wanted it to just be a single 360, so they rotate a little bit slower, we can definitely turn that back down, so it'll just be one roll in, or you could go a little bit wild, maybe get it to do five different 360 rotations, and it's going to spin in quite a few times here. So you can have fun. You can play around with that exactly as you want. As I said, I think from the reference project, the one that we're trying to recreate, I think that was just a 720 degree roll that they did there. So that's how we can apply that. Nice and simple. We don't really need a specific target because it's just the start rotation with a 720 degree offset to get that spinning twice. This also bridges a big thing that we've been missing for a long time, even though we've had the code ready in our project. And that is that the player and the enemies shouldn't actually be moving side to side until they've done their sporn animation. Again, this is just for parity with the example project. If you play it from the content examples, you'll see you're not actually allowed to control and move the ships until they've finished this intro animation. So we can actually make use of that. We already have our movement enabled. That's always been getting checked, so we just haven't been doing anything with it. So we can now grab this from our base class. We can set this to default to false, so we won't allow movement to begin with. So if we come in and press play, we're now completely stuck, and so is the enemy, which is kind of what we wanted. And this is where kind of introducing the animation system to you was important. Because remember, I said this is telling us all of the information whilst the animations still happening is being called here. And then as soon as the animations finished, this is being called. So we can now pull from here or we could just drag in the B movement enabled. That would give us a setter just here, and we could set this to be true only when the intra animation is done. So we can now go in and we can test, we can play. We're not going to be able to move until that animation is finished. So again, completely optional, but just trying to bring this as close parity with the Example project as possible. It's a feature they implemented, so I thought I'd cover that here. That's pretty much it, though. It is technically working. As with anything, I wanted to dive a little bit deeper with you. This is definitely 100% option, but I find this is a really, really powerful thing to know about the timelines and a way that we can manipulate playback speed of our animation without jumping into the event graph. Now one thing I think I do want to do, just one final thing to tweak is I'm going to grab the node here, and I'm going to give that a little bit more of an exaggerated movement into. It's going to kind of speed in and then taper off through that animation. So this is purely visual, but this gives you a nice amount of freedom on how smooth or sharp some of the animations are. So I don't want it to overshoot. If we had a curve like this, it will actually go past the target point, which would be around about here. So the target points here, it would go past that target point, and then it would animate back to its target point. So that would look a bit weird. Let's have a quick look. Gonna better actually if I hit Simulate. You can see that the enemy was way past its target point and needed to animate back. So just be careful you don't do anything like that, but I could make it a little bit sharper, so we come in just a little bit faster. A little bit hard to see because there's that loading time, but that's probably perfectly fine. Now the thing I wanted to introduce, though, is, as I've mentioned, one thing I see a lot of developers do is that they might try this and think, Oh, actually, 1 second is too fast. I want this animation to take 3 seconds. So you need to come back up, adjust your length, grab your key frames, move your keyframes across, play around with the curves again. You can see how this is going to get quite fiddly, if you have to keep going back and forth. So we're not going to do that. If you were just following along then, press control that a few times, and we'll get the curve length back to, so the full length back to one. What we can do instead, and this is that trick that I think is really powerful with timelines is we can come back up here and maybe just after we're setting the location and rotation. We can grab our timeline information is actually made into a component. So if we drop down this component in the variable section, we can grab our Anim in timeline details and drop that in here. So we'll get the information for Anim in. From here, we can pull and we can find something called set play rate. So we can see this is the playrate how quickly this timeline is playing. And then if we plug that in here, we now have full control because we know that we've normalized this, so we've made this 0-1 seconds. If we make the play rate, let's just say we'll work with simple round numbers. We'll say two. This is now playing twice as fast, so it's going to take half a second, because if it's playing a default of 1 second, that's its length. If it's playingtwice as fast, obviously it's going to play for half as long. So this whole animation will now be done within half a second. So we basically due to loading, we don't even see that happen. I'd need to simulate. And yeah, it's almost impossible to see. But if we'd set this to something like 0.5, this is now going to take twice as long, so it's going to take 2 seconds. So we have a nice, long, smooth animation now, 2 seconds for them to come in. So we now have with some really basic maths, we now have full control over how long this takes. We can turn this down to something really, really slow, 0.1 is still doing the same curve. It's still doing the same animation, but it's now taking the extra amount of time based on what you do to the play rate here. Hopefully, you can see the value in that, as I've mentioned. It just means that we never need to go back into our timeline. We never need to tweak the curve again because we've normalized this to arrange. And if we wanted to increase or decrease the playback time, then we can just grab this value over here. And I think what I might do, I think 1 second was probably a bit too fast, so I might set this to something like 0.75. We can hit compile and save, go and double check. I think that looks quite nice and smooth, so I'm happy with that. But again, you can definitely play around, tweak the values. And that's the whole goal of these topics as I'm not giving you some predefined specific result to aim for. I'm trying to show you all of the different ways that you can expose and extrapolate some of the values and variables that we're working with and really make the project your own. So if you want them to be really snappy and rotate three times, you've got full control over that with the variables down here. You've got the playwright. And again, it's probably a good time to start considering the cleanliness of your project, as well. We now have a lot of variables. So we may want to start putting these into categories. We've got some magic numbers, so we could also start promoting things like this to a variable. We could promote this one to another variable, call it something like intro role degrees or something like that. Trying to make it somewhat clear. It's the intro animation and not specifically the rotation we want to end up, but the number of degrees it's rolling. And then over here, we could have the animation play rate, so we'll promote this to variable as well. So this is nice because the main thing, as I keep mentioning with variables, even if we're not doing anything specifically with them, this is great because we don't need to dive into our code. We always want to avoid needing to come in and dive into hard coded pieces of our logic. Instead, if we ever wanted to try something quickly, we could just grab the plane base now. We can hit compile, make sure that's been updated. We can grab all of the variables and just find here that we could tweak the play rate either way and any of the values that we wanted to quickly see some changes with. As I mentioned, the same with C plus plus, and it happens in Blue. You end up with a lot of variables, so you may want to start putting things into nice logical categories or something like that, but that would be something for you to do in between topics. Another thing for you in between topics, a small piece of experimentation here because we've only really just taken a brief look at animations. I definitely recommend playing around with things. See if you can get this to play in reverse after it's already played once. Do something like making use of custom events. Remember how we can create our own custom events, how we can call these from anywhere. We could definitely hook something up to call reverse. So again, trying to avoid things like when this is finished, you'll see a lot of code examples come background and have this horrible kind of looping logic here. It'd be much nicer to see something like the custom event you've created, have this one called in code. So if you wanted to play in reverse, something really simple. I said experiment with that yourself, but I've just shown you the actual answers there. But do things like that. Play around with the different properties and features provided by these. Maybe get it to stop midway, reverse from a different point, coming in play with different curves, maybe purposely overshoot or add different keyframes into the curve animation and see what happens at different points. Maybe even add some scaling. So you can make it look really cartoony with them starting off at a really small scale, bouncing into existence by doing some kind of squash stretch animation scaling, and then ending up at their normal scale. Kind of start having fun and play around with the different options available to you. But that's the spawn animations complete. The ships now entering with style, and next, we're going to get onto camera shake and our screen effects. 27. 26 - CameraShakeKnockback: Not done yet. There's still more polish that we can add to this project. We're going to be looking at knock backck for the collisions and camera shakes, ensuring that every hit feels like it matters. We'll look at the knock back a little bit later. That's a little bit more hands on than the camera shake. We'll start here. So to start, we're just going to go to our content drawer and just make sure we've got our folder structure set up. And inside of the main Blueprints folder, we're going to create a new folder here, and we'll just call this one camera shake. We can add a few different types of camera shake to our project. So we can have some intents for the explosions, and we can have maybe some softer ones for the projectiles firing or just hitting. Again, completely up to you, depending on how much impact you wanted to get across to the player. To create a camera shake, we actually need a custom class, which is really nice and easy. We right click. We can go to our blueprint class up here. I'm not too sure where it is in the categories, but quite simply, we can search for camera shake. You can see, because it's something that Epic New a very popular thing to add to many games, they've actually created some custom classes for us to work with. We want this one here, the camera shake base. We've got some legacy options and a custom version of this, but we're just going to work with the rudimentary base version, hit Select, and we'll call this one BP Underscore Camshak Small. We'll double click to open this, and we actually don't need the event graph in the background here. So I'm just going to very quickly close this because a really simple way to get back into data only view in some Blueprints is to close them, double click to reopen them. And all we really need to see are our camera shake properties just here. We won't be programming anything, so we can just work with the predefined properties provided. The main thing we want to change, we have the root shake pattern set to none. We're going to drop this down and search for Perlin. This is providing some nice randomization using some pre generated Perlin noise patterns. If you remember these shortcuts I mentioned many topics ago, this is where things like holding shift and dropping the arrows down here can be really helpful. So if we click this again with Shift held, this folds out every single category. And if we want to fold all of these back in, we can hold Shift, click these back up. And again, that just tucks them away nicely. So because there are so many different properties we do want to access, I'm going to fold down everything, so we can go through these in order and find what so to begin with, we have the location amplitude and frequency multiplier. I'm going to leave these at one. This takes the entire animation that's generated and adds a multiplier to the amplitude and frequency on the X, the Y, and the z for the rotation, location, and the field of view. This is handy if we do want to do some really big changes, but generally, we want to work on a per point basis. So for the small impact that we're making at the moment, maybe every time a projectile hits something, we want the camera to kind of jolt forward a little bit just to show that some impact has been received. So we know that forward is the X, so we can take that and start adding some amplitude to the X location. If we add a value of 200 here, this is an amplitude, kind of a strength which is applied. And then the frequency is how many jolts forward and backwards this will play. So if we just keep this at one, it will just be like a nice single jolt every time a projectile hits. You want to avoid adding too much frequency in a lot of cases because that's what will get people feeling a little bit motionsick. It will make it a little bit harder to read and judge what's happening on screen. But again, all of this can come down to personal preference, type of project, and the general style that you had in. I'm going to leave that as one for the frequency, and I'm taking that in mind, we can go through a little bit faster for the other axes here. So I want this to wobble side to side a little bit more, but with less intensity. So I'm going to set this to 40 and ten for the frequency. Again, ten may be too much side to side wobble, but by selling this to something larger, we can come back and refine that later if that is a problem. For the rotation, I'm going to leave all of these as their default. Quite often, we don't want to rotate the camera that is almost more than anything else, that is almost guaranteed to cause some kind of visual feedback upset, you'll definitely get some motion sickness coming from. Unless you have a really good reason to use it and you know what you're doing. So I'm going to leave the rotation completely untouched. Similar for the field of view, you can play around with these if you want, but I don't want to tweak those too much. The final things here, the blending in nighttime, how long these take to get to their peak and how long it takes to go from the peak back into not animating and then the duration. So I'm going to make this last for 3 seconds. It's going to be a relatively small impact, but it's going to last for a decent amount of time. So we had compile and save. That's pretty much our camera shake class setup. Now, with the core property setup here, whilst we're doing this, we may as well make a few different options for us to drop and test things in. So I'm going to take our camera shake small, press control, indeed, to duplicate this, and I'll create one called medium. I'll duplicate that one and call this one large. So we could have maybe different types of impacts, especially for things like the explosions. I'd probably want a slightly different but much bigger impact because it's not happening too often anyway, and then we can alter or iterate between the small and medium impacts for different reasons. For medium, I'll come in here. We'll take the amplitude up to something like 500, maybe increase the amplitude on the Y to 90, 15 on the frequency. Again, we can always come back and we can change these if any of these are too much. I've just realized actually thinking back on how I wanted to set this up. In camera shake small, I think we want the duration to be 0.3, not 3 seconds. Lasting for 3 seconds. If you imagine me talking now is about 3 seconds. The screen would have been wobbling for the length of that sentence, which is a little bit too much. So we're going to compile that, turn that down to 0.3 because we don't want to overstimulate people visually. So we can do the same thing here. We'll set this to 0.3. We can maybe make this 0.4 because there's more shake going on. There might be reason to have it lingering around for a bit longer. So we'll set this to 0.4. Same thing for large. We want this one to really have quite a big kick because there's going to be feeling as it's quite close to the camera when something actually explodes those big particle effects are playing. So this one makes sense it might be a little bit stronger. So I'm actually going to change the overall multiplier here to really give this some kick. So we'll set this up to five, and then the amplitde, I'm going to set this up to 900. The Y amplitude, put this up to 160. I'm going to leave the frequency at ten. We don't want this going side to side too much. I think the time frame again, maybe around about set this one to 0.5 seconds or just give them small increments, so they're all noticeably different when we're doing our playback and testing. So again, there's no right or wrong. I may have gone up to really high values, maybe even too high, but we can always refine it and bring it back, saying that this is definitely too high. I said 900, but at up to 9,000. That would be almost impossible to see what was happening, so we're going to make sure we tone that back down. Don't say that too high. So we don't have three intensities to test, and we just need to implement this into our code. So nice and simple, is a pre built system ready for us. Going to go back into BP underscore plane base. I'll keep these open just in case I do want to come back and tweak the values at any point. Inside of BP plane Base, one of the obvious ones to handle first would be the death function. So we have our handle Death function. We still have our to do here if you needed to find it that way. So I've got my todo comment, and this is the final effect that I knew I wanted to come back and implement. So we can pull from the execution pin here. We'll search for a function called Play World camera shake. You can see that just here. This is very similar to our three D signs, so our directional signs and even our particle system. So everything again, once you kind of start learning how to do one of the different features inside of Unreal, you somewhat understand how to do the other things as well, if that makes sense. So we've got here, for example, the epicenter, which is the location the shake starts playing from. So, nice and simple, we're going to duplicate this, the get actor location and plug that in. So the camera shake should be starting from which Aber ship has just exploded, and then it will kind of rip light towards the camera is the general kind of idea here. We do this based on a radius, so the inner radius is where this is going to be strongest. So if the camera is right next to the plane that explodes, it's going to get the full force of the camera shake plane. So we'll set this to something like 2000 units of radius. And then the outer radius is where anything outside of that zone probably won't be affected by the camera shake, or if it's really close, it might just get the final sort of end wobble. And I think we'll set this to something like 5,000, and again, you can play around with these if it's happening too much or too intense, you can change the radius and kind of play with that. As I've said, for the explosions, when the actual ships are blowing up, we're going to drop this down, and we're going to search for the BP underscore Cam Shake Lidge, so that's going to be our explosion camera shake. We can hit compile and save, and we can just test this one because we definitely have the functionality ready to Okay, so that's kind of what I meant where some of these might be a little bit too intense. So that is going to be easy to fix. We can I think the radio should be fine because the camera just double checking. If the players start is 130 on the z and the camera is 2,400 units on the Z, that means we're just slightly outside of the inner radius of any of the explosions anyway. So we should be getting a somewhat tapered off camera shake happening. What we can do. I think maybe I just went straight into the multipler little bit too high. But like I've mentioned, that's actually a very common approach to this type of thing is double, driple or quadruple what you think a reasonable value might be and then scope it back once you've kind of tested it. It saves you making loads of really tiny incremental changes which can often take even longer. So that feels a little bit better, maybe 1.5, so we can do a larger override, but keep the general settings here the same. And I think that's fine. So I'm not going to spend too long refining this. That is now watchable. I can see and carry on playing that with too disturbed, so I'm gonna leave that as that is. So that's our large camera shake and again, you can refine this and completely change it to fit your project as you want. So then we get to the question of our projectiles. Should we make this shake every single time a projectile fires or every single time a projectile hits something or impacts? That would be essentially constant shaking, which could be somewhat nauseating. So I think in this type of project, a better approach might be just to make it something which notifies the player that they've been hit because that way we're taking this visual polish but we're actually implementing it into gameplay. So one of the most annoying things for any player of games with combat involved is being hit but not realizing that you've taken damage or being able to differentiate it from the enemy's tan damage. So this kind of solves two different problems because at the moment, we get a tiny bit of feedback if we get hit because we get the spikes on our ship. But it's not super clear that we're losing damage or losing health until we've been destroyed. So this might be a nice kind of fix up to that. This communicates that the player is taking damage. It doesn't show that the enemy is taking damage, but they're about to explode anyway. So it's not quite as important because they don't have as much health to go through. And we're still giving that visual feedback that the particles are impacting the enemy plane. So that's going to be kind of clear. A nice simple way that we can do this, then, with that in mind is we're going to go to our projectile class. We want to do everything projectile related in the projectile class. If you're not already here, I just happen to be right next to my to do comments, so we're going to go to that to do comments again. And in fact I'm going to be a little bit lazy like I've done in the past. I'm going to go to the plane class. I'm going to grab all of this code, so the get actor location and the playworld camera. Press control in S to copy this. Whilst I'm here, I can get rid of this comment as well. We've now actually done all of our to do list. We've got all of the different effects that we're going to implement, so I'll get rid of that. And then we'll head back on over to the projectile base, placed in the play world camera shake, move things around just a little bit. And the logic is very similar. So we want to do the same thing again. We want to get the location that the impact just happened, and we're going to play from there, same radius because we still want the same kind of camera fall off. But all we're going to do is we'll maybe check, see if the small impact is big enough to begin with, and we may want to change this to medium. And that's another thing. If you think this isn't impact enough, you could try changing it. So every single hit provides a shake, but perhaps on the enemy ships, it's only the small shake, and on the player ship, it's a medium shake. Again, completely up to you. We've got different classes now. You can play around with that and see what works. I think for my project, I'm going to leave it just to indicate that the players taking damage, though. Now, there's one thing that we just need to make a little bit of space for, and I want to again, show you the kind of way that I've seen people accidentally break their code. So we know that we've got a condition here. We only want this to happen if we're hitting the player ship. So what you might do is you're throwing a branch here, so we're going to do a branch check. And we want to check. We already know we're tracking the thing that we're hitting, so we can take our overlapped actor. I don't think any of this is kind of new concepts for you. So we'll duplicate this in, so I'm going to go and get my overlap actor, making sure I'm holding that one. Press control, indeed, to duplicate. And we want to check if the thing that we're overlapping, we know it's not the owner, we're already past that problem. But if it's equal to, and we've seen this before already, we can use the get PlayerPawn. So if it's equal to the thing which is currently tracked as the PlayerPawn, only if that's true, do we want play the World camera shake? So we just tide did this up a little bit. And you've probably seen this isn't exactly the biggest problem or issue to solve. Some of you might have already seen it, but if that's not true, if that's false, if it's something else, then we're blocking off this function call here. So, remember this is something just to get familiar with is the order of operation, the flow control of your code. In written C plus plus, is much easier because we'll be using FL statements that we can just drop out of inside of the function body. So it's much easier to kind of keep track of that. In Blueprints, it's very easy to accident stop the rest of the functionality happening when a single branch check has been made. So I just wanted to mention, with this, if you're following this flow where you're just additively updating and improving your code or adding features, it's very easy to accidentally whenever the enemy gets hit now, we wouldn't destroy ourselves. So something that we could do instead is, again, we could solve all of this with the use of a sequence. So if I just move this on over, and this is really just to try hammer home the importance of thinking about code cleanliness, not just to waste time and make the code look pretty in Blueprints, but also because it's literally making it easier to read and maintain and make sure that you're not making these really easy to make mistakes. So if we throw our sequence in here, and again, we want to kind of think of this as do a specific line of functionality, which in our case, is applying damage, and I might be tempted to do is everything which isn't then related to damage, everything which is related to playing an effect. We're going to throw into the then one. So I'm just pressing Control X. And we're going to hook these up. So we're going to apply damage first. And then if damage is applied, we're going to play our particle effect, and we're going to play our camera shake, but only for the player. And then we're going to do the final thing. So again, it gives us a nice order of operation. We can see the exact order in which our functions are going to be called. And like I've mentioned previously, when we looked at sequences, what would make this a little bit clearer is if you gave these comments. So this could be the damage row. This could be the effects row, and this could be the clear up row or something like that. And you could give yourself a reminder of that in comments. So again, just a really quick, simple change, and we now know that regardless of whether this gets fired off or not, we're still definitely going to always destroy our actor because once this has been checked, if this gets called, it's going to then go back up to the next part of the sequence and come back. If this doesn't get called, it's still going to come back to the next part of the sequence and then come back down. So the important thing is that this gets called regardless. So now we can go in and test, and just double check. It's not happening with the enemies. So that was the explosion. So, the other thing is that if the projectiles were hitting each other, it could have potentially caused the camera shake when the projectiles hit each other as well. So now I'm just going to let myself get hit with nothing else happening. There we go. So you can kind of see if I'm not dying at the same time. You can kind of see maybe a little bit too difficult to get hit, but you can see a camera shake, which is the important thing. Like I've mentioned, that may be too subtle. If that is, that's perfectly fine because we have our medium shake that we can test. If we really wanted to make it clear, that the players getting hit. There we go. So that's giving a much kind of bigger indication of being hit. Again, personally, I think that the small one is perfectly fine. We might want to add maybe a little bit more strength to it, but keep the frequency the same or something, but just as a heads up that they've lost health, I think that's clear enough. The next step will be going into things like materials and making a flashing health damage material or something if you really wanted to visualize that. But I think this is already a big step up to what we had before because it's just making things a little bit more visual and a little bit clearer. Thing is we're going to quickly add a simple knockbck when the planes collide with each other. For this one, we want to handle this purely in the player class because that is the only one which is kind of doing a pseudophysics. So I'm going to close all of the other tabs. I think we're done with the camera shakes, and we'll go into the BP underscore plane player. And inside of here, what we want to do is we're going to grab that sphere. We're going to go down to the collision section in the details panel, and we're going to bind the on component hit function here, like we've done in the past. And I'm going to keep this really nice and simple. And again, I think this is the same as the example project anyway, where we can bounce off of the enemies and the walls. So whenever we hit something, we don't need to check what it is. We're just going to take our current movement speed, and we're going to multiply that by a negative value with a little bit extra. Makes the game feel kind of fun and bouncy. And more importantly, again, just giving the player that feedback that you're actually touching another object in the world that has some permanence to it. So we're going to get the current speed, as I mentioned. We're going to multiply this by a negative plus a bit of a value because we don't want to just invert it. We want to add a little bit of a forceful knock back. So let's say -1.5. And whatever this is, we're then going to set the current speed back to that. This is obviously being accounted for in the larping anyway. So the larping interpolation, which is happening with our general movement function is going to try to take our new movement speed and then smooth that back into the kind of the normal speed it should be going and in the direction that we're pressing. So this isn't going to feel too unpleasant, I think. One other thing that we could do here, whilst we've been doing a lot of these camera shakes anyway, thinking about it. And again, completely optional, but just something I'm going to try. I'm going to go into the projectile base. I'm going to copy the code here, the world camera shake, paste this in here, and maybe we will make use of the medium camera shake, so we'll drop this down. Same radius, same Apicenter, and we can just come in and press play. No, that's definitely that's probably too heavy, I was just thinking we could have it shake a little bit when the bounce happens. We'll try and do this with the enemies. There's too much going on there. So, again, trial and error, that may have worked I may have looked quite cool, but it didn't. Maybe we can just make it a small shake. The main thing we want to make sure that we bounce off of the enemies, as well. So we'll just double check. So nicely bouncing off the wall. Bouncing off the enemy. I think the enemies are taking too much damage from our shake. They should I know it was one hit, wasn't it, so that's fine. It's the player that dies after two hits. So yeah, perfectly fine with the hell. And then if you're wondering why this doesn't affect the projectiles are currently we can still be hit by projectiles, but we're not bouncing. We're only bouncing off of the walls and the enemies, which I think looks pretty cool. The reason for that is going back to our collision setup. So hopefully this will help the whole collision structure make more sense, as well. So remember that our sphere on the player is set to hit. So it's only checking for physical collisions with other physical objects. So this is doing a hit on hit check here inside of our player and our enemies. The projectile, although it looks relatively similar, kind of the same this is doing a different type of check remember. This is set as an overlap collider. So this is why it's useful to think about how you're going to set up your different colliders. It means that you can completely obuscate different types of collision setup to react and respond to their different counterparts in unique ways. So that means that we don't need to worry about the projectiles, providing any knockback or kind of physical pushing to other objects. They're literally just flying through space. They're not doing any physics. They're not physically colliding with things. They're just checking if anything else is in the same space. And if something else does end up in the same space, all it's doing is it's trying to apply damage, trying to play some effects and then destroying itself. So that's why we can get away with that. And again, of course, the level bounds are set up with the same collision, so these are set to block the enemies and the player. So that's it. The project is polished. I think that now feels a lot more interesting to interact with. It provides a little bit more of a lively and interesting experience to interact with. Again, a lot of this will come down to refinement. I'm not expecting you to leave the project, as I've given you with these steps so far. If you're not liking the camera shake at all, or you think you can do it a little bit more, that's perfectly fine. Add, remove, change all of those properties as you see fit. And that's just something again for you to test and experiment with in between topics and really get your hands on with the project. And create new camera shake classes if you can see a use case for something which shakes with more frequency or less frequency, but with more amplitude, for example. The main thing is that every impact should feel appropriate to the feedback that the game is trying to emphasize. And that's what's going to take your projects from feeling like a very amateur beginner type of game to something which has a little bit more thought, care and attention applied to all of the different elements that the player will be interacting with. Other things to try is that you could try the wave instead of the pearl in noise as the camera shakes and any of the other topics which I've kind of mentioned, but we won't have time to go fully in depth with every single caveat of all of the different processes. With that done, though, we've got our shake, knock back. The weight and impact of the field is getting quite real now, which leaves the wrap up of creating a UI to handle the full game loop. At the moment, the players just kind of stuck in an endless void if they're destroyed, and we obviously don't want that to be the case. 28. 27 - UI: Okay, so we are on the final topic. This is going to be the game over screen, the user interface, providing a restart system and a proper exit. Ideally, we'd have some buttons to press. But we're going to keep this UI nice and simple. You finish. You can press the I button to restart and then you get straight back into play. In a larger project, you may want to add a menu screen or something like that. But with this, you'll have a complete and playable game. The first thing that we want to look at is the concept of widgets. So the UnreLEngine handles menu systems and UI overlays button presses and things like that through something called a widget class. So inside of our folder structure, I'm going to create a new folder, and I'll call this one widgets or UI. You'll often see this called one of those two when you're looking through other people's projects. Inside of my Widgets folder, we want to right click in here. We're going to go to User Interface. We can see here we have the option for a Widget blueprint. So there's still blueprint classes, but this just allows us to put a kind of visual interface with some code linked to it. We'll select this option here and create a standard user widget. We'll just and then hit Select, and we'll give this one the name of WBP for Widget Blueprint underscore Game Over. We'll double click to open this, and you can see that the layout is slightly different. The first thing is to note on the right hand side, top right hand side, we have two different tabs. We have the designer tab, which is where we are now. This provides a visual layout and a really nice canvas system to drag and drop different elements to set up the way that this will show on screen. We then have the graph, which is our standard blueprint editor, very similar to what you've seen before with just some slight nuances. While still in the designer element here, we're going to go to the left hand side, and we want something called an overlay from these different categories you can see there are a lot of different categories, so I'm just going to search for overlay. And this is just essentially a panel for us to place all of the other important things into. So we'll drop this in, and you can see here this is our overlay in the screen. We just want to make sure that this is a 19 by 20 panel. So we can drag this, and we'll put this. I just need to give myself a little bit more space. We can drop this and we can see it kind of snaps with clicking onto the bounds here, and this will snap into place here. So we have a default high rise 19 by 20 panel. Now, these will be set to scale and change based on the screen that they're being placed on. But we'll just default, assuming we're on a 19 by 20 display, and we can work on that premise. Next, we can add some nice kind of visual to this, as well. So we can search for something called a blur panel or a background blur, and we'll just drop this on. We can see that the hierarchy here, the overlay is where everything will kind of be dropped onto, and then the background blur is a child of the overlay. We can see if we zoom in the background blur is a tiny bit small. We'll find this ever so slightly difficult to see if we were to press Play and have this shown. So what we want to do is on the right hand side, we can align this to our overlay. So if we tell it to take up, fill all of the space horizontally and fill all of the space vertically you can see that is now matching the size of the parent component, which is the overlay. For the blur strength, I'm just going to grab this value here in the details panel, and we'll give this a blur strength of 15, and we can see that's added a slight blur to our panel, which I think is going to look quite nice, kind of modern and just give it a little bit of visual flare. Next, we want to look at how we could format the text that we're going to want to show on screen. So we're going to show something like this, just a simple bit of text saying game over, and then a tool tip for how to restart. A nice easy way to manage multiple elements in a single area on a menu would be using something like either a horizontal buy if you wanted things to span horizontally across your widget or a vertical box. In this case, we're going to go vertical. So if we search for a vertical box, we can drop this on and just make sure that this is a child of the overlay and not the background blur. For the alignment of this one, you can see this is taking up the entire box. Instead, we're going to set this to center line for the horizontal and the vertical settings just here. That's perfectly fine. We can make this scale to the elements inside a bit a little bit later. The elements, as I mentioned, are going to be very, very simple for this. We're just going to give this a text element, so we're going to search for a text, and we'll drop our first text element into the vertical box. And you can see that's reallyal popped out the vertical box to fit the element here. For the initial text, I'm going to set this to say GameOver. So with the text selected, we want to find our text option here. You can see it's currently saying text block, and I'm just going to change this to game over. Change our font size. So at the moment, if we drop down our font properties just here, the size is 24, and I think a size of around about 50 would be ideal for this one. We can change the typeface. Roboto bold often looks a little bit unpleasant. We can just change it to regular or even light can look pretty nice. Just looks a little bit cleaner. That is now looking not quite a impactful. Maybe we want to change the font size to something like 60 to make it look and sound out a little bit better. Now with this text selected, we can press. So we're going to grab this over here in the left hand side or duplicate our text. So control, indeed, to duplicate. And you can see that because it's inside of this vertical box, it's now making sure that this is aligned vertically. And if we kept adding text elements, we'll have this nice alignment in a vertical order. So this just makes it a little bit easier to manage and work with multiple elements when you want them to be roughly in line with each other in the same place. Now for this one, I'm just going to grab the new text element, or change the text to say press R to restart. Probably not the most interesting game over screen, but it gets the point across. We're going to change the size of this a little bit. It doesn't need to be quite impactful as the game over. So we'll give this a font size of 30. We could also give these a little bit of padding. So if I grab the GameOver, first of all, in the details panel on the right, we can give this a default heading of let's say ten, so it gets a little bit of a boider around it. And we can do the same thing with the press R to restart. We'll give this a little bit of a boider as well. And the main thing is we want this to be center aligned. So we can grab the horizontal alignment and make sure that both of these are center aligned. So they're kind of placed in the middle of their vertical box, nice and tidy, and we didn't need to do a whole lot of work. One other thing you might want to do is change the visual colors a little bit. So we could grab the game over. Game oververs are normally represented with a red or an orange or something like that, so we could change the color here to be a little bit more of a negative color, make that just stand out against the input here. It looks a little bit more interesting because we have some more stuff going on. Some more polished passes that you might want to make to this, you can bring your own fonts in really easily to unreil and then you can just change the font that you want to use to make it look a little bit more custom. Play around with things like the boldness, the font size. You can even add things like outlines if you wanted to emphasize certain parts. So if I just zoom in a little bit. I could add an outline of two pixels around and we get an outline to our menu elements just here. So whatever you wanted to do to try make this look a little bit more interesting, as I said, we're going to keep this nice and simple just to show that the game is over and give the player a prompt on what they can do next. So we're going to dive very briefly back into the input system because we now need to have something fire when this user interface is shown on screen. So if we go back into our input system, just recap what we have. We already have our restart key. Now, as I mentioned, in a full game where you've got more of a complex menu system and things like that. You may have different input mapping context classes. In this case, we've just bound everything to a single gameplay, and I think that's kind of fine because we don't have any overlapping uses so if you wanted to use, for example, the back button on a gamepad, so B on the gamepad, to be an input like jump, but you also wanted it to be the restart button, that would be where you might want to share that out. So if you were in a menu, then B would be mapped to restarting. So you'd have your menu mapping context. And then when you're in gameplay and it was jump, then you'd have your gameplay mapping context. In this case, I think just using this one would be perfectly fine. So we're going to go back into our GameOver, and we just want to make that binding that we've done previously. So this is where the code side of our menu comes in, and this is where some of the really cool flexibility of the enhanced input system makes things like this much easier than it used to be. It was definitely possible with the legacy system, but this is definitely a step up. We'll navigate to the graph over here, and these are fairly similar to what we've seen before. So we have an event tick, and in this case, we won't be doing anything on tick in our menu, so we can get rid of this. So the event construct is essentially the same as the begin play. This will happen as soon as the game starts, and the menu is spawned into existence. So when we show the menu on screen, this will be five once. Pre construct, you need to be a little bit careful with. This is useful for editor changes. So we could code things so that we could get live editor updates, but you need to be careful here because if you're referencing other classes or doing things outside of the menu, this can cause issue if you're not yet in play mode. So to avoid confusion, we're going to get rid of this one, and we'll just think of this as our so on Begin play, what we want to do or on event construct is we're going to get to the player controller again, and we want to do exactly what we've done previously. We want to find the enhanced input local player subsystem. This one here with the pinkish purple function icon just there. And again, same as we've done before, we're going to add the mapping context. So multiple classes can have the same mapping context, so we're going to have the mapping context now in our player class, which is handling the input for moving around and firing. We can add that same mapping context to our menu. So when this is spawned in, it will also be tracking the relevant button presses. Now, in this case, this is fine because the relevant button press is only going to be IA underscore restart. So we can right click in here. We'll search for IA underscore restart. We want the event again, so when something happens, we get the execution pin file of when this is pressed. We quite simply want to reload the level that we're currently and we're just restarting the game. We only have one level, so this is nice and simple. There's a function that we can use to do this, a built in feature in unreal. We can use the get current level name. So this function just here, hook this up to started. So again, when this is pressed once, this will return the name of the level that we're currently in, which remember is just called main. So we could do this hard coded and just type in the level main for the level that we're about to reload. But just in case you ever start adding more levels or change the name, this is just, again, a nice, dynamic and reusable way to implement this. So then once we have this, we're going to pull from here, and we're going to search for the function load level, and we're going to search for the function open. Can see the option here, open level by name. Now we get two slightly different variables, so we have current level name returned as a string, and we have level to load returned expected to be fed in as a name variable. Perfectly fine. Again, we can just plug these in. They're both a type of text, essentially, and unreal will make that conversion for us. So this will take the current name of the level we're in main, and it will open the level by the same name. So we're just restarting our right, the other problem I've just realized here is that we don't have any way to leave the game. Something that I'm really trying to help with here is not to have this feel like a complete beginner game by the end of this process. One thing I find when testing and demoing games by new developers is that you're often stuck in their game and you have to alt F to close the panel. So whilst we're looking at the input, we may as well get rid of this problem, as well What I'm going to do is I'm going to duplicate IA underscore restart. I'm going to rename this one to quit. Inside of the IMC underscore main, we just want to add a new mapping. So we know that restart is a boolean, a binary input. So we already have quit set as a digital Boolean, as well, which is perfect. So in IMC Underscore main, we're going to add a new mapping. We'll drop this down and select the quit button. So for testing in the editor, this is a little bit complicated because by default, escape is mapped to end playback anyway. So if we're playing and testing, we wouldn't be able to test. Without disabling our menu system or our quick button. So I'm going to give this. If we were to package this, though, and give it to somebody else. I do want the escape key to just close the project. There's going to be no menus, there's going to be no pop ups, just a quick way to close the project, so at least I'm not stuck once I've built the project out. For editor testing, what I quite often do is I'll give myself another binding, and I'll set this to something really random that I otherwise wouldn't be pressing inside of my gameplay. So I might set this to be the delete key. And this means that what I can do is I can go back into my player class. So I'm going to go into the core the plane player. And again, we're getting away a little bit from ideal ways to set this up now. In a real project, you would probably do this inside of a player controller or different menu systems, but we're not going quite that deep to make full menu systems. So we're just going to drop this into our input system here. So we have our input graph. We're still keeping things nice and tidy, at least. This is still inside of our IMC underscore main. So that means that all of our new bindings are still being tracked. And what we can do is we can find the IA underscore quit. And when this button is pressed, so we want the event again. And when this button is pressed, so started, we just want to call the built in function, which is quit. So quick game. And this will just automatically close the instance of the game for you. So if we hit compile and save, we can test that straightaway. Remember that in play mode, if I press Escape, it would have closed anyway. So I'm going to press the delete key, and it's just ended playback. So that will work for our packaged version, as well as our editor version here. So we don't need to do anything else here. And I just wanted to introduce you to the quick functionality, because like I've mentioned, it's really easy to overlook, but it's super, super simple to implement. And it's just one of those things that takes your initial kind of feelings and grading of a game that you're playing just takes it up a step and makes you feel like you're playing a game made by someone with a little bit more experience. Final thing is we want this user interface to show when the player dies. We know exactly when that happens because we have our handle death function inside of the player class. So with this, we're going to find our handle death function. We want to go to override, and we want to find Dyna handled. We want to make sure that we're still calling all of that important parent stuff, so our effect and everything like that is being handled. But just before that happens, or maybe yes, just before that happens because we're calling Destroy in the parent version, we want to spawn in our user interface. And we can do this really simply, so we're going to pull from here and we'll find a function called Create Widget. So this is a function to create one of our blueprint widgets. We can plug this in here, and we want to find our WBP underscore game. Now again I'm going to make a little bit of a point of this just because you'd be surprised how many people forget the next important step. That kind of feels as though this should work. But if I quickly go in and allow my plane to be destroyed, we're calling that function, we've given it the certain widget, but we're not seeing that widget on screen. So again, very common thing. Even though it feels like that's everything we need to do. There's actually one extra step. We want to pull from this return pin. So this is created in the background. There is a game over widget just kind of lingering around in memory. We want to take that reference and memory and we want to say add to viewpoint. A special function called Need to Make here, and then we're just going to hook all of these up. And now, this will actually work. So if we come in and press play again, just get my planes to be destroyed, and there we go. So this created that menu, but it's also added it to the viewpoint. If I press R, we've got that binding in the menu, so we now have a restart, as well. So we can come in and restart the game really, really easily now. We're going to end up with the same thing. And if I press Delete, something I overlooked there. I was going to say, if I press Delete, I can quit. I can't because the players been destroyed. And remember, our binding for quit is in the player. So something we might want to do a little bit of duplication. We're going to take our IA underscore quit and the quit game function here. We're not going to remove it here. We want it in both places. We're going to go back into the game over just going to paste this down here. So when the player has been destroyed, they no longer exist. We obviously cannot quit the game. So we're going to move that functionality over to the GameOver widget instead. So perfectly fine to do. Just make sure that the player still dies. And then now if I press delete, we can close the game. So same functionality, we just always need to account for if something's completely removed from game, completely removed from memory, we need to make sure that all of the functions that were relevant are still available somewhere else. And again, if you're finding, like when I was looking at that, I feel as though we had quite a lot of space on the screen there. So I think what I might do is just a few very small tweaks is maybe make this really big double the size of our game. And then double the size of our tool tip here. So it's nothing major, but it's just going to again, just getting a general kind of fill and making everything look as good as possible, trying to present the game in the best way. So yeah, probably a much better kind of general style there. Press to restart, everything's working. So pretty perfect. I think for a beginner project, a very simple kind of remake of something that exists, but trying to expand and improve, especially the code base where possible, but also the general feedback and fill and visuals will hopefully be a nice kind of learning point and step in the right direction for your future projects. But that's pretty much a wrap on this. So a few more things for you to kind of take away in those learning points. You can play, shoot enemies, get hit, die, game over screen appears, and you can do the whole thing again. Everything looks quite nice. We've got a kind of semi blurred, modern looking, simple user interface happening. You can press escape or delete and close the game from anywhere. So again, we're not making this feel like a complete newbie project where you're stuck and you have to alter F four out of someone's weekend project. So hopefully that's a nice improvement on what you might have seen as well. So we have a full complete game loop. We can play, die, restart, and quit. So over the space of all of this time, we've gone from an empty project to a complete game, movement with frame wrate independence, projectiles, dynamic materials, enemies with inheritance, and a brief look at components. We've got spawners with randomization, ensuring that they've been made in a generic way, but generic in a good way that we can create the class once and use them for multiple properties. Looked at particles, audio, camera shake, knock backack, and now UI. So this is, hopefully, a good foundation. You can build upon this. As I've mentioned, it would be great if you add some unique enemies, try and give them different movement patterns, different weapon patterns, maybe speed them up and just make them kamikaze enemies with known projectiles. Whatever you're feeling for your extra steps to make this project unique. Bring in some new assets and share your progress and updates with other people. Could add in things like scoring as well, which would be really easy to do through the shared handle death function. We know exactly when the enemies are dying, so you could build upon that and create some kind of scoring system. So really make this project your own. Take references from other games, look at the content examples, and I definitely recommend that at this point is actually look at the content examples. Take a look at the blueprint input map and see exactly where this project was inspired from and compare the code that you have against the code in that project. And again, this isn't to pass judgment, but it's just to see that there are always different ways that you can approach a single problem. And see what your take is on what we have versus the original project. But the important thing is that you've now built a game, and that isn't nothing. That is an achievement, and even more so if you've been taking those extra steps, and you really feel as though you're beginning to understand at least a little bit of what you can do inside of Unreal. Some of this will take a little bit of repetition. You may need to go back and try things again, but being able to tweak and implement these features by yourself is really the big first learning hurdle that I think a lot of people get stuck on. So thank you for following along seriously. If you've got this far, I truly appreciate you dedicating your time to something I've helped create. But I go and continue making things. That's the whole point here.