Learn Godot Game Development: From Basics to 3D Shooter
Course Overview
- Create seven diverse games using Godot engine: runner, platformer, farming, monster battle, 3D space shooter, 3D platformer, 3D shooter
- Suitable for complete beginners: no prior coding knowledge required
- Option to access full course on Patreon or Udemy
Godot Engine Basics
- Understanding video game frames and real-time frame creation
- Godot’s scene and node system as building blocks for game elements
- Importance of scene trees, nodes hierarchy, and parenting
- Using 2D and 3D viewports and editor customization
GDScript Fundamentals
- Writing and attaching scripts to nodes
- Variables, data types (strings, integers, booleans, arrays, dictionaries, vectors)
- Functions with parameters, return values, and scope
- Conditional statements (if, elif, else) and loops (while)
- Using signals for event-driven programming
Practical Coding Exercises
- Create a Godot stick figure using nodes and scenes
- Implement bouncing sprite with position constraints
- Perform math calculations like Pythagoras’ theorem using GDScript
Game Development Projects
Frogga Style Game
- Project setup: scenes for game, player, backgrounds
- Applying sprites and configuring viewport and resolutions
- Player movement with keyboard input and vector math
- Camera setup to follow player with smoothing and limits
- Collision handling with physics bodies
- Scene organization and node management
- Spawning cars via timers and adding animations and colors
- Managing game flow: title screen, score timer, scene transitions
- Sound integration for background music and effects
Metroid Style Platformer
- Advanced animations: animation player and tweens
- Tile map layers for building levels with collision shapes
- Player movement including jumping and shooting with reload timer
- Implementing enemies (drones) with area detection, movement, health, and explosion animations
- Shader basics and flash effects on hit
- Using groups to manage multiple enemy instances
3D Space Shooter
- Transition to 3D: nodes for 3D objects, meshes, and camera
- Importing and setting up GLB 3D models
- Lighting systems: directional light, point light, shadows
- Basic 3D player movement using CharacterBody3D
- Shooting mechanics with lasers and meteors
- Spawning meteors with randomized positions, speeds, and rotations
- Collision detection and game termination on impact
- Materials and shaders for advanced visual effects
- Obstacles placement and randomization
- Camera positioning and smooth player follow
- Final polish: adding sounds and animated effects
For strategic insight into shooter game mechanics and player tactics, check out Mastering Positioning in Third-Person Shooters: Essential Strategies.
Tips and Best Practices
- Organize scenes and nodes to keep clean structure
- Use Godot’s built-in documentation and editor hints liberally
- Employ signal connections for decoupled event handling
- Manage collision layers and masks to optimize interactions
- Use delta time in physics updates for frame-rate independent movement
- Leverage tweening for smooth animations
- Customize shaders for enhanced visuals
For a comprehensive guide on Godot horror game creation, which complements skills in scene and node management, explore Godot में बिना भूत के हॉरर गेम कैसे बनाएं: पूरी गाइड.
Conclusion
By completing this course, you will gain a solid foundation in Godot game development, covering 2D and 3D games including coding, scene management, animations, physics, input handling, audio, and user interface design. You will be equipped to create complete games and expand into various programming areas confidently.
[Music] Hello. In this course, you're going to learn how to code by making games in
Godot. We will create seven games in total. A runner game, a platformer, a farming
game, a monster battle game, a 3D space shooter, a 3D platformer, and a 3D shooter.
All the games will be made in the popular game engine Godot, which is entirely free and super easy to use.
By the end of this course, you will be able to create games and you should feel comfortable coding. Meaning you can
branch out into other areas like data analysis, AI, or app development. For this course, there are no requirements.
I will assume that you have no coding knowledge at all. Although, if you can code already and
you just want to learn Godot, you can skip the first part and jump straight into game development.
You can get this course on Patreon for £5 a month. On there, you also get all of my other
courses and you see future courses early. And if you don't like subscriptions, you can buy this course
on Udemy for about $10. This is why for the super regular discounts. And if you don't want to buy anything,
this video contains the introduction to coding and three of the games. The runner, the platformer, and the basic 3D
shooter. This will teach you all the basics of coding and Godot. So, I hope you enjoy.
Before we start coding, we need to talk about how video games work to understand what a game engine like Godot does.
Fundamentally, any video game is just an interactive movie. Like in a movie, we are playing
individual images in sequence. And for some reason, these images are called frames. If that happens fast
enough, it looks like a movement. For a movie, that is usually 24 frames per second. For a video game, you want
to have at least 30. Now, the difference between a movie and a video game is that video games
dynamically create each frame depending on player input, enemy movement, timers, and a range of other options. So, what a
video game engine like Godot does is it first calculates where stuff needs to be.
For that, it checks player input, the movement of every object, if an enemy was defeated, and loads of other things,
whatever you specify. And once all of that was calculated, we are drawing one frame.
This we do at least 30 times per second until the game is over. This is how any video game works.
So, Godot basically is giving you tools to capture user input, it lets you draw images and 3D objects, it can play
sounds, and you can use it for lots of other more advanced things like physics calculations, light simulation, and even
online communication. Godot is a really powerful tool and it is entirely free and open source.
To get it, all you have to do is go to godotengine.org and click on download latest.
The website should then give you the right download link. If you want something specific, just
scroll down. Godot works on basically any platform. Just choose what works for you.
Once the download is done, unzip and open the folder and then click on the icon that doesn't say console.
You should be seeing a dialogue like this. And yes, Godot does not need to be
installed. Just keep the downloaded folder somewhere safe so you don't accidentally
delete it. With this dialogue, you can manage all of your Godot projects. At the moment, we don't have any.
To fix that, you can either create a new one or import an existing project. I want to create a new one. Then you get
a create new project dialogue where you can give the project a name. Let's just call it test
project or whatever you want to call it, doesn't really matter.
After that, you get a project path where you can select where you want to store it.
Just choose a good spot. Ideally, create a folder specifically for your Godot projects.
Also, keep in mind, Godot is going to create a folder by default inside of this path.
If you don't want that, just disable this toggle. After that, we have three options for
the renderer. Forward plus, mobile, and compatibility. On the right side, you can see a bit of
information about them. Forward plus is going to give you the most options and the best look.
I'm going to use this one throughout this entire tutorial. However, if you want to make, for
example, mobile games, then you have to account for less powerful hardware and more limitations. Compatibility is
mostly for browser games where you have very limited amounts of hardware power. Now, in our case, we only care about
forward plus. That's the most straightforward one. You can also use version control, but that we don't
really care about. Once you have all of that, click on create.
And here we are. This is what you see when you start up Godot for the first time.
Now, I will be talking about the different elements in just a second. First of all, though,
you can customize this thing quite a bit. For that, you want to go to editor and
then editor settings. In there, you have interface and editor, which allows you to customize a lot of
different things. For example, if all of this is too small for you, you can set the display scale.
The default is auto at 150%, but this you can increase. Which you can even push further with a
custom display scale. In my case, to make sure all of this is visible, I'm going to set it to 2.5 and
then the display scale to custom. That way, we're getting this scale. If you now go to save and restart,
Godot is asking if you want to save the current scene, which I do want to do. And then we get the same editor with all
of the elements quite a bit larger. Now, in your case, you probably don't have to do that. Just choose something
that fits your needs. Most people leave it as it is, which is totally fine. But in my case, lots of people watch these
videos on a phone. For those purposes, this thing has to be larger. Besides that, you can go to theme and
choose a custom theme. Under preset, default is this bluish color, which I wouldn't recommend
because blue is just a bit distracting. My default is gray. If you have that one selected, after a
second, Godot is going to change the look and there we go. We have something much more neutral.
Finally, if you want to hide the top bar, you can press shift and F11
and then you are in full screen, which is what I'm going to go with, but in your case, just choose whatever you
prefer. Cool. With that, we can start working inside of Godot. Let's talk about the different UI
elements. The really big thing right in the middle of the screen is the viewport.
At the moment, it's 3D. If you hold the middle mouse button, you can rotate in here.
Later on, we are going to explore this quite a bit more, but for now, it's not too important. Instead, if you look at
the top, we have 2D, 3D, script, game, and asset library. We are mostly going to work in 2D for
now and in there, we have a 2D space. Besides that, we have script in which we are later going to have all of our
scripts. Then we have game and finally, we have the asset library, which allows you to download different kind of
plugins. We are going to have a deeper look at this later on, but in the most basic
sense, it allows you to expand Godot by adding specific tools. And by the way, you can switch between these different
modes using the F keys. F1 is 2D, F2 is 3D, and F3 is script. Those are the ones you're going to use all the time. For
now, I want to work inside of 2D. Inside of this scene, you can place a whole bunch of elements.
Those you usually get from the file system that you can find in the bottom left.
By default, Godot has one folder with one image file, icon.svg. This is the Godot logo.
To add this to your game, in the most basic sense, all you have to do is drag and drop it into the viewport and then
you have one image that you can move around. And well, you can also scale it
and do quite a few things. I'll talk about this in a bit more detail later on, but for now, I want to undo it with
control Z. While you could simply add a graphic into this viewport, this is not usually how you're supposed to approach
it. Instead, you want to look at the scene panel. This allows you to create a scene tree
where we can create a 2D scene, a 3D scene, a user interface, or create another node.
If you click on other node, you can see all of the nodes in Godot. And nodes in Godot are incredibly important. I'll
talk about those in just a second. But basically, in here, we have a whole bunch of nodes that can do different
things. The easiest one is a timer, which is just a basic countdown timer.
I think this one is self-explanatory. Besides that, we have an animation player or if you look under node 2D,
we have an animated sprite 2D, which is just an animated picture. We have GPU particles, we have a camera,
we have a collision shape, and if you look under collision object 2D, we have, for example, an area 2D that lets you
check if something entered an area. For now, don't worry too much about these nodes. I'll talk about them in a
lot more detail in just a second. I guess what we can do for now is drag the image back into the viewport and then
you can see inside of the scene tree, we have one node called icon. That's because this file name is called icon.
The actual node is a Sprite 2D. If you hover it, you can see type Sprite 2D.
Once you have that, you can look on the right side. There you have the inspector. This gives you all of the
properties of this node. For example, under transform, we have the position, rotation, scale, and skew of this one
node. If you change these values, you can see they update inside of the viewport in
real time. And in there, you can do a ton of different things and
manipulate this node to your heart's content. Although not something I want to do,
let's undo all of this. That way, we are ending up right here. Every node has a ton of properties that
you could be changing. And being able to change them is pretty much what gives you a game.
And all right, with that, we have the basics of Godot. So, let's talk about how we combine all of these different
elements. To understand how Godot works, you have to understand scenes and nodes. They are
the fundamental part of literally any game made in Godot. Let's start with nodes.
Those are the fundamental building blocks of literally any project in Godot.
They could, for example, be images, timers, sounds, 3D objects, animation players, and really a lot more. Godot
has hundreds of nodes. Back inside of Godot, if you click on this plus icon, you can see all of the nodes.
There are lots of drop-down menus in here. If you click on them, you can see everything that's available.
Now, the way this is supposed to work is that you are combining different nodes inside of a scene. Scenes actually have
two purposes. Number one, they are container for nodes. In other words, you're combining different nodes inside
of a scene. Besides that, scenes also manage what is displayed inside of the game.
For example, a scene could be a level. Imagine a Mario level where you have a fire world, a forest world, a water
world, and so on. Each of these levels would be one scene and you can tell Godot which one to
display. Now, this is a concept we really have to practice. So, let's have a look at all
of this in practice right away. Back inside of the editor, I want to get rid of this Sprite 2D by simply deleting it.
Godot is going to double-check I do want to delete it. After we have that,
I want to create a root node. For now, a 2D scene. This node we can rename to, let's say,
first scene. Usually in Godot,
you are going with this naming style where you start with a capital letter and then you don't add spaces and
instead you are adding for the second word another capital letter. You could add spaces, but when it comes to coding,
those can be really confusing. So, I would recommend to not do that. This will be our first scene, which I want to
save. This you can do either by pressing control S or inside of scene, you can save the scene.
Then Godot is asking you for the location. I want to keep the default one here. So, click on save and then in the
file system, you can see first scene. Also, if you look at the viewport, we have our first scene.
And inside of this scene, we have one node at the moment, a very basic Node 2D. This is our root node.
To this, we can add other nodes. This you do by either clicking on the plus icon or pressing control and A. In
either case, you get to create new node. In there, you can select any of these nodes. There is kind of a color skin
going on. 2D nodes are blue and they are inside of Node 2D. If you click on the drop-down menu, you
can see all of the 2D nodes. Green ones are for user interfaces and there, for example, we have text and
text input. There's quite a bit more in here, but for now, that doesn't really matter.
Then we have red nodes. Those are for 3D games. These tend to be a bit more advanced,
but for now, it's not too much of an issue. Finally, we have other nodes and those
tend to be white-ish. For example, in there, you have a timer or you have an animation player
that can be used for both 2D and 3D games. I think at the beginning, this can be quite overwhelming. So, don't
worry too much about any of this. For now, you primarily want to work inside of Node 2Ds.
And one of the easiest nodes that you're always going to start with is a Sprite 2D.
And at the moment, I can't find it. Oh, there it is. Sprite 2D. Now, most of the time, you don't
actually go for this menu manually. Instead, you search at the top.
I want to have a Sprite and then Godot is already going to a Sprite 2D. This I want to use, so click
on create. Then we have our first scene and a Sprite 2D.
This Sprite 2D at the moment is not visible. If you zoom out,
then you can see that, well, we can't see anything. That is because this Sprite 2D needs to
have a texture, which you can see inside of the inspector. At the moment, the texture is empty.
Now, if you click on the drop-down menu, Godot is giving you a ton of options. For example, you could create a simple
gradient 2D and then you are getting something that you do want to customize. So, in there, you want to drag and drop
the points around and then you get a proper result. Although not something I want to do. So,
let's undo it. Instead, you are supposed to drag a texture in there that you're getting
from the file system. In my case at the moment, we only have a single file, icon.svg.
Drag this one inside of texture and then finally, you can see the actual image. And once again, inside of the
inspector, you can transform this thing to your heart's content. Like you have seen a few minutes ago.
By this system, you can add more and more nodes to your scene. For example, we could be adding another Sprite 2D
with another Godot logo and then move the second Godot logo somewhere over here.
To make those two distinct, I could go to visibility and then modulate. This one lets you add a color tint. If
you click on it, you can change the color quite a bit. Let's make the Godot logo quite red or
really whatever you want. It doesn't really matter. With that, we have two Godot logos inside of the scene.
Or in other words, we are building a scene tree. This is effectively what we are creating in here.
Now, really important thing that you do have to understand is that these nodes are related. For example, this Sprite 2D
is a child of the first scene node, which means when we are changing this first scene, the changes also apply to
both of the children. If I go to first scene and then to transform,
there we have rotation. If I rotate this node, the children rotate as well. However, if I rotate a child on its own,
like this Sprite 2D, then the other nodes are not affected. In other words, a parent affects the
child, but the child does not affect the parent. On top of that, these two nodes right now are siblings.
They both have first scene as the parent, but otherwise, they're not really related, which means if you move
one, you don't affect the other. However, what you could be doing is attach this Sprite 2D 2 by simply
dragging it onto Sprite 2D and then Sprite 2D 2 would be the child of Sprite 2D, which itself is the child
of the first scene. Because of that, when we are changing Sprite 2D, we are also changing its
children. And if we change the first scene, then we are changing all of them as well.
I hope this system makes sense. Basically, once a node is a child of another node,
it is affected by all of the changes to the parent. A super important thing to understand. Now, besides that, if you
want to create a new scene, all you have to do is click on this plus icon. Godot already tells you add a new scene and
then you can create another scene. Inside of this scene, I want you to do an exercise.
Create a Godot stick figure by using Sprite 2Ds. The end result should look something
like this. This should happen via nodes and scenes. Pause the video now and see how far you
get. Inside of Godot, I want to create a 2D scene
that I want to rename right away to stick figure.
To this node then, I want to add a Sprite 2D. You can do this either via what you had
selected before or inside of recent nodes. Click on create and then once again, we can't see
anything. For that, you want to drag icon.svg into texture.
Then we can see Godot and let's say this part could, for example, be the torso. To make that look a bit more realistic,
we want to scale it up by using these points or by going to transform and then increasing the scale on the Y axis.
By default, those two scale together. If you want to scale one individually, click on this chain icon and then you
can change these individually. Alternatively, you can also drag these points and then change all of this
dynamically. This gives us the torso. Attached to that, I want to have another Sprite 2D
which should be the head. For this head, once again, we want to add icon.svg to texture. Once we have
that, we can't really see a difference, but if you move the head to the side, you can see that, well, the head,
because it's a child of the torso, is going to get the same scaling behavior. In other words, if I change
the scaling of the torso, we are also affecting the head. Which is usually not ideal, but for now,
what we can do is simply scale this thing quite a bit down to something like so and so
and then move it on top of the node. That way, we're getting some kind of a head.
Besides that, once again, I want to add another Sprite 2D, which you can also do by simply dragging icon.svg into the
viewport. That way, Godot is automatically creating another Sprite 2D with the name of the file you just
added, which is not what I want. This should be, let's call it the right arm. Which at the moment is not a child of
torso, so it doesn't get the scaling behavior applied to it. That's totally fine. This thing, I just
want to scale down a bit to something like so and then, inside of transform, I want to
rotate it to something like this. Next up, I want to press control and D. That way, we are duplicating the node, and
this gives us the other part of the arm. Which we want to rotate as well. Let's say something like this.
And I guess right arm two should be a child of the right arm. That way, if we're changing the upper arm, we're
changing this node as well. Next up, I want to add another node. This is going to be
the left arm. Which at the moment is a child of the right arm, which I don't want.
I simply want to drag it onto stick figure, and then left arm is not a child anymore.
Because of that, if you go to transform, we can undo all of these changes, and then we get the default Godot logo.
And this I want to once again scale down a bit and rotate so it looks a bit more like an arm.
Then we can duplicate this thing again, move it here, and transform it just a bit more.
Let's say something like this, and then left arm two should be a child of left arm.
Two more things I want to add, and for that, I simply want to add the other Godot logo, and once again, this should
be a child of stick figure. Then I can undo all of these changes. And for this part, I simply want to add
a leg. So, let's make this a bit thinner and a lot longer and move it down here.
This should be the right leg, which we can duplicate and then move it to the side.
This would be the left leg. And with that, we have a very basic Godot stick figure.
All of this, I want to save as stickfigure.tscn. Click on save, and now, inside of the
file system, we have stickfigure.tscn. Looks pretty horrendous, to be honest, but I think you get the idea. Now, once
we have that, what is super important to understand is that this scene can be added to another scene.
For example, to our first scene, we can click on this icon, where we can instantiate a child scene.
If you click on it, you can see all of the scenes inside of the project. At the moment, we only have two. First
scene, that's our current scene, and then the stick figure, which is what I want to add.
If we are doing that, we are getting the stick figure. At the moment, it's rotated because
first scene itself is rotated by a certain amount, which I don't want to have.
If I undo that, then we have the stick figure inside of our first scene. And I hope
you understand how this system comes together, because it's really important to understand. For example, later on,
the stick figure would be replaced by a proper player, which has all of the parts that the player actually needs.
And then the first scene would be a level. That itself is a scene with all of the
parts of the level, and one part of this level is the player itself. So, effectively, the scene is going to be
what you display inside of the game, and the scene is something that organizes different nodes. It does two things. If
you then combine different scenes together, you can create pretty complex projects that are still maintainable,
because you only ever touch small parts of it. We are going to practice this sort of system a lot more throughout
this course. I just hope for now you understand the absolute basics of it. Two more things that I absolutely want
to cover. Number one, let me move this thing a bit down here, this thing here, and this Sprite down here.
All of this should be inside of this blue frame. This is what you actually see when you
are running the game, which by the way, you are doing via these buttons up here. The most important button is run
project, or F5. If you click on it, Godot is asking you what the main scene is going to be. You can select one
manually, or you can select the current one, which in our case is first scene, which is what I want to do.
If you click on it, then the game is going to run, and we can see the stuff that we have just created.
On top of that, on the top of this window, we get a lot more things that aren't too important, so don't worry
about them. Although, the one thing you do want to worry about is the resolution, which at the moment is 1152
by 648. Now, if you resize this window, this can change. You can also go to project, project
settings, and then, under display and window, set a custom window resolution. Although,
that's not too important at the moment. Besides that, what can be really confusing is the positioning inside of
this window. If you click on first scene, we have a position of zero and zero. This is in the top left. You can
see it via this red cross dot thing. If you click on the first Sprite 2D, this one here, it currently has a position of
151 and 168 for X and Y, or the horizontal and vertical axis. What do these numbers actually mean? To
understand them, you have to understand how the window works. The origin point is in the top left. This is position
zero for X and zero for Y. If you go further to the right, then X is going to become larger and larger
until you reach the right side. This at the moment would be position 1152. In other words, this for example could
be 100, this could be 200, and so on until we reach the right side of the window.
I think this part makes sense. The further right you get, the greater X becomes. On the Y axis, this gets a bit
more confusing, I think. To go down, you want to increase the number. For example, this would be 100 pixels. If
you go down a bit further, you'll be at 200 pixels, and if you keep on going down, at some point, you reach the end
of the window, which I think was 648. This can be sometimes counterintuitive. You want to increase the number to go
down. Most game engines work like that, where the origin point is in the top left, and
if you want to go up, then you have to go with negative numbers. So, this part here, for
example, would be negative 100. Especially in the beginning, this can be confusing, but once you work with it for
a bit, it does become fairly intuitive. And just to play around with it for a bit,
if you have this node selected, I can set the value to zero and zero. That way, we are right in the center.
If I then increase this value for X to, let's say, 50, we are going further to the right, and
if I set the Y value to 100, we're going quite a bit down. If I set Y to negative 100, then we are
going up. And via that system, you can place different nodes inside of this window. And well, with that, we have the
foundation of Godot. At this point, you just have to learn about more nodes that do more things, and you have to learn
about GDScript to make all of this interactive. To add a script to a scene, you want to have a node selected, and
then click on this icon. Via that, you can attach a script. Let's stick with our first scene, and
then we can select the language. The default here is GDScript, that's totally fine.
Then we have inherit. This has to match the type of node that we are working with, and usually, you
want to stick with the default. The next important field is the path where we want to save the script that we are
creating. Most of the time, you can stick with the defaults and simply click on create. Once you have that, inside of
the file system, we have firstscene.gd. And we can write some code in here. That way, we can create the logic for our
game. From this point on, you can continue in two ways. If you don't know any coding,
simply keep on watching from here. I will talk about of coding. If, however, you already know some coding, even just
a little bit, I would recommend that you skip ahead and start with the first game. If you
have done any kind of coding before, you should be good to go already. And of course, you can always go back and look
at the coding part. I guess if you are not sure, here are a couple of code snippets that we are going to use inside
of the first game. If this doesn't look overwhelming, you safely skip ahead. Let's get started with coding. And for
this part, I will assume that you have never touched a programming language before.
Don't worry, it's actually not that hard. In the most basic sense, all that we are
doing is create some kind of logic. For example, if you want to move an object to the
right, if its position is less than 800 pixels, it would look something like this.
We start by creating an if statement by writing if. Then we are checking a value that can change. So, this position X
could be part of an object that is moving to the right. This we are comparing against a static value, in
this case 800. This condition can either be true or false. If it is true, then we are doing the stuff that is
indented on the next line. And if that is the case, we're getting the current position X,
and we are assigning it a new value, which is going to be the current value plus five. So, we are moving it further
in a certain direction. And that's really all we are doing most of the time. The majority of the logic
isn't going to get that much more complicated. In practice, this kind of logic can be created in multiple
different ways. Godot is actually supporting multiple coding languages. The default one is called GDScript,
which is really really easy to learn. It is actually very similar to Python, which is one of the most common
programming languages. If you know GDScript, you basically know Python. And Python can be used for a ton of other
things, like data science, databases, AI, and so on. Besides that, Godot does support other languages, but I'm going
to focus on the basics. So, we're going to work with GDScript. Once we have that, to actually write code in Godot,
you want to attach a script to a node. And via that, you can change the property of that node, like the
position, size, rotation, and anything that you can find inside of the inspector. Or in other words, back
inside of the scene that we have seen earlier, if you click on this Sprite2D,
you can change any of these properties, like position, rotation, scale, and skew.
You could also change the visibility, modulate, and really anything that you see in here can be changed via GDScript.
All you have to do is hover over the property, and then Godot is telling you how you can change it. And there can be
a ton of information in here. You can also change other nodes, create new nodes, target other scenes, and even
get data from the internet. There's basically no limit to what you can do. Godot is incredibly flexible and lets
you do a ton of things. We are nearly ready to start. There's just one more thing that we have to
cover, and that is that Godot organizes the code via functions. Functions, in the most basic sense, are
just blocks of code. Via those, you are grouping different lines of code together to do a certain thing.
For now, we are going to work inside of two functions that are inbuilt into Godot. The ready function, which is
executed when a scene is ready. You'll see in a second what that means. Besides that, we have the process
function, which is run on every single frame of the game. And before I start throwing too much theory at you, let's
have a look at all of this in practice. Back inside of Godot. This is getting a bit too complex, so I
want to create a new scene, which is supposed to be a plain 2D scene. Let's rename this one to coding one.
This I want to save right away. You could create another folder in here if you click on this icon, but in my
case, I'm going to leave it as it is. Click on save. And now,
we have a scene with one node, coding one, which is totally fine for now. To this scene, I want to add a script,
like we have done before. So, attach a new script, and leave all of the defaults here in place.
Then create. And now, to actually do something, we have to work inside of a function,
because remember, all of the logic in Godot has to be inside of a function. To create a
function, you want to write the keyword funk, and then the name of the function.
You can basically choose any name in here, but there are some special names. They usually start with an underscore.
If you simply type an underscore, then Godot is suggesting you all of the inbuilt Godot functions.
The one that I want to work with is ready. If you click on that, you get ready,
then two parentheses, an arrow, and then void. This last part we actually don't need, so you can get rid of it to keep
things easier. This would be the most basic way to create a function.
You have the keyword funk, this always has to be there, then the name of the function, and then
two parentheses. We will see later why these are important. If we only type this, Godot is going to
complain, which you can see at the bottom, that Godot is expecting an indented block
after the function declaration, which means we have declared a function here, and after that, if you press enter,
Godot is creating an indented line, which basically means that we are pressing tab, and whatever we are
writing next is going to be indented. Having this line being indented means it is part of this function.
That way, if we later on create another function, let's call it something,
then anything that is indented afterwards is part of this function. Or in other words, via indentation, you
can group things together. So, what can we actually do inside of this ready function?
And well, the most basic thing that you want to do is to print data for now. For that, you simply want to write
print, and then use parentheses again. After that, whatever you write inside of this
print statement is going to be printed out once we are running the game. For now, let's simply write a number, 1
2 3. Could be any number for now, doesn't really matter.
Once we have that, we want to run this one scene, coding one. If you simply click on this icon,
run project or F5, then nothing is going to happen because earlier, we have set this first scene as
the main scene, which means no matter where you are, if you are clicking on this icon, you always run the first
scene. If you don't want that, inside of coding one, you can click on
this icon here, where you are running the current scene, or simply press F6.
If you are doing that, then the game is going to run, and if you look at the bottom,
there we have the output tab, and this at the moment says 1 2 3. That we are getting because inside of script, we are
printing 1 2 and 3 once the scene is ready. Alternatively, what you can also do,
inside of project, project settings, you can go to run, and then set the main scene.
I guess for now, we can set the main scene to coding one. Then I can close this dialogue, and
simply run the project. And now, we are running coding one via F5 or this button.
At the moment, we are using two inbuilt functions of Godot. Print is a function, and ready is a function. But this might
be a little bit confusing, because for the first ready function, we had to use funk, whereas for the second function,
we don't do that. So, what's happening here? To understand that, let's talk a bit more about functions. There are two
things that we are doing at the moment. Number one is we are creating a function. For that, we are using the
funk keyword, then the function name, and then we have parentheses that can accept parameters, which we don't have
at the moment. That's going to come later. But via those, we could add information to the function. After that,
we have indented code with the actual logic. Now, the important thing is that this is only creating a function. It
doesn't actually use that function, at least by default. To actually use this function, you have to call it, which is
another way of saying that you want to execute the code that you have created. For that, you want to use the function
name, and then add in arguments, depending on how many parameters you have. Now, in our case, we are creating
the ready function. And this function is special because Godot is calling it internally. Whenever we are executing
the project, this function is being called by Godot once the scene is ready.
So, we only have to create it, Godot is calling it for us. But for the print function, it's the
other way around. It's an inbuilt function into Godot, so we don't have to create it, we just have
to call it. And when we are calling it, we have to add an argument into it. At the moment, this is 1 2 and 3.
And the result of this is that Godot, inside of output, is printing 1 2 and 3.
If you want to display some text in here, you couldn't simply write something.
That wouldn't work. Instead, I will talk about later why this doesn't
work. But what you have to do is put this inside of quotation marks. Single or double ones would both work, you just
have to be consistent. I will use double ones because that's what Godot uses internally, but it's
really up to you. If I now run the project again, we get something inside of the output.
And that way, you can print anything inside of the output. To practice all of this, I want you to create a new
function and call it as well. So now we're doing both things. The new function should print something
and 1 2 3. In other words, you want to call the print statement twice inside of that new
function. And the new function itself, you want to call inside of the ready function.
Pause the video now and try to figure out this one out and see how far you get.
First of all, we want to create a new function. For that, once again, we will need the funk keyword and then the name
of our function. I guess I should have mentioned this earlier, but this has to be inside of
one string of characters. This could, for example, be something. After that, you need the parentheses,
opening and closing ones, and then colons. Then, inside of the function, we need one level of indentation, like so.
Godot should be doing this automatically, and then we want to call the print function.
And write something. And on the next line, we also want to print 1 2 and 3. By that, we have a new
function that does two things. We are printing something, and we are printing 1 2 and 3. But this function, by
default, doesn't do anything. If I run the code, the output is only something, which we
are getting from this print statement. To actually use this function, you have to call it, which you do by writing the
function name, in my case, something, and then add the parentheses afterwards, opening and closing ones.
If you're doing that, then we're getting something twice and then 1 2 3.
The first something comes from this print statement, the second one from this one, and then we get 1 2 and 3. I
guess the first print statement should say something else, let's say test, then this becomes a bit easier to see. We
have test, something, and 1 2 3. And by that, I think you can already see a couple of basic things. The most
important one is that Godot is executing code line by line. We are starting inside of the ready function, which is
called once the scene is ready. In there, first of all, we are calling print and test.
After that, we are calling the something function, which is down here. Inside of this function, we are printing
two more things. Something and 1 2 and 3. Because of that, we can see test first,
then we have something, and then we have 1 2 and 3. So with that already, we can create a new function, and we can call
functions as well. Either in-built ones or ones that we have created ourselves. In a little bit, we are going to talk a
lot more about functions. They tend to be slightly more advanced, and usually you cover other things
first. But since Godot executes all of the code inside of functions, I had to cover it right away. So I hope this part
isn't too confusing. The only thing that you have to really understand is that you are creating
functions via funk, the name of the function, and then parentheses. Also, don't forget about the colon.
You want to indent some lines, so you know that this line belongs to this function.
After which, you can write any kind of logic. In the next part, we're going to talk about that a lot more. To call a
function, you want to write the name of the function and then parentheses. Inside of the parentheses, you can add
arguments, which we don't have for the something function, the one that we created, but
for the print statement, we can add one argument, and this one prints a certain thing.
So with that, we have functions. Next up then, we can work on actual logic. Now that we can create basic functions, we
can work on logic and variables. And let's start with logic right away inside of Godot. Here we are again, and
I want to keep on working inside of coding one, specifically the script. At the moment, we are only printing
test, which doesn't really do very much. So instead, let's do something a bit more interesting.
And I guess while we are here, we can get rid of all of this stuff as well, because it's going to be a bit
confusing. I only want to have the ready function where we are printing something.
And that something is not going to be a word, it's going to be a math equation. And for that, you can simply type
something like 2 + 2, run the project, then you get the result, four. Other operations would be
2 - 2, then we get zero. You can also do two multiplied with two,
or let's say two multiplied with three, that way we are going to get six. And finally, you can do two divided by
three. Although then we would be getting zero, because Godot is rounding this value, that we can change later. For
now, not too important. Let's do 9 divided by 3, that way we're getting three as the result.
These are the very basic things that you can do via math inside of Godot. And Godot supports a huge range of math
operations. The best way to learn about them is to use the documentation.
Godot actually has multiple kinds of documentation. There's one inside of the Godot engine right away.
If you click on help, there's search help, and then you basically get an overview of all of the parts of the
engine. This isn't really a tutorial, it's more of a manual. If you go to Google, you
can look for GDScript reference. That way, the first result should be the
Godot documentation. If you click on that, you get a page that tells you all about
GDScript. It is quite a long page, but in there, you have a ton of information. I would recommend to have this page open
all the time, it's super super useful. Right at the beginning, we have an example of GDScript, and there are some
things that we have already seen, like for example, a function with a couple of parameters, that we're going to cover
later. Besides that, there are loads of things that you haven't seen yet, so don't worry too much about it.
Instead, go down a bit further to the operator part. In there, you can see all of the math operations, like x +
y, x - y, and then you get all of the other operations as well. The only things that you need for now are these
operations, everything else is going to be a bit confusing. But it is still super useful to at least
have a look at this page and see what you can be doing. By the end of this course, all of this
is going to make sense. Now, using this information is really important, so I want you to do an exercise right away.
I want you to calculate Pythagoras' theorem, where side A is 10 and side B is 20.
Calculate the output using GDScript. Just in case you don't know what Pythagoras' theorem is, you can go to
Google and then look for Pythagoras' theorem calculator,
then Google is giving you the formula that you have to write inside of GDScript.
You are taking the square root of the square of A plus the square of B. By that formula, you can calculate a
side C inside of a triangle simply from knowing side A and side B. So in our case, we know side A is 10 and
side B is 20. The result should be around 22. This is what you should be getting.
So effectively, what I want you to do is to put this formula into GDScript. For all of that, you want to look at
these couple of lines inside of the documentation and figure out how to combine them. Pause the video now and
see how far you get. Back inside of Godot, I want to print the result, like we have
seen before. Although before that, I want to add a hashtag and then add exercise.
If you add a hashtag, then Godot knows this is going to be a comment, meaning the code is going to entirely ignore
whatever you write in here. Which makes it much easier to document your code.
Not something you have to do, but I think in this case, it does make things a bit easier to read. Once we have that,
we have to write the actual formula. You can see it in the bottom right at the moment. First of all, we want to get A
squared, which in our case is 10 and then squared. Which you do by adding two asterisks and
then two. That way, you are squaring the number 10.
Then we want to add something, which is the value for B, which in our case is 20. Which we also want to square.
So two asterisks and then a two. If we run the project now, we are getting 500.
That is because the square of 10 is 100, and the square of 20 is 400. If you add both of them together, you're
getting 500. But now, we have to take the square root of all of this.
Now, getting the square root might have been one part that is a bit more complicated, because there's no square
root in any of this. However, what you can do is use the power once again.
If you take half the power of any value, you are taking the square root. Or in other words, let me demonstrate it
really quickly. We can print nine asterisk asterisk, so the power of 0.5.
That way, we are taking the square root of nine. The result is going to be three, or at least I hope it is, and it
is, 3.0. However, now there's another thing that we do have to worry about. If I simply add
asterisk asterisk and 0.5, we are not going to get 22. If I run the entire thing,
we are getting 120. And to only focus on the value, let me get
rid of this one. This value should return 22. Whatever we have seen down here with 22.
The reason why we don't see it is because this square root only applies to this value. But we want to apply it to
the entire thing. To do that, you want to wrap them inside of parentheses, like so.
And let me add a bit more spacing to this, that way it's going to be a bit more visible.
We first of all want to do this operation, and then take the square root of the entire thing.
Via these parentheses, we can control the order of operation. If I now run the entire thing, we should
be getting 22.something, and we do. This looks really good. So, I hope this part wasn't too overwhelming.
But effectively, we have used different kind of equations and put them together to create a bit more advanced logic. And
this logic you can control via parentheses, and that way, with a bit of practice, this should become fairly
straightforward. But now, what if we want to make all of this more dynamic, where the numbers aren't always going to
be 10 or 20? For that, we have to learn about variables. Variables are basically buckets that
store data. You can either read them, or you can change the value.
This is super useful to keep things organized. And once again, if you look at the
GDScript reference, at the top, we have a whole bunch of variables. Let's create some of those.
Those you can create outside of a function, and they are just about the only thing that you can create outside
of a function. To create a variable, you always start with var. That way Godot knows you want
to create a variable. Then you need a variable name. This could, for example, just be A.
After that, you want to assign a value to this variable, which you do via equal, and then the value you want to
assign. Like, for example, a 10. Next up, we can create another variable, B,
and this one can be 20. And now you have two variables, which you can use inside of this
equation. All you have to do is replace the 10 with A, and the 20 with B. If you run the code now, we are still
getting 22.something. But if I change these variables to, let's say, 20 and 30,
then we are getting something else, whatever the output is. Via that system, you can be much more
descriptive in terms of what your code can do. So, I think just looking at this, it
makes quite a bit more sense instead of just having random numbers in here. Although, when you're naming variables,
you do want to be careful. There are a couple of things that you have to do. Number one,
variable names cannot start with a number, have a space in them, or use special symbols,
like the dollar and the ampersand symbol. Those are reserved by Godot.
Other than that, you are basically good to go. And by the way, these rules also apply to functions.
They basically have the same kind of naming scheme. Besides that, variable names are usually
written in the snake case convention, meaning you only use lowercase letters, and if there's a space, use an
underscore. That's what most people use, but it's just a convention. You don't have to follow it.
That being said, it is recommended that you do. For example, instead of simply using A,
you might want to go with side_a. And for B, you want to have side_b.
Once you're doing that, Godot is complaining because now variable A and variable B don't exist
anymore. Instead, they have been renamed to side_a and side_b.
Once you're doing that, Godot is happy again, and we are good to go. That way, the variable naming is also a lot more
descriptive. Most of the time, you do want to be quite careful how you name variables.
If you mess this up, and your code becomes more complex, this can be incredibly confusing.
Other than that, though, this is basically how you create variables. You create them in the first few lines of
the code, and then inside of any function, you can use them as a stand-in for any other number.
So, at the moment, we are simply reading a variable. Besides that, what you can also do is override the value. So, for
example, side_a can be, instead of 20, a 100. After the line, I can print side_a again,
run the code, and now as an output, we are getting a 100. And I think now it's important to understand how Godot is
executing the code. It goes through it line by line. It first does this line,
then it does this line, and then it does this line. Because of that, for this formula, the value of side_a is 20. But
on the next line, we are changing that value to 100. And just to demonstrate, let me run the project. The current
output of the formula is 36.something. This is what we're getting from this statement. But if we move side_a being
100 one line before that, and run the thing again, then we are getting a different number,
104. That is because now side_a is 100. So, inside of this formula, side_a is 100.
But if we update value A afterwards, then this side_a is what we're getting up here,
20 in my case. So, it's really important to understand how this code is executed. Godot goes
through it line by line, and then simply goes through all of these operations. Really important to understand,
sometimes this can give you some weird results. Besides that, along with variables, we
have to learn about data types. Now, inside of Godot, there are a ton of different kinds of data types. So, for
now, I am only going to focus on the most common ones. First of all, we have text.
These, inside of Godot, and really any kind of programming language, are called strings. You are creating them via
double or single quotation marks, and then whatever kind of text you want to have inside of them. Besides that, we
have numbers. And those are actually two data types. We have integers and floating point values, or simply floats.
The difference between them is that integers are full numbers, meaning 1, 2, 3, 20, and so on. Could also be
negative, like -100. Floating point values have decimal points, like 1.0 would be a floating
point value. Could also be 1.5 or -2.4. Then we have true and false values.
Those are called booleans. They can only be true or false. Once we get to if statements, those are becoming
incredibly useful. Finally, we have different kinds of lists. Those can store multiple values inside
of a single variable. Now, those I am not really going to touch yet. There's going to be a
separate part on them. But there you have arrays, dictionaries, and vectors. I do want to mention them now because
they are incredibly important, really, really common. Variables and data types.
You can force a variable to only accept a certain data type. For example, you can tell a variable to
only hold integers or floating point values. That way, it would be impossible to assign text to it,
which is generally a good idea. It's a good safeguard to make sure a variable doesn't get the wrong kind of data.
Although, if you don't do that, variables by default can hold any kind of data. But let's play around with all
of that. Back inside of Godot, let's say I want to create a new variable.
And this can just be called test. Test should only hold strings. Like, for example, some
test string. This value I want to print. And this we can do all the way at the
end, inside of the ready function. I want to print test. If I now run the project,
we're getting some test string. That works pretty well. We are printing this piece of text, or
this string. However, before this final line, we can assign a different value to test. Like, for example, 1 2 and 3.
If I now run the code, as an output, we're getting 1 2 and 3, which would be an integer, because now
we have a full number. If we want to avoid this kind of behavior,
what you can do, after naming this variable test, you can add a colon,
and then tell it to always be a certain kind of data type. For example, a string.
If we have that set up, then Godot, on this line, is going to throw an error that we cannot assign a
value of type integer as a string. We are telling Godot that this variable always has to be a string, meaning we
cannot assign an integer to it. If we change this to some other string,
Godot would be perfectly happy because we still have a string. Besides string, we also have integer.
And if I do that, Godot is going to complain on these two lines that we are trying to assign a string to
a variable that can only hold integers. Which means now, this has to be numbers. So, 1 2 3 or really any
other that you can be very explicit in terms of what kind of value you are working with, which usually is a good
idea. Especially once projects become more complex, you want to be very very
explicit in terms of what values you have. Without that, you can very easily get
some random errors that you don't understand. And always having the right data type is a good way to avoid that.
Now, this can be sped up quite a bit. If you simply add colon and equal to a certain kind of
value, then you're forcing this variable to only hold the data type that is first
assigned to it. In this case, an integer, meaning now if I try to add a different kind of
value to test, like for example, a string, then Godot is going to complain because
this variable can only hold a type integer because that's what we first assigned to
it. Or in other words, this has to be a string. So, with that, we have
different kinds of data types. We have integers and we have strings. Besides that, you
can also add floating-point values, like 30.0 or let's say 30.2. Now, both of those are numbers and if
you use them inside of an equation, they would still work. So, if I run the code, Godot is still going to execute all of
this and we are good to go. But sometimes, this difference between an integer and a floating-point value
does become apparent. We have actually seen this earlier. If we have 2 / 3, so the first operation
we did, in the beginning, we have gotten a zero because we are dividing an integer by
another integer. And the result is also going to be an integer, which has to be a full number.
Godot is actually giving us a warning about that. If you look at the debugger down here, we are getting errors.
Although in there, we have errors and warnings. Yellow ones are warnings. We have an integer division, where the
decimal part will be discarded. That happens on this line. To avoid that, to get an actual value, you want to have
2.0 divided by 3.0. That way, if I run the thing again, we are getting an actual value, 0.666
and so on. So, sometimes using different data types is incredibly important
even for basic math equations. And without knowing the different data types, this kind of behavior would be
very confusing. So, always make sure when you want to have very precise results, you want to
use floating-point values, anything with a decimal. The next important part are properties and methods.
And those you kind of actually already understand. But let's go through it step by step. So
far, we have covered variables and functions. Variables store values and functions are
blocks of code that you can execute. I hope at this point this is reasonably straightforward.
But besides that, we have properties and methods. These are also basically variables and functions, but they belong
to an object, like a Sprite2D for example. Or in other words, inside of a Sprite2D,
we have a position, which is just storing the position of that Sprite2D. Besides that, the Sprite2D has functions
that are part of it, like for example, look at. And that way, you are rotating the Sprite2D to look at a certain point.
In practice, position and look at are just a variable and a function. But to emphasize that they are part of an
object, we are calling them a property and a method. There isn't really that much of a difference. The only thing
that you really have to be careful about is that you're adding a dot after the object and then you can use
the property or the method. Once you understand that system, you can actually dynamically change nodes inside of
GDScript, which gets you a lot closer to an actual game. So, let's have a look. Back inside of Godot, I want to create a
new scene where once again, we have a 2D scene as a root node, which I want to rename to coding two.
Maybe not the best name, but it's good enough for now. This I want to save right away as coding two.
After that, I want to assign a script to it called coding two. That way, once again, we have another
script and we still have coding one and the first scene. Now, in here, I want to work with a Sprite2D, which at the
moment is difficult because inside of this scene, we don't have a Sprite2D. That, however, we can change by simply
clicking on plus and then adding a Sprite2D. Which once again, needs to have the
texture, so let's drag it in there and then we have the Godot logo. As a reminder,
inside of the inspector, we can see all of the properties of this object. For example, there we have position, we
have rotation, we have scale and so on. And to be a bit more explicit, this Sprite2D is an object. Or to be fully
precise, it's an instance of an object, which is a fancy way of saying it's one object that we can use inside of our
game. That means, inside of GDScript, we can access all of the properties and methods of the Sprite2D.
This once again has to happen inside of a function. So, let's work with function ready.
Once again, we don't need this void part. To avoid confusion, let me remove it. I'll talk about later what it
actually means. To actually work with this Sprite2D, you can simply drag it into the code, like
so. And then you can see what Godot actually does. It adds a dollar sign and then the
name of the node that you want to work with. That way, you are getting the object. And on this object, you can add
a dot and then get all of the properties. For example, somewhere we should be having a
position. This should give us the same position that we can see inside of the inspector,
which at the moment is 251 and 169. This value we want to print. So, we want to put it inside of the
print statement and then run the current scene. Really important,
coding one is the main scene at the moment. But we want to run coding two. So, you
don't want to click on this button, you want to click on this run current scene button.
If you're doing that, you can see the Godot logo, that looks good, and you can see the position.
Inside of output, we're getting 251 and 169. Exactly what we can see inside of the
inspector. Besides that, we can also get the rotation
of the Sprite2D. Let me run the current scene again. We should be getting 0.0 and we do. That
looks really good. So, fundamentally, this rotation is a variable. It stores a certain kind of value, whatever we are
getting inside of rotation. But it is called a property because it is part of a Sprite2D.
That being said, it behaves just like a variable, which means we can assign a value to it. For
example, the rotation could be 20. If I now run the current scene again, then we're getting an output of 20 and
on top of that, the Godot logo is rotated by 20°. By that system, you can update all of
the nodes inside of Godot in real time, which is how you get an actually dynamic game. This would be properties.
Besides that, we have methods, which are basically functions that are part of an object. So, once
again, I want to have the Sprite2D. For that, you can either write the dollar sign and then Sprite2D
or you can simply drag and drop the node in there, like so. Whatever you prefer. If you now add a dot, Godot is giving
you all of the properties and methods of this object. And if you scroll around, you can see there are, well, a lot.
The most important ones are usually at the top. If the left thing says dot P, you're
getting a property. If it says F, you're getting a method. There are a few more. I'll talk about
those later. They aren't too important for now. Most of those are properties and functions.
One method that is very easy to use is rotate. This is a method that kind of does the
same thing that we have done up here. It rotates the node by a certain amount. Let's say 90° and I want to comment out
this line by adding a hashtag in front of it. If I now run the current scene,
then you can see the Godot logo is rotated by a certain amount, 90°. And on top of that,
we are printing 90° because we are using this print statement. Once you have all of that, you can
actually start moving this Node2D inside of the scene. For that though, first of all, I want to get rid of all of the
stuff that we have done so far and instead introduce another function that Godot calls automatically. It is called
underscore process. This one is slightly more advanced. Now we have one parameter that we can
actually entirely ignore. So don't worry about this part. On top of that, we can get rid of this void
but don't forget the colon. This process is called constantly. Every time Godot generates a frame, it is
calling process. In other words, if I now print something, this something is going to be
printed multiple times per second. So if I run this current scene, we get something a lot. So there you can
see the outputs we have, they are increasing very very fast which is totally fine. In fact, this is
what we want because now I can grab the Sprite 2D and then rotate it by a certain amount.
Let's say something very slow, 0.5. If I now run the current scene, then the Godot logo is going to rotate.
It is going to rotate quite fast but that we can control. Let's say 0.1 in here.
That way this feels a bit better. Besides that, we can also move this Sprite 2D in any
direction. Although for that, we have to target the position which if you have a hover it inside of
the inspector has a data type of a vector two, this value here.
I cover this kind of value in the next part but basically via a vector two, we get a list that has two values for X and
Y. Those we can target via dot X and dot Y. Let's say for now I only want to have
dot X and this value should get the current position
of the node. Let me add a bit more space so we can see this properly.
I want to assign a new X position to the node that is the current value plus some kind of addition. Let's say plus one.
And then if I run the script again, the Godot logo is going to be very slowly moving to the right.
If I increase this value to let's say 10 and try this again, it's going to be more visible. Now Godot is moving to the
right. So with very few lines of code, we can move stuff around inside of the game. By
the way, this kind of operation is really common and there's a shorthand to make it easier to write.
Instead of writing all of this, you can simply write plus equal 10. That way we're getting
the current value and adding something to it. In other words, these two statements
are doing the same thing except that the second one is much easier to read. As a consequence, let me comment out the
first one. If I now run the game again, we are getting the same result. Most of the time you are going to use
this kind of line. It's just much easier to write. All of this we should be practicing. So
I want you guys to create another moving Sprite 2D. This one should move from the top left to the bottom right and while
it is doing that, it should also scale up. Pause the video now and try to implement this kind of logic.
I want to add another Sprite 2D to the scene and make sure this Sprite 2D 2 is not a child of the first Sprite 2D.
In fact, let me rename it to exercise Sprite. That way it's a bit easier to read.
This exercise Sprite once again will need a texture inside of the inspector otherwise we can't see it. Drag the
Godot icon into it and there we go. In the top left, we have the Godot icon. After that,
inside of the script in the process function, I want to add another for the exercise.
Now in here, I want to get the exercise Sprite and do a couple of things. First of all,
I want to get the position dot X plus equal let's say five. That way this Sprite is going to move to
the right. And to make them a bit easier to distinguish,
this exercise Sprite under visibility and modulate is going to get a different color. Let's make it really blue,
something like so. Doesn't really matter but it makes it a bit easier to understand. Now we have
both of the Godot logos moving to the right. Good start.
Besides that, I want to duplicate this line which I can do by pressing control or
command shift and D. Besides X of the position, I also want to update position dot Y
and increase this value. Remember, if you want to go down, you have to increase the Y value.
If I now run the project again, the Godot logo is moving to the bottom right.
We could reduce the speed here just a bit to let's say three. That way we are going a bit more towards
the actual bottom right of the window. Also in just a bit, we're going to find a better way to update the position so
you don't always have to target X and Y separately. But for now,
I want to press control shift and D again because besides the position, I also want to get the scale. Now for this
one, you do want to be careful. If you add plus three in here and run the thing, then you get some weird behavior.
We are scaling this thing up very very fast. A better way would be to multiply the
current value with something like 1.1. Like we have seen with plus equal, multiply equal takes the current value
and multiplies it with whatever you write in here. That way this scaling becomes well,
still quite fast but a lot slower than before. Let's set this to 1.001. That way it doesn't grow that fast.
I guess you can still kind of see it. Once again, control or command shift and D
and then scale dot X. And now we are scaling the Godot logo in all directions.
A bit hard to see. Let's do this with 1.005. That way it should be more visible.
And there we go. This is definitely scaling up. With that, we have learned about
properties and methods which are basically variables and functions but they are part of an object
like for example a Sprite 2D. Once we have that, we can actually do dynamic things inside of the game which
is a massive upgrade. And to get really good at Godot, you want to be able to use as many properties and functions of
an object as you can. The best way to learn about them is to go to help and then to search
help. That gives you once again the Godot documentation. In there for example, you can look for Sprite 2D,
double click on it and then inside of the script view, you are getting the documentation for a
Sprite 2D. There you can see all of the properties and all of the methods or at least some
of them. If you go a bit further down, you are getting a lot of descriptions in terms
of what you can do. Being able to read through here and understand what's going on is incredibly helpful to really get
good at Godot. With that, really quickly before we finish, back inside of coding two. At
the moment, we have a script attached to the parent node coding two of type node 2D
and from there we are influencing a Sprite 2D or the exercise Sprite here and here.
But what if you want to influence the node itself with the script? In our case, this node 2D.
Let's say for example, once the scene is ready, func underscore ready,
then we want to move this node or in other words, the entire scene to the right.
Well, for that all you have to do is grab the property of this current node and you can do that right away. For
example, if you want to update the position, you just have to type position without any of the dollar sign stuff
before it. Once again, you just want to look at the inspector and then find the property
that you want to change like position at the moment. This once again is a vector two so in
there we have position dot X and this we could set for example to a value of
200. If I now run the script, everything is going to be 200 pixels
further to the right. A bit hard to see. I guess if I change this to 600, it's going to be much more visible.
Here you can see now everything starts much further to the right. And that's basically the system. If you
have the node itself with the script, you simply target the property or you can call the method and if you have a
child, you target that child with the dollar sign and then the name of the node. After that, you can call any kind
of method or use any property that you want. The next important topic is going to be
booleans and logic flow. Via that, we are going to learn about if and while statements. These allow you to
control what code gets run and how often it gets run. Using an if statement, you can run a
code once if a certain condition is true. With the while statement, you can run
code forever as long as a condition is true. Both of these are really easy to
implement, but we have to figure out what is true inside of code. And for that, we have to look at the
data types again. Up to this point, we basically only worked with text and numbers.
Those at this point, I hope, are at least reasonably straightforward. Next up,
we are going to look at booleans, which can only be true or false. That's the only two options that you have.
So, let's talk about how to create these kind of values. And most of the time, you are creating them via logical
operators. We have smaller than, greater than, is equal to,
is smaller than or is equal to, and is greater than or is equal to. All of these operations are either true
or false, which is a boolean value. Now, there are other ways to create
boolean values in Godot, but those aren't that important yet. Although, what is important to mention
that you can also combine multiple conditions. Like, for example, A is greater than five and B is equal to two.
And by the way, if you're wondering why we are using two equal signs for the comparison here,
that happens because when you are creating a variable and you assign a value to it.
For that, we are using the equal sign already, and we can't use it here and here at the same time.
For all of this, I want to work in Godot once again, create a new scene where the root node is a 2D node.
Let's call this coding free boolean to add a bit more detail. Then I can zoom in a bit and add a
sprite to the scene. Like so. After that, I want to attach a script to the scene like we have done
before. All the defaults here are totally fine, and then create. Just for testing purposes, I want to
create a func underscore ready. In there, for a really, really basic if statement, you first of all want to type
the if keyword, and then the condition that you want to check. For example, 10 is greater than five,
which is going to be true. If that is the case, add a colon and press enter.
Then, once again, we have an indented line. Via that, you know that any code that
comes afterwards is part of this if statement. Just like for the if statement, we have one level of
indentation, and that way we know that the if statement belongs to the ready function.
Same system, really. Now, if this statement is true, I want to print,
let's say 10 is greater than five. Now, I want to save the entire scene. coding free boolean.tscn is totally
fine. Click on save, and then run all of this. In the output, we are getting 10 is
greater than five, which is perfect. If, however, I change this condition to four is greater than five and run this
again, then we are not getting any kind of output.
This line only gets run if this condition is true. If it is not, then Godot is going to ignore all of this.
And to go into a bit more detail, let me get rid of all of this and then print the comparison operator right away.
Let's say four is greater than five, which we know is not true. But, if I run the script,
then Godot is telling me that this is false, and this is a boolean data type. If I make this a true statement, let's
say seven is greater than five, and run this again, then we're getting true.
Other comparison operators would be an equal comparison, for which you need two equal signs.
And for this, let's check if five is equal to five, which it should be. And it is.
Perfect. If I change this number to a two, then this is going to be false.
And besides that, we have smaller or equal to and greater or equal to. Besides that, one more that I forgot to
mention is this one, which checks if two values are different from each other.
So, at the moment, we are checking if two is different from five, which is. So, if I run this, then we are
getting true. However, if I check five is different from five, then we're getting false because, well,
five is five. So, with that, we have basic boolean values and we can use them inside of an
if statement. Once again, as a reminder, all you need is if, and then let's say four is equal
to four, a colon, and after that, you can run any kind of logic. Let's say print
four. Besides that, what you can also do is via and expand the if statement.
So, for example, we only want to run this if statement if four is equal to four and if 10
is greater than three. Both of these statements are true. That way, we are getting four.
However, if one of them is wrong, then this if statement is not going to run. So, if I change the three to an 11,
then this if statement is not going to run. Both of these statements need to be
correct. If you only care about one of them, you can use the or keyword.
That way, only one of those has to be true. So, if I run it now, since four is equal four, that part is
true, the entire thing is still going to be run even though 10 is not greater than
11. And that's pretty much it for basic if statements.
Although, there's one more thing that you can do. Two more things, actually. For a basic if statement, let's say if,
and I want to have a false statement, four is greater than 10.
If that is the case, I want to print A. This condition, obviously, is not going
to run. However, if it is not running, I want to do something else instead. That I can do via the else statement.
In which case, I want to print B. What is going to happen now is that Godot checks this if statement first. If
it is correct, it's going to run this line. However, if this doesn't run, so it's
false, then Godot is going to run the else statement and will print B. In other words, if I run this now, we
should be seeing B, and we do. However, if this statement was to be true,
then it would only run the first line, in which case we would get A. You only ever get one of these. They
can't run at the same time. And you can be even more detailed in here via the elif statement.
This is going to run if this one is false and you want to attach another condition to it.
Let's say one is equal to one. If that is the case, I want to print C. And let's make sure the first
condition is going to be false, so eight is greater than 10. They can run the thing again, and now we
should be seeing C, and we do. So, at the moment, Godot starts on this line and checks if it's true, which at
the moment, it's not. Because of that, it's going to the elif condition and then checks the boolean
value. In this case, this one is true, so it runs C and then discards anything that comes
afterwards. However, if this condition wasn't true, let's say two is equal to one,
then Godot is going to print B. So, it checks this condition, it's
false, it goes to the next one, it's still false, so it goes to else and we're getting B.
Obviously, these conditions aren't really that useful yet. But, once we start using properties and things like
node positions or health values, this becomes incredibly powerful. If statements are being used all the
time. You see them constantly. So, something we are definitely going to practice in just a bit.
First of all, though, there's one more way to execute code that you don't see that often, and that is a while loop.
This while loop runs code as long as a condition is true, which means you do have to be really,
really careful. If you do something like while one is equal to one,
and then you run any kind of code in here. Let's say print A. This while loop is never going to stop,
meaning any code that comes afterwards is not going to be executed. The code would simply stop working. In fact, if I
run this now, we would get a ton of output. So, if you look at the console,
you can see a ton of stuff, but well, Godot doesn't run well anymore, simply because this line destroys everything.
And now Godot has crashed. So, when you're using while loops, you do want to be careful.
Here we are again, and let's create a while loop that doesn't crash Godot. For that, you want to create a condition
that doesn't stay true forever. I think the best way to approach that is by first of all creating a variable.
Let's say var A, and this has to be an integer with a default value of zero. This we can make a bit shorter by simply
doing this. That way, we have a variable with the name A, and we're assigning an integer
to it, and because of this colon, this variable can only have integers. This we want to use inside of this while
loop. Let's say I want to check while A is smaller than 10.
While that is the case, I want to print the value of A, and then also print while loop is
running. This would still crash Godot. Because A doesn't change. It would
always be below 10, because the current value is zero. However, what we can do is A plus equal
one. That way, this condition is going to be false eventually.
Let's try to run the scene. We are getting the scene running perfectly fine. Godot doesn't crash.
That's a good start. And inside of the output, we are getting zero, while loop is
running, one, two, three, and so on. We're getting up to nine. And then the while loop has stopped.
That way, we could run code multiple times. Which occasionally can be super useful.
Although in practice, you don't really see while loops. Simply because if anything goes wrong,
you are crashing the entire game. And there are better ways to run code multiple times.
But once in a while, it does come up, and you do want to know what it's doing. That being said, what you really want to
practice is the if statement. So, let's do that. I want you guys to bounce the sprite that we have created left and
right. Or in other words, make the sprite move left and right, and whenever it hits the window border, make
it bounce in the other direction. There are a couple of ways of doing that. I guess just get started and see
how far you get. First of all, we want to move this icon. I guess we can start that by moving it
to the right. For that, we will need the func_process. I want to grab the icon,
update the position.x. And let's say plus equal two.
If I run the scene, the icon should move to the right. It's a bit slow. I guess we can go with
10. And that looks reasonably all right. Although this number has to change. So,
if we hit the right side of the window, this 10 should become a negative 10. I think the best way to approach that is
to create a var direction. Which once again is going to be an integer, and the default value should be
one. After that, I want to multiply this 10 with the direction.
That way, by default, with this value being one, this value keeps on being a 10. Or in
other words, we are still moving to the right. That looks good.
But if we change this one to a negative one, and run the whole thing again, then we are going left. And we do.
Perfect. In fact, what we can do is replace this 10 with a proper variable. Like for example, speed.
This once again could just be an integer with a default value of let's say 15. This 10 was still a bit slow.
And instead of this 10 now, I want to have speed. That way, we can control the speed and
the direction of this Sprite2D. And this kind of logic you see a lot, where you have a speed and a direction,
and this you apply to some kind of node. We are getting really close to having actual games.
But anyway, for now, the start direction should be one. So, we are going to the right.
However, on every single frame of the game, I want to check if
the icon.position.x is smaller than or equal to the right side of the window. Which in this
project is under display and window, 1152. If you change this number, go with
whatever's in here. In my case, I want to check if the icon.position.x is greater than 1152.
If that is the case, direction should be negative one.
That way, if I run the scene, we are going to the left. And I realized this should be
greater than or equal to 1152. That way, this should actually work. And now,
we are going to the right by default, and then we are bouncing and going to the left. Although we never go back.
That we can address via another if statement. Where we want to check Let me copy all of this. If the current
position.x is smaller or equal to zero. If that is the case,
direction should be one again. And if I now run all of this again,
the Sprite2D should bounce left and right. And it does. That looks pretty good.
And all right, with that, we have if statements and while loops, but most importantly, if statements.
Next up, we're going to work with data. Once again, here are the most common data types I have talked about earlier.
At this point, we have covered the first three, so we should be good on those. So, finally, we can work on arrays,
dictionaries, and vectors. Vectors is what we're going to start with, because those you are going to use
the most. Vectors are basically a better way to store numbers.
We have vector twos and vector threes. Now, technically, there are also vector fours, fives, sixes, and so on, but we
only really use vector twos and vector threes. For a vector two, it just stores two
numbers. Vector2.x and Vector2.y. If you have a vector three, then you
have X, Y, and Z. Other than that, they work in exactly the same way. By that system, you can
store, for example, positions incredibly easily. On top of that, vectors are really
useful because of vector math. The most common example is when you multiply a vector two with a integer. Or
float, doesn't really matter. In that case, you are multiplying both values. So, the five becomes a 10, and the two
becomes a four. Both were multiplied with two. Besides that, you can add two vectors to
each other. That way, you are adding the X part, so in this case, the 10 and the one together, and the Y part, the three
and the two. The end result would be 11 and five. There are a few more examples of vector math, but we get to those when
we make the actual game. Finally, vectors have a ton of methods that are incredibly useful.
And I guess let's have a look at all of those in practice. Once again, back inside of Godot, I want
to create a new scene with a 2D root node. Coding for data.
This I want to save right away in the usual folder. And also attach
a sprite to this scene. So, we can work with something. Finally, I want to attach a script to
the entire thing. Create all the defaults here are fine. And then, I want to work inside of the
ready function. And for now, print the position of this icon.
Drag it in there, and then look for the position. If I now run this script,
as an output, we are getting the values 327 and 310. This is a vector two.
Or in other words, if you look at this icon, the Sprite2D, under transform, you can find the
position. And if you hover over it, Godot is telling you the data type. It is a vector two.
Which means on this vector two, we have two values that we can access via X and Y.
If we go with X, then we are going to get the first value, 327. Let's just try that, and there we go.
327. And of course, you can also create new vector twos via a variable. For that, you want to create a new variable.
Let's call it pos for position. And then, you simply assign it a vector
two. And inside of Godot, we have vector two and vector two I. The I means that you're only getting
integers as a value. By default, you're getting floating point values. Most of the time, you are simply going
to go with vector two, three, or four, depending on how many values you need. The integer version isn't really that
common. It's very specific, and we are not going to use it. You want to have parentheses, and then
you can set the X and the Y value. Let's say 200 and 300. This would be a very simple vector two
that we can use to, for example, update the position of the icon. icon.position is going to be the pos
variable. If I now run all of this again, then we have moved this logo around.
So, if I change the number from 200 to 500, run all of this again, then the Godot logo should be roughly
here. And it is. Perfect. And that's pretty much all you have to know for the absolute basics of it.
You can create a vector two by simply writing vector two, and then you assign the values to.
Besides assigning values, you can also add dot, and then you have, for example, down,
left, one, right, up, or zero. Those are simply pointing in different directions.
If you, for example, have right, you would get a vector two with one and zero. Meaning, in a 2D space, you'd be
going to the right by one unit. And remember, in the last part, when we tried to move the icon around, we could
do that on the x-axis quite easily. But now, via vector two, we can do it in any direction.
For that, and this is something you see all the time, you want to use the process function.
Add icon.position, and then add the position. Although, a better name
for that would be the direction, because this is what this thing is effectively. Then we can get rid of the ready
function. We don't need it anymore. And instead of position, this is going to be direction.
This at the moment would move the Godot logo, but it's also going to be very, very slow.
But let's try. There we have the Godot logo moving to the right.
If I change this vector two to, let's say, one and one, we're going to the right, and we're
going down. Remember, to go down, you have to increase the Y values. And there we go. We are going down and
right. On top of that, what we can do, because of vector math, we can create another variable. Let's call it speed.
And this can be a basic number like 20. This I can multiply with the direction, and then we are still going in this
direction, but now at a much higher speed. Inside of the game,
that is looking pretty good. And just to demonstrate how this would be working, if I print another vector two, so vector
two right away, with the values of two and three, I can multiply the entire vector with,
let's say, three. Then we're going to multiply this two and this three both by three. As a
result, we would get six and nine. Let's try. There we go.
We are getting six and nine, both of which are floating point values, because a vector two always stores floating
point values, unless you add vector two.i, then you would get integers, which you
usually don't want. And well, that's pretty much it for the basics of vector twos.
Especially for positioning, they are incredibly common. We have two more data types, and the
easier one is arrays. This is just a list that stores values. It would look something like this.
You can have any kind of data type in here. Any kind of number, strings, booleans. You can even have an array
inside of another array. By that, you can store any number of data points. And if you want to get one value, you are
doing an indexing operation, which simply means that you are adding square brackets afterwards, and then you define
the index that you want to grab. The index always starts at zero, and then goes up.
The first value would be index zero, then you have one, two, and so on. Which means, if you have square bracket
zero, you're getting the first value. If this was a one, you'd be getting this value.
And that's pretty much it. You can also loop over all of the values inside of an array. For that, you would need a for
loop. You start with the keyword for, and then you define a variable. Very often this is called I, but you can call
it whatever you want, as long as it's a valid variable name. And then you need some kind of array.
Now, inside of the code that has to be indented, we are running the code for every single value inside of the array,
and we are storing that value inside of the variable while we are running it. That way you can grab every value and
work with it. But let's do all of this in practice. And for that, I want to create a func
ready once again. To create an array, you simply want to,
let's say for now, print, then square brackets, and then any kind of value. Let's say one, two, and three.
We can have a string. We can have a boolean value that you can create directly. All you have to type is
true or false. And finally, you can add another array into an array with values inside of it.
One, two, and three. If I now run the script, we are getting, inside of output, all
the way in the beginning, the array. And that's really all we need.
And I guess while we're doing this, let me comment out this line, so we only see one output. I hope the system here
makes sense. You simply have square brackets, and then values separated by a comma.
There's no limitation on data types or number of items in here. It's a really flexible system.
And if you want to grab one item after the array, you are adding square brackets, and then the index. For
example, zero would give you the first item. You always start counting from zero,
which sometimes can be a bit confusing, but you get used to it. Pretty much all programming languages work like this.
But now, if I run all of this again, we are getting one, which is this integer here.
If I set this to three, we're getting zero, one, two, and three. Meaning, we would get the string.
At least if I can count right. And we do. That looks good. Once again, I think this operation is pretty
straightforward. I guess one thing that you can do here is count from the back via negative
numbers. Negative one would be this last item. Negative two
would be false. Negative three would be the string, and so on. You basically go backwards.
We're getting the array as an output. Or, to be a bit more specific, the array inside of the larger array.
That works pretty well. Besides that, what you can do is a for loop. We're for I in, and then I want to
grab this array. Let's actually store it inside of a variable.
var, let's call it test array, and then this value. Or any kind of array, it doesn't really
matter. This we can replace here, test array.
Makes all of this a lot more readable. And also, for I in test array. Don't forget the colon.
And now, Godot is going to go for every single value inside of this array, and it's
going to store it inside of this I variable. Which means, inside of the for loop, we
can print I, and we would be getting every single one of these values. Let me run it again.
Inside of the output, we're getting this one, two, and three because of this print statement. But
after that, we're getting one, two, three, string, false, and the array one, two, and three.
We went for every single value inside of this array. By that system, you can loop over the array and do pretty much
anything you want in here. Which is a fairly common operation. And besides arrays, we have
dictionaries, which are pretty much arrays, except now we have key-value pairs.
This system looks like this. We have curly brackets, and then we always have a key, a colon, and a value. With the
key-value pairs being separated by commas. On top of that, both the keys and the values can have any data type.
Our first key-value pair is this one, where the key is the string A, and the value is the integer one. The same
system applies to the second key-value pair. But for the third key-value pair, the key is the string C, and the value
is another string called test. Finally, for the last one, the key is the integer four, and the value is an array.
All of which would be totally fine. Like for the array, you can still do indexing.
Although, this starts to get a bit messy to observe, but effectively, we have the dictionary here, and then we
have square brackets with the key that you are looking for, which currently is this key here.
The output is then going to be the associated value to it, the one here. And this is what we're getting.
Also, like for an array, you can use a for loop. We're for, then the variable name, in,
and then the dictionary. Then you cycle over the dictionary. Although, for that, you do have to be a bit careful, but
let's do all of this in practice. I am inside of the code, and I want to create another variable.
test dictionary To create a dictionary, you want curly
brackets, and then the key-value pairs. Let's say, A could get a value of one. Then I can have a key called one, two,
and three with the associated value being an array with one, two, and three.
And finally, you can even have a key like true, the Boolean value where the associated key is
I know, a string. Let's just call it string. For keys and values inside of a
dictionary, any data type is fine. Although, I guess what I haven't mentioned is that these keys need to be
unique. You cannot have duplicates in there. Which means using something like a
Boolean value is usually a pretty bad idea, but you could do it. Once you have that, and let me comment
out the stuff that we have done for the array. Oh, and by the way if you select multiple lines of code you
can press control or command and then the forward slash. That way, you're commenting out multiple
lines of code. Super handy. I want to print the test dictionary. If I now run the current scene, we are
getting the dictionary that we have just created. And this stuff should have been commented out.
You get the idea. We have a dictionary where we have key-value pairs. And these keys you can access via
indexing. For example, if I add square brackets and then the key one, two, and three.
That's this key here, and the associated value is the array one, two, and three. So, if I now run all of this
the value that we are getting is one, two, and three. With that system, you can store data and
be much more explicit what it's about. Having too many values inside of an array can be a bit confusing. So,
sometimes using a dictionary is much more elegant and much easier to understand.
Besides that like for a dictionary, you can use a for loop with for I in test dictionary
and then for now, I just want to print I. And let's see what we get. And to not make this confusing, I want to comment
out the earlier print statement as well. So, the only output that we are getting is
from this for loop. Let's run it. There we go. As an output, we're getting
A, one, two, and three, and true. If you compare this to the actual dictionary that we have created
you can see that we are only getting the keys. We're getting A one, two, and three, and the Boolean
value true. If you use a for loop with a dictionary, you will only getting the keys returned.
Which sometimes can be what you want, but very often not really ideal. To customize that every dictionary has a
couple of methods that let you customize how the for loop is going to work. By default you're getting the keys.
If you don't want that, you can use values. That way, this for loop is going to cycle over the values inside of this
dictionary. Let's try. And there we go. Now, as an output, we are getting
one, the array one, two, and three, and string. Which is the values inside of this
dictionary. So, that is something you do want to be careful about. Using a for loop with a
dictionary can either get you the keys or the values. Now, most of the time, we are not going
to work that much with data. So, arrays and dictionaries aren't going to be a common thing that we will work with. But
once in a while, they are very handy. The most important thing that you want to practice though are vectors. Those
come up all the time. For that, I want you guys to make a sprite bounce in all directions.
The sprite should move via a vector two, and it should bounce on every single window border.
Pause the video now and try to implement this one. You will need a vector two and if statements for all of this.
Back inside of Godot, I think just to keep things organized, I want to add another Sprite 2D to the scene and
rename it to exercise sprite. After that
inside of the process function I want to create another variable, and all of this is for the exercise.
I want to have an exercise direction which is going to be a vector two. That let's say by default is pointing
to the right. So, X is going to be one and it's going to point up. Y is going to be negative one.
This variable should only hold vector twos. Which you can do by defining
the vector two as the data type or simply do this operation. That way, the first value assigned to
this variable is a vector two, and afterwards, it cannot change the data type.
Besides that, I want to have an exercise speed which is going to be an integer with
let's say 30. After that inside of the process function
I want to get the exercise sprite and update the position. To this value, I want to add the
exercise direction multiplied with the exercise speed. And now, this line is getting quite
long. Which makes it a bit hard to read. If that's an issue for you, you can click
on this button. That way, you are expanding the code window. Quite handy sometimes.
Let me try to run the code. The Godot logo should move to the top and to the right.
And there we go. We are moving in the right direction. Although, we are not bouncing.
Which is the whole point of the exercise. We can check if and I want to get the exercise sprite
position .y is smaller or equal to zero. If that is the case I want to get the exercise direction and update the Y
value. This one should now be one. That way, and let me reduce the speed
just a bit to 20 we should be bouncing. There we go. We are bouncing once at the
top of the window. And effectively now, we have to duplicate this thing once
and check if the logo is exceeding the value 648 48 or whatever you have set as the
window height. The default is 648 if you haven't touched this value. If that is the case
exercise direction.y should be negative one. Then we can duplicate all of this
and then change position.y to position.x. If position.x is smaller than zero, we
are on the left side of the window. Which means exercise direction.x should be one.
If the position is greater than 1152, then we are on the right side of the window. So, the direction.x
should be negative, meaning we are going left again. And with that, we should be done.
There we go. This looks like a pretty good bouncing. It is time to talk about functions in a
bit more detail. Along the way, we are also going to talk about scope and return.
Really important concepts that you have to understand to actually understand functions.
But let's go through it step by step. We have already seen that you can create and call functions.
Creating functions looks like this. You start with the func keyword, then you add the name of the function.
Afterwards, you add parentheses with the parameters that we haven't done yet. Then you have to add a colon, after
which you have the indented code with all of the logic. That way, you are creating a function.
To actually use the function, you have to call it. Which basically means you want to write the function name along
with parentheses, and inside of the parentheses, you can add all of the arguments.
That way, the function actually does something. We haven't actually created any parameters. So, let's have a look at
that. As always, here we are back in Godot, and I want to create a new scene. Just as before, this is going to be a 2D
scene that I want to rename to coding five functions. Let me expand this just a bit, like so,
and then save the entire thing. The location doesn't really matter, so click on save, and then we have a scene that
we can work in. All I want is some code, so I want to attach a script to the node. Click on
create with one of the defaults. And then we can write some code. For now, I want to create a function.
That we can just call test func. That would be the name of the function,
and afterwards, you need parentheses and a colon. Then inside of the function, you have to
write the logic that you want to execute. Let's do something simple. I want to print test function.
And that's all that this function does. With that, we have created a function. And this we have done a couple of times
by now. However, if I execute this one scene nothing is going to happen. We don't get
any output. That is because this function does not get executed. Or in other words, it's
not being called. The functions that do get called automatically are functions that start
with an underscore. Like for example, func_ready. This function gets called by Godot once
the scene is ready. Which means what we can do inside of this function, we want to call
this function. We simply have to do test funk, then parentheses, opening and closing ones,
and then we are calling all of this code. Which means if I run this one scene,
instead of the output, we are getting test function. This part should still be super
straightforward. We have done this a couple of times already. But all of this we can expand.
Most importantly, by adding parameters. That means inside of the parentheses, you can have parameters. For example,
parameter underscore one. Simply by doing that, Godot is already going to complain. That Let me expand
this. There are too few arguments for the test function call. Godot expected at least
one, but received zero. Or in other words, there's now one slot that we have to fill when we are calling
this function. This at the moment could be any kind of data type. I could for example write
one, two, and three, and Godot would be happy once again. Also, let me fix my typo. This should be parameter one.
This parameter one becomes a variable inside of the test function. The value assigned to this variable is
what we have added as an argument when we are calling the function. Or in other words, I can print
parameter one, run the scene once again, and as an output, we are getting 1 2 3.
The value that we have added when we called the function. With that system, you can make your
functions much more flexible. And you can also force a certain kind of data type. All you have to do is add a colon
after the parameter, and then define the data type. For example, right now, I only want to allow integers for this
data type. That way, if I change this to, let's say, a string that says something,
doesn't really matter, then Godot is going to complain. You cannot pass a value of type string as an integer. This
function only accepts a single parameter that has to be an integer. Let's change this back to 1 2 and 3. Although, you
can also add multiple parameters. Let's call this one parameter two,
which can be a string. And then Godot is going to complain once again. So, now we have to add a second
argument, which now has to be a string. And once again, Godot is happy. And to actually use parameter two,
what I haven't covered yet is that when you're calling the print statement, you can add multiple arguments as well.
For example, we can add parameter one and parameter two. Then, if I run all of this again, we're
getting 1 2 3 and string all combined. If you want to have a space between the two, simply add one more argument with a
string that has one space inside of it. That way, this looks just a bit cleaner. And
effectively, those are parameters and arguments. Parameters are slots inside of a
function that you can fill with arguments, and that way, you are giving them a value that you can use inside of
the function. Super handy. Finally, you can give a parameter a default value. Let's say for parameter
two, we want to have a default value, and that you do right after a default value of test.
That way, if we don't give an argument here, then Godot is going to use this value.
Or in other words, I can run the entire thing again. We're getting 1 2 3 and test.
Godot used this value for parameter one and this value for parameter two. The one thing that you always want to be
careful about is that these parameters are organized by their position. This is the first parameter, so the first
argument is going to go to this parameter. Then you have the second parameter, and
the second value would be for that one. So, second would be here. Always be careful about that one. The
position of these parameters and their arguments are really important. It's the only way to really link them.
But other than that, this is basically as complicated as it gets. If you understand it, you understand functions
reasonably well. That being said, there are two concepts that are really important that can be
quite confusing. The first concept is scope. Or more specifically, you have a global
and a local scope. What that means is inside of a function, you can create variables. But if you create a variable
inside of a function, it only exists in that function. Or to be a bit more technical, the variable exists in the
local scope of the function. Variables created at the beginning of the script are in the global scope,
meaning they are accessible everywhere. Inside of this test function, you can create variables.
This happens in a normal way. You type var, and then the name of the variable. Let's call this one the local variable.
This could be any kind of data type. For now, let's go with local var. Once you have that, you can
use this local variable like any other variable. You can print it, you can change it. Let me just print it
and run the script. We are getting local var. So, that looks good.
We have a variable like any other. However, inside of the ready function, this variable does not exist.
If I try to print it here, print local underscore variable,
Godot is already going to complain that the identifier local variable is not declared in the current scope.
What that means is that this function has a scope, and this function has a scope.
And local variable only exists inside of this test function. It does not exist inside of the ready function.
I guess an easier way to think about it is that every single function is its own box.
Inside of this box, you have a ton of logic, and you can create variables. But these variables only exist inside of
this box. If you create another function, this function would be another box with its
own content. That's really all that local scope means. Variables created inside of a function are only available
in that function. However, what you can do in the beginning of the script is create a
variable, and this is what you have already seen. Let's call this one global variable
with the value global var. And this global variable is available everywhere. We can print it inside of the ready
function, and we can print it inside of the test function. It would work in both of them. So, we're
getting global var twice. First from this print statement, and then from this print statement. The
reason why the system exists is that functions should be self-contained. All of the logic in here shouldn't connect
too much to stuff outside of the function. It really helps to have modular logic, where a function does its
own thing, and simply gets its data via the parameters. If you get too much data from other
sources, things can get really complicated and confusing. So, be careful here. And with that, we have
covered scope. Next up, we have to talk about return values. This is how data is moved around. And
also, you can specify what kind of data a function returns. Something that I haven't talked about
yet is this arrow and then void. This defines what kind of data this function returns, and void means it
returns nothing. This part actually is necessary. If you get rid of it, nothing would happen. But you can make it
useful. For that, I want to create another function. Let's call it function return
something. No need for any parameters, but I do want to return an integer once
we are calling this return something function. After that, you need to colon, then we
can print return something function was run. However, now Godot is still complaining
that not all code paths return a value. Effectively now, Godot expects when this function is run, it returns a value,
specifically an integer. To get that, we need the return keyword, and then some kind of value that we want
to return. For now, let's go with 100. And then Godot is happy again. If I print
return something, really importantly with the parentheses, so we are calling the function,
I can run this script, and we are getting 100. And I think it's important to understand
what happened here. Let's go through it step by step. Inside of this print statement, we have
called return something, which means Godot is looking at this function. First of all, it is going to print some kind
of string. Doesn't really matter what it is. After that, we are returning the integer
100. Or in other words, this function is replaced with 100, or whatever was
returned in here. This 100 is then being picked up by the print statement, and Godot prints out
the value. Effectively, this return something function, after that is called gets the value of this 100.
We could even change the value. For example, you could multiply it with two, rerun the script, and now we're getting
200. Programming languages, when it comes to this kind of system, are super flexible.
And generally, understanding how return works is really, really important. It pretty much defines how data is sent
across your code. For example, here is a slightly longer line of code.
First of all, we are creating a vector two that has an X value of three and a Y
value of four. This will be the first value that gets returned. After that, we're calling a method
on this vector, where we're getting the length of the vector, which means the returned value from the
vector gets changed to something else. After that, we are multiplying the entire value by a
factor of two. Or in other words, the thing that was just returned, this value here, gets
changed once again, and we're getting something else. After that, all of this
gets passed into the STR function, which turns any value into a string. Which means this becomes, once again,
another kind of value. Finally, we are at the print function, we are printing this final value.
This does look a bit complicated, but once you play around with it, it becomes really straightforward.
You always have to go from the innermost part to the outermost part, and then work through it step by step. Getting
used to all of this will take you some time, but not that much time. You'll be fine.
For now, the one thing that you really have to understand is that when we have this arrow at the end of the function,
we are defining some kind of return value. If that is the case, inside of the
function, you have to return some kind of value at the end of it.
And all of this should be practiced. So, let's do an exercise. I want you to create a calculate
function. It should look something like this, where you can add two numbers and then
the operation as a string. It should be plus, minus, divide, and multiply. The correct answer should be returned,
which means inside of calculate, you shouldn't use any kind of print statement. You simply want to return the
final value, and then print that return value. Pause the video now and see how far you
get. I want to create a funk calculate, where we have three parameters.
Let's call it number one, number two, and the operator.
All of those should only accept a certain kind of data. For number one and number two,
let's say, to keep it simple, they should only be integers. Whereas for the operator, this should
always be a string. The return value, once again, to keep things simple, should also be an
integer. And just to test if things are working, I want to check if the operator is equal
to plus. If that is the case, I want to return number one plus number two.
And if that is not the case, else, I want to return, let's say, zero.
That way, inside of the ready function, and let's comment out all the other
stuff, so this doesn't get confusing, I only want to print the return value from the calculate
function, where for now, we are doing one plus two, like so,
and then we should be getting as an output the number three. And we do. That looks good.
Although this logic here doesn't work terribly well yet. That we have to work on. First of all,
I want to create a local variable called result. This is going to be an integer with a
default value of zero. And this is the only thing that we are going to return at the end of the
function. In between that, via if statements, we are going to do the actual math.
So, let me copy and paste this in there. First of all, I want to check if the operator is plus,
then the value of result should be number one plus number two. Elif operator
is equal to minus, then result should be number one minus number two.
And I think you get the pattern. If operator is equal to multiply, then result
should be number one times number two. And finally, else,
this has to be divide, so result is going to be number one divided by number two.
And that is pretty much all we need. We have a couple of parameters inside of the function,
then we create a local variable, and we're adding a whole bunch of logic that assigns a value to the result. At the
end of the function, we are returning one value, and that one value is being printed out.
This should still give us a three. Let's try. That looks good. We're getting a three,
and if I do 10 minus three, then we should be getting seven. And we do. That looks really good.
I suppose one thing that you can do is give the operator a default value of plus.
That way, if we only give it two numbers, like so, then we're going to add them together by
default. Let's try. We should be getting 13, and that looks good.
All right, perfect. With that, we have basically all you need to know about functions. And by the way, inside of
Godot, the default style guide tells you that there should be two spaces between
functions. Not something you have to do, but something that you usually see. It's
just to keep your code a bit more readable. Anyway, with that, we have another super
important concept. All righty, let's get started with the first project, where we are going to
create a Frogga style game. Obviously, this isn't going to be the most sophisticated game. However, it is
going to teach you all of the important parts of Godot, like using scenes and nodes, using GDScript, and using all of
this to create logic. By the end of it, you should already have a pretty decent idea how Godot works on a basic level.
Now, to get started for the first part in this project, we are going to create a very basic outline, where we have a
player and the background. Or to go into a bit more detail, we want to create a basic game scene,
and then place the player inside of it. On top of that, we want to move the player by just a bit.
Although for now, this is going to happen without input. That's going to be the next part.
Here we are in Godot, and at the moment, we don't have any kind of scene inside of the engine.
That being said, if you look at the file system, we have a couple of folders. Most importantly, we have graphics. In
there, we have, for example, the player, a simple version and the tile set. We're going to come to that later.
Then we have a whole bunch of objects and so on. We also have fonts and audio, but those
aren't too important. Now, to get this project, you want to look at the video description. In there, you can find a
link to a Google Drive folder, which is going to look like this. In there, you have the project files for every single
game. For example, right now, we are working on the Frogga game, and
in there, you have the Frogga project setup and the Frogga start files. The second one you want to download, so
download, and then your computer should be downloading the entire thing.
Inside of the project stages, you're going to find this, by the way, the finished code for every single part
of the game. If you get stuck somewhere, you can use that to figure out what went wrong. Should be quite helpful.
But much more importantly, the file you just downloaded is going to look like this, and you want to extract it
in the usual way. Then you're getting the Godot starting files. Right now, this is just a folder.
To import the entire thing into Godot, you want to open the editor, and then you can simply drag and drop the files.
From the folder, the one you have just downloaded with all of the stuff inside, just drag it in there, and then you're
good to go. Open this one, and you should be seeing the project. Alternatively, you can go
to import, and then navigate to where you have to be, which in my case is going to be the
downloads folder. In there, you have the Frogga start files. Open this one, and then you want to open this project file
inside of the folder. You're going to get the same result that you are seeing in here.
Double click on that, and then you get exactly what I have started with.
Other than that, this project does not contain any kind of script. All of this we have to create ourselves. And well,
for that, first of all, I want to create a 2D scene, which I want to rename right away to
game. Then save the entire thing with control S or scene and save scene.
And I want to save it inside of the scenes folder. Game.tscn is totally fine.
And now we have our first scene for the game and this is going to contain all of the logic.
So for example, attached to this is going to be the player, the background, all of the cars and so on.
Speaking of which, first of all, I want to add the background to it and for that we can look at graphics in
there somewhere we have map.png. This I want to import. Or in other words, I want to create a
sprite 2D which can hold a texture. So in other words, I want to drag map.png into this
texture slot and then we have the background. That is actually already pretty solid. That way we have at least
something to look at. Next up, I want to create the player. For that, I want to create a new scene
and then create another 2D scene. This is going to be for the player and to that I want to add another sprite
2D. This sprite 2D is going to display the player graphic. So let me zoom in a bit
and then go to the folder graphics, player and player symbol. Drag it inside of the texture and then
we have the player. Although if you look closely, this is looking horrible. Also, while I'm
talking about it, if you look at game and you zoom in into this map, this is also not looking great. So what's the
problem here? When you have graphics like for example the player, Godot has to scale it. This original player
graphic for the player is 12 by 14 pixels, a really really small graphic. And Godot, to make it this big, has to
scale it up by a lot. Which is totally fine. The issue is that there are different ways to scale a graphic and
the one that we are currently using is really really not working. To fix that, all we have to do is go to
texture and there we have filter. At the moment it's set to inherit, but if we set this to nearest, then this is
looking significantly better. That being said, this isn't what I actually want to do. So let me undo it
simply because I want to do this operation for every single graphic inside of my graphics folder. Doing it
one by one would be kind of a pain. You can simply go to project, project settings and then look for texture
and then look at textures. In there you have a default texture filter which is set to linear by default, but we want to
set it to nearest. That way by default we are scaling all of the graphics in this filter style and
that is going to look a lot better. Nice improvement. Then I want to save the scene.
Player.tscn is totally fine and then we have the player. And this player we want to add to the
game scene. So with game selected, I want to either instantiate a child scene or press
control shift and A and then we can add the player scene to the game scene. If I now zoom in a bit, we can see the
player. Let's move the player roughly here so it's a bit easier to see.
And then we can try to run the game. For that, click on this button here or press F5 on the keyboard
and then you want to select the current scene to start the game. Select current, then we're getting a
graphic and it does work, but it's really small at the moment. That we're going to fix later. For now, this is
totally fine. I am happy with that. I suppose to see the player a bit better, we can scale the sprite 2D which we do
under transform and then scale it by a factor of six. If I now go back to game, the player
should be much more visible. And that is definitely better. All right, with that we have two scenes
that work together. Next up, we want to move the player in a certain direction. For that, the player scene needs to have
a script. So click on attach a new script. We want to have inherit node 2D and save
it inside of scenes and player.gd. All of this is fine. Click on create
and now we can write a couple of lines of code. Now for the player, first of all, I want
to have a couple of basic variables. I want to have a variable for the direction
and this needs to be a vector 2. If we set this data type, the default value that we are getting is going to be a
vector 2 with X and Y both being zero. Which is exactly what I want. Although if you want it to be a bit more specific
here, you could do vector 2.0. Same thing really, choose whatever you like.
Besides that, for the player, I want to have a speed which should be an integer and for the default value, I want to go
with let's say 200. Once we have that I want to call a function that runs on every single frame.
This could either be func_process or it could be func_physics_process. I am going to use physics process
because later on for collisions, we want to have just a bit of physics. Although at this point it wouldn't
really matter. And in here we want to move the player position. Or in other words, we want to
grab the position of the current node. Remember, when we are attaching a script to a node like for the player at the
moment, we get access to all of the stuff inside of the inspector. So at the moment we are targeting the
position via this position value. If you hover position you can see that this value wants to
have a vector 2. And the way I am going to approach it is I'm going to add a value to it which is going to be our
direction multiplied with the speed. And that's actually all we have to do. Although at the moment this isn't going
to do anything. So if I run the main scene the player is not going to move. But
that happens because our vector 2 for the direction is zero. We have a vector 2 with zero and zero and this we are
multiplying with 200. The end result is going to be a vector with zero and zero, so the position
doesn't change whatsoever. However, what we can do is change this vector 2 to a value that's not zero.
I want to have a vector 2 where X is one and Y is zero. And now if I run this thing again
our player is moving to the right, although it is quite fast. Let's change this to a five.
And there we go. Now the player is moving to the right. If I set this to one and one
then the player is moving right and down and that is working pretty well. Perfect. With that, we have a really
basic setup. Obviously it doesn't do very much, but at the very least we can move things
around. Let's do an exercise. I want you to create a new scene with a few objects like trees, boxes, whatever
you like inside of the graphics folder. Add all of that to the scene and then add the scene to the game scene. So you
are decorating the entire thing a bit effectively. On top of that, use GDScript to scale up the scene over
time. How fast this is going to happen is entirely up to you. Pause the video now and see how far you get.
I want to add another scene which once again is going to be a 2D scene. Let's call this one exercise object. The
name really doesn't matter and then save it. The scenes folder is totally fine, save in here and then we want to attach
a couple of objects from the graphics folder. Let's say I want to start with a basic
tree. You can actually just drag and drop it into the scene. Godot is automatically going to give you
a sprite 2D. Although by default the position is going to be a bit weird.
So let's reset that one and then the tree is right in the center point of the scene. I guess besides that we can add
another tree and let's say we could add a box like so. This I want to save and then inside of the game
I can add via this button or control shift A the exercise object. This we can place somewhere let's say
here. Then run the game and we have another scene inside of the game.
It doesn't look amazing yet, but at the very least we have something. Besides that
I want to add a script to the scene. All of the defaults here are totally fine, then create and like for the
player I want to run a function on every single frame of the game which I am doing via
physics process once again. Now for this thing, I want to target the scale property
which we can target via scale. So hover over the property inside of the inspector and then you are getting some
bits of information that are super useful. Once again, Godot wants to have a vector
2 with X and Y and that determines the scale. So to target it, I want to type scale and then assign a new value to it
where we want to get scale and multiply it with a value of let's say 1.1. Let's try the entire thing and that
might be quite fast and yeah, it definitely is, but you get the idea. Let's change this to 1.001.
Then it's not so strong anymore. Now we can see that the scene that we have just created is growing very, very
slowly. Not something we can use for the actual game, but for an exercise, this is
pretty all right. And with that, we are pretty much good to go for this part. Although there's one thing that is a bit
annoying. If you look at the bottom, Godot is complaining with the debugger, where we have not two errors, but two
warnings, that we are not using the delta parameter. To fix that, I want to add an underscore
to both of them. That way Godot stops complaining. If I run the scene again now,
the debugger doesn't complain, which feels a lot better. Next up, we are going to move the player around using
player input, which is bringing us a lot closer to an actual game. For that, we have to capture user input, which in
Godot is quite easy. We can simply create an input map. Via that, we can capture keyboard, controller, joystick,
and mouse input, which is really all that we need. So, back in Godot, you want to go to
project, project settings, and at the top, we have an input map. The way this thing is working is you define an
action, and associated with this action, you have some kind of input, like a key press on the keyboard, for example.
For example, I can type left and press enter, then we're getting one action for left. And to this action, we
can assign events, which is a fancy way of saying a button on the keyboard, a mouse button, a
joypad button, or whatever you have available in here. Godot is also listening for input, meaning if you
press a button on the keyboard, for example, the left arrow key, then it gives you the right option right
away, which is super handy. So, in my case, I want to click on okay, and now to the left action, we have the
left physical button on the keyboard. Besides that, another action I want to add to it is the A button on the
keyboard. Click on okay, and now we have two key bindings for this action. You could also add a controller, although
for now, I am not too worried about it. What I rather care about is to add other keyboard bindings for up,
right, and down. For all of these, I want to add the
keyboard bindings. So, for up, it's the up arrow on the keyboard, or the W key.
For right, I want to have D on the keyboard, or the right arrow. And finally, for down, I want to have
the S key, or the down arrow. And that is all we need for now. We can close this dialogue,
and then work inside of GDScript. Specifically, I want to work inside of the player. Now, there are a couple of
ways to work with the keyboard bindings. The easiest one, I think, is to use input, and then is action pressed.
This is looking for an action that is being pressed. Let's start with the right action, and
then print the result. If I now run the code, by default, we're getting false, because
the button is not pressed, but if I press right on the keyboard, we are getting true. If I release it, we're
going back to false. The way we could use that, for example, is to only move the player if this value
is true. Let me cut it out, and add it to an if statement. Only if you're pressing the
right button, then we want to move the player. If I run the thing now, the player is
not moving, but if I press to the right, then the player is moving, which is already quite a bit better.
Although this still isn't exactly what we want. To actually move the player in a certain
direction, I want to keep position is equal to direction times speed.
And then, when we're pressing the right button, I want to change the direction to a vector 2.right,
which is the same as a vector with one and zero. Besides that, I want to duplicate all of
this, and then get the action pressed for left. If that is
the case, direction should be vector 2.left. If I now run the code,
I can press to the right, the player is going to the right, and if I press left, we are going left.
And this kind of works, although the player never stops, which isn't ideal. Meaning we have to figure out some kind
of system to organize the input, and then get the right direction. There are a couple of ways of doing it,
and the best way to get started is to look at input. For that,
hold control, and then click on input. Then you get the documentation for input.
If you go down a bit, you can see all of the methods associated with it. There are quite a few. The one that we
care about the most is called get vector. Click on that,
and then we are going to do an exercise. I want you to read the documentation and figure out what input.get_vector
actually does, and how you can use it. Pause the video now, and see how far you get.
I hope you read this entry. I think overall, it's fairly straightforward. Basically, get vector wants to have four
inputs, negative X, positive X, negative Y, and positive Y.
Or in other words, for normal people, left, right, up, and down. These are the directions
that we can add to this method, and as a result, we are getting a vector 2 that organizes all of them super easily.
Or in other words, back inside of the player, I want to get rid of all of this,
and instead, update direction via input and get vector. Now, we need the four different
directions. Negative X is left, positive X is right, negative Y is
up, and positive Y is down. Once we have that,
I can run the game, and now move the player around. Feels like magic. If I stop pressing any of the buttons,
then the player stops as well, which works really, really well. Get vector you see all the time. It's
incredibly useful, and fundamentally, it's not that complicated. All it does is it takes four inputs, and then
returns a vector that we can use right away inside of the direction. And to finish all of this up, I want you
to do another exercise. Create another input, this time for the space key, and then inside of GDScript,
when this one is being pressed, print something. And as an extra challenge, make sure that when the button is
pressed, you're only printing it once, not multiple times. You will definitely have to read the
documentation for all of that one again. Pause the video now, and see how far you get.
Once again, back inside of Godot, I want to go to project, project settings, input map, and then create a new action.
I call this action confirm, because later on from the title screen, this one goes back to the actual game.
Confirm might not be the best name, but it's going to work for our purposes. The actually important part is I want to add
a key binding to this action, with the space button. Click on okay, and now we have space
physical. That's all we need. Next up, inside of the player script, I want to check if input
is action pressed with confirm. If that is the case, I want to print
something. And you might think that this is all we needed. However, if I now run the game,
and I press space, even if I press it once, we're getting something multiple times.
Not ideal. So, what's the problem here? Is action pressed checks if this key is pressed on every single frame of the
game, and physics process always runs at 60 frames per second. Meaning this function checks every 60
milliseconds if this key was pressed or not. Even if you only press it for tenths of a second, for Godot, this is
quite a few inputs. Now, there are a couple of ways to fix it, but the easiest one is to not use is action
pressed, but rather is action just pressed. That way Godot is only checking if the
button is pressed, but not if it is held down. If I now run the thing again,
I can press space, and we only get one output every single time. Much better. With that, we have user input. At the
moment, we do have some basic input, but the game just doesn't look very good. Most importantly, everything is tiny,
and we can only see part of the background. All of this is going to be fixed in this
part, where we are going to add the camera. That way we can zoom in, and actually
follow the player on the map. Unfortunately, cameras in Godot are really, really easy.
You simply create a camera, and then tell the camera to follow the player. That's all we need. The camera could
also follow any other object, really whatever you need. On top of that, you can also set limits
and speeds for the camera. That way, you have a ton of control over how this thing is moving.
First of all, the player shouldn't be scaled up anymore, but the scale should go back to one and one.
On top of that, inside of the exercise object, I want to get rid of this code, so we're
not scaling it up anymore. That way, I can run the whole thing and everything is super super tiny.
To fix that, we want to add a camera and zoom in. On top of that, the camera should follow
the player. Now, for that, through the player scene, I want to add a camera 2D. If I now go out a tiny bit,
you can see this pink purple-ish frame. That is the frame of the camera. And simply by having it attached to the
player, it is already going to follow the player. In other words, if I now run the game and I run around, the camera is
following the player, which is already a lot better. Besides that, if you look at the
inspector, we can modify a couple of things, most importantly the zoom level. If I set this to a factor of four for X
and Y, then the frame has gotten a lot smaller and if I now run the game, we can see the player quite a bit
better. And that is actually already making a pretty big difference.
You can also set the offset and for example move the camera down by quite a bit or up by quite a bit
because the player might want to look a bit more ahead, but I guess for our purposes, we can leave it at zero and
zero. Besides that, if you look a bit further down, we have a few more options.
We can set a limit for the camera. By default, these are basically infinite. Then we have position smoothing and this
you always want to enable. That way the camera follows the player a bit more smoothly. You can actually see
it right away. If I run the game and I move around, the camera takes a little bit of time to
catch up with the player, which right now looks a bit silly because the player is too fast.
But if we increase the speed of the camera to something like 20, then
this is going quite a bit better. Usually, you do want to have position smoothing enabled. It just looks and
feels better. But once again, whatever you think is best. Finally, we can set a limit. For example, for the left side,
we never want to go further than zero pixels. If I now run the game again,
I can go to the right, but the camera stops at position zero. And that is
pretty okay. Obviously, you want to measure this value.
For that, inside of the game, you want to look at where this map starts and ends.
The best way I find for that is to drag the ruler and then you get a pixel measurement.
So, if you hold it there, this is going to be negative 192. Since this thing is symmetrical, the
right side is going to be 192 as well. And for the top, we have negative I think this is going to be 320
and positive 320 because once again, this map is symmetrical.
Now, to get rid of the rulers, you simply want to drag them outside of the screen.
After that, inside of the player for the camera, the left side should be negative 192.
The right side should be 192. The top should be negative 320 and the bottom should be 320.
Top and bottom can be very easily confused, but remember, going up means you are decreasing the values.
But let's try all of this now and I can move in the different directions and we cannot go outside of
the window. And that is working pretty well and makes the game feel quite a bit nicer.
I guess while we're here, what you could also do inside of project and project settings in general,
you can look at display and window. In there, you can set the viewport width and height.
Usually, I set them to 1280 by 720, which is a much more standard resolution. Besides that, you can set
the mode. You can stretch the thing in different ways. You can work with landscape or portrait and do lots of
different things in here. Although, I'm not going to touch them too much.
If I run the thing again, now you can see our resolution is 1280 by 720. And obviously, you can also resize the
entire thing. It works totally fine. And with that, we have cameras. Our game is coming together quite nicely, but
there's one really important thing missing right now, physics or in other words, collisions.
That we're going to fix in this part. And for that, we have to talk about physics in Godot.
We want collisions and for that, Godot has dedicated nodes. There are three that basically do all of
the work. The first one is a static body 2D. This is a well, static physics object.
It doesn't move and it's basically for objects like houses, trees, bins, really whatever doesn't move and needs to be
collided with. Next up, we have a character body 2D. This is a physics object that can be moved via code.
Pretty much any kind of character is going to be a character body 2D. This could be the main player, this
could be enemies, this could also be a car, really anything that moves and doesn't have to be physically accurate
while moving. More or less, anything that moves and needs to collide with something is going to be a character
body 2D with some minor exception. The most important one is for rigid body 2Ds.
These are physics objects that can be moved via physics. These are going to be used for arrows, cannon bolts and things
like that. And just to be explicit, the difference between a rigid body 2D and a character body 2D is that a rigid body
2D gets some code in the beginning to determine the direction of the movement
and then everything else is covered by physics. Whereas a character body 2D is going to
continuously get directions from the code, meaning these objects can be updated constantly. Besides these three
nodes, there's one more node that you see very often and that is an area 2D. This one simply checks if a body is in
an area and that's all it does. On its own, it doesn't do any kind of physics, but it works very similar compared to
the others. As a consequence, they are usually grouped together. And before we get started, there's one more super
important thing and that is that physics objects are not moved via their position.
Instead, we're giving them a velocity and then Godot calculates the position. By that system, Godot can account for
physics right away. First of all, I want to create a static physics object. We can actually use the exercise object for
that. Now, at the moment, we have a node 2D at the root node. This I want to change.
For that, I want to right click on it and then change the type.
If you now look at all of the nodes, we can go to node 2D and in there, we have a collision object 2D.
If you expand that, we have an area 2D and we have physics body 2Ds, but we have a static body 2D, a character body
2D and a rigid body 2D. The nodes I just talked about. These are the main physics objects.
Although, they're not the only ones. If you expand static body 2D, you also have an animatable body 2D, which is
basically a static body 2D that can be moved a bit. And if you expand rigid body, we have
physical bone 2D, which is going to be useful for skeleton animation, but not something we need at the moment. So,
instead, I want to work with a static body 2D. Click on change
and now Godot is going to complain that this node has no shape. As a consequence, it cannot collide or
interact with anything. To fix that, we have to add another node. Either a collision shape 2D or a
collision polygon 2D. To get started, I want to add a collision shape 2D.
If we add that, Godot is going to give us another warning that a shape must be provided.
With the node selected, you want to look at the inspector and there we can add a shape. Loads of options in here. Choose
what you think is best. Usually, a rectangular shape 2D is working the best.
And I guess just to keep things simple, we can get rid of the box and of the second tree
and then change the shape of this thing so it fits the tree reasonably well. And by the way, if you hold command or alt,
then you can scale this thing while mirroring it around the axis. This works on X and Y.
And let's create the box like so. Cool. With that, we have one physics object. If you now look inside of the
game, we have a tree, basically. That being said, at the moment, we are
still not getting collisions and if I run the game, the tree just doesn't do anything.
For actual collisions, both the player and the tree need to be physics objects. At the moment, the player is simply a
graphic that can move around. No collision yet. Now, before we start working on the
player inside of the tree, in the script, you want to make sure that you are extending the right node.
At the moment, Node2D is incorrect because this is a static body 2D. Meaning, this should be static body 2D
as well. Without that, the code would start doing weird things.
Now, that being said, this exercise object doesn't need to do anything anymore. We can remove the
script entirely. And close the script, we can simply discard it.
That way, we simply have a tree. And I guess while we are here, we can rename it to tree.
And just to be consistent, inside of the scenes folder, this shouldn't be exercise object, this
should be tree. And the script, so exercise object.gd,
we can delete. Remove it. And then, the game is going to be a bit
cleaner. Now that we have that, we can start working on the player.
First of all, the player root node needs to change the type.
We want to have a character body 2D. Once again, Godot is going to complain that we have
no collision shape. This we can change by adding a collision shape 2D and then giving it a shape.
For the player, I want to go with a rectangular shape and then scale it so we are hitting most
of the player. You want to leave a bit of space at the top and at the bottom
because these parts should overlap with other objects. That way, you get a fake kind of 3D effect. Looks really nice.
You'll see later what I mean. Besides that, what's really important to do inside of the script, we want to
extend a character body 2D. And now that we have that, inside of the game,
I can move the player around and we still don't have collisions. That is because, at the moment, we are
changing the position of the player via the position property, which you can do. You wouldn't get an error, but you also
wouldn't get collisions. The way you are supposed to approach it is you are supposed to override the velocity,
which is a property of a character body 2D. This needs to be moved in a certain direction, which we are getting via
direction multiplied with the speed. Once we have that, you want to call move and slide.
That way, it applies this velocity and actually moves the character body 2D. As an outcome,
the player is going to move a lot slower. That we can fix by changing the speed back to 200. Might actually be a
bit too fast. But now, the important bit is we have a collision with the tree.
So, I cannot go further than this tree and that is working really, really well. That is all we needed for collisions.
For the player, we have a character body 2D, which we are moving via the velocity and move and slide.
Besides that, we have a tree, which is simply going to be a static object with a collision shape.
We also have an image, but that isn't actually necessary. At least for the collision. For the
actual game, this is important. So, with that, we have a pretty good system.
To finish up this part, let's do an exercise. I want you guys to restrict the player
to the map. Or in other words, use static bodies that ensure that the player is not able to leave the screen.
Pause the video now and see how far you get. Back inside of the game,
in the main game scene, I want to add a new child node, which is going to be a Node2D.
Let's call this one borders. To this node, I want to add a static body 2D
along with a collision shape 2D. For the shape, I want to add a rectangular shape.
That way, once again, we're getting the shape and the player is not able to move past that. So, here, we cannot go
further up. And this shape, I want to move to the right.
So, we are here on the right border of the map. And then, I want to scale this thing up
by a lot. So, the player cannot go further to the left and leave the screen. Let's try
this one now and the player is not able to leave. This we have to do for all of the four
sides. Although, you do have to be careful. If I simply duplicated this static body 2D
by clicking on duplicate or control D, then we would get another static body 2D that we can move to this point here and
that would work. I can now run the game and the player cannot go further to the right than this border. The issue is
if I wanted to change this collision shape, I would also change the other one.
If you duplicate a static body 2D with a collision shape, these collision shapes
are going to be linked, which sometimes can be useful, like in this case where we do want to have the
same shape. And this one should be a bit further here.
But for the top and bottom bit, this would not work. So, do be careful here. Instead, I want to add another static
body 2D with another collision shape. And for this collision shape, I want to
create a new rectangular shape that is going to be all the way at the top here.
And let me move it to the right position. Here and here is fine. Finally, I want to duplicate this shape
and then move it all the way down so we are covering the final part of the map. And now,
the player is not able to leave the screen in any direction.
Perfect. Next up, we're going to animate the player, which is going to make the
entire thing feel much more dynamic. In Godot, there are a lot of ways to animate things. The easiest one is to
use an animated sprite 2D, which is a sprite 2D that's, well, animated. Inside of this node, you can create animations
and then select them. That's pretty much it. Let's have a look. Back in Godot, I want to work
inside of the player and then get rid of the sprite 2D. Instead, I want to add an animated
sprite 2D. Which at the moment is going to give us a warning
that we have to add sprite frames to it. For that, you want to look at animation and then inside of sprite frames, create
new sprite frames. This right now doesn't really do anything, but if you click on it again,
then you're getting a sprite frames editor. In there, on the left side, you can see
the animation that you want to play. Let's say for now, I simply want to play the left walking animation for the
player. Then, inside of this main field, you can play the actual animation. This you can create in basically two
ways. Number one is you could simply direct graphics in here. For example, if I grab a couple of these random objects
and drag them in there, I can play the animation and then Godot is going to play one frame after
another. Which would work, but for our purposes, this isn't ideal. Let's undo all of
this. Instead, for the player, we have a player tile set. If I drag it in there,
you can see that we have all of the player frames in this kind of setup. And from this, we want to select
specific frames. Like for example, this graphic here. To work with that, inside of the sprite frames, you want to click
on this symbol where you can add frames from a sprite sheet. We want to go to graphics, player and
then select the tile set or the sprite sheet. And then, we can zoom in by holding control and using the mouse
wheel. Godot wants to separate this graphic into specific cells.
We have four horizontal cells, but only three vertical ones. The size lines up pretty well. So, this looks pretty good.
And now, we have to select the right kind of frames for the animation. At the moment, we want to get the left
animation, which is the left one. And if you click on it, you're getting a number. This would be frame zero,
then we have frame one and then frame two. These are the three frames that I want
to add to it. And now, we can play this animation.
Although, at the moment, it doesn't look great. This animation is actually supposed to
have this frame, then this frame, then this frame again and then the final frame.
To get that, you can simply have the first frame selected, then press control C and control V. And now we have
duplicated the frame. And to move this frame, you can either move it in the right position or use these arrow keys.
That way, we can move frames around quite easily. We are getting a much nicer walking
animation. You can also set the frame rate, if this thing is looping and if it's auto
playing. You can also set the frame duration, but I don't really care about any of those values.
Just make sure that you are looping the animation. Otherwise, it would simply stop on this frame. Also, for now, I
want to auto play this animation. And that way, if I run the game, we are getting a very basic animation.
That is looking pretty all right. And now to work with this, we want to use GDScript. And to keep things
organized, I'm going to create another function for the animation. No need for parameters.
I basically want to check if we have a direction, then we want to actually play the animation. If we don't have a
direction, then the player should be static, or in other words, we only want to get the first frame. That way it
looks like the player is idling. If we have a direction, I want to play the animation.
Which for now means we are simply not doing anything because we already have an animation, it's just not doing the
right thing. However, if we don't have a direction, so we are not moving, then I want to get
the animated sprite and update the frame property. This one should always be zero.
Now, by default, this value increases. So, we are selecting different frames. But if you set it to zero, we always
taking on the first frame, so this one here. That way, at the very least, we can play
the animation or not play the animation. Really important, don't forget to actually call this animation function.
Which I want to do before we are doing move and slide animation. Although, the actual position of
animation doesn't matter. Anyway, let's try this now. If I am not moving, the player is also
not animating. But if I start moving, then we are getting the walking animation.
And that is working pretty well. Next up, I want to get the right walking animation. For that, you could add
another animation in here. So, simply add another animation with right, and then select the player and the right
movement. So, these frames here. However, we don't actually have to do that. We
can approach this much more elegantly. So, let's get rid of the right animation.
Instead, all that we have to do, if the player is moving to the right, we want to flip this animated sprite
on the horizontal axis. Which we can actually do super easily. With the animated sprite selected, we
have offset, and there we can flip H. And if you hover it, the property is called flip {underscore} H. Inside of
GDScript, what we can do, if direction {dot} X is greater than zero, then the animated sprite 2D {dot}
flip H property should be true. Let's try that. If I go left, we're looking left, and if I go right, we are
looking to the right. Now, if I look left again, we're not going back to the left side. So, that's
not great, but at the very least, we are making progress. To fix this part, all we have to do is
add an else statement, and then copy all of this with the difference that flip H should
be false. Let's try this again. And now, we're getting the left animation and the right
animation, and that is working really well. Cool. Definitely progress.
Although, we are trying to do something very simple, and we are using four lines of code for it. This could be more
elegant. The way to approach that is to first get the animated sprite 2D,
and then flip H. The value we want to assign to it is going to be direction {dot} X being
greater than zero. And then we are done. This line is doing the same thing as these four lines.
And I think it's much easier to read as well. So, let me get rid of these four lines.
Try this again. And we are getting the same result. Doing this kind of stuff comes with
experience. If you work inside of an engine for long enough, then at some point, you find ways to make your code
more efficient. So, for now, if you work with more if statements, that's totally fine, but this would be more elegant and
much easier to read. With that covered, let's do another exercise. I want you to finish the animated
sprite. Which means you should be adding the logic for going up and down. So, in
other words, select the right animation depending on the player movement. Pause the video now and see how far you
get. Back inside of Godot, I want to work with the sprite frames and then add two
more animations for up and down. These, unfortunately, we cannot mirror.
I guess let's start with up. I want to click on this button again, select the player tile set, zoom in, and at the
moment, we are looking at up, which is this animation. Add the three frames,
and then, once again, I want to duplicate the first frame and place it between these two walking frames. All I
have to do is press Ctrl C and Ctrl V, and then drag this frame in the right position.
Just to double-check, let's play the animation, and that looks pretty all right. Cool.
Besides that, for down, we want to do the same thing. So, grid, player, get the down walking animation, add the
three frames, duplicate the first one, and drag the frame in the right position.
Then we are getting the down walking animation. Perfect. With that, we have the right animations
that we can work with. We just have to figure out when to play which using GDScript. I first checked if
direction {dot} X is different from zero. If that is the case, I want to play
animated sprite 2D and update the animation. This one should be left. We are still
flipping the sprite on the horizontal axis. So, if the player is going to the right, then left is automatically going
to become right. Besides that, else, if we are only going up and down, then I want to get the animated sprite
2D and update the animation again. Let's say for now, this one could be up.
Let's try this one. I can go left, I can go right, and I can go up.
Although, I cannot go down, at least not with the right animation. But we are making progress.
Now, at this point, you could approach this in two ways. You could either check if direction {dot} Y
is smaller than zero, and if that is the case, we want to do all of this. If that is not the case, let me duplicate all of
this. Direction {dot} Y is greater than zero,
then the animation should be down. Let's try this one now. I can go left, right, up, and down.
This, once again, is working, but we are also doing something very simple over four lines of code.
A better way of approaching this would be to get the animated sprite, then the animation,
and assign it up if direction {dot} Y is smaller than zero, and if it is not
the case, else, we want to assign the down animation. That way, we can get rid of all of this,
and we would have the same result. Much more elegant. Let's try all of that, and now I can go
in all of the four directions, and the player is animating or static when we are not moving.
At this point, we have basically finished the player. Which means we can start working on the cars.
For those, we have to do a couple of things. Number one, cars should spawn
automatically in a specific interval. For that, we are going to create a timer in just a
second. On top of that, they should start in a set position and move in a certain
direction. There are a couple of things that we have to go through. So, let's go through
it step by step. Number one, we have to learn about signals.
These are a vital component of Godot. And basically, any node can emit a signal. For example, a node can trigger
when there's a collision or when a timer triggers. Every node has its own signals,
depending on what they are supposed to be doing. This is a super important concept. Let's have a look at it. Back
inside of Godot, let's say I want to look at the player, specifically the root node, so the character body 2D.
On the right, we have the inspector, and there we can also see the node. This one contains signals and groups.
The thing that matters for us are the signals. In here, you can check all of the signals that this node can emit. For
a character body 2D, there aren't many because this node, in general, is just supposed to collide with things and not
do that much more. So, we can check some general things, but they're not really that useful.
Instead, a much more interesting node that's added to the main game is going to be a timer.
This is, well, a timer. If you look at the inspector, you can get a wait time, if this is supposed to trigger once or
multiple times, if it's auto starting, and that's kind of all you need. Let's say I want this timer to auto start and
then run for 4 seconds. This right now is not going to do anything. If I run the scene, at some
point the timer is going to trigger, but it doesn't really do anything once it triggers. So, this is no good.
But, what we can do, inside of node, we have the timeout signal. This one is going to be triggered when
this timer times out. The way you're going to use it is you double click on it, and then you have to
connect it to a script. At the moment, the only node that we have with a script inside of the scene is the player.
So, what I want to do is to add a script to the game scene. Add script, the default CR fine, create.
And now, with the timer selected again, double click on timeout again, and then add the signal to the game
script. Connect. And then, inside of this script, we can run a function. Let's say I want to print
timer has triggered. If I now run the entire thing again, every 4 seconds we should be getting
timer has triggered. There we go. This should happen multiple times. There we go. This looks pretty good.
If we never one shot, this would only happen once. This system I want you to practice right away as well.
So, I want you to create an area 2D node. This should be at the top of the map,
and when the player enters, it should print player entered. An area 2D node works like the other
physics objects. Meaning you have the node, and then you need a collision shape.
After that, look at the signals and see what you can work with. Pause the video now and see how far you
get. I want to add an area 2D node. Once again, Godot is giving us a warning
that this has no shape. This we can fix by adding a collision shape 2D or collision polygon 2D. In
your case, you could just add a collision shape 2D with a rectangle, it would be totally fine.
But, just to illustrate what you can do with a collision polygon, let me add this one. So, create, and now,
nothing has happened. But, if you look at the top, we can create a bunch of points with this thing is selected. Once
you have that, you can simply draw a couple of points vaguely at the top of the screen,
like so, and to close the shape, you want to click on the origin point again. Now, we have another shape for this area
2D. For the area 2D, the inspector doesn't matter that much, but if you look at the signals, we are getting a
ton of stuff to work with. Most importantly, we have body entered. This one checks if a physical body has
entered this area. Which is what we are looking for, so double click on it and connect it to the
game script. On area 2D body entered would be a decent name.
Although what you could also do is rename this area 2D to finish area 2D, and then the name here would also
change. It's usually a good idea to have descriptive names in here. Since our game is going to be quite
small, this doesn't matter so much, but for more complex game, you really really want to have precise names. Anyway,
click on connect. And now, we have another function that is going to be called whenever a
physical body enters this area 2D. Once that happens, we can print the body that we are getting, and we can print
as entered. Let's run the entire thing. And if I move up, at some point we are
getting, inside of output, the player character and has entered. So, this function here did trigger. So, with
that, we have signals. Those are incredibly useful inside of Godot. They make any kind of node much more
interactive and give you a ton of options. Before we finish, there's one more thing that I want to do, and that
is to organize the game scene a bit better. Inside of game, I want to add another
child node, which is going to be a plain node, which I want to rename to timer, and then add the timer that we have just
created to it. And that way, I can also remove timer two. Godot added the two because you cannot have the same name
for two nodes. The timer that we have created should be called the car timer. And for now,
I want to disconnect this signal, which you can do by right clicking on it and then disconnect.
Also, inside of the script, this timer function should disappear. We're going to replace it in just a
second. For now, I just want to have a clean start. On top of that,
the finish area 2D can go inside of borders. I think they kind of work together.
And finally, this exercise object should just be a tree. That's the better name for it.
On top of that, the sprite 2D is simply going to be the BG or the background. That way, all of this feels much more
organized. This is something you are going to learn really, really fast. A scene tree can become cluttered
incredibly fast, and you want to keep it as organized as possible. Now that we can create a working timer, we can start
working on spawning the cars. For that, we have to go through a couple of steps. And along the way, we are
going to learn how to spawn scenes via code. In other words, we want to create
instances of a car whenever a timer triggers. For that, number one, we have to create
a basic car scene. This is going to happen in the normal way. We are just going to create a new scene with a
sprite and an area 2D. After that, we're going to store the car scene in
the main game. Next up, when the timer triggers, we are going to
create one instance of that car scene. And finally, we can attach the car scene to the
current game scene. That way, we can actually see it. First of all, inside of Godot, I want to
create a car scene. Add a new scene, and the root node is going to be an area 2D. This area 2D, by
the way, I have used because there are no actual collisions between the player and the car. Whenever there's any kind
of collision between the two, the game is ending right away. So, an area 2D here is totally fine.
To this area 2D, I want to add a sprite 2D so we can see something. The graphics for that are going to come
from cars. Select one of them and then assign it to the texture of the sprite 2D.
Then we are getting one car. After that, for the area 2D, I want to add a collision shape 2D.
Which for this one can simply be a rectangle shape. Make sure you cover the whole car, like so and so, I guess, kind
of like this. Doesn't have to be too precise for our purposes, but obviously for real game,
do spend some time on it. All of this I want to save as car.tscn. Click on save, and now we have one car.
That actually should be renamed to car. And then, inside of scenes, I should have done that before, it shouldn't be
area2d.tscn, it should be renamed to car.tscn. Much better.
Now, we have a car scene. This I want to spawn inside of Godot whenever this car timer triggers.
Speaking of which, for this one, wait time should be 1 second for now, and it should auto start.
After that, for node, I want to go to timeout once again, and then connect on car timeout timer to the game script.
And now we get on car timer timeout. Whenever this thing triggers, we want to create one instance of the car scene and
attach it to the game. For that, we already have a car scene. So, next up, we have to store the car
scene inside of the game scene, which we do all the way at the top. I want to create a variable car_scene.
The data type for this one is always going to be a packed scene. You are getting it by preloading
a car scene. Godot is now giving you all of the options of data that you could preload.
In my case, I want to get scenes and car.tscn. That way, let me add just a bit more
space. We have the car scene ready inside of the game scene, which means we have
covered the second part. Next up, we have to create one instance of this car scene.
Which is going to happen every time the timer triggers. To create one instance of the car scene,
we have to call instantiate. That way, we are creating an instance of it, which we want to store in another
variable. Let's call it car. That way, we have one instance, so we have covered the third step. Finally, to
actually see the car, we have to attach it to the current scene. This happens via add child.
I want to add the car, and then I can run the game, and we should be seeing the car
somewhere around the origin point. So, there we are getting the car. On top of that,
with the game still running, you can look at the scene tree, and there we have remote,
where you can see the nodes in real time as the game is running. And there you can see Godot keeps on adding area 2D to
it every time the timer triggers. Besides that, you can see the nodes that we have added ourselves.
So, this is a real-time view of the game. And well, you can see quite well that we
are adding more and more cars. Inside of the game, you can't see them because they're on the same position, but that
we can work on. Inside of the car scene, I want to add a script to it. The
defaults here are fine as they are. And then, for the car, I want to add var direction,
which is going to be a vector two that at the moment points to the left. Besides that, I want to have var speed,
which is going to be 100. After that, I want to call func underscore process,
in which we are updating the position of this area 2D node. So, once again, inside of transform, we
can update the position of this node. Like we have done in the beginning of this project, I want to get the
direction multiplied with the speed. And you might be wondering now, why do I use process for the car, while for the
player I used physics process? And well, for the player, we have collisions, so we want to include
physics. Whereas for the car, we don't actually have collisions. Once again, as soon as
there's a collision between the player and the car, the game ends right away. So, we don't actually have collisions,
we are simply checking for an overlap. In practice, you could also use physics process. It doesn't really matter for
our purposes. But anyway, with that, we should have the car movement, although I think 100
is a bit too much. Let's go with two. Let's try out of this now, and there should be a car going to the left. And
that is looking pretty good. Perfect. Once again, if you look at remote in the
game, you can see we are adding more and more car scenes to the game. That being said,
there is a bit of a problem. Number one, adding the cars to the game scene is going to clutter the scene tree a lot,
which is going to make it difficult to work with. So, what we can do at this point is add another node 2D
that we can call objects. All of the objects in the game are going to be inside of this node 2D, including
the player and the tree. Add them in there, and then, inside of the script,
I want to add the cars to this objects as well. You can simply drag and drop objects in
the right position. Don't forget the dot. And now we're adding the car to the
objects node. If I run the game inside of remote, all of the cars are going to be inside
of objects. If you minimize it, this looks a lot cleaner. Once again, not something you have to
do, but it's going to make your life quite a bit easier. Besides that, it's actually not a great idea to spawn cars
indefinitely. So, at the moment, we keep on adding more and more cars, and this goes on forever. It never
stops, which isn't great for performance, because once a car leaves the screen, it
shouldn't be there anymore. You can't see it, you can't interact with it, it should just disappear.
And think of a more complex game. The more objects you have, the slower the game is going to run. So, you want
to keep the amount of objects as low as possible, which is generally a good idea.
In other words, for the car, as soon as the car leaves the screen, we want to destroy it.
That you can do by adding another node, which is called a visibility screen notifier 2D. This can check if
node is entering or exiting the screen. It should roughly fit the texture. That looks all right. It doesn't have to be
too precise. And then, inside of the signals, you can check if the screen was entered
or exited. We care about exited. If I connect this to the car scene, we want to destroy the instance of the
car scene as soon as it leaves the screen, or when this function is triggered.
This in Godot, you are doing via queue free. This destroys the current instance of
the node. Or in other words, if I run the game again and go to remote, inside of objects, we only ever have
maybe two cars. As soon as the car leaves the screen, we are destroying it. So, we only ever have
that many cars. And inside of the game, you can see we only have two cars on the screen, maybe three cars sometimes.
That's why we are making sure that we only ever have so many cars on the screen, which is going to be really good
for performance. Now, obviously, for our game, since it's really simple, it doesn't really matter,
but it's generally good practice. Besides that, I want to set start positions for the
cars. At the moment, the car always starts in the origin point, which obviously isn't
great. To fix that, I want to add another node 2D to the
scene tree that I called car start positions.
In here, we want to store a couple of nodes that have the start position for the car.
For that, you could use a node 2D. It would work, but Godot also has a marker 2D that works a little bit
better. So, with this marker 2D, if you move it,
you can see that we have the origin point along with this cross around it. So, it's a little bit easier to see.
But aside of that, we only really care about the position of this node, and this we are getting by a transform and
position. These marker 2Ds, I want to move all of the car lane start positions.
Left or right doesn't matter so much. You can alternate, or you can stay on one side, whatever you think is best.
So, let's do this again, like so and like so. And then, we have a start position for
every lane. Once we have that, as soon as we have a new car, we want to update the position of that car. For
that, first of all, I want to get a random marker from the car start positions.
Let's store that in a new variable, position marker. The way you're getting that is first of
all, you want to get the car start positions, and then get children.
This is going to return an array with all of the child nodes. On an array, you can call the method
pick random. That way, you're picking one random child of car start positions.
And once we have that, we can get the current car and update the position
of that car. Now, right now, you don't get position as a suggestion.
That is because Godot doesn't know what kind of node this car is that you have just instantiated.
If you want all of that, you can add S, and then the type of node that you want to create. So, in my case, an area 2D.
That way, if you have car, you can get all of the suggestions again, which can be super helpful.
And generally, being really picky about the right data type is a good idea. It can make your
life much easier. Anyway, for the car position, I want to have the position marker, and don't
forget, position marker is just a node. On this, we want to have the position as well.
If you want to be super precise here, this could be S marker 2D. If I now run all of this, we should be
seeing a car coming from the right. And that is working pretty well. If you go further down,
there are also some cars that can also disappear, and that looks pretty good. The issue now is on the left side, we
can't see any of the cars, simply because if a car starts here, for example, it only goes left.
Not great for our purposes. But that we can work on. Inside of the script for the car,
once the car is ready, or in other words, func underscore ready, we can simply check if zero.
If that is the case, we know we are on the left side. If that is the case, direction.x
should be one. That way, we're turning the vector left to a vector right.
Although, if I run the game now, this is not going to work. We are still getting the cars on the right side,
but we have no cars coming from the left side. The issue is that this ready function
only runs before we are updating the position. Or in other words,
the ready function, this one here, is called when you're adding a node to a scene,
which currently happens on this line when we are calling add child. Which at the moment is a bit of a
problem because we are adding the car here and the current position of the car is zero and zero. And then we are
running this line here, which is never going to trigger because the position is zero and zero.
Only after we are doing all of that, we are setting the position of the car. As a consequence, this if statement is
never going to trigger. I can actually demonstrate. I want to print
car direction. Let's run it and this function is never going to run.
So, this doesn't do anything. Fixing that, fortunately, is very simple.
We want to update the position of the car before we are adding it as a child to
the scene, which is totally fine to do. When we are calling this ready function, the car already has the right position.
And that way we should be getting cars coming from the left as well. So, there we get car
direction. I just have to find it. There we have the car. This can be kind of annoying in Godot.
Understanding when these inbuilt functions are being called sometimes can be a little bit tricky. Always remember,
ready is called when you adding the node to the scene tree. And while we are here,
I don't want to print something when it happens. Instead, I want to get the Sprite 2D and flip H
should be activated. That way the car is pointing in the right direction.
And with that, we should be having cars that go reasonably well in both directions.
At some point there should be one coming from the right as well. Let's look up here. That definitely works.
This we can refine, but I think for now this is working reasonably well. Now, before we continue, there's one
important topic that we do have to cover that does get a bit more technical. Frame rates and delta time.
So far, we did not care about the frame rate at all. The game simply worked and that's totally fine. However, in some
instances, this can be an issue. That is because the frame rate is affecting the movement of objects.
Not always, but in some circumstances and that we have to talk about. And while we are here, inside of Godot
for the car, when we are calling process, we are getting delta. Same for the player
inside of physics process, there we also have a delta. And so far, I just didn't talk about it, but it's a really
important parameter. But let's go for it step by step. Now, the main issue is that a frame rate
determined movement can be inconsistent. Here's a table with the same movement but different frame rates.
The actual speed is the first column where we always want to move at 10 pixels per frame.
Or in other words, inside of our game, for the car for example, we are moving the car by a certain direction
multiplied with a speed. In our case, for example, this would be a speed of two on the horizontal axis.
This change in position is applied on every single frame. So, effectively, we are moving at pixels per frame.
Besides that, we have the amount of frames per second. This number is going to vary on the
computer. If you have a really slow one, you might only get 10 frames per second. If you have a really good computer, you
might get 500 or more frames per second. This also depends on what happens inside of the game. If you have a lot of
objects, the game is going to be more difficult to run. Generally, you want to have 30 or more frames, that way your
game is going to feel smooth. But for this example, it doesn't really matter what the number is. What does matter is
the final column where we get the pixel movement per second. This is simply the multiplication of
these two numbers. For example, if we are moving at 10 pixels per frame and we have 10 frames
per second, we are moving at 100 pixels per second. Which on its own would be totally fine.
But the issue is, depending on different frame rates, we are getting a different speed because we are applying a change
in position multiple times per second. And this number varies. If we apply the change in position 10 times a second or
500 times a second, it's obviously going to be different. Which for our game is going to be a
problem because, well, you can see it here. If our pixel movement is 100 pixels per second or 5,000 pixels per
second, the game is going to feel significantly different. Or in other words, the movement speed of your game
will change depending on the computer. And this is really, really not good. Just imagine any game where the movement
speed could either be 100 or 5,000. No game would work like that. And as a consequence, we have to account
for it. Which we are doing via delta time. All that this concept describes is the
difference between the current frame and the previous frame. Or in other words, it tells you how long it took your
computer to render the current frame. Doesn't sound that important, but it's actually super useful
because imagine you have a game that runs at 30 frames per second, then delta time is simply 1 / 30 or 33
milliseconds. If you have different frame rates, then you get different numbers, obviously.
The specific number here doesn't actually matter. What matters is that the higher the frame rate becomes, the
smaller delta time will be. Which is incredibly useful because let's look at the table again with delta time.
The first three columns are what you have seen before. The only addition now is that we have delta time.
For example, if our frames per second are 10, then delta time is 1 / 10 or 0.1.
If our frame rate is 30, then delta time is going to be 1 over 30 or 33 milliseconds. All of that is useful
because delta time and the frames per second effectively cancel each other out. Imagine this formula here written
in a slightly different way where we have 10 pixels per frame from here multiplied
with the frame rate, in this case that's 30 frames per second, this number, and this we are multiplying with delta
time. This number here, 1 / 30 or 1 over 30. The end result has to be 10. For the
simple reason that 30 and 1 / 30 are simply going to be 1. The only thing that's left is the 10
that we are getting at the end. And this works with all of these numbers. So, if we have 60,
we would have 60 multiplied with 1 over 60, 500 multiplied with 1 over 500 and so on.
Frames per second and delta time cancel each other out. The only thing left are the pixels per frame. And that is really
all we want. That way it doesn't matter what the frame rate is, you always getting
constant movement. And this you should use whenever you change the position or rotation of any
node. The one place where you don't have to use it is with physics bodies because
for those Godot automatically applies delta time. And finally, let's do all of this in
Godot. That way all of this can also be a lot more practical.
We want to work inside of the car where at the moment we are updating the position
by getting the direction and multiplying it with the speed. Which on its own works totally fine. If
I run the game, the cars are going to move. Let me find one. There we go. We have movement.
However, this movement depends on the frame rate. In other words, if I go to project, project settings,
and then toggle advanced settings, then under run, we have a maximum frame rate, which right now is set to zero,
which means we have no maximum frame rate. Godot will try to run the game as fast as possible.
Let's say I want to limit the frame rate to 12. That way we get at least a little bit of movement.
If I now run the game, it's going to feel really rough, but the important thing is the cars are moving
very, very slowly. If we increase the number to 30, then they should be moving a lot faster.
And there we go. Now the taxis are quite a bit faster. And this is a problem.
The frame rate shouldn't affect the movement inside of the game. Let's set it to zero for now. That way
Godot will try to run the game as fast as possible. Now, to actually fix this, all you have
to do is multiply this value with delta time and then you are done. That's all you have to do.
So, if I run the game now, the cars are going to move quite a bit slower because delta time is a very low number. So,
there you can see the cars are not fast at all. Simply change the speed to a larger
number, let's say 200. And now the cars are a good bit too fast. Let's
go with 100 here. And then we have a car that is moving at a much nicer pace.
Really importantly now, if I go back to limit the frame rate to 12,
the game is going to be much choppier, but the speed of the car itself is not going to change. And that's the
important part. That is the point of delta time. It makes sure that you can change the
position independent of the frame rate. A super important concept that you have to use whenever you're changing the
position or the rotation or any of these basic properties of a sprite to default example to make sure that the frame rate
doesn't affect the speed of it. However, if you have a physics object that you move by the velocity and move
and slide, you don't have to do that. You do a place delta time scene automatically. Next up, we are going to
work on the player car collisions. Or in other words, we have to add the signal to the car that is going to be
emitted whenever there's a collision between the two. However, this has to happen inside of
the game scene because there is all of the logic for the actual game. Or in other words, we have to connect
the signal once the car is spawned via GDScript. Here we are back in the game and the issue we have at the moment is
that we want to check if the player overlaps with this car and for that we can use a signal.
Body entered would be best. The problem is we can connect this to the car, but then we have a script
inside of the car, so in here. I guess that would do something, but it wouldn't really be that helpful. All of the logic
for the actual game is inside of game.gd. So in here, I want to run the script for the collisions.
Fortunately, what we can do once the car has spawned, we can connect the signal via GDScript. All you need is the car
that we have created. Really important, we want the car instance, not the car scene. On that thing, we can run a
method called connect. Then we need the name of the signal, which in our case is called body entered. Simply type the
string of it, body_entered. The second argument is then the function that you want to call,
which in my case is going to be go to title because that's what we later on want to do once there is a collision.
Also, really important, I want to pass this function as an argument in here, but I do not want to call it, meaning
there shouldn't be parentheses afterwards. We only want to have the word go to title without anything else.
After that, I want to create this function. So funk go to
title. No need for parameters and now, once that is the case, I want to print player
car collision. And that should be it. Let's try. Inside of the game,
I am getting a lot of errors. You can see in the bottom, Godot is really really unhappy with us. Under
errors, we are getting an issue and let's read it out. Godot has an error calling from signal
body entered to callable go to title, the one that we have just created here. And the issue is the method expected
zero arguments, but was called with one. Understanding these error messages is incredibly important, so let's go
through it. Go to title expected zero arguments because there's no parameter. However,
because of connect and body entered, we are calling this function with one argument. This number here at the end.
Effectively, when Godot is calling this function, it passes an argument into it, but inside of the function, we don't
have a parameter and as a consequence, we are getting an error message. This happened because when you look at
the signal body entered, we are getting the body as an argument passed into it and this is what Godot
wants to do. To fix that, all you have to do is add one parameter, which would be the body. Although in our case, we
don't really care about it. But just to demonstrate what is happening, let's print the body that we are getting.
And if I now rerun the entire thing, we are getting a whole bunch of collisions actually.
Let's have a look at the output. The first collision that we are getting is a static body 2D collision.
Then we have another static body collision, another one, and then we have the actual player collision.
We are going to work on that in just a second, but for now, the important thing that you have to understand is if you
want to connect a signal via GDScript, you get the node or the scene you want to connect, then you call connect with
the signal name and then the function you want to call. And always make sure that you are counting for all of the
arguments that Godot is passing into this function. And once you have that, you can connect signals via GDScript.
Now, besides that, we were expecting the player body and player car collision, simply the string.
However, besides that, we got a whole bunch of additional collisions. What happened here?
When we are looking at body entered for the area 2D, it looks at any body. This would include these borders around
the screen. Static bodies are still bodies. So whenever a car is being spawned, it will collide with this
border. A car could also collide with the tree that we have created and it could also
collide with any other object that we might create, which at the moment isn't really the
desired behavior. So how can we fix that? For that, we have collision layers.
Effectively, every physics body has a layer and a mask. A mask is determining what physics object a node can see.
And a layer determines what other physics objects can see the node. That way, for every single physics object,
you can determine which objects can see each other. So with that, back in Godot. If you look
at any physics object, like for example the player, in the inspector under collision, we have layer and mask. All
of these are just numbers and this determines what layer and what mask the player is on. If you have all of these
numbers at the moment, you only get layer one, but what you can do under project, project settings, and if you
scroll down a bit, somewhere we have 2D physics. And let me disable advanced settings. We don't need it anymore.
In here, you can name all of the various layers. For example, we could have layer one being terrain,
layer two being the player, layer three could be the cars and layer four could be the walls.
And by walls, I mean these walls around the screen. And to get started with the player, the player should be on layer
two, which is only for the player. So this determines what other objects
can see the player. And on the flip side, for the mask, the player should be able to see the
terrain. He should not be able to see itself. The player should be able to see the cars
and the player should see the walls. Then for the tree, for collision,
the tree should be on the terrain layer and for mask, we can disable everything simply because the tree doesn't have to
see anything else. And just to demonstrate, if I now run the game and go to the tree, we are still getting a
collision and that is working pretty well. However, once I put the tree on a
different layer, let's say layer five that we are not using, then the player can walk right through
it. There's no collision anymore. The only layers that the player can see are
determined by the mask 1, 3, and 4. If the tree is not on any of them, the player is going to ignore it entirely.
All of this you should really practice. For that, set up the layers and the masks for the cars and the walls.
Really important, the cars should not see the walls or each other, but they should see the player. Pause the video
now and see how far you get. I want to start working with the car, specifically the area 2D node.
For collision, the layer should not be on terrain, it should be on cars. That way the player can't see it. Next
up, for the mask, the car should only be able to see the player itself and nothing else.
That way a car should already not see the walls around the screen anymore. Let's try this and now we should only
get a collision between the car and the player and that looks pretty good. Finally then, for these static bodies
around the screen, you could leave them on the terrain layer. It would kind of be possible, but
I would rather have this organized, so they should be on walls and they should not see anything else. This I want to do
for all of them, so this should be only four. Same for this one.
And same for the final wall, only on layer four. That way the player should still see them. There we go. This works.
But the cars are going to ignore it entirely in two different ways actually. With that system, we can control a lot
better what is colliding with each other. Finally, all the way at the top, we have
the finish area 2D. This one should not be on any layer. Whatever words, no other physics node
should be able to see it. However, it itself should be able to see the player.
That way, if I run the game again, the player should still be getting up here and that works, but no other physics
object can interact with it. At this point, we have covered all of the basic concepts to create a level. If you added
lots more stuff to it, you would have a pretty decent basic game. And this is what we are going to do in
this part. However, before we get to it, there's one important concept that I still have to cover.
At the moment, the player can either be behind an object or in front of it. But, this system is static. The player
couldn't dynamically change the position. Back inside of Godot,
the position of the player relative to the tree, these two objects, is determined by the node tree.
So, at the moment, the player comes first, and then we have the tree. And as a consequence, if I move the
player around, the player is always behind the tree. Inside of the game,
you can see that the player right now is in the right position, behind the tree. But, if we go around,
now the player is, well, still behind the tree, but the player should be in front of the tree.
That we have to fix. The system for that is called Y sorting. All that it does is it decides which
object is going to be on top depending on their Y position. Just imagine that this line up here is Y
being zero. The player position in this case is, let's say, 10 pixels, and for this
player it's going to be 20 pixels. While the car is always going to be on pixel position 15. Same for this one. If
all that Y sorting is doing is it compares these two numbers. Whichever has the higher Y number is
going to be drawn on top. That way, if the car is further down, it's going to be on top of the player.
If the player is further down, it's going to be on top of the car. And that's literally it. It's a really
simple concept, and you can apply it to Godot super easily. All you have to do is grab the parent node, objects in this
case, and then go to ordering inside of the inspector. There we have Y sort enabled. This
should be enabled, and then all of this is going to work. So, the player can be behind the tree,
like so, and the player can be in front of the tree, like so. And that is feeling a lot more
realistic. And by the way, it's really important for this system that the collision
shapes for the player and the tree don't cover the entire height of the object.
For the tree, we have a bit of space at the top and the bottom, and same for the player. We have a bit of stuff below and
above the player. That way, these two can overlap a bit, which makes the game feel much more
three-dimensional. And well, with that, we have the basic system in place. Now we can add lots of
objects. To place all of these elements, you want to organize your project so the entire
thing is reasonably streamlined. Let's do some examples. We already have one object, the tree,
where we have a static body 2D, a sprite, and a collision shape. We could, for example, copy this tree,
and then place it here and here and here. Run the game.
We would have more trees. Pretty straightforward. If you wanted other objects,
all you would need to do is create another static body 2D node. Let's say for this one, I want to create
a box. For which, I want to go to graphics, and then inside of objects, we have a couple
of boxes. I want to choose one of those. To add it to the scene, I want to add a sprite 2D, and then box.png is going to
be the texture. After that, zoom in quite a bit, add the collision shape 2D.
For this one, we definitely want to have a rectangle that we then want to scale down quite a
bit to something like this. Afterwards, save the entire thing inside of scenes, box.tscn,
and then we have a box. That once again, we want to attach to objects,
which means in there, box.tscn, and then in the origin point, we have a box.
And by the way, if you try to move it, and you move something else, like for example, right now the car's start
positions, you can avoid that by having the box selected inside of the scene tree, and
then hold command or alt. That way, you're only moving the node that you have selected inside of the scene tree.
Can sometimes be super handy. But anyway, once we have that, we have a box, and then inside of the game,
we can collide with it. That being said though, for something as simple as a box, this is kind of
overkill, because a box is, well, really, really simple. We don't need a separate scene for that.
What we could also do, inside of objects, is simply create another static body 2D
that we could rename to, let's say, bench. To this bench, we can add a sprite 2D
right away. And for that, I want to use bench.png for the texture.
Then we have a bench, and after that, we can add a collision shape 2D with a rectangular shape that we then
want to scale like so and so. And then we have another object that we
can work with, and we didn't have to create another scene. Although, you do want to be careful.
Moving this bench around right now can be kind of annoying. There's a good chance that you accidentally only drag
the collision shape or only the sprite 2D, which would, well, cause a ton of
problems. So, this I don't want to do. To avoid that, you want to click on this symbol.
If you activate it, you're getting this symbol next to the node. That means you can only move the parent node.
So now, we're always moving the entire bench as one object. Super handy. That way, we can move it,
let's say, here. And once again, inside of the game, we are getting another object that we
can collide with. There are lots of useful icons at the top. The other one that's super useful is
locking it. That way, this node cannot be worked with anymore,
which, for example, we really want to do for the background, because this one really isn't supposed
to move. Same with the borders, actually, and with the car's start positions.
None of those should ever really move. Although, the bench shouldn't be locked, but it should be grouped. This would be
another way to create an object inside of a scene. Once you have that, you can simply
duplicate this bench, and then you can create as many benches as you like. Which is also a way to approach it.
Although, if you want to create a lot of objects, this might still not be ideal. For that, we can approach these kind of
objects in a different way. Let me add another node 2D,
where I want to have a whole bunch of, let's just call it stuff. Doesn't really matter what you call it.
To this node, I want to add lots of random sprites. I can simply drag them in there, like
so. There we have a bin. We can put a box here, a box there. A third box here.
Let's say we can put a car in this position, and a light here-ish. The specific shape does not matter. I
just want to have a whole bunch of sprites inside of the stuff node. And make sure
that for this stuff node, you have ordering Y sort enabled. Now we have a whole bunch of objects.
The problem is, inside of the game, we have no collisions whatsoever. For that, we will need a static body 2D.
Now, what you can do to a static body 2D is add multiple collision shapes, which means I can add a collision shape
2D with a rectangle for now, and let's say this one is just for the car.
So, I can resize it to something like this. And then, to the same static body 2D, I
can add another collision shape or collision polygon. Let's say collision polygon, and then I
can draw a rough shape for these objects. So, something vaguely here, here, here,
here. Here. Here, and nearly done.
Like so. And to refine this a bit, you can have this symbol selected, and then move
these points. There we go. Now we have a collision shape, and inside of the game,
if I go to this position, we have a collision with the car. That works, and with these boxes as
well. And that is working pretty well. The same thing we can then do for these
objects and for this object, and you simply go through it step by step. That way, you can very easily add lots of
objects with one collision shape, which I think is the best way to approach it when you have a lot of stuff to add to a
scene. With that part covered, we can do an exercise.
I want you to place a lot of elements, so that by the end of it, you have a proper-looking level.
Place a whole bunch of static bodies and Sprite 2Ds. How you're doing this is entirely up to
you. You can create separate scenes or a few nodes inside of the game scene. It really doesn't matter for our game. Just
choose what you think is best. Pause the video now and see how you get. I went ahead and created a whole bunch
of objects. Most of those I placed inside of the stuff node where we now have a ton of
sprites and on top of that somewhere in there is a static body 2D. Which holds a whole bunch of collision
shapes. Inside of the game, all we see though is that we have a ton
of collisions with different objects. Let's go here, here. Always have collisions and that is
working pretty well. Now obviously to create a level, you have to have some patience and place a
ton of elements. Which sometimes can simply take a bit of time. Especially doing all of the collision
shapes manually can be quite annoying. And later on, we are going to learn ways to make this more efficient. But for
now, this is the easiest way to create a level. On top of that, for this bit up here, we
don't actually need collision shapes because once the player gets to this bit, the game should be over.
I guess one thing that we can do to finish up this part is at the moment, we don't really get that many cars,
which does look a bit weird. To make that work much better, let me minimize borders and objects and then
look at the car timer. At the moment, we're creating one car every 1 second. If I change this to 0.1,
then we're getting a lot more cars already. So now, this feels a lot more like a
game. This number you can tweak. I think I went with 0.15 for the actual project.
But obviously just test this and see what feels good for your purposes. Also, while looking at this, I think we
should vary the cars just a bit more. At the moment, our car is always going to be a taxi.
That being said, inside of cars, we have three different kinds of cars. And those we want to select randomly.
For that, we have to work inside of car.gd. First of all, we have to load these
three cars into memory. That we do first of all by creating a variable.
Let's call it colors. Inside of this variable, I want to store an array
that stores the green, red, and yellow car. To load them, we want to preload
and then the name of the file that we want to preload. Which in my case is graphics, cars, and green.png first of
all. Besides that, and let me write this over multiple lines so it's more readable. I want to duplicate this line
by pressing control, shift, and D. Besides the green car, I want to have the red car and I want to have the
yellow car. That way we can use these graphics inside of the game right away.
And effectively what we want to do once the scene is ready, I want to get the Sprite 2D.
The Sprite 2D here and then update the texture. Which I can simply grab via the texture
and then assign one of the images that we are storing inside of this colors array.
Now to pick a random value from it, all we need is pick random. That way we are picking one item at random.
If I now run the game, we are getting very unlucky. But now, this looks much
better. We have three different cars and that makes the
game feel significantly more interesting. All right. At this point, we have a
basic game where a player can move around. We have static obstacles, we have moving cars,
and we can accept player input. However, what we don't have is a way to communicate with the player directly via
text. That we're going to work on now. Where we are creating a timer on top of the window while the game is running and
we're going to create a title screen. So let's talk about creating layouts. Up to this point, we have placed nodes
using absolute positioning. Which is a fancy way of saying that every node get an XY position that is
independent from the window. That means back inside of Godot, let me create a new scene, which is just going to be a
2D scene. Inside of this 2D scene, we have the origin point up here. This is position
zero and zero. If I now place any kind of object via a Sprite 2D,
then this object is going to get a position that we can see inside of transform.
We can have some cleaner numbers. Let's say 100 pixels and 120 pixels. This number tells us the XY distance
from the origin point. This number does not care about the window size, the blue frame we have
right here. In other words, if I save this scene, let's say inside of the main folder
right now, and run it, we have the car here and if I resize the window, the car doesn't really care.
It's always in the same position relative to this top left point. Which is fine for 2D nodes. They are supposed
to work like that. But layouts work differently. They are placed using relative
positions. Which in effect means that instead of giving them an XY
position, we are placing them 10% from the left side of the window or 20% from the top of the window.
Things like that. That is really important. For example, if this is the game window,
a UI should always be in the same top left position. For example, for health bar up here or for some indicator in the
bottom left. Even if the window becomes smaller, let's say the new window is something
like this because it's resized. Then the UI should still be down here. And maybe scale down a bit, but it
shouldn't fundamentally change the position. Let me close the scene that I have just
created and delete it entirely. So node2d.tscn, we can delete.
Instead, I want to create another new scene. Which this time is going to be a user interface.
Once we have that, we are getting something that looks a little bit different. We are getting a
red frame and we're getting green needles around it. I'll talk about those in just a second.
If I add a new node to the scene, so far, we basically only work with node2ds, the blue ones.
Now we are going to work with the green ones. Those are control nodes, which you're using entirely for UIs.
In there for example, you have a label that gives you text. You have a panel that is a basic panel that you can
style. There's also texture rectangle. That way you can display an image. We are going
to work with these nodes in just a second. Although for now, the node that I actually want to work with is called a
color rectangle. This one is, well, a rectangle that displays a color.
If you look at the inspector, we can select any kind of color in here. Let's go with a reddish value for now so we
can see what's going on. And then we can resize this thing like any node2d.
That part is fairly straightforward, I think. That being said though, that's not the
system you are supposed to be using. Instead, if you look on the right in the inspector, we have layout and in there,
we get a whole bunch of options. The most important one is the layout mode. There we have position and
anchors. If you have position selected, then you can treat this thing basically like a
node2d where we can work with transform and give it an XY size, a position, a rotation, and a scale. All of the basic
things. This can be useful if you want to display text inside of a game. So for
example, later on inside of the game, we want to display some text and for that, we do want to use the position so we can
integrate a layout node in a 2D scene. However, purely for the layouts, we want to work with anchors.
And for those, we don't really need transform. Instead, we're getting anchor presets. Loads of options here, but I
want to work with custom. We're getting three options. Anchor points, anchor offsets, and grow
direction. The really important part for now is anchor points.
In there, we have left, top, right, and bottom. Via those points, we define the distance from the node to the side of
the screen that we are talking about here. Just for an example, let's leave left
and top at zero and zero. But right, we can set to 0.4 and bottom to 0.2. If I then zoom out just a bit, yeah, we
can see things better. And now let's talk about it. At the moment, we have this blue
rectangle. That is our screen. At the moment, in my case, the screen
dimension is 1280 by 720. You might have different numbers. It really doesn't matter because Godot for
layout nodes changes these numbers to a scale from zero to one. So, only for the X axis
from here here we are going from zero to one.
Same for the Y axis. We are starting up here with zero and then we are going all the way down to
one. That's the bottom border. Because of that, for example, for the right side
when we are defining 0.4 we are talking about 40% of the screen width. At the moment for the left side,
we have defined a value of 0.0. That means the left side of the node is on the left side of the screen.
However, for the right side we have set a value of 0.4. That means the right side of the node
gets 40% of the width of the screen. In other words, we have another 60%
that the node is not using. Afterwards, we get to the end of the screen.
The same system we are using for top, where we start on the top of the window. In other words, the top of the node has
a distance of zero from the top of the screen. But the bottom side
is 20% of the screen height from the top of the window. That is how you have to think
about layouts. You don't define an XY position. Instead, you define a distance to the screen borders.
Anchor points for that are the most important part. Besides that, the one that I do want to talk about in this bit
is anchor offsets. Those are in pixels and they give you an offset. For example, for left, we can add, let's
say, 20 pixels. And that way besides the anchors, we're getting 20
pixels from the left side. By that, you can also see what the green needles are for. They tell you the
anchor points. Besides that, right now for the top, I also want to have 20 pixels.
And then I can save the entire scene inside of scenes and let's call it title.tscn,
which would also be the name of the root node. title.tscn
If I now run the scene in the top left, we are getting the node that we have just created and this thing
grows or shrinks with the screen. Which can be really handy. That way, if
your game runs on a really small screen and if you have a really large screen, then it still works.
Even more importantly, what you can do is place this thing in the bottom right, down here.
Where, for example, the left side can have a value of 0.7 the top can be 0.6
the right can be one and the bottom can be one. Then we don't need any offsets. And now, if I run the current scene
again I can resize the window and the node is always going to be in
the bottom right, regardless of the window size. And that can be incredibly useful.
So, I hope the system makes sense. You basically, at least for now, want to work with layout mode anchors and then
for the anchor preset, you want to have custom. That way, you get the anchor points and the anchor offsets.
Besides that, if you look at the top, you have this button. By that, you can set anchor presets. For
example, you can place the node right in the middle, in the top left, in the bottom right
or you can make it cover the entire available space. This is usually just a starting
position. Once you have clicked on any of those buttons, you can still go to anchor
preset and go from the one you have selected to custom and then change whatever you like.
With that covered, let's create the actual title screen that I want to use for the game.
For that, first of all, this color rectangle is going to be the background. I have a custom color, 633C65
which is a purpleish color. After that I want to add another node, which is going to be a label.
If you now look at the inspector for label, we basically have text where we can write, let's call this one the car
game. Then, in the top left, you can see car game.
Although, the font is really really not ideal. It should also be quite a bit larger.
The way we can change that is inside of the inspector, we have theme and theme override.
Basically, you can apply a theme to multiple layout nodes, which we are going to do later. But for this project,
we only want to apply custom styling to this particular node, which we can do via theme overrides.
We can change, for example, the color and give it a different font color, which I don't want to do.
We can also give it a custom font and a custom font size. For the font size, I want to have 140.
That way, at the very least, this thing is quite a bit larger. For the font, we will need a font file
that I have inside of the folder fonts. In there, we have Better VCR and Crackman.otf.
Crackman.otf is what I want to use, so simply drag and drop it in there. Then we have a much nicer looking font.
This I want to center. Which I can do by going to this symbol up here and then placing it in the
center. That way it's right in the center, which is a decent start
but not perfectly yet. So, to customize that and move this text up just a bit I want to go to layout and then change
the anchor preset to custom. That way I can use anchor points and anchor offsets once again.
The only thing that I really want to do is inside of anchor offsets, I want to change the bottom.
And then move the entire thing up by some amount. Let's say negative 250.
If I now test this one scene then we have
a bit of text that is slightly on top of the center. This looks good. Besides that
I want to add another layout node, which is a texture rectangle. By that, we can add a picture.
I want to go to graphics, then to player and use player simple.png. Simply drag it in the texture
and then we have the player. This player should be right in the center of the window, which means anchor
presets, center. And then we have the player. That looks good.
We can also scale the entire thing. Go to layout, transform and then for the size
we can change this to, let's say 100 and 100 and then once again, you want to apply
the center preset so it is right in the center. Finally, I want to add another label.
This is going to be for the score. For now, let's simply write high score with some kind of number, 123.
This once again, I want to have right in the center of the window and then move it down a bit.
Which we can do by going to layout, anchor presets, custom and then anchor points
we want to move the top down by a bit. Let's say something like so. After that, I want to go to theme
override, fonts and font sizes. For the font, I want to use fonts and Better VCR.
Simply drag it in there. Then we have a nicer looking font and for the font size, let's go with 50.
Maybe a bit large. 30 is better, I think. Once again, play around and see what you
like. Anyway, with that, we have the basic title screen. That looks good.
This is all we need in here for now. Besides that inside of the game let me run it.
At the top of the window, I want to have a bit of text. And now, you do have to be careful.
You can add a layout node to a node 2D, like our game, for example. I could simply add a label to it
with, for now, let's say, some text and then move it to the center top of the screen.
Like we have done before for the title. The issue is, if I now try to run the game
I can't see it in the top, but if I move up, there we have some text. And this text is entirely static, so not great
yet. Effectively, we have to tell Godot that this label should be relative to the
camera that we have selected, which in our case is the camera of the player. The way you are doing that is by adding
another node that is called canvas layer. If we add the label to this node as a child
and run the game again then we have in the top left some text. A bit hard to see. We have to basically
redo the anchor preset. Then you can see, if I zoom out we have it up here.
Which still looks a bit confusing, but if I now run the game again at the top of the screen, we have some
text. And that looks pretty good. With that, we can do an exercise. I want you to customize the label on top
of the window, the one for the score. Number one, it should count the time since the start of the game. For that,
you can use a timer. Besides that, give it a custom font and an offset from the top of the window, so
it looks a bit nicer. If you want to experiment, you can also give it a custom size and a custom color
and just see what you can change. Pause the video now and see how far you get.
The most important thing is that this text displays some actually useful information.
Or in other words, it should display the elapsed time since the start of the game.
The way I approach that is, first of all, I created another timer. That should be the score timer, like so.
This one should auto start and have a wait time of 1 second. After that, I want to connect the
timeout signal to the game scene. Connect. Then, inside of the game scene, I want
to create another variable for the score, which has to be an integer with a
default value of zero, which is what we're getting by default. So, score int is totally fine here.
Every time the score timer times out, I want to increase the score by one. When that happens, I want to get the
label and change the text of that label. Once again, if you look at the inspector
for the label and you hover over the text or the property you want to change, then you
can see how you approach it inside of GDScript. For label, if you want to change the
actual text, you simply want to get label.text and assign a new value.
Which has to be a string. Let's say for now, I want to change it to something. If I run the game, after 1 second, some
text should become something, and it does. That is working well.
That unfortunately means we couldn't assign the score right away, because the score is an integer while the label
wants to have a string. If I run the game now, Godot is going to crash. So, this is no
good. Luckily, this we can change very easily. You want to wrap the score, so the
integer, into the str function. That way Godot is turning the number into a string,
after which the entire thing should work just fine. We get 1, 2, 3, 4, and so on. This is
working pretty well. If you want to be fancy here, you could also do something like
the string "Time elapsed" and then plus the string that we are
getting from the score, like so. Make sure you have a space in there, and then, if you run the whole thing,
you're getting "Time elapsed" with the current time. You could do that, although I don't
really care about that. I simply want to display the number. I think that's enough.
Now, besides that, to style this text, I want to go to, first of all, theme override and then
fonts and font sizes. The font should be Better VCR. And the font size, let's go with 40.
Inside of the game, this should also look okay. And yeah, this works reasonably well.
Besides that, for the offset at the top, I want to go to layout, anchor presets, change it to custom,
and then anchor offsets for top should be, let's go with 20. After that, inside of the game,
we are getting I think that looks all right. Finally, the default text should be zero. And to
make sure that this zero is actually centered, we want to set the horizontal alignment to center. That way, we are
actually in the center. If I now run this thing again, we get 0, 1, 2, 3,
and this is working pretty well. Perfect. The last major part of the game that we
have to work on is the transition between the game and the title screen. Once we have that, we have an actually
functioning game. So, let's talk about it. We want to transition.
Or in other words, we want to change the scene from the game scene to the title screen.
This should happen when the player reaches the finish area or when the player gets hit by a car. We also want
to be able to go back. If we're on the title screen and the player presses a button, then we're going back to the
game. On top of that, we want to keep the score information, which means that the
score inside of the game should also be displayed inside of the title screen. For that, we have to talk about global
data. But, let's go through it step by step. Back inside of Godot,
when the player reaches this finish area 2D, we want to load the title screen. And you can see, for this area, we
already have a function via signal, which is this one here. To load another scene inside of
GDScript, you only need a single line of code. First of all, you want to get tree.
This is a really common operation inside of Godot. The tree is basically the scene tree. This one gives you a ton of
options, and to change the scene, you have two methods.
Either I change scene to file or change scene to packed. Scene to file is easier. Let's work with
that for now. Then, as an argument, you need the file path to the scene that you want to load,
which in our case is going to be scenes/title.tscn. If you have that,
I can run the game and go to the top part, and then we have the title screen. Although, on top of that, the debugger
is not happy. First of all, the parameter body is never used inside of this method. That
we can fix easily simply by adding an underscore to body. Then, we have an actual red message that
Godot does not like. We are removing a collision object node during a physics callback, and this is
not allowed and might cause undesired behavior. Effectively, what that means is, inside
of the game, we have lots of physics objects, and if you change a scene while the physics are updating, you might get
an error. Now, all that really means is that when we are changing the scene, Godot might get stuck on a physics
operation, and this we want to avoid. The way you are doing that is, and Godot actually tells you that,
you are supposed to use call deferred, which is just another function. Call deferred.
This one calls a function, so we have to add a name here of a function. Let's create a new function called
change_scene. This one doesn't exist yet, so func change_scene.
Only for parameters, all that we want to do in here is get tree and change scene to file title.tscn.
Simply by having that, I can run the game, go to the top, we have the title screen, and Godot doesn't
throw an error anymore. This setup can be a bit annoying, but it's really important to get physics
right. Most of the time, when you are changing a scene, you want to call deferred,
then create another function, and inside of that function, you are changing the scene. That way, you make sure the
physics objects don't cause problems. Besides that, when the player gets hit by a car,
then we also want to go to the title screen. At the moment, we are, for the cars,
connecting the body entered signal to the function go to title, meaning inside of this function, we want to do
something. And well, fundamentally, we simply want to call deferred once again,
and then change the scene to the title scene. So, now, if I run the game and I get hit by a car, we are once again
getting back to the title screen. And once again, Godot is complaining that the parameter body is never used.
There should be an underscore before body. Let's try this one again, and now we can
get hit by a car. We get the title screen, and Godot is not complaining anymore. Perfect.
This system we should practice right away. I want you to go from the title screen
back to the game. This should happen when the player presses the space button while the title
screen is active. Pause the video now and try to implement this one.
On the title screen, we want to do something, for which we're going to need GDScript.
Create a new GDScript. All the defaults here are fine. I want to constantly check for some
keyboard input. That we can do inside of physics process or simply inside of process. It doesn't really matter. Let's
go with process. And for this, we also don't need delta, so there we can add an underscore.
I simply want to check if input is action just pressed, and then, inside of project settings for
the input map, earlier, we created an action called confirm, which is for the space button.
Which means, for this one, we can look for confirm. If that is the case,
I want to get the tree and then change scene to file And that is actually all we need.
Since there are no physics objects inside of the title screen, we don't need call deferred.
In other words, if I run this current scene and press space, we're getting to the
game scene and the game runs just as before and we can go back. This is looking pretty good. Finally then, we
have to get the score from the game to the title screen. There are a couple of ways of doing that. The way I want to
cover is by creating a global node. Effectively, what that means, I want to create a new scene. And this scene is
going to be a plain node. It's not going to hold any complex logic. I simply want to change the name first of all
to global. Then save it inside of scenes, that's totally fine.
And add a script to it. global.gd, the defaults here, once again, are totally fine.
The only line of code that I actually want to have in here is a var score
an integer with a default value of zero. This scene really doesn't do very much. It simply stores a score.
But the important thing is that this global scene should be available in every single scene. So, game, player,
tree and most importantly, title. The way we are doing that is you want to go to project, project settings and in
there you have globals. In my case, I want to click on this symbol, go to scenes and then get
global.tscn. And make sure you don't get GD by accident. You want global.tscn, the
scene. Open this one. Then you can give it a name, global is
totally fine, and then add. That way we have a global scene called global. Make sure it's enabled, it
should be by default, and then you can close this dialogue. And really important now, if I run the
game and I go inside of Godot to remote, while the game is running, there we get
global, even though we didn't add this global scene to the scene tree inside of the game.
And on top of that, if I finish the game, we're going to the title screen, but we still have global available.
So, effectively, if you add anything to this globals, the node will be available in every
single scene, bar none. This can be quite nice. For example, inside of the game,
when the player has reached this final area, I want to get the global score. So, global.score.
And then update the score to the current score from inside of the scene. global.score refers to this variable and
this score refers to the score inside of the current game scene. Meanwhile, inside of title.gd,
when this scene is ready, which means func_ready, I want to update, let me show the scene
tree, I want to update this score, which we get via label two.
This thing should get text and for now, I simply want to create a string of global.score.
And that should be it. If I now run the game and I can run up, let's hope I don't get hit by a car, we're getting
two inside of the title screen. If I try this again and let's wait a few seconds, make sure I don't get hit by a car.
And then we're getting eight because it took me eight seconds to finish the game.
With that, we are moving data around the different scenes. Or to be a bit more specific, we have
one global file that stores the score. And this thing we are referencing all the time. Although, I only want to do
this operation if the score is greater than global.score,
only then do we want to update the global score. This we don't want to do when the player
gets hit by a car because then it's basically game over. Let's try that and let me go really fast
to the top of the screen. So, we have three seconds. If I try this again and I need longer than three
seconds and let's hope I don't die on the way. Now we have five seconds and I just
realized the time should be smaller than global.score because the
lower the number, the better the score. I think you get the idea. Besides that,
what I also want to do inside of the title screen, this label two
should have the horizontal alignment not left, but center. That's going to look a lot better.
On top of that, what you can do if you really want to, inside of title, you can add another string for high
score and then simply add the string to it.
That way I can run all of this again, try to get to the top of the screen. And now we're getting zero.
Okay, not great because the score right now by default is going to be zero.
And the score that we can achieve in the game is never going to be below that. I guess a really easy way to fix that
is to set a default value of 10 for the score. Then I can run this again,
try to finish the game. Now we get a high score of two. That feels a bit better.
And well, with that, we can move data between different scenes. Which is giving us the basic game.
All we have to do now is add sounds and then we are done. To finish up the first game, we have to add sounds. For that,
Godot has three nodes. An audio stream player, an audio stream player 2D and an audio stream player 3D.
The only difference between them is that a 2D and 3D version care about the position, meaning the further away you
are, the quieter it will become. And then obviously, the 2D node only cares about the 2D space, the 3D one cares
about the 3D space. Other than that, adding audio is pretty straightforward. Let's jump right in.
Back inside of Godot, if you look at the file system, we have a folder for audio, in which we have the
file for the car and for the background music. For now, I just want to play the
background music, for which I don't care about the position.
Which means I can simply add an audio stream player, this one here. And then besides that, we have the 2D
version and the 3D version. You can also listen to audio, although I never found a good purpose for that.
Anyway, I want to create an audio stream player. Click on create and then on the right
side, you get a ton of options. The one that really matters is the stream. This wants to have the actual
audio file, which means in my case, I want to drag happy.mp3 in there. And then we have the sound. If you click
on playing, [Music] you should be hearing a preview of it.
If you're setting it to autoplay and start the game, then it's going to work right away. I
guess besides that, you can also set the volume and a few more specialized things, but they are not that important.
I guess the one thing that you do want to care about is looping. This one should be enabled. Especially for
background music, this one is actually quite important. That being said, if I try the game
[Music] and I go to the title screen, then we don't get music anymore.
That is because the audio stream player only exists inside of the game scene. In the title scene, it does not.
And if you added it in here, like we have done inside of game, then it would start again, which I don't
think would be a good experience. I want this music to play continuously. That we can achieve by attaching it to
the global scene, where at the moment, we are only storing the score.
All we have to do is add the audio stream player and by the way, for any kind of node, you can have a right click
and then cut and copy or simply press control X or control C. Works like in any other program.
Now, inside of the global script, we are still doing the very same thing with the same stream.
If I run it, [Music] we're getting music continuously, which
I think is much better, although I do want to lower the volume just a bit. For volume, the default is zero. And if
you want to make it quieter, you need negative values. Let's say -10, which you can fine-tune, just play around and
see what you like. Besides that, we have audio and car.mp3. To test this one, I don't want to
autoplay happy.mp3 so we can actually hear what's going on. To the car scene,
I want to attach an audio stream player 2D. Make sure you're getting this one. To that, I want to attach car.mp3, which
sounds like This we do want to loop, so looping should be enabled.
And then we want to make sure that the player only has this part if we are close to the car, which happens with the
2D version of this note right away. Although we do have to make sure that this is set to play
The difference between them, by the way, is if you have playing enabled, it is going to play inside of the editor.
If you set it to autoplay, then it is going to start inside of the game, but not in the editor.
You usually want to go with autoplay. Anyway, now inside of the game we are getting a whole bunch of sound.
Which at the moment is way too loud. Let's lower the volume to -20. That way it's not going to be so
overwhelming. That works. Once again, play around if you want to customize this.
The one value that really matters is max distance. If we exceed that distance, we cannot
hear the audio file anymore. At the moment, it is set into 2,000 pixels, which is a lot.
If you look at the map, the distance from the top to the bottom is 600 pixels.
A lot less than those 2,000. I guess we can set it to 100 pixels and just see how this works. If I now try
this again, inside of the game right now, I can't hear anything. But if I get close to the
cars, then you can tell the audio is getting louder and quieter depending on my
position. You can customize this even more with attenuation.
Which is a curve that determines the volume given the distance. For example, you could make this thing
fall off a lot faster. That way, after this amount, we can't hear anything anymore. Although this I
don't want to do. But after that, we are basically done. Still works pretty well, and I do want
to make sure that the background music is enabled. So, autoplay should be true. And now
[Music] cool, this works. Now I guess before we finish, there are
a couple of minor things that we can work on. For example, inside of the player,
the move speed is quite fast. I think 100 here is fine. That way the player feels more controllable.
We also don't need this part. That was just for practice earlier, so we can just remove it. It's not going to
make a difference. Finally, what we should also do, the default direction should be vector 2.0.
Now this isn't going to make much of a difference since we are changing direction right away using the input
function. This isn't going to matter, but sometimes it might. It's just a bit
cleaner. In fact, we don't need this vector 2.0 at all. This is totally fine. The default value
is vector 2.0. You don't have to specify it. So now, we have created our very first game.
Well done. Let's get started with the second game, a Metroid-style platformer. While making this game, we will learn
about a few more advanced concepts in Godot. Most importantly, number one,
animations. Godot has a ton of systems to animate things, and we are going to learn about two.
The animation player, that's the node, and tweens. By that, with GDScript, you can animate any property.
Next up, we have the tile map layer. This is an amazing tool to build levels. Finally, we will look at lights and
shaders, which are essential for a good-looking game.
Now to get all of this started, here we are in Godot, and I already have a couple of things. No scenes or code, but
in the file system, we have a whole bunch of graphics that we are going to use. For example, there
is the player. Looks a bit weird right now, but we'll work on it.
We have a couple of audio files. Those are very simple. And then, inside of project, project settings,
we have an input map with left, right, jump, and shoot. The one thing that's new here is that we
are capturing mouse input, left mouse button. Which you can get by clicking on plus and then mouse buttons. That's all
I've done here. I got left mouse button. Besides that, inside of general, for the window, I'm going with 1280 by 720. That
one is very simple. And finally, under textures, I have set the default texture filter to
nearest. Because once again, we are working with pixel art, and we want to get the right filtering behavior.
This would be the starting project. And to set up the basics, I want to create a 2D scene, rename it to let's
call this one level. Save this one inside of a new folder called scenes. I suppose inside of this
folder, we can create another folder for levels, and then save it in there. That way, inside of the file system, we
have scenes, levels with the main level. The only thing that I want to have in here is a static body 2D with a
collision shape. For this collision shape, I want to have a rectangle shape that we can expand
quite a bit to something like so. This is going to be our floor for testing purposes.
It's not going to be visible by default inside of the game. If I run the scene and select the
current level scene to run, at the moment, we do have a scene, but we can't see the collision shape.
For that, under debug, you can enable visible collision shapes. If I now run this again,
we can see the floor. Can be very nice to get things started. Next up, I want to create a new scene
where the root node is going to be a character body 2D, which will be our player.
Once again, this I want to save inside of scenes. In there, I want to have a folder for let's call it entities.
We are going to put the player and the enemy drones in there. For now, player.tscn, that's fine.
And to this body, I want to add a collision shape, which is going to be some kind of capsule-shapey thing.
We can refine this later on. For now, I want to save, go back to level, add a node 2D
called entities. As a child of that node, I want to have the player.tscn file. And then, we are
getting the player collision shape. If I run the scene again, you can see we have the floor and the
player. Looks very plain at the moment, but it's a good way to get things started.
Nearly done with the first setup part. The last thing that I want to do is to move this player to the right. That's
going to be your exercise. This should be reasonably straightforward if you paid attention to things in game one.
Pause the video now and see if you can remember. First of all, we have to give the player
a script. Click on this button. The defaults here are totally fine. And then, we need func_physics_process.
In there, to move a character body 2D, you want to always call move and slide. This, by default though, is not going to
do anything. So, right now, we have no movement whatsoever because we have to set a velocity for the player.
This can be something like vector 2.right multiplied with, let's say, 50.
If I now run this again, we have the player moving to the right. Looks pretty good.
Although, all of this should be a bit cleaner. I want to have a variable for the
direction of the player. This is going to be a vector 2, and I want to have var speed.
Which is going to be an integer with a default value of 50. That way, for velocity, we can multiply
direction with the speed. The end result is going to be the same. Actually, right now it's not because
direction doesn't have a default value. This we can set vector 2.right. And now we can try this again.
We are moving to the right. This system we are going to refine quite a bit for proper movement. For now,
I simply want to add an underscore to delta so Godot stops complaining inside of the debugger.
And then we are done with the setup for the first proper part of the platformer game. I want to have proper movement so
we can go left, right, and jump. Here we are back in Godot, and I want to move the player.
For that, at the moment, we have a vector 2 that defines the velocity.
That does work, but that's not the system that we want because I don't really care to move in
all four directions at the same time. I only want to move left and right from the keyboard, and then be able to jump.
The down movement is covered entirely by gravity. That means, to get started, I want to
only move left or right, for which we will only need a direction_x, which is going to be a floating-point
value, for which we don't need a default value. Meaning this value at the moment is
going to be 0.0. Which we are then going to use to update velocity.x.
It's going to be direction.x multiplied by the speed. That part is still fine. Once again, we don't have
any movement and that is expected. Cool. To change that, I want to get funk and something like get_input.
Inside of this function, we're going to overwrite direction.x. Via input and the function I want to use
is called get_axis. This works very much like get_vector, except now we are only looking at one
axis. In other words, we have a negative and a positive action.
The negative action in my case is going to be left, the positive one is right. After that, let's print direction.x
and don't forget to call get_input. Which I want to do inside of physics process before we are doing anything
else. Get_input. If I now run the game, I can press right, we get 1.0, if I press left, we
get -1.0. And that works really well. Also, you can see inside of the game, we
are actually moving the player. Really good start. That means we don't have to print the
direction anymore. Next up, I want to work on the jumping mechanic. Which will consist of two parts. We have jumping
and we have falling. Let's start with jumping. I want to check if input.is_action_just_pressed
and we have a jump action. That is space on the keyboard. If that is the case, I want to update
velocity.y. And this needs to have some kind of negative value because we want to go up.
Let's say for now -10. If I run the game, I can press space and the player moves up. That looks good. We
will refine this in just a second. First of all though, I want to have the second part of the equation, which is gravity.
That I have covered in a separate function. Let's call this one apply_gravity.
And all that this really does is velocity.y + = and then some kind of value. Let's
say for now + = 10. That way, we are going down and we are accelerating. This we have to call.
I want to apply_gravity. Let's try now. And the player falls down. Although, if
I press space, we're not jumping because the gravity is too strong. But if I set this value to negative 200,
then this should be enough. I can now do basic jumps. Obviously, not amazing yet, but a
really, really good start. And I think what we should also do is add a camera to the player so we're not
so zoomed out. I want to have a camera 2D with a zoom level of three.
That way, it fits quite a bit nicer. So, we do have a basic jumping mechanic,
but we do have to make it a bit stronger so the player can jump higher. I want to have two more variables.
One is going to be jump_strength. Which is going to get a default value of, let's say for now, 10. And this
always needs to be an integer. Besides that, I want to have gravity. Which is also going to get a default
value of 10. When we are getting input, I want to have the negative value of
jump_strength. Now, this might look a tiny bit weird. We are defining a variable and then we are getting the
negative value. The reason why I'm doing it is that when we are defining the variable, I only
want to care about the distance that we want to jump. The actual logic in which way we are going can be covered right
here. A negative jump_strength would just look weird, but that is subjective. You could
add a negative value in here just fine. For the gravity, I want to use velocity.y and add gravity to it.
This right now would give us very low numbers, so not much is going to happen. To test this a bit better,
we can add export in front of both of them. That way, with the player selected, we
are exposing these variables to the inspector and then can set a custom value. This even works when the game is
running. So, let me run the game. We are getting basically no jumping because
jump_strength is too low. But if I simply click on the inspector and you can see at the top, the game is
still running. We can pause the game or we can restart the game. If I now set jump_strength to, let's say, 200, I can
return to the game. And now we are getting a very basic jump.
Once again, if I increase this number to, let's say, 600, return to the game,
then we are jumping quite a bit more. Maybe something like 400 would be better.
And that is looking not too terrible. The next change that I want to work on is gravity. But before we can get to
that, there's one more change that we have to make. This should be + = gravity * delta.
Which we are getting from the parameter. So, in there I want to have delta. After that, when we are calling gravity,
I want to add delta as an argument, which we are in turn getting from the parameter of physics process.
That way, inside of apply_gravity, we're getting delta. Which at the moment is going to be a really, really low value.
If I run the game again and I jump, we are, well, not really falling because we are multiplying 10 with something
like 0.001. You can even see it if I restart the game, gravity is incredibly low.
But inside of the inspector, while the game is still running, I can set this to, let's say, 100.
And then we are getting something more reasonable. Let's try a thousand instead.
Then we're getting slightly better jumps and this is now frame rate independent. We can get back to this later on to
refine numbers, but I think for now this is all right. That means to finish up this part, I want to add an exercise
where you are getting the input for shooting. For now, simply print shooting if the player presses the shooting
action. On top of that, add a half a second reload time to it. So that we have an interval to control the player
shooting speed. Pause the video now and see how far you get.
To get started, inside of get_input, I want to if input.is_action_just_pressed
as always, and then I want to get shoot. If that is the case, for now, I simply
want to print shoot. If I run the game and I press the left
mouse button, we are getting shoot. That looks pretty all right. The issue is I can press really fast
and then we are getting shoot very, very of reload mechanic so we can't shoot too fast.
I want to add a timer. Rename this to reload_timer and then set a waiting time of 0.5.
This timer also should one shot. After that, you could go to node and work with timeout, but you don't have to.
Instead, what you can do, when the player has triggered this shooting action, we want to start the
timer. So, reload_timer.start. Then this timer is going to run for half
a second. And while this timer is running, we don't want to allow this if statement. In other words, I want to
check for two conditions. If the player is pressing the button and the remaining time on this timer has to
be zero. That way we know it doesn't run anymore. All we need for that is the
reload_timer.time_left and this value should be zero. I can now shoot really fast
and we only get shoot every half a second. If I change the value of the
reload_timer to two seconds, try this again and click as fast as I can.
We are only getting shoot every two seconds. Cool. This works.
Next up, I want to work on the bullets. That way, we can do at least some very basic shooting.
We are also going to learn about custom signals. Meaning you can create your own signals and emit them.
But let's go through it step by step. Number one, inside of the player, when the player presses the shoot action,
we want to shoot a bullet. But, for that, we have a problem. The bullets need to be inside of the
level. That way, they can interact with other entities. But, we get the player input inside of
the player, which means we have to connect this line here
to the actual level. For that, first of all, the level is going to need a script. Attach a script,
the default here fine. Then, we can do something in there. The thing that we want to do is from the
player, we want to capture a signal. Now, the player does have, if you have the player selected inside of level,
we have all of the signals for the character body 2D, which can be useful, but for our
purposes, they're basically useless. So, instead, I want to create a custom signal,
which you can do by simply typing signal, and then add the name of the signal. In
my case, shoot. Simply by adding that in the inspector for the player, we are getting shoot.
On top of that, you can define parameters. In my case, I want to have a position,
which needs to be a vector 2. This will be the starting position of the bullet,
and I want to have a direction, which is also going to be a vector 2. Once you have that inside of the
inspector, you get the two parameters you have just defined along with the data type. Back
inside of the level, with the player selected, you can click on the signal and connect it to the
level script. I guess for now, we can simply print the position and the direction.
Inside of the player script, when this if statement is being triggered,
I want to get the shoot signal and emit it. For that, we will need two arguments,
the position and the direction. For position, we can simply go with the position of the node.
That's all we need. For the direction, at least for now, I want to have a vector 2.right.
And that should be it. If I now, inside of the game, use the left mouse button,
we are getting different outputs. We are emitting the signal here, and inside of the level, we are capturing
these two bits of information and printing them. By that, you can create custom signals.
It's a really easy system and incredibly useful to connect two scenes to each other.
And in our case, the position is already the right bit of information. The one thing we have to change, though,
is the direction. The bullet should go in the mouse direction relative to the player center.
In other words, if the mouse is vaguely here, then the bullet should go this way. If the mouse is on top of the
player, then the bullet should go up. For which Godot actually has a function. It is called get_local_mouse_position,
and that way, we are getting the local mouse position. This is the mouse position relative to this origin point.
Or to be a bit more specific, the origin point of the scene that has this script attached to it.
Let me actually demonstrate. Inside of the game, if I press the left mouse button and the mouse is vaguely
here, we are getting 197 and -7. If I'm up here,
we're getting 2.3 and -100. If I'm on the left side, we're getting -170 and -5.
If I am right in the center, so basically here, we're getting pretty much zero and zero.
I am always looking at the bottom numbers. The first one is for the position, and this one doesn't change
because the player isn't moving. Now, this works, but this is not a good direction.
In just a bit, we want to use this direction to move the bullet.
But, for that to work properly, the direction should always have the same length, which at the moment, it does
not. Imagine if this is the player scene with the center point here. If I click here and here,
we're getting this direction and this direction. This vector could have a length of one, and this could have a
length of three. This difference is really, really bad, because for bullet movement, we want to
multiply both of those with some kind of speed for the actual bullet speed. But, if the length of the vector is
three, then this bullet is going to move three times as fast. Obviously, not ideal.
And the way around that is to normalize the vector, for which you simply need normalized.
That way, any kind of vector is going to get a standard length of one. In other words,
if I restart the game now and I press somewhere here, the vector we are getting has a value of
vaguely one and then basically zero. Or to draw all of this, I have clicked vaguely here. So, the original vector
that we are getting is this. And by normalizing it, we are setting a length of this vector to
one. And it is always going to be one. I could have clicked here, I could have
clicked here, I could have clicked, let's say, here, and we would have gotten very different lengths.
But, from normalizing the vector, we are always getting a length of one. So, it would be here,
here, or it would be here. That way, we're getting a constant direction, and this we can multiply with
a speed to always have the same speed. This, by the way, we didn't have to do in the first game for the player
movement, because when you do input.get_vector, the vector that's being returned is
already normalized. If you look at the documentation for it, Godot tells you the vector has its
length limited to one. That means it's normalized. Although, this we don't want to do at
the moment. With that, we get the bullet start position and the
proper bullet direction. That means inside of the level, we have to spawn a bullet that starts on
this position and moves in this direction, for which, inside of graphics and fire,
we have a default bullet. Looks fairly basic, but it's good enough.
This part is going to be your exercise. I want you to create a bullet scene, spawn it when the player shoots, and
then move the bullet in the right direction. Pause the video now and see how far you get.
I want to create a new scene, and the node I want to create has to be an area 2D.
I can rename this to bullet, and then add two nodes to it. I want to have a sprite 2D, and I want to have a
collision shape 2D. Also, all of this I want to save inside of scenes and a new folder called
bullets. bullet.tscn is totally fine in here. And then,
for the sprite 2D, I want to have default.png. That way, we can see the bullet. And
next up, for the collision shape, I want to have a rectangle shape 2D that I want to scale down quite a bit to
something like this. Next up, inside of the level, I first of all want to add another node
2D with the bullets. After that, in the level script,
I want to preload the bullet scene. So, var bullet_scene, which we are getting from preload and
bullet.tscn. When the player shoots, so when we are getting on_player_shoot,
I want to create one instance of this bullet. That we do via bullet_scene.
instantiate. And this we then want to add as a child to the bullets node that we have just
created. $bullets.add_child(bullet). That way, if I run the game, I can press
the left mouse button, and in the top left, you can barely see it up there, we are getting a bullet.
To refine this, I want to get the bullet itself and update the position. And by the way, to be a bit cleaner
here, when we are instantiating the bullet, this should be as an area 2D.
That way, we always get the right kind of node, and we get the predictions, so now position does show up. Position is
going to be the position we're getting from the parameters. So, position in here.
If I now run this again, I can press the left mouse button, and we are creating a bullet in the center of the player. That
does work, although I feel like this could be organized a bit better. Instead of setting the position in here, I
instead want to call a setup function on the bullet itself, where we are going to add the position
and the direction. That way, we can keep all of the logic inside of the bullet, which is just a
tiny bit cleaner. To the bullet, add a script, the defaults here, as always, are fine. And
we will need funk setup with a position vector two and the direction, also a vector two. First of all, I want to get
the position and set it to the position that we're getting from the parameter.
Just to test if this is working, if I press the left mouse button, we are getting a bullet. That looks
good. Although, if you look closely, the bullet shouldn't start in the center of the player, it should be to the
right. We want to get the position and then add the direction
multiplied with some kind of offset. Let's try 12 for now. Inside of the game,
if I create a bullet, it is just a bit outside of the player. I guess 16 here might be a bit better.
But once again, once we have graphics, we can refine all of this a good bit. Let's leave it like this for now.
Besides that, I want to update a direction for the bullet, which doesn't exist yet.
For that, I need var direction. This has to be a vector two.
And then the direction is going to be the direction we're getting from the parameter.
And this we can use now with funk physics process. I want to update the position and add
direction times, let's say 30. Don't forget, we also need delta. If I run this,
I can shoot a bullet in a direction. And that is working pretty well. With the basic setup covered, we can
work on the animations, for which we have to learn an incredibly important node, which is the animation
player. By that, at least for now, we're going to animate the legs.
The top part of the player is going to come later. Also for now, inside of the game, I
think these collision shapes are going to become a bit distracting. So instead,
to the static body 2D, I want to add a sprite, which is going to get
icon.svg, the Godot logo. And then I'm going to move the Godot logo
in the same position, like here. Then we can expand this thing so we are
covering roughly the collision area. Then we can go to debug, visible collision shapes disabled, run
the game. And now, we do have the player, but the player
itself isn't visible. We do can see the floor because of the Godot logo. That's good enough. With
that, I want to work on the player legs, which we are still going to store inside of a sprite 2D.
For those, I want to add inside of characters, we have player.png. If I drag this in there,
then you can see we have the player, but cut up in various parts. The top part we're going to cover later.
For now, I want to look only at the legs. First of all, we have to cut this tile sheet into individual parts.
For that, inside of animation, we can set the H frames and the V frames. We have 1 2 3 4 5 6 7 8 H frames.
And we have three V frames. That way, we can select one of the frames and we get
one part of the animation. Looks pretty all right. We want to animate this frame property.
If I just do it with the mouse, let's say from this point forward, if you play these frames fast enough in sequence,
you get a running animation. This can be done in a couple of different ways. You could write some
code to loop over these frames and then play them continuously. Would be possible, but also quite a bit
of work. Alternatively, you could use the animation layer.
In there, you can, if you click on animation, create a new animation. You can give it
a name. Let's call it the run animation. After that, you get a timeline to which you can add tracks
and you can animate different properties. The easiest and most common one is the
property track. If you click on that, Godot is asking you what kind of node you want to animate. We want to go with
the sprite 2D. If you click on okay, then Godot is asking you what property of the sprite
2D you want to animate. In our case, that is going to be the frame. And now, if I click on open, we can work
inside of this timeline. If you hold control or command and use the mouse wheel, you can zoom in.
This bright gray area is the timeline. At the moment and by default, we're going from 0 to 1 second.
Although you could change this here to whatever number you want. In fact, the walking animation should be
1.4 seconds. The button right next to it determines if this animation is looping
or ping-pongs. Ping-pongs means we start from zero, go to the end, and then go from the end
back to zero. And do this over and over. In our case, we want to loop the animation. This button should be blue.
That's the basic setup. Now we have to add values to this timeline. This you can do in two ways. You can
either right-click and insert a key. If you're doing that, you can see a small preview. And most importantly, on
the right, you can give the property that we have to find here, the frame, a value.
So effectively, if you're looking at this sprite 2D under frame, we are changing this value
with whatever we have set in here. Also, once we have the animation player, you can see next to the property this key.
If you click on it, you can add the property straight to the animation
timeline, which I don't want to do right now. Let me get rid of all of this.
Just to get started, I want to add a key and the first value is nine. You can already see a preview there,
although it might be quite small. Then after 0.2 seconds, I want to add another key,
which is going to get a value of 10. Next up, another key. This is going to be 11.
0.2 seconds later, I want to go to 12. Then at 0.8, and I think you can see where this is going, I want to go to
value 14. After that, another key. This is going to be value 14.
And we have one more. Insert key. Value 15. That way, we are getting the walking
animation. And if you drag the indicator left and right, you can play the animation or you just click on this
button, then you can see that we have a running animation.
Although fundamentally, we are still working with a sprite 2D for the visuals, and that's really important to
understand. All that the animation player is doing is it's changing this property, which
means at the moment, we have to move the legs up to align with the collision shape.
I guess here is broadly fine. With that, inside of the game, we have the legs, although right now, they don't
animate. That is because inside of the animation player,
we have to give it a default animation, which means you want to click on this button, then you're auto playing it, and
inside of the animation, you get this auto play icon. With that,
we have the running animation. Besides that animation, I also want to add a new animation
called idle. For this idle animation, I want to add a track, property track with the sprite
2D, and once again, I want to target the frame. The animation length is going to be 1
second, and this is what I actually want to auto play. You can only play one animation automatically.
For this animation, I want to insert a key starting right at the beginning. The value should be 16.
And also, this should be looping. From that, we are getting the legs being entirely static, which is fine. Now we
have to figure out how to move between idle and run. This we can do in GDScript.
And for that, I want to create another function called funk animation. If the player is moving,
we want to play the walk animation. If the player is not moving, we want to play the idle animation.
To get that, first of all, we need the animation player node. The property we want to target is called
current animation. In fact, if you have the animation player selected and you look at the
inspector, there we have the current animation. At the moment, we have run and idle and
reset. This one you can ignore. This you have to give a value, which has to be a string.
Let's say for now, I want to go to walk. But this I only want to do if the player is moving, which we know is happening if
we have direction X. If that is not the case, else, we want to idle.
Make sure you are calling this function where you want to call the animation. And now, inside of the game,
by default, nothing is happening, but if I start walking around, then we are making the debugger really unhappy.
Let's see what happened. We have animation not found walk. That's a good way to indicate what went
wrong. So, check inside of the animation player. We have idle and run.
So, in other words, this shouldn't be walk, this should be run. Once we have that,
this is looking much, much better. So, I can idle and I can run. Wrong direction, but generally this
works. And while we are here, what we can also do, with this sprite 2D selected, and in
fact, I should rename this to let's call it legs. These legs by default always point to
the right, but if the player is moving to the left, they should be facing left. Which we can get fairly easily. I want
to get the legs, and then flip H. If the player is moving to the left, I
want to flip the legs on the horizontal axis. This value has to be true or false.
And we can set it by direction X being smaller than zero. This operation is going to return a
Boolean. It's going to be true if we are moving left, and it's going to be false if we
are moving right. So, with that, I can go to the right, I can go to the
left, and that is working pretty well. We don't have jumping yet, but that we can work on next.
That part is actually going to be your exercise. I want you to add the jumping animation.
This should also be played inside of the game when the player jumps. A tip for this one, via is on floor, that is just
a function inside of a character body 2D, you can check if the player is on the floor or not. That's going to be
really important. Pause the video now and see how you get. Number one, inside of the animation
player, we have to add one more animation called jump. For that, we want to add a track,
property track, we want to target the legs, the sprite 2D, in there we have the frame.
On the timeline, we want to insert a key, and then find the jumping animation.
If you toggle these values, you can see a preview what we need. There's only one frame for the jumping animation, and
it's frame 70. This we do want to loop, and we don't want to auto play it.
That covers the animation player. Good start. Next up, inside of the code,
we have to figure out when the player is jumping. So, technically, what you could be
doing, when we are getting input and the player is jumping,
you could change the animation to jump. Although, what would be easier, inside of animation,
we can first of all check if the player is on the floor. Only if that is the case, do we want to
play the run or the idle animation. If that is not the case, else, then we basically only have one option,
the jump animation. Which basically means, I want to get the animation player, set
the current animation to jump. And then we are done. Inside of the game, I can now jump, and
we have some legs that are well animated. That is looking pretty good.
That means, next up, we can work on the upper body. For that, we have another sprite 2D,
which I can rename to torso. Once again, we want to get player.png, drag it into a texture, and then for the
animation frames, I think we had eight H frames and three V frames. Then we want to move this sprite 2D up
by a couple of pixels. If you use the keyboard with the arrow keys, this is going to work much better.
Let's try it inside of the game, and that is working pretty well. Obviously, we have to actually animate
the upper body. And for that, let's think about the problem. If you look at the player
frames for the upper body, we essentially have eight directions that the player can face. This right,
bottom right, down, down left, and so on. We have to pick one of those depending
on where the mouse is pointing. If the mouse is here, so to the right of the player, then we want to get this first
frame. If the mouse is on the left here, then we want to get this one.
This unfortunately cannot be done in the animation player. At least not efficiently. This could
actually be a pretty good exercise. Try to get the right upper body frame. And just be creative, see how far you
get. If you can't do this one, don't worry about it, it is a tiny bit more
advanced. Let's go through it step by step. First of all, we need to get the mouse
position relative to the center of the player. This we have already done. Inside of get input, we have used get
local mouse position and normalized it. That we can reuse. Let me copy it, and inside of animation,
I want to get a new variable, let's call it the raw direction. As a reminder, if I print this value,
I can see where the mouse is relative to the player. Down there, I have very close to one and
zero. We are to the right of the player. If I go up, then we are at zero and negative one.
These values we need to clean up so they are more easily accessible. Let's create another variable, adjusted
direction. This should be a vector two with X and Y,
where X is going to be the rounded value of the raw direction.x. The same thing I want to do for Y,
except now we have the rounded value of raw direction.y. Let's print this one, that should be a
bit easier to see. Now, if the mouse cursor is to the right of the player, we get one and zero. If
I'm to the left, we get negative one and zero. If I am above the player with the mouse cursor, it's zero and negative
one. And so on, this works for all eight possible directions.
What you can also do is set this vector two to vector two integer. That way, instead of floating point
values, you are getting integers, which I think for this purpose is a little bit
cleaner. Both would work though, in this case it doesn't really matter.
So, at this point, we have eight possible values. These we have to convert to the
frames that we are getting for the player. This would be frame zero, frame one, frame two, and I can actually show
it inside of the torso. At the moment, we are on frame zero. The player is facing to the right.
If I set this to a one, we're going down right, frame two is down, frame three is bottom left. I think the easiest way to
connect this vector two to the frame is by creating a dictionary. This can be a constant value where we
have gun directions, which is a dictionary, where for example, we have a vector two
integer with one and zero, the associated value, or the frame, is
going to be zero. For the next value, we're going to the right and down, the frame is one.
Then we are only going down, so X is zero, Y is one, the frame would be two, and this is going to continue for a bit.
Let me actually copy it all in so you don't have to watch me type for too long.
These would be all of the eight values that we can work with. And if I add a bit more space, then this looks a lot
cleaner. I hope the system here makes sense. We have a dictionary where we have a vector two with a frame. The
vector two we are getting from the mouse direction. And then the frame is going to be what
we need inside of the player for the right animation frame. Once we have all of that, all we need to
do is get the torso sprite 2D and adjust the frame.
The value is going to be the gun directions with the adjusted direction.
And that should be it. Also, I realized this should all be vector two integers.
That way, we are keeping constant values. Now, let's try the entire thing, and
the player is following the mouse cursor. The other animation is also working
pretty well. So, that is looking really nice. We can also start to shoot, although the
shooting speed is a bit low, but that's a problem for later. Next up, I want to add a marker so the player knows the
shooting direction. This is also going to be animated, but for this animation, using an animation
player would be overkill. It's just too simple of an animation. So, we're going to learn a simpler
system to create animations called twins. By those, you can animate any property
with just a couple of lines of code. First of all, inside of the player, I want to add a crosshair so we know the
direction that we are pointing at. I want to add another Sprite 2D that I want to rename right away to
marker. For this marker, you can find a texture under UI. There we have crosshair. Drag
it in. And we are getting a crosshair. This crosshair, we want to place in the
right position using GDScript, which I guess could be another animation. You could also place it inside of
animation, I guess, but let's keep things organized. I want to have a function update marker.
All I want to do in here is get the marker and change the position to the position that the player is
pointing at, which once again, we have done this a couple of times by now. We want to get the mouse position
relative to the center, like so. And then multiply this with some kind of value. Let's say 40.
That's basically all we have to do for the position of the marker. Although, we do have to call the function
update marker. Then we can run the game and inside of the game, I now have a marker,
which is quite a bit too large. To fix that really quick, with the marker selected inside of the inspector,
you can go to transform and change the scale to 0.5. For both X and Y, they should be linked.
Next attempt. And that feels much more reasonable. Now we come to the interesting bit. When
the player is shooting, I want this marker to scale down and then scale up again.
We could use an animation player. Let's actually do that. I want to use an animation player,
in which I want to add a new animation. I guess we can call this one the scale. I want to add a track, property track,
get the marker, okay, and then the property I want to animate is going to be scale, a vector
2. You could also target X and Y separately, but I want to get vector 2
scale. In there, I can insert a key at the beginning.
I want to start with 0.5. Let's say around 0.2 seconds, scale to 0.1
for both X and Y. And then, let's say by 0.6 seconds, I want to go back to a scale of 0.5. If I
play this, we are getting a short animation. This we want to play when the player is
shooting the gun. All we have to do inside of get input, when the player is shooting,
I want to get animation player two and then play the animation. We need a name for that, scale.
That is basically it. If I now shoot, we are getting a short animation
on the crosshair. This would work, but it's kind of overkill.
The animation player does a ton of stuff, and we only want to have a super super basic animation,
for which we can use a simpler system. Let me first get rid of the animation player two and then of this line, so we
don't get an error. That way, we are back to the crosshair not doing anything besides
the position. I want to create what is called a tween, short for in between.
All that it is doing is it takes a property and moves it from one state to another state over a set duration of
time. To create it, you want to get tree and create tween.
Then, on this tween, you have lots of different ways to change a value. The easiest one is tween a property,
for which, first of all, we need an object that we want to target. This effectively is a node in
most cases. In our case, we want to get the marker. On this marker, we need a string with
the property that we want to change. That is scale, or the name, if you look at the inspector, when you hover it.
This property is called scale. This is what you want to target. If you wanted to change the position, it would be all
in lowercase position. After that, you need the value that you want to go to,
which in our case is a vector 2 with 0.1 and 0.1 for X and Y.
Finally, we need a duration. Let's say 0.2 seconds. And that is it. With two lines of code,
we can animate the crosshair. If I now run the script and shoot, the crosshair becomes really really small.
To make it large again, all we have to do is do another tween property line, where we want to have the marker node,
target the scale, and now our target scale is going to be 0.5 and 0.5 with a duration of, let's say, 0.4
seconds. The tween is going to play these animations one after another. It first
plays this one and then this one, which means inside of the game, we're getting a nice animation. And this
is working really well. This you can also customize a lot. For example, you can make it play forever.
You can make it play in parallel if you want to. There's a ton of stuff you can do. To learn more about it, you want to
go to help and then look for tweens. There's a really good documentation for all of it, and this thing is incredibly
powerful. Most importantly, you want to work with the different methods. All of this here
is pretty much what you need. For example, via tween interval, you can create a pause.
By a stop, you can stop my animation. By a set loops, you can set the loops. By a set parallel, you can make the
animations play in parallel. And that's kind of it for basic tweens. For a very basic animation, this is what
you want to use most of the time. It's a very simple system. It's super reliable and very easy to implement.
Let's practice this one. I want you to scale the bullets when they are being launched.
They should start from zero and go to one. And this is for both X and Y.
Pause the video now and see how far you get. To work inside of the bullet,
we can simply target func_ready. So, once the bullet is being spawned, we want to create var tween once again
via get tree and create tween. Then we want to have tween and tween property.
On that, we want to target the Sprite 2D. Drag this one in there, and then we want
to get, once again, the scale. The final value is going to be a vector 2.1.
With a duration of, at least for now, 0.5 seconds, so we can see what's happening.
This right now is not going to do anything because the default scale of the Sprite 2D is already one and one.
So, what we would have to do is when we are getting started, get the Sprite 2D, update the scale to vector
two .0. That should already be enough. If I now
launch the game, shoot a bullet, the bullet is growing when we are shooting it. That is working.
Alternatively, what you can also do, instead of running this line to scale the bullet down,
you can add after tween property {dot} from. That way, you can set a start value,
which in this case is going to be vector 2.0. And now, we are getting the same result
with one line less. And I think this is also a good bit more readable. Although, I guess that can be
slightly subjective, but it's still a really really useful system. At the moment, we only have the player
and pretty much nothing else. Let's change that by creating the level. For that purpose, we are going to learn
about a new node called a tile map layer. Via that, you can create really complex levels inside of a single node.
You can also add animations, collisions, and even auto tiling. Although for now, we are going to keep
it simple. Back inside of Godot, first of all, this static body 2D for the floor really
doesn't work anymore. Let's get rid of it entirely. Instead,
I want to add a tile map layer node. Notice here, we have a tile map and a tile map layer, with the tile map being
depreciated. It still works, but you are not supposed to use it. It's just there for backwards
compatibility. So, use tile map layer, create, and then,
first of all, nothing happens. Although, we did get a tile map button at the bottom of the editor, in which at the
moment, we can't really do anything. For that, first of all, you want to look at the inspector. There we have tile
set, and we have to create a new one. Click on that.
And then we get two buttons at the bottom, tileset and tilemap. To understand what's happening here,
first of all, inside of the inspector, when you click on tileset,
you get a whole bunch of options, a tile shape and a tile size. Those are the really important ones.
I want to keep all of the defaults here and then go to tileset and look at graphics and tilesets as
well. There we have subway and walls.png. For now, I want to drag subway.png into
this area. If you drop it, then Godot is asking you if you want to create an atlas texture,
which we do want to do. Once we have that, I can actually explain what's going on. So,
we start with all of this. This is one PNG file. And via this tilemap layer node, we are separating it into little
chunks of 16 by 16 pixels or whatever you have specified in You can also get a different shape and a
different size, but in my case, the defaults work perfectly. And the fundamental idea is that
you can use the tilemap via this tilemap button, take one of these parts and then place
it inside of the level, like so. For that, make sure you have this pencil button selected. Then you can draw
and if you have this rectangle tool selected, you can create entire areas quite easily.
Although that's not what I want to do. If you left click and you have the rectangle tool
selected, you can delete entire areas and if you have the pencil selected, you can place stuff with the left mouse
button and remove it with the right mouse button. The basic way you are supposed to use it
is to place tiles deliberately. So, this could be one floor. Then I want to place this tile, place it here.
Then I want to have this tile, place it there. And maybe this middle bit for a bit of variety,
something like this. Then we would have one part of the level.
That being said, if I run the game, this is not going to do very much. Well, it is inside of the game, but the player
doesn't collide with it. Also, there should be this one here. We're going to fix that in just a
second, but for now, the main thing that you have to understand is that fundamentally, when you're working with
a tileset, there are basically three steps. Inside of the inspector, you can create
a tileset where you set a tile shape and a tile size. After that, inside of tileset, you can
look at what you have created and modify it. That we're going to work on in just a second. Under the paint tab, you can
do a couple of things. Finally, with tilemap selected, you can grab different parts. This can even be
multiple bits and place them inside of the level to make them visible. If you spend enough time on it, then you
can create really nice levels. Although, it is going to take a while. Creating levels is just going to take some work.
What you can also do, if you have the tileset open, you get a whole bunch of extra stuff.
The most important one for our purposes at the moment is the physics layer. Via that, you can add collisions.
First of all, we have to add a physics layer. Then you get the usual stuff where we
have a collision layer and a collision mask. For which, first of all, I want to go to
project, project settings, then find 2D physics and I want to name a couple of layers.
The first one is going to be terrain. Then I want to have layer. Besides that, I want to have the drones and the
bullets. That's all we need. Once we have that, inside of the tileset, the tileset should be on the first layer,
terrain, and it doesn't have a mask. It doesn't need to see anything. Just to keep things consistent, the
player should be for the character body 2D collision, we are going to be on layer two.
We can see layer one, layer three, and we cannot see the bullets.
Only the player can shoot bullets and the player shouldn't be able to shoot himself.
Then finally, for the bullets, the area 2D for the collisions, it should be on layer four.
It can see the terrain and it can see the drones. That way, we have collisions covered.
Although, if I run the game, this still isn't doing anything. The reason for that is to actually get
collisions, these tiles need to have an actual shape for the collision. That you can find
inside of tileset under paint. Once you have defined a physics layer, inside of select property, you will find
physics layer zero. In there, once you have it selected, you get this shape and this you can place on
different tiles. If I place it for now on all of these tiles, the one that I have used inside of the game,
then I can run the game and now we have collisions.
You can also change them. The basic idea is that you are modifying this shape, for which you can also add
snapping, which I would generally recommend. Grid snap is pretty good. I usually go
with a separation of eight. And then, you can drag these points to a specific part,
for example, something like this, and then use them like so. If you click on one, you get a preview.
And let's say for this one, I want to be it roughly here. This doesn't have to be too precise, but
you do want to spend some time on it. Also, via these three dots, you get a couple
of shortcuts to rotate or flip it. For example, if I press H, then this is being flipped and I can use it here.
Although, I probably want to expand it just a bit. Something like that.
This I also want to do for this tile, but we do want to cover the entire bottom bit. Like so, like so, and like
so. It looks pretty all right. I guess we can do the same thing here and here.
And then, inside of debug, I do want to have visible collision shapes. Run the game.
And now, you can see that we have collision shapes that work pretty well. On top of that,
if you put this tilemap layer before the player inside of the scene tree,
I can run the whole thing again and now the player is on top of them, which looks a lot better.
If I now disable the visible collision shapes, try all of this again,
this is looking pretty all right. At least for the basics. Now, in my case, I want to have multiple
layers, which effectively means I want to have multiple tilemap layers. The one we have
created so far is the, let's call it the collision layer. Besides that, and let me organize all of
this inside of a node 2D called layers. This should come right at the beginning of the scene tree
and contain all of the layers. I want to add another tilemap layer. This one is going to be the background
layer and then I want to have a background detail layer.
Finally, I want to have a foreground layer called FG layer.
Just to demonstrate how this system is going to work. And the BG layer and BG detail layer
should come first. So, we have background, background detail, collision, and foreground.
For the background, I want to create a new tileset. Once again, the defaults here are totally fine. Then inside of
tileset, I want to use subway, create an atlas texture.
This one doesn't have any collisions. I basically want to grab this black tile. Then inside of tilemap,
grab the rectangle tool and draw some kind of black area for the background.
You can also drag another PNG file into the tileset, like so. Click on yes and then you get a lot more
options. For example, for this one, I can select all of this.
And then with the pen selected, I can place something like this.
For BG detail layer, I want to have a new tileset. Once again, details here are fine. Then
inside of tileset, subway.png is the main thing I want to use. And there, for example, we could use a
bin. For that, you want to go to tilemap, select the bin, and then place it, I
don't know, let's say here. Once we have that, I can run the game and now
we are getting something that is looking a lot more like a real game. Still not perfect, but this is
definitely progress. The last thing that I want to do, inside of FG layer,
the usual stuff, new tileset. Inside of the tileset, I want to add subway.png. I do want to have an atlas texture and
then I want to place, let's say, this light source here. Via tilemap selected,
and then place it, Let's say here. Now, this should be in front of the player. But, inside of the game, this
doesn't work. Let me demonstrate. So, I can get there and the player is in front of the light.
That is because we have the layers first in the scene tree and then we go to entities in which
we have the player. So, the player is drawn after the lights.
To change that, with FG layer selected, you can go to ordering and there you have the Z index.
This determines when stuff is being drawn and by default, you always get zero. If you set this to a one,
then it's going to be drawn after everything else.
The basic logic is that Godot goes for all of the Z indexes in order and then draws them.
By default, everything is zero. So, if this value gets a one, then this layer is always going to be on top, no matter
what you do. By that system, you can keep all of the layers inside of one node and don't
worry so much about the order. Keeps the scene tree a bit more organized, which I think is a good idea.
Let's do an exercise. I want you to create a proper level. Make sure that you are adding lots of stuff to it and
that the player cannot see the gray background. There should be a proper surrounding
area so the player can never leave the level. Pause the video now and see how far you
get. Ready. Back inside of the level, I'm going to create a level and speed up the
video so it doesn't get too boring. Okay. I think that was definitely enough for a reasonable level.
I think just by watching the video, you can tell that creating levels does take a while. Simply drawing the border
around the level took me 5 minutes just on its own. Now, later on, we are going to learn
ways about automating this process. Where you are placing one tile and then Godot selects the right one for you.
Makes the whole thing so much easier. But, for now, I think it's a good idea to appreciate
how difficult level creation really is. That being said, there's one thing I forgot to tell you.
Let's say with the collision layer selected, via this selection button, you can
select elements and then copy paste them. Super handy.
We are nearly done. There's one last thing that I want to do. Let me run the game again
and we definitely have to speed up the player, but also we can look outside of
the level. For that, first of all, with BG layer selected, I want to draw a
vague rectangle around the level. So, we have a clear border on one side. Something like this looks all right.
Don't need this part, I think. But, you need those bits those bits
up to here. We need this part, this part this part.
Then I guess we can cover this area. Nearly done. Here and here.
Then we need this area. And we have to cover the entire bottom. Like so.
Once we have that, you can look at the top. There Godot's telling you the pixel coordinates.
For example, this line here is at around -100. I'm pretty sure it's going to be -96.
So, what we can do, inside of the player camera 2D, we can go to limit and then go to left
-96. Now, I can go to the left and we cannot see the background. This gray
bit here is because of the window. If I rescale it, then you can see this is just part of the background. If you
click on this button, that disappears and then Godot scales properly and we can see nothing of the background
in the actual game. The same thing I want to do for basically all of the sides.
So, at the top, we are at, let's say, -32. On the right side, we should have a much
larger number, let's say 2,780. And at the bottom, we are at around 900.
So, on the player, this is going to be top -32. Right was, let me go to the bottom
right, then I can see both numbers. And if you zoom in a lot, you can tell much better where you are.
So, 2,780 should be all right here. Right, 2,780. For the bottom, I want to have
960 exactly. 960 and then we shouldn't be able to see the
background. Let's try. So, I'm going to zoom out a lot.
Place the player. I guess we can start in the default position so we can check the top.
And that definitely works. Then we can place the player somewhere, let's say, here.
We cannot see the bottom and we can't see the right. Also, if you move the player around,
the purple rectangle around it is the camera and you can see that at some point the camera stops,
which is a really good indicator. And with that, we have a level. It is time to create an enemy, which is
going to be a drone. This thing is just going to move towards the player and explodes once there's contact.
But, the player can also shoot at it and after three hits, the drone explodes. All of this is going to be an exercise
right away. I want you to create a drone yourself. For that,
it should activate when the player gets in range. That's going to be an area 2D. When activated, it should move towards
the player. Once again, the drone is going to be a character body 2D that you move via move and slide with the
velocity. Once it gets close to the player, it should stop and explode. So, you stop
the movement and use the animation player to play the explosion animation. Finally,
when the player hits the drone three times, the drone should also stop and explode. That is going to be quite a
bit, but I think at this point you can manage this. So, pause the video now and see how far
you get. Should get started. I want to create a new scene with a character body 2D root
node that we can rename right away to drone. I want to save the whole thing inside of
scenes and entities, drone.tscn. Then let me move to the center. There we go.
To find the drone image, you want to go to characters and there we have drone. Keep very basic animation. You can
barely tell that it's even animated. We could play all of this via a sprite and for example, an animation player.
Alternatively, we can also get an animated sprite 2D for which we can set a sprite frame
with a default animation and in there, if you click on this button, you can select from graphics, characters and
drone. We want to have four horizontal images and only one vertical one. Like so. Then
we can select the different images. Add four frames and now we can auto play the animation.
That way, you get some basic effect. Alternatively, you could have also used a sprite 2D with some GDScript or you
could have used an animation player. There are loads of different ways to get a basic task done in Godot
and I really want you to think about the different approaches you could be taking. I think this is the easiest one,
but there are alternatives. Besides that, I want to have a collision shape 2D,
which I think for this thing can be a circle shape. Let's resize it to something like this. That looks okay.
And then, just to test things inside of the level, in entities, I want to add the drone. Oh, and by the way, at this
point, once your levels become a little bit larger, it's really, really easy to drag the wrong node from the origin
point. So, instead of the drone, I dragged the bullets, which shouldn't be the case.
The easiest way to get around that is with the drone selected inside of the scene tree.
You want to hold command or alt and then you are only moving the node that is selected inside of the scene tree. Can
be incredibly helpful. Besides that, with the bullet selected, you can also
lock them via this button. That way, you can't move them anymore. The same thing I want to do to layers
and two entities. They shouldn't be draggable inside of the game. There are a couple of useful buttons at the top. I
would recommend to go through them when you have time. There's for example a ruler in there or
there's grid snap. It can be quite helpful, but I want to focus on the basics, which is the drone.
Let's run the game and see how it looks. It looks pretty respectable. Now that we have that, I want to create
an area to denote, which I want to rename to detection area.
To that, I want to add a collision shape 2D, which is going to be a circle shape that
needs to be reasonably large, something like this. Let's see how it looks in the game. And
I think that's a good detection area. If the player gets into this range, the drone is going to activate. For which we
want to add a script. The defaults here, as always, are totally fine. And for the detection area, I want to go
to node and then body entered. Although first of all, I forgot one thing.
With the character body drone selected, I want to go to collision and this should be on layer three.
It can see the terrain, it can see the player, and it can see the bullets.
Besides that, for the detection area collisions, it shouldn't be on any layer and it can only see the player. And for
now, let's simply print the body that we are getting. It should only be the player. We can actually rename it to
player. And this has to be a character body 2D. And the player is being spawned right
next to the drone. But if I go outside and come back, this should be enough. We are going to see player again.
So that is definitely working. Once we have that, I can create a var direction. As always,
this is going to be a vector two without a default value. I want to have a var speed.
This can be an integer with a value of for now, let's say 50. And I want to store the
layer. A character body 2D. Now inside of this function,
when we are getting the player, I want to assign the player to player.
So I want to get the variable and assign the parameter. Although you can't see, this logic is going to be quite
confusing and Godot really doesn't like it. This causes a ton of errors. I guess inside of the parameters, we can
call it the player body. Now once we have a player body, I want to run funk {underscore} physics
process and check if this value exists. So there's anything inside of this variable, then
this if statement is going to trigger. If there isn't, it is not. All I want to do, if we have a player,
is first of all get the direction to the player. Let's call it var dir. This we can get from vector math. We
need the player {dot} position and from that subtract the current position of the node. This we also want to normalize
like we have done for the bullets. Super important so that when we are multiplying the direction with the
speed, we are getting a constant speed. Speaking of which, all we have to do is get velocity
and assign it direction multiplied with speed. And also, we want to call
move and slide. To test all of that, I want to move the drone and the player a bit further apart
so I can actually walk into this area. Let me try. And now,
if I get close enough, the drone starts moving towards the player. And we really should increase the player
walking speed. It is really, really slow. I guess 120 should be quite a bit
better. That feels definitely better. Now I can also run away from the drone.
If the player goes outside of the detection area, the drone should stop. Which we can check via body exited.
Once again, we have the player {underscore} body, which has to be a character body 2D.
You don't have to rename these, but I think it's good practice. It just makes the entire thing much more
understandable. If that is the case, I want to remove the player body from the player
variable. To do that, I want to get the player variable and
assign it a value of null, which is empty. Once we have that, back inside of the game,
I can walk towards the drone, it follows me, and if I run away, it stops. This works multiple times. Perfect.
Also, Godot is really unhappy with us because inside of physics process, we don't use delta,
and inside of player body, we don't actually use the player body. So we should add an underscore before
it. If I now try this again, the debugger should be happy, and that looks much
better. Now we have a drone following the player.
After we have that, I want to play an explosion once the drone gets really close to the player. For that, first of
all, I want to add a sprite 2D, which we can rename to explosion sprite. Inside of the inspector for the texture,
I want to get fire and explosion. Which is an explosion with 1 2 3 4 5 6 7 8 frames, or 8 H frames to be specific.
This we want to animate via the frame property. For that, you could use another animated
sprite. Although to practice things, I want to add an animation layer,
which is going to get a new animation, explode. I want to add a track property,
explosion sprite, and get frame. With the timeline open, I want to go to the explosion sprite and then set the
animations. I can select all of them. We should be going from zero to seven.
So the first one is zero and we go all the way to seven. And I think this might be a bit too
fast. So we want to space things out. I can just select all of them and then
give each 0.1 seconds. Like so.
So so and so. Then the entire duration of the
animation should be 0.7 seconds. Let's try it now. And for some reason, the first frame is
always a bit longer in the preview, but inside of the game, this isn't going to be an issue.
Which means next up, I want to add another area to denote
to the drone. This one is called collision area.
Once again, I want to have a collision shape 2D, which is going to be a circle shape, and this one should be just a bit
larger than the drone, something like this. Also,
for collision, you don't want to be on any layer and you can only see the player.
If a body enters now, then we want to explode the drone. For which we don't need the body, so
this one can get an underscore. All I want to do is get the animation player
and play explode. Let's see if that works.
And that is maybe not ideal. First of all, the explosion sprite should not be visible.
By default, it should be hidden, so click on the eye and then
once the node collides with the player, I want to get the explosion sprite and show it.
Next attempt. Much better start, and now we get one explosion. This happens
multiple times. So we can test things. We are nearly done. The one issue that
we have is that when the drone is exploding, so somewhere here, we don't actually get rid of the drone. It sticks
around. To fix that, first of all, when we have an explosion, the drone should stop
moving. Which we can do very easily. We just have to set the speed to zero.
That way, if the drone gets close, it stops moving. Good start.
Besides that, I want to hide the animated sprite 2D so we can't see the drone anymore.
That part is also super easy. Animated sprite 2D and hide.
Also, the explosion sprite by default shouldn't be visible. Once we're doing that,
the drone can move towards the player and this almost works.
Basically, what we now have to do, once the explosion has finished playing, we want to delete the entire scene.
Which we can do in two ways. Either with the animation player selected, inside of node and signals,
you have animation finished. You could delete the node when this function is running. Alternatively, which I think is
the better approach, inside of this function, we can
await the animation layer
{dot} animation finished. We are waiting for the animation to be finished, and then we
want to delete the node via Q free. Let's try this one. And now, if the drone comes to us, it explodes and
disappears. And I realized the been here really doesn't work.
But first of all, let me explain why this line is really important. If we didn't include it, we would play the
animation, but then delete the node right away, which means we wouldn't see the animation.
Now, the drone simply disappears. Now, we are still playing the animation, but we are deleting the entire thing right
away, so you can't really see it. For which we have this await line, and then everything works just as expected.
Next up, when a bullet hits the drone, the drone should lose some health.
Inside of the bullet, I want to check for a body entered and connected to the script of the
bullet. For now, let me simply print the body that we want to target.
Also, the bullet speed is incredibly slow. 30 is just not very good, and this should be speed anyway.
integer with, let's say, 100. And then this speed we want to use for the bullet.
Let's try all of that. If I now shoot at the drone, I do have to make sure I hit it. There we get drone. Also, we get
collision layer. That is expected. The bullets can't see the drone and the terrain. If a drone
hits the terrain, I simply want to delete the bullet, which we can do via Q free.
However, inside of the drone, I want to have a funk hit, which for now will simply
prints drone was hit. Once we have that, inside of the bullet,
I want to check on the body that we are getting from the parameter, if there is a hit function, I want to
run the hit function, but only then. So that when we are getting the terrain, we are not doing anything.
And just to demonstrate, before Q free, I want to run body.hit, which is going to work for the drone,
but not for the terrain. Let me get to the drone, make sure I hit it on the first try. There we get drone
was hit. This one works, but if I hit anything else, then Godot is going to crash
because there's no hit function inside of tile map layer. We first have to check if the hit
function exists inside of body. That luckily, we can do very easily. If string hit is in body.
That's all we needed. By that, Godot is checking if there's a hit function or property inside of this
node, and then it is going to execute it. So, next attempt.
I can now shoot at the drone a couple of times, make sure I hit it. That looks good. Drone was hit. And if I hit
anything else, the drone just disappears. That means, finally, inside of drone, I
want to set var health, an integer with a default value of
three. This we then want to reduce, so health minus equal one, and we want to check if
health is smaller or equal to zero, and then I want to play basically what
we have done in here. In the most basic sense, you could simply copy all of this, and you should
be good to go. Let's try. But then I have to make sure I hit the
drone. One, two, and three. We get the explosion, and the drone disappears.
That being said, you shouldn't copy lines of code like this. It's a really bad practice.
A better way of approaching that would be to create another function. Let's call it explode. We want to have all of
these lines, paste them in there, make sure the indentation is correct for all of them.
And then, if health is below zero, we want to explode, and if the drone gets close to the player, we also want to
explode. That way, you are referencing the same code twice, and if you want to make
changes, it's really easy to do. So, let's try. I can get to the drone, I can shoot at
it. It explodes. That looks good. And if I just get close to it,
then it also explodes. Perfect. With that, we have the basic drone. There's one more mechanic that I want to
add. When the drone explodes, it should damage everything around it, including other drones.
To achieve that kind of effect, we have to group all of the drones, by which we can introduce a new
mechanic. Back inside of Godot, if you look at node, beside signals, we have groups.
Those can be incredibly useful. For example, inside of the drone, with the root node selected, I can create a new
group and call it drones. Then we have a scene group.
What can we do with that? Well, inside of the level, let's say, when the scene is ready, func
{underscore} ready, I want to print everything inside of this drones group.
If you look at the scene tree for the drone, we now have this symbol, indicating that the node is inside of a
group. If you have a hover at Godot, it is telling you what the group is called.
To access all of these nodes, all you need is get {underscore} tree. Don't forget to call it, and then you
can get nodes in group, or you can get get first node in group. Quite often, if you want to have access
to one node globally, everywhere inside of the script, like for example, the player, you might
have a group dedicated to the player only. Then get first node in group usually
makes sense. But for our purposes, we want to get nodes in group. Then we need the group name, which in
this case is drones. If I now run the game, I am going to get an array with one node
inside, the drone. If I add multiple drones, let me duplicate it, and I don't want entities
to be locked. That way, we can move stuff inside of it.
I just want to have a couple of drones, like so, so, and so. If I now run the script,
we get an array with four drones. And that is pretty much all you have to
know about the basics of groups. When you have a scene, you can assign the node to a group, and then every time
you create an instance of the scene, you have access to that via the group. And this, by the way, is going to work
everywhere. We can check inside of the game scenes for all of the drones, and we can check
inside of the drone itself. For example, in there, I want to have func {underscore} ready
as well, and then simply copy from level this one line.
Then I can run the script, and now we should get the same output a couple of times, and that looks pretty good.
Every single drone is going to output this print statement. By this system, you can target scenes basically anywhere
inside of Godot, and you're not really constrained by any kind of scene setup. Now, to test all of
this, I want you guys to implement the drone explosion. Basically, make nearby drones explode
when the drone explodes. You can set the range yourself, just choose something that you think looks good. Pause the
video now and see how far you get. I want to work inside of the script for what a drone, and we don't need the
ready statement anymore, and we also don't need it inside of the level. All of this can go. Instead, when the
drone is exploding, this function here, before we are running Q free, but after the animation, I want to for
drone in get {underscore} tree and get nodes in group. The group I want is drones.
This works because the return value of this function is an array, meaning we can use a for loop to get every single
item inside of it. All we have to do then is drone.explode. [Music]
Although this we only want to do if the drone is within a certain range. Now, to test all of this first, inside of level,
I want to move all of the drones really close to the player, so we can see them at the same time.
If I now run this, we get the drones, and if one of them explodes, they all explode.
That being said, inside of the drone, we only want to explode nearby drones.
That we can do via the current position, and then we have distance to.
We want to check drone.position. If this value is, let's say, smaller than 10 pixels.
Then I want to run drone.explode. Also, this should be an if statement. Let's try this one now, and we have to
make sure that the drones are close to each other. And well, that was quite hard to see.
Let's move one drone over here. So, we can test things a bit better. The drones explode, but this one is
totally unaffected. And if I move the player just a bit further to the side,
like so, meaning we don't trigger the drones automatically, then I can play with this a bit better.
So, I want these two drones right next to each other. Then, inside of the game,
make sure you don't move forward. Now, I just have to shoot at them, actually hit, and
I guess we need to have a bit more of a distance between them. Let's try 20.
And that works much better. So, this would be the most basic setup. However,
back inside of the game, if I had simply explode the top drone, you can see that we have the explosion
and then this waiting time, which is kind of annoying. Ideally, we want to explode the other drone while
this animation is playing. Or to be a bit more specific, if I open the animation player,
inside of explode, we want to probably damage things somewhere here. How could we do that? And well,
first of all, we will need another function. Let's call it func
chain reaction. This function doesn't need a parameter. All we want to do in there is
effectively this for loop. Let me copy it, then paste it in there. After that, it's really important that
this script is saved. There shouldn't be an asterisk right next to it. But for now, let me not save it because what I
want to do inside of animation player, in the animation, I want to add a call method track.
If I do that, then Godot wants to have a node. I want to get drone. On that node,
I can insert a key, and this key is going to be a method that we want to call.
We do have all of the inbuilt ones of character body 2D. For example, move and slide or is on
floor, whatever you need. But besides that, we also have the functions that we created, like hit and explode.
But notice here, we do not have chain reaction. That is because this
script was not saved. If I save it, so the asterisk has disappeared, then I
can add another key, and now we get chain reaction. This can be a really annoying beginner
mistake. But now, when we get to this point in the
animation, Godot is calling for you the chain reaction function. Which means, inside of the game,
I can shoot at the drone three times, and we get a much better effect. And with
that, we have the drones completed. Next up, I want to add lights. That way, the game is going to look significantly
better. For all of that, back inside of Godot. First of all, I only want to have a
single drone. So, let me delete all of the other ones and move this drone to the side.
We are going to add lights to this one later on, so we get a bit of a blinking effect.
But that's going to come later. First of all, to add lights, we have the level node selected. I want
to add a node and simply type light. For the 3D nodes, we have a ton of different lights, but those we don't
care about yet. For 2D, we have two kinds of light, a directional light 2D and a point light
2D. The point light 2D is basically a lamp, whereas directional light is more or
less the sun. Let me add this one first. If I do that, then you can see the entire level is much, much brighter.
If I hide it, then you can see the effect much better. Also, on the right,
we can change the color to whatever you need in here. And you can even change the blend mode,
so you are subtracting the color instead of adding it. If I change the color to something red,
then the level will become green because we are removing all of the red values. Can create interesting effects, but not
what I want to do right now. Besides that, we can change the energy, and this one
changes, well, the energy. Something you want to use very, very carefully.
I guess for now, what I want to do is to make the color much darker, something like this.
Then for the blend mode, I want to mix the color. So, we are mixing basically all of the layers with this color. And
this is definitely too dark. I guess something like this works a lot better.
And to see the difference, if you toggle this node, this is very much noticeable. For all practical purposes, we now have
a sun that makes things darker. Besides that, if you look at the inspector, we also
have shadow. This we can enable, but at the moment, it's not going to do anything.
I guess to make it visible, let me change all of this back to normal and then add another node.
The node I want to add is a light occluder. This is basically a collision shape for lights. If you add it, by
default, nothing is happening and Godot is giving you a warning that you have to add a polygon.
Inside of the inspector, in the occluder, you can add a new occluder polygon 2D.
Do that, and then still nothing happens. But now, at the top, you get three buttons that let you draw a shape.
Let me draw something random, like so. And now, this shape
is blocking the light, and anything below is casting a shadow. For that, you do want to make sure that
shadow is enabled. If it isn't, then this is just blocking out the light. You might also be wondering, why do we
still get a warning for this occluder node? If you simply go outside of the scene and come back to it, it is
disappearing. It's just a small bug, doesn't really matter. Now, for this scene, I don't want to do
that. So, let me get rid of it, and for directional light 2D, I want to set the blend mode to mix and the color to
something darker. So, we get more atmosphere inside of the game.
Let's see how that looks. And that is definitely a change. Just play around and see what you like.
Now, the second kind of light that we can work with is a point light 2D, effectively a lamp.
If you add that one, once again, by default, nothing is happening because for this light, you will need a
texture. Inside of graphics, we have lights, and there we have a couple of textures.
The one you want to use for now is called main.png. If you add that one, in the origin of
the game, we have a light source that is defined by this texture.
If we have white points, we get a lot of light. If we get more grayish points, we get much less light.
And this works together with the directional light. If I make everything super dark,
then you can still see this outline. You can also scale it inside of the point light.
You have a texture scale, and you can make it smaller or larger. You can also offset the entire thing.
Although, this is not what I want to do, but besides that, you can also mess with the color and do
something weird in here. You can update the energy and the blend mode. Shadow also works in exactly the
same way. Now, in my case, I don't want to do any of this. And for directional light, I
want to have a bit more visibility. Something like this. And then, I want to add the point light
2D to the player character. Inside of the player character, point
light 2D along with main.png. Then the player is going to be much brighter,
which does look a bit weird, but back inside of the level, we get a pretty nice effect.
And by the way, what you can also do, if I add another point light 2D to the level scene,
if you click on the drop-down arrow for the texture, you can create new textures from
scratch. For example, there is a gradient texture 2D.
If you click on it, you are getting a texture that you can modify right inside of the engine.
So, if you look closely, let me zoom in a bit. And I guess if we move this thing over
here, it's much more visible. This gradient can be changed inside of the editor, and then you are getting a
real-time preview of how it's going to look. This you could, for example, use all the
way on the right. We have a couple of doors that could do with a bit of illumination.
Let's move it here. And then, we can move the light roughly here.
And you can already see, if I toggle this light, this is making a difference.
We can maybe move it a bit more like this. Besides that, if you change the color,
you can reduce the amount of red and then make this thing a lot greener. Although,
you do want to play around with what looks good in here. I guess adding a bit of red to it could
be a nice effect. I hope you get the idea. By this system, you can make things look much more
appealing. If you compare this door to this door, this one just looks better. And finally,
what we can also do by adding another point light, we can illuminate this sign,
for which we have l3.png. You can already see if I drag it around, we have the right light source,
and this one's being spawned in the origin point, but I want to have it here.
That way, this sign is looking much more interesting inside of the game.
It is now illuminated, and it works along with the player.
If you compare this sign to the other sign, you can definitely tell the difference.
Also, what we can do, if I have the point light selected, we can, for example, change the energy and
make it flicker just a tiny bit inside of the level script. When this thing is ready, func
{underscore} ready, I want to create, let's call it light tween,
for which I want to once again create a tween. And by the way, you can either get the tree and then call create tween,
or you can simply go to create tween right away. Both would work. Choose whatever you like.
Then, for the light tween, I want to tween a property. The node I want to target is point light
2D2, this one, and from there, I want to get energy. As a string, energy.
The final value has to be a float for now, so we can see something. Let's say 10.0.
Duration could be 2 seconds. If I run the game, you can see the sign is definitely getting brighter.
This should be much less strong. 1.1 maybe.
After we have that, I want to duplicate the line and go to, let's say, 0.9. That way, you are going to get a very,
very minor effect. And this I want to play forever, which we can do via light tween and then set loops.
If you don't add any value in here, let me open the documentation, if you add any kind of value in there,
like a two, you're going to play it twice. But if you call it without any
arguments, it's going to run forever, which is exactly what we want. That way, inside of the game, it's going
to be quite hard to see, but if I make the value stronger, let's go with 1.5
and 0.5, then you should definitely see it. There, you can absolutely see that this
is making a difference. I think the main issue is that 2 seconds is a bit too long.
0.3 maybe. Now we have more of a flashing effect, but you get the idea.
Let's stick to 2 seconds, and then we get at least some kind of effect. Should be something to indicate that
here is an important part of the level. Anyway, this is kind of all you have to know about lights to get started.
You have either directional light 2D for the sun, or point light for a specific kind of light source.
For those, you have to add a texture, which you can either create yourself in something like Photoshop, or you can
create them via the drop-down menu. To practice all of this, I want you to add a drone light,
which is basically going to be a red light that flickers on the drone. Animate this via the animation player.
You could also use a tween, but I want you to practice using the animation player. That one is super important.
Pause the video now and see how far you get. I want to work inside of the drone.
And for now, let me hide all of the areas, so we can actually see what's going on.
Much better. First of all, I want to add a light to this red part of the drone,
for which I can add a point light 2D. I still have it selected, and then add a main.png
file. Now, we're making the entire drone much too bright. That is because this light
source is quite large. To scale it down, you can set the texture scale to 0.1, and then you can see a bit better what's
going on. We have a light source that we want to be, let's say, here. Besides that, I want to change the color
to something more red. For that, reduce the amount of green and blue, and then we are already getting close to the
effect that we want if I simply toggle the color. Alternatively, what you can also do is change the energy to
something like this, where we toggle between a value of 1.something and 0. Let's say 0.3.
This we want to do via the animation player. We already have one.
In there, I want to create a new animation for the red light.
I want to add a track for property. I want to target point light 2D, and then target energy.
Open this one. The duration for the animation can actually be 1 second. I want to start with a key.
The value for the light should be 0.3. You can see a preview. If I now go to, let's say, 0.4 seconds,
I can add another key with a value of 1.2. This animation I want to loop.
Let me run it. And just in case you're confused, we are starting at zero, then we go to
this value, and then Godot automatically tries to go back to this original value, which is
why we are reducing the light already. And we can just loop the entire thing, and then we have the effect already.
If you ping-pong this value, you are playing the animation back and forth, which could also work.
If you reduce the animation length to half a second, it's going to do pretty much the same
thing. Just choose whatever you prefer. With that, inside of the game,
we can look at the drone, and it still explodes. That looks good. At this point, we have basically a game.
However, I want to make this thing look a bit more interesting. Specifically, once the drone gets hit, it should
flash. For that effect, we have to learn a new topic that does get quite a bit more
complex called shaders. Using shaders, you can do a crazy amount of special effects, and I am only going to cover
the absolute basics. But fundamentally, all that we are going to do
is that we are going to determine how elements are being displayed. That means, if you have the Godot logo,
for example, and this would also work with any other graphic.
When we are displaying this in the game engine, what internally happens is that the GPU
separates this thing into smaller chunks and draws each of them individually in parallel. And this process you can
manipulate. For example, you could tell the GPU to draw this part purely in yellow, or make this part white.
You can also change the position. For example, you could move this top part here further to the right.
Via this system, you can turn a 2D graphic into basically anything else. There's actually a whole website for it
to give you ideas in terms of what you can do. It's called godotshaders.com. If you click on shaders,
there you can get an idea of what you can do with them. Canvas items are for 2D shaders, and
spatial shaders are for 3D objects. If you look at canvas items, they can see that you can do a crazy amount of
stuff using this system. But keep in mind, all of this does get a lot more complex.
But I want to keep it simple, so we are only going to do the absolute basics, for which, back inside of Godot, I want
to work inside of the drone, and specifically inside of the animated sprite 2D.
We only have to apply a shader to the visible sprite, which is the animated sprite 2D.
To give this thing a shader, you want to go to material, and there you can give it a canvas item material
or a shader material. Shader material is what we want to go with, so click on that, and then nothing
happens, but you do get a sphere. That's a preview of what the shader is doing.
If you click on that, then you can create a new shader. There are two types of shader, a script-based shader and a
visual shader. I actually want to work with a visual shader. Then we have the mode. You have canvas
item, spatial, particle, sky, and fog. Particle, sky, and fog I think are self-explanatory. Canvas item is for 2D
items, sprite 2D, animated sprite 2D, things like that. Spatial shaders are for 3D objects.
Don't worry about those yet. We do want to create a canvas item shader, and then we have to save it
somewhere. In my case, I tend to prefer to save all of the shaders inside of a folder called
shaders. That keeps things a bit more organized, but you could save it wherever you want.
It doesn't really matter. After you have that, click on create. And then nothing happened.
To actually do something, you have to click on this item again, and then you can see. Let me expand it.
We have a field where we can create a shader using visual nodes. The most important thing is the output.
And to that thing, we can add different information. The only two values that we care about
are color and alpha. Although, there are quite a few more. And next to them, the dodge determines what kind of data type
we are working with. Gray means Godot's looking for a floating point value, and shades of
purple means some kind of vector. Color wants to have a vector three for red, green, and blue.
Down here, we have shadow vertex. This one is a lighter shade. This is going to be a vector two, which doesn't matter
for now. And fundamentally, the way this system is working, let me show the drone and
the output node, if you right-click on this field, you can add other nodes.
For example, in there, if you look at color, we have under variables a color constant.
If you double-click on that, you get a color constant with a golden note.
If you click on this arrow, Godot's telling you what the individual parts of this node are. We have red, green, blue,
and alpha. Meaning this is a vector four, which is fine for our purposes.
Effectively, what you want to do, you want to drag this node into color, and then we have turned this node white.
Also, you can see that Godot has converted a vector four into a vector three,
which it is doing by discarding any extra information. Meaning from this vector four, we have
red, green, blue, and alpha values, but to this color vector three, we only get three values, red, green, and blue.
Meaning alpha was discarded. And well, now, inside of color constant, we can change
the color, and this will show up inside of the game right away. If I launch the game, then you can see
that the drones are now colored in this specific way. On top of that, what we can also do, let
me give myself just a bit more space, like this. Right now, we work with the color
values, but besides that, if I right-click again, we can add other nodes. And most of the
time, you are simply searching for the node you are looking for. For example, I want to add a float constant.
And then connect this to the alpha value, which right now makes the drone
invisible. That is because we have set alpha to a value of 0.0. If I set this to a one,
then you can see that we have made every single pixel visible. The reason for that is, if I disconnect
this node, a computer graphic is always a rectangle. This red outline you see is the actual graphic. The reason why we
can see a drone shape is because all of these pixels are invisible. But via the shader and the alpha
property, we can make all of these pixels visible. As a consequence, we would get a rectangle,
which obviously is not what we want to do. That being said, if you have disconnected and set it to a value of
0.5, then you have 50% transparency. But once again, not something that I
want to do. We have to learn about just a few more nodes.
One of the most important ones is called color, and you want to look for vector four color.
This is different from color constant, the one that we have just used, because this is giving us the current color of
every single pixel inside of this graphic. If I create it,
then once again, we are getting a node with a golden output, meaning this is a vector four.
But this isn't a static color like we have done here. Instead, this is the color that is inside of the original
graphic. Meaning, if I connect it to color, then we get the original drone.
If I expand it and only use the red pixels, then we get a grayscale image where the red pixels are brighter. This
would work with all of them, which can give you some fairly interesting effects.
You can also get only the alpha values, like so. And that way, we would get a white drone. So,
fundamentally, what is happening now? Let me draw this actually. The way you want to think about shaders, and this
can be a bit confusing, is Godot internally, via the GPU, separates the graphic into individual chunks,
something like this. For each of those chunks, let's say this one to get started,
Godot's getting a whole bunch of information. Most importantly, at the moment, we are looking at the color.
Meaning for this chunk, we're getting the red color, the green color, the blue color, and the alpha value.
And currently, we are taking the alpha value, which for this pixel is a one, and set this to the value of the color.
Since for this color, it wants to have a vector three, which needs to have a red value, a green
value, and a blue value. But from alpha, we only get one value, which at the moment, once again, is a
one. To make this conversion happen, Godot is setting the value of all three of these
to one. So, one, one, and one for R, G, and B. And as a consequence, we are getting one as the actual color value.
If you want to learn more about it, I have made a couple of shader videos on YouTube. Check those out for a lot more
detail. But once we have this kind of setup, what we can also do
is get our float constant back. And then, if you want to modify the alpha value of this graphic,
all we have to do is multiply this alpha value with the float constant, which we can do via a multiply node.
And notice here, we got multiply, and then the data type to the left of it. This is for float. We also have
integers, and if you scroll down, we have vector two, three, and four as well. At the moment, we only want to
work with the floating point values. And fundamentally, I want to multiply the float constant with the alpha of the
original pixel, and set this as the alpha value. Also, I don't want to use the color
value for now, and right now, we can't see anything because this float constant is zero.
If I set it to one, we're getting the original. If I set it to a 0.5,
then we are making the drone semi-transparent. And also, at this point, we can get the color constant
back, and now this is working like you would expect it to work. I can set this thing back to one or to
0.1, and this works pretty well. And once again, just to understand the
system, via this input node, we're getting the original color, so red, green, and blue,
along with the alpha value. And this alpha value, for every single pixel, we are multiplying with some constant via
this node, which we are then setting as the new alpha. Pixels that are invisible, like all of these pixels
around it, stay invisible because they, from the original input node, have a value of zero.
And anything multiplied with zero is going to be zero. So, I hope that system makes sense. Once again, getting used to
shaders does take a while. Don't rush yourself with this one. In my case, to make the drone flash, I
actually don't want to do any of this. Instead, I want to add a color
parameter. And this color parameter basically gives us a color. We can set a default value,
and then, like we have done before, set some kind of value. The default is white, and this is what I want to use,
but you could change it to whatever you like. Once again, this color, we can use for
the color, and then we get an output. So, so far, this is identical to the color constant.
The difference is that this color parameter can be changed inside of the inspector.
So, let me connect it to the output, and then, if it is connected to the output, in the inspector, under shader
parameters, you can see a color parameter, and you can also change it. That way,
we can work with the shader from inside of GDScript. And effectively, what I want to do, let
me expand this again. I want to mix this color with the original graphic, and then control which
one we see. For that, Godot has a mix node. And keep in mind here, we want to mix a
vector three for the color. Create this one, and then, Godot wants to have three values,
two for the colors, and then for the weight. The first color is the one we get from
the color parameter. The second color is the original color from the graphic. So, vector four color.
This you want to connect to the second one. The output is going to be the color,
and right now, this thing is set to 0.5 for every single one of the colors. Meaning we are mixing the original color
with the color parameter, which currently is white. To change this value, all we care about
is a float parameter. This, like the color parameter, gives you a parameter that you can
change inside of the inspector. And this I want to connect to the weight.
Once again, we are converting a float to a vector three. The way Godot is doing that is it
assigns the value of the float to all three values of the vector three, meaning R, G, and B are all getting the
same value, which is exactly what we want. Inside of the inspector, we have a color
parameter and a float parameter. And if I change the float parameter to one,
you can also just type a value in here, then we only get this original color. If I set it to zero,
then we only get this color parameter. If I set it to 0.5, then we are mixing these two together.
And that is pretty much all we need. Although, one thing I do want to do to have easier names, this should just be
the color and for the float parameter, let's call this one progress. Once we have that, we have to figure out
how to target all of this from inside of GDScript. For that,
inside of the drone, specifically the hit function, besides reducing the health,
we want to do animated sprite 2D, I want to get the material
and on this material, we have a shader parameter.
Although, in this particular case, I want to set the shader parameter. You want to have the name of the node first
of all, which in our case is either going to be progress or color. For now, I only want to work with
progress. So, pro gress
and then the second argument is going to be the value we want to set to it. Let's have a quick look, once again,
inside of the drone. If we set this to zero, we get the white color. If we set it to one, we get the
drone. As a consequence, the default should be 1.0, so we can see the drone. But once
the drone gets hit, this should be set to zero. And once the drone gets hit, we want to
get a value of 0.0. Let's try this one. By default, the drone should have the
normal color. That looks good. And once I hit it, the drone becomes white. And that is
working really well. That's a good start, but this should be a bit more animated, for which I want to
use a tween, meaning var tween, create tween. Like we have done before, I want to
tween a property where we want to get the animated sprite 2D {dot} material.
The property we want to target is called shader {underscore} parameter {slash}
progress. Or this progress is whatever value you set inside of this field. After that, we
will need to target value 0.0. And finally, a duration. Let's say 0.3 seconds.
Once that's done, I want to duplicate the line and then set the value back to 1.0 over duration of, let's say, half a
second. And then we don't need this last line. Let me get rid of it.
I can run the script again. And now, if I hit the drone,
it flashes and after three hits, it disappears. And perfect. With that, we have shaders
along with tweens. We are nearly done with the game. I just want to add some very minor parts. First
of all, I want to play sounds. There are only two sounds for the explosion and the laser.
This part is actually going to be your exercise right away. Add the sounds for the laser and the explosion. Pause the
video now and see how far you get. For the laser, we want to work inside of the bullet and basically play the sound
whenever this thing was spawned, which means I want to add an audio stream player to the
We don't have to change the name, but I do want to add laser.wav. It's going to sound like this.
This we want to play whenever this bullet is spawned. When the scene is ready, I want to get
the audio stream player 2D and play it. Let's try. That is working pretty well. Besides
that, for the drone, when the drone explodes, inside of the explode function,
I want to play another sound. I want to add another audio stream player 2D,
which gets the explosion.wav sound. Which we then want to play once the drone explodes.
Right at the beginning, I want to get audio stream player 2D {dot} play. Let's try this one.
Now, this kind of worked with a couple of issues. Let me play it again, actually.
And pay close attention to it. If I shoot the drone three times, we get the explosion sound twice. If I
just play the sound on its own, you can hear it, we only have it once. And if I, inside of explode, print
explode and try the whole thing again, you can see the issue.
Once again, I want to shoot it three times. We can see explode twice.
That is because once the drone's health goes below zero, we are calling this function and then we
are playing the explosion animation, this one here. For which,
we are playing the animation and then we are calling chain reaction, where we are checking if a drone is
nearby and then we are calling explode again. As a consequence, this function can be
played twice. This we obviously want to avoid, which we can do in a couple of different
ways. I think the easiest one would be var is {underscore} exploding, which is going to get a boolean value
that by default will be false. When we are getting to health below zero, I want to set is exploding to
true and call explode. That way, we are still doing all of this.
After that, though, inside of chain reaction, I only want to call drone explode if not
is exploding. That way, we are doing all of this only once. And just to test things,
let me print explode once again. Although, now we should only hear the
sound once. And that feels much better. Besides that,
I guess let me run the game again. The bullets at the moment are really, really slow. The player can even catch
up to them. That's an easy thing to fix. Inside of bullet, the speed should be something
like 200. Let's try that. And now, this feels much more realistic.
Besides that, inside of the level, this bin is really, really annoying. For that, we want to work on the FG
layer and simply get rid of it. Then, instead of BG detail layer, I want to find the bin again. It was
here and draw it in the same position. Like so.
That way, the thing is actually in the background. The last thing that you want to do for
your game is to add a lot more drones throughout the level. So, this would just be
distributing the drones all throughout the level while making sure that you have a fun
game. I guess that's the thing that you can do on your own. We have the second proper game. And at
this point, you have a pretty good idea about the basics of Godot. So, just practice and see what games you
can make. It is time to get started with 3D game development.
For that, we are going to create a basic spaceship shooter. Not the most complex game, but enough to
cover 3D game development. Fortunately, in Godot, 3D mostly works like 2D
in the sense that you are still placing nodes and scenes, use GDScript and signals, you're using layers, you're
getting input, things like that. These concepts do not change. On top of that, a lot of 2D nodes have a
3D equivalent. A sprite 2D becomes a sprite 3D. A static body 2D becomes a static body 3D.
the camera 2D becomes a camera 3D, and so on. This happens to a lot of nodes and makes
it quite accessible. On top of that, there are loads of nodes that remain exactly the same.
Timers, for example, don't care if you work in 2D or 3D. Same for the animation player, it just
plays an animation. It doesn't care about the dimensions. That being said, there are going to be
some changes. Most importantly, we are now working with a third dimension.
We have X, Y, and Z coordinates for basically everything. Besides that, lights and cameras are
becoming much more important. In fact, without using them, you pretty much don't get anything useful.
Finally, we're getting visuals from objects, not drawings. Meaning we are going to start using 3D
files with graphics and animations. Or we use Godot to create basic shapes. Both would be possible. And just to
explain in a bit more detail what that means, here is the character from our first game.
For all of this, we are using pixels. This would be one pixel, this is another one, there we have another one, and
another one, and so on. Somebody spent a ton of effort to put all of these pixels in their position
with the right color. After that, we are taking an image, and if you want to animate it, we are
cycling through different images. And that's all we really do. Fundamentally, we are just showing
different images. This is all we need for 2D, but for 3D, this obviously doesn't work anymore. For
that to work, we need a different system. That system is going to look something like this, where we have a 3D
object that we can look at from different angles. The way all of that works is that this
object is defined by a lot of different points. For example, this would be a point, this would be a point, this would
be a point, and this would be a point. If you connect these four points, you are getting a surface that you can
add the color to, which would also be visible from every single angle.
You can even change these points. So, here is the edit mode, and I can drag any of these points and move them around
and thereby change this object, although not something I want to do, but something I could very easily do. Let me
change this point, then you can see it a bit better. We are basically creating a whole bunch
of points and then connect them in a 3D space. Every single one of these points has an
X, Y, and Z coordinate. Along with that, each point is also connected to other points, and if these points are
connected, you can add the color to them and that way you're getting a surface.
That's the basic principle for pretty much any 3D object. This is still a very simple object.
These can get much more complex and they can also have animations. But that's not too important for now.
The only thing that you really have to keep in mind at this point is that we are not working with images anymore,
instead, we have 3D objects. But enough with the theory, let's work in Godot.
And in this project, I already have a couple of files. We have a few audio files, those don't
really matter. And inside of graphics, we have a lot of stuff.
In there, we have a ton of files that are usually called .glb. This is the file format that you want to
use the most for 3D game development. By that, you can store 3D shapes and animations. Super handy.
Although keep in mind, there are alternatives, but GLB is the most common one.
You can find all of these files inside of the description. Make sure you have them ready, but for
now, I just want to work in the 3D space to get started. You want to have 3D selected at the top, that way you're
getting the 3D space. If you hold the middle mouse button, you can pan around.
If you hold shift, you can go in the different directions. And if you hold control, you can zoom in
and out. You can also use the mouse wheel to zoom.
It's pretty straightforward. And to create our first 3D scene, I want to create a, well, 3D scene.
Click on it and then we are getting a node 3D. Inside of the viewport, we're getting
this gizmo that you can move in different directions to change the position.
We have the X axis, we have the Z axis, and we have the Y axis. These little panels here let you move on
two axes at the same time. If you click on the green one, you are pretty much excluding the Y
axis, meaning you only move in the X and the Z axis. Might be a bit hard to see, but if I
hold it like this and then use the green panel, you can see it quite well. We are only
going in this axis. Besides that, you have these circles around it and those let you rotate.
Although for basic node, this doesn't really do anything. If you look at the transform,
there you get all of the information and now if I rotate it, you can at least see that we have a Z
axis rotation. Since a node 3D is a single point in space, rotating it doesn't really do
anything. In fact, all of this should be zero. You can also change the scale, but once
again, I think this is straightforward. This is giving us a really basic 3D space. This I want to save right away.
For that, I want to create a new folder. Let's call it scenes. And this should be a test 3D scene.
We are not going to use this one for the game, although the naming scheme should be
consistent. This is going to be test 3D. To this scene, I want to add another node. So, press control A or plus icon,
and then we can add a new node, and we want to add a 3D node. If you click on a drop-down menu, all of
these red nodes are for the 3D space. If you look at the top, there you have collision object 3D. And
there you have an area 3D and physics body 3Ds with static body 3D, character body 3D, and rigid body 3D.
Those should look reasonably familiar. If you use the search function, you can also find a sprite 3D and an animated
sprite 3D. And finally, we have a camera 3D as well,
although that's going to come later. For now, I want to work with a sprite 3D.
Possibly the simplest 3D node to get started with, because this is basically a 2D node in 3D space. We only have a
texture like in a sprite 2D, which means we can get the Godot logo, drag it in there,
and then you want to zoom in quite a bit. At some point, you can see the Godot logo.
With the middle mouse button, you can rotate around and, well, you have the Godot logo.
If you now look at transform, all of these properties are going to make a lot more sense.
If I rotate it around the X axis or the Y axis or the Z axis, you can see pretty well what's going on.
Same with position, X goes left and right, Y goes up and down,
and Z goes backwards and forwards. Scale, I don't think I have to explain. This one works just as before. This
would be our very first actual 3D object, where we have a sprite in a 3D space.
Although this in practice is kind of useless. You want to have actual 3D objects.
Which means sprite 3D can go. We basically never use it. Instead, the 3D object that most people start with is
a simple mesh instance 3D. By that, we can auto-generate a mesh. By default, this node is not visible,
because on the right in the inspector, you have to create a mesh. And there are loads of options. Just choose whichever
you like. I'm going to start with a box mesh. If you click on that, we're getting a
box. On this box, on this box, you can change quite a few
things. For that, you want to click on the mesh, and then you get a ton of options.
You can change the size in all three dimensions, and then you get the different
subdivisions along with a material. What does that mean? Well, for that, you want to click on perspective, this menu
button, and then you get a ton of options. Under display,
by default, we're getting normal, but you can also display a wireframe. And that way,
like we have seen just a second ago with the spaceship, we can see the different edges of this model. This is how this
model is defined, where effectively, we have a point here, a point here, a point here, and these
three points are connected, with the enclosing space being visible. And this is working in 3D space, so we
can look at it from any angle. And the subdivisions tell you how many points you get. If I increase this
number, we're getting more and more points on these different axes.
Let me add quite a few more. And now, this model is defined by a lot more
points, which currently doesn't really help us very much. Since this is a very simple
shape, it doesn't need that many points. So, this is not necessary for our purposes.
But later on, for example, you might want to change these points using a shader, something that we are actually
going to do. And by the way, these points are called vertices. Sometimes, you really want to have a lot of them,
but not at the moment. So, once again, click on perspective and then display normal. That way, you can
see the cube again. Finally, we have material. Via that, we can change the color of this cube. Right
now, it's empty, but I want to add a standard material 3D to it. If you do that, the color already
changes a bit. And if you click on material again, then we're getting a lot of options. The
easiest one is albedo. This defines effectively the color. If I change this to really anything
else, we are changing the color of this object. You can also give it a texture. Although
this by default is going to look a bit weird. If I drag the Godot logo in there,
then you can see we are getting the Godot logo, and let me change the default color to white.
But the model doesn't really know where to put the different parts of the image. And that should make sense. We have to
give it a bit information to place this image, a 2D image in a 3D space. By default, it
doesn't know that. Although at the moment, that really doesn't matter that much. So, let's get rid of the Godot
logo and just give this thing a color. I'm going to give it a bluish color. Let's add just a bit of green.
There we go. Bluish color, good enough for our purposes. The other options you are usually going
to use besides albedo, metallic, and roughness. By that, you determine how light is
reflected. If I change this number, you can see that this thing has changed a bit.
Although quite hard to see, same as specular, we are changing the color just a bit,
but not very much. That is because this property only really makes sense once you have a light, which we can add very
easily. What I want is a light 3D, and then we have three options.
Directional light, omni light, and spotlight. I want to add a spotlight. Then we can see a light cone that I want
to move this way. And via the red dots, you can change this thing. If you hold shift, you can
change it more gradually. Let's do something like this, and then move it a bit closer.
Under spot, you can change all of these attributes as well. And under light, you can change the energy.
See how this looks. And now, if you zoom in a bit, they can see the light along with a bit
of shading. I think if we go from an angle, this is going to be a bit more visible.
Something like this. There we go. This is much better. Now we have a cube with some light
shining at it. And if I go back to the cube, under metallic, if I change this value, you
can basically see how metallic this object is, or in other words, how much light it reflects.
Same with specular. Although I guess still a bit hard to see. For the proper effect, you want to
have a proper scene with more reflections. But I think you get the idea. Metallic
only really makes sense once you have light. Same with roughness.
If I change this value, you can kind of see how much light is reflected, especially if you look at the
preview. They can see it a bit better, I think. So, if I have a low roughness,
we're getting very sharp lights. If I have a high roughness, the light becomes much more faded out. At least to get
started, you mostly want to work with albedo, metallic, and roughness for a decent
look. But once you get further into it, all of these other options give you a ton more
stuff to work with, which can be quite nice, but also a bit daunting. So, go for it step by step.
I guess in my case, I'm not going to worry about it and simply delete the spotlight and only work with this mesh
instance. Because next up, I want to change it using GDScript. I'm going to add a script to the test 3D node. All
the defaults here are fine. And click on create. Just as before, we can use the basic inbuilt functions.
Like for example, ready. Let me add just a bit more space. And then get the mesh instance 3D
and call one of the methods or work with one of the properties. For example, what we can do is rotate it along the Let's
do the X axis. Then we need an angle. Let's go with 20, something random. Once we have that, I
want to run the current scene. And we should see a rotated cube, but we don't. We don't actually see anything.
Why is that? Well, for 3D game development, you will need two important things to see anything, or
at least anything useful. You will need a camera and lights. The first thing, a camera, is quite
easy. All we need is to add a camera 3D. Once you're doing that, you have a camera, and you can see a preview on the
right in the inspector. Let's say I want to move the camera a bit down the Z axis,
and then in the preview, we're getting something. So now, if I run the scene, I should be
seeing this cube rotated because of GDScript. But in practice,
we are getting something, but not the right result. We do have the cube, and the cube is rotated around the X axis.
And I think rotating around the Y axis makes this a bit more straightforward. I guess you get the idea.
The issue now though is that this doesn't look like anything in the preview. Let me run it again.
This is what we are supposed to see in the preview. But this is what we actually get. Why is
there such a difference? In the viewport at the moment, we're getting a preview for the sky and the lights from the
editor. But in the game, this doesn't apply. This we have to add manually. And for that, you have those two
symbols, where you can toggle the preview sunlight,
and you can toggle the environment. If you click on these three dots next to them, you get a bit more information.
Via that, you can change how the world looks. For example, for the sun color, we can make it, I don't know, something
random. If you're doing that, the cube is going to look different.
Just pay attention to it when I change it again. If I change the sun color to really anything else,
the cube is changing its color as well. This can be a bit stronger or a bit weaker, depending on what you need. Just
imagine holding a red light next to any kind of object. That object, even if it isn't red, is
going to look more red because of the red light. And this we're doing in 3D space as well.
By default, all of these lights are very, very white because this is the most neutral option,
which is totally fine to get started with. Besides that, we have sun energy,
which is also usually one or at least very, very low. Then you can set the shadows, and that
is really all you need for the sun. Besides that, we have the environment, and in there, you can set the sky color
and the ground color, which I think are fairly self-explanatory. We can change the color of the world.
Most of the time, this is blue because, well, the sky is blue. I am going to stick with the defaults.
Once you have what you like, the important thing is that you want to add the sun to the scene and the environment
to the scene. So, click on both of them, and then you get two more nodes, a world environment and a directional light 3D.
Once those two are actually added to the scene tree, you can run the current scene again,
and you see a proper result, the thing that we can see in the preview for the camera. Via that, we could also tell
that GDScript still works just fine because we do get a rotation. On top of that,
you can use things like func_ process to rotate it at a constant rate.
Although this should be a very low number. Let's say 0.1 * delta.
Although I guess we can rotate around another axis. Let's say Z. If I now run the scene again,
we are getting a very slow rotation around the Z axis. Just as before,
if you want to change any of the properties of the node, you want to look at the inspector and
then go through it step by step. Most of the time, you want to work inside of transform and change something
in there. But you could also work inside of the mesh and the material to change, for example, the albedo color.
If you have a hover it, Godot is pretty much telling you what you need to know. All of this we should practice.
For that, I want you to create a pink donut that moves to the right. On top of that, change the color via GDScript.
Pause the video now and see how far you get. I want to hide the current mesh instance
and then add another one. From recent, I can get mesh instant. Double click on it,
and I want to rename this to exercise donut. The name doesn't really matter. Choose
whatever you like. What actually matters, inside of the inspector, for the mesh, you want to have a torus mesh.
That's effectively a donut. This I want to rotate by 90 degrees. Which you want to rotate
it and hold control. That way, you're rotating it in increments. Makes things much more easy to work
with. Let's run the current scene to see how this looks, and that definitely is a
donut. Perfect. This donut should get a material so we can get the pink color. Material,
standard material 3D. Click on it again and then in albedo we can give it a color.
Something pinkish. Like so. That looks all right and with that we
have a pink donut. To move it you want to use GDScript. Get the donut and then update the
position. Position and this is a vector three. We have dot x, dot y and dot z.
At the moment we want to go down the x axis. So this way.
Meaning we want to get position.x and then increase it by some amount. Let's go five times delta
and run the scene to see how it looks. That works reasonably well, possibly a bit fast. Let's go with two times delta.
Then we can see things just a bit better. I suppose what we can also do is change
the rotation. I want to rotate around the y axis and that way
we can see some spinning behavior. Once again, just get the property or the method that you want to get the desired
result. Although one thing that can be a bit confusing is
we have a mesh instance, then we have a mesh and then we have a material. How can we change that? Well, for that
I want to get the exercise donut once again. In there we can find a mesh.
Inside of this mesh we have a material. Which by the way you can also print to see what you have available.
Let me print it, run the currency again. And then we're getting a standard material 3D.
The thing that we have assigned earlier. If you have a over it you can see we have a standard material 3D.
Also all of this should be inside of the ready function. We only want to do this once.
Inside of this material we have a standard material. As a consequence inside of this standard material we have
the albedo property. Albedo_color. If you have over the value you can see
what Godot wants. The data type here is called color. So just type color and in there you can
create a color from a whole list of options. You could add simple integers in here. For example one, zero, zero and
one. Gives you the full amount of red, no amount of green or blue and the full
amount of alpha. As a consequence the donut should be red and it is. Perfect. You can also simply get color. Dot and
then you get some preview colors. Let's go with something a bit more interesting. I guess we can use
deep sky blue. If I now run the entire thing again we're getting a bluish color.
And well, with that we can change the material color of the donut. And by the way, inside of the mesh
you could also change the inner and the outer radius. That way you would change the size of
the donut. As always just hover over the property that you want to change. Godot lets you
change pretty much everything. And all right, with that we have the basics of 3D in Godot.
Now we can start working on the actual game. With the basics covered we can work on the actual game. For now I want
to create a super basic floor and the actual player. The player can also move around from
user input so we are already getting something. For the player we are going to use a character body 3D.
Which works just like the 2D one. To move it you want to change the velocity and then call move and slide.
The only real difference is that velocity now is a vector three. Meaning we need x, y and z.
But other than that you don't really change that much. But before we get to that we have to
cover a few smaller things. So let's work in Godot right away. Here we have the scene from the last part which we
don't need anymore. You can just close it. You could also delete it. I guess I am going to leave
it in just so you guys have it as well. Now, before we get to the player I want to create a general game scene which is
going to be a 3D scene. That I want to rename to game and then save it inside of the scenes folder.
Besides that I want to create another scene where the root node has to be a character body 3D.
This is going to be our player. Save this one as well, same folder. That's totally fine. And then Godot is
giving us a warning that this node has no shape. To change that we have to give it like in 2D a collision shape 3D
and assign this one a shape. The only difference now is that these are 3D shapes.
If I add a capsule shape we're getting well, a capsule. Good start but we don't really see anything. And for that you
want to look at graphics. If you open this one you can see we have a ton of files to work with.
To actually use them you can simply grab one. Let me get the first one, barrel.glb and drag them into the scene.
Then if you find the right spot you can see we have a barrel. That's really all you have to do. Although
if you look at the inspector something interesting happened. We have a node 2D and this node 2D is a scene. Meaning you
can open a scene and then Godot is asking you if you want to create a new inherited
scene or open anyway. I just want to open anyway for now. That way we can see what's actually
happening. When Godot is importing this object it creates a node 2D, another node 2D and
then we get an actual mesh with the object. This is what is actually visible. On the right side in the
inspector you could make a ton of changes to it but these changes are going to be lost because this object is
going to be imported. So when you make changes it doesn't really overwrite that. It's simply going to be discarded.
Which we don't want to do. Let's close the scene. Instead what you usually want to do if
you want to keep things simple you can either leave this thing as it is or you could right click on it and then
click on make local. That way you can edit all of this. In our case we only care about this barrel
mesh. If you now make changes to it these are going to be permanent. That being said I don't want a barrel
for the main character. That would be slightly weird. Instead I want to find craft speeder A, B, C or D or racer.
Really whichever one you prefer for the look of the main character. If you drag in craft speeder A
you get the default spaceship. Besides that we also have B C and B along with the racer.
Which you could all use. It doesn't really matter which one you choose. It's just for the look.
All the other code is going to work the same. So choose which you like the most. I'm going to stick with craft speeder
A2. This I want to make local and then attach the mesh to the parent
node. After that we can get rid of these two nodes. They're not necessary. And then
inside of craft speeder A you want to go to transform and reset any kind of position. That way
we are perfectly in the center of the scene and we have our player character. Although at the moment the collision
shape doesn't line up. For that you want to rotate this thing by 90 degrees. If you hold control
and look at the bottom left you want to rotate it 90 or negative 90 degrees. Both would be fine.
Also the craft speeder A should be moved down just a bit so we are in the center of
it. And also the collision shape should be a bit smaller. You can either click on the
small red dots or work inside of the inspector and reduce the radius to 0.2 maybe, maybe a bit larger. I'm going to
stick with 0.2 and 2.2 or rather 0.25. It's going to feel a bit better.
And that looks pretty decent at least for a very basic shape. Although keep in mind for these
collisions the wings are going to be excluded. Which is what I want but might not be what you have in mind.
We're going to find better ways of dealing with this later on. For now this is not too much of an issue.
Once we have that inside of the game like as before I want to instantiate a child scene.
If you just look for player we're getting the scene right away. Player and there inside of the game
scene we have the player. On top of that for a very basic floor I want to add a mesh instance.
And this mesh instance is going to get a new plane mesh that I want to move down just a bit.
That way we're getting a floor. If you click on the mesh once again you can set a size.
I want to go with a hundred by a hundred. And that way we're getting a much larger
area. Besides that for the material, I want to have a standard material where we can
set an albedo color. The actual color isn't so important at the moment. Let's go with a brownish
color. It looks a bit spacey. I guess this one's all right.
This game scene is what I actually want to run. So, click on the run project and then select the current scene.
After you're doing that, you can see that you can't see anything. That is because we don't have a camera and we
don't have lights. Adding all of that is going to be your exercise.
Most importantly, add lights and a camera so that the game is actually visible.
Besides that, make the player move. The direction of the movement should come from data from the input map.
Try to figure this one out and see how far you get. First of all, we will need a camera 3D,
which you could either add to the main game scene or to the player. For this game, I actually want the camera 3D to
be static. So, I'm going to add it to the game scene itself. Under recent, we have a camera 3D
that I want to move vaguely here for now and look down on the player just a bit. Something like this just for testing
purposes. Then let's run the game and we can at the very least see something.
To get the actual result from the preview, you want to add the sun to the scene and
you want to add the environment to the scene. That way, I can run all of this again
and we can see something that looks much much better. Perfect. That covers the first part. Next up,
this player should be moving from input. That means inside of project project settings, I want to create an input map
or rather a couple of actions inside of the input map. We have left, right. You could also add
up and down, although those aren't actually used in the game, but we can use them for now.
For left, I want to use A. For right, I want to use D. Up is going to be W and down is going to
be S. Choose whatever you want in here. Whatever works for you is fine.
Just make sure that you have these four actions available. After that, give the player a script.
The defaults are fine. Create. And then to move a physical object, you want to use physics process, like we have seen
before. The most important thing in here to move anything is to update the velocity,
which now is going to be a vector three. So, once again, we have X, Y, and Z. For now, I want to set velocity X to one
and then call move and slide. If I now run the game, the ship is going to move to the right.
So, we are getting very basic movement. That looks really good. So, now we have basic movement, but obviously, we want
to use the input map to make this much more interactive. For that, I want to get var direction,
which needs to be a vector two with a default of zero and zero, which is what we are getting.
And keep in mind now, our direction is a vector two, but the entire movement is a vector
three. Other words, we have to convert the 2D input into a 3D movement. We are primarily going to do that by
ignoring the Y axis, meaning the ship can only go left, right, forward, and backward. It cannot go up or down. I
also want to have var speed, which is going to be an integer of four. I don't want to set velocity.x
to one. Instead, I want to get the direction and then input.get_vector
with left for the negative X action, right for the positive one. Negative Y is going to be up
and positive Y is going to be down, like we have seen in the 2D space. Next up, we can actually update the velocity
and this is going to be direction times speed. Delta's going to be applied
automatically, so we can just ignore it. Although now, Godot is going to give us an error that the value of type vector
two cannot be assigned to a variable of type vector three. And that should make sense.
This direction is a vector two, meaning we have X and Y values. But the velocity of this object is
three-dimensional. It needs to have X, Y, and Z values. The easiest way to fix that is to target
the values individually. So, velocity.x is going to be direction.x * speed. Then duplicate this
and velocity.z is going to be direction.y. Velocity.x is going to be direction.x *
speed. Should make sense. Direction.y is going to be us pressing up or down on the keyboard.
And this we want to translate into forward and backwards movement in the game, which is on the Z axis.
Remember, in the game, if you want to go up or down, you move along the Y axis, which we
don't want to do yet. Converting 2D input into 3D movement sometimes can be a bit confusing, so I
hope this makes sense. But anyway, let's run the game. I can move left and right and if I press
forward and backward, we are getting all the right inputs. So, that is working pretty well. What we
can also do is work just a tiny bit with physics inside of the game.
I want to add a static body 3D where we can add some kind of more
interesting object. Let's go with rover. Attach it as a child and then we are
getting a rover that's admittedly a bit small. That we can fix by making it local,
getting the mesh, and attaching this straight to the static body 3D.
And also transform should be zero. Most importantly after that, the scale should be, let's go with four.
That way, this thing is quite a bit larger. Although I do want to move it forward
just a bit so it's not on the player right away. Let's move it to the surface like so and
after that, we can add the actual collision shape to it.
And by the way, like in 2D, we have a collision polygon and a collision shape. Although collision polygon 3D is very
rarely used. You'll see in a second why. For now, I want to add a collision shape 3D, which is going to be a very basic
box shape that at the moment is a bit too small, but this we can scale up to something
like so and so. And like in 2D, if you hold shift, you get more granular values and if you hold
alt, then you're mirroring it along the axis. And that looks pretty all right.
If I now run the game again, I can move forward and once we get a collision, I cannot go any further.
And that is working pretty well. And I hope this makes sense. All of this is pretty much identical compared to 2D.
That being said, what if this rover was much much larger? I want to set the scale to 20.
That way, this rover is significantly bigger and now I want to have a bit more control over the collision shapes. So,
for example, these wheels can't really be described by a simple box anymore. Although let's not do the rotation. That
way, all of this gets a bit simpler. What we could be doing right now is to create a box shape for all of this like
so and that would kind of work, but in 3D, very quickly, these shapes become too
complex for that. So, this isn't something you actually want to do.
As a consequence, I'm going to get rid of the collision shape 3D. Instead, you could add a collision
polygon 3D like so. And then you can add the polygon using
the points at the top. Although this is very very difficult to use and I wouldn't recommend it. Let's actually
not do it. A much better system would be, with the mesh selected,
click on the mesh button at the top and then you can create a collision
shape. Then Godot is giving you a couple of options.
Do you want to have a collision shape placement as a sibling or as a static body child?
I want to have sibling and then you can select the actual shape. These give you different kinds of
collision shapes with tri mesh being the simplest one. If you now click on create,
Godot is going to create a shape that's going to be a bit hard to see, but if you look closely,
Godot has created a shape that exactly matches the shape of the mesh that was built on.
Down there, you can see the wheels. Up here, you can see the actual body. A bit up there, you have the antenna.
And that way, you have a perfect shape for this object. If I run the scene,
you can Well, this is slightly hard to see, but you can definitely see the
collisions are working. And that's pretty much all you have to worry about in terms of physics. Like in
2D, you have the character body or a static body,
then you have something visual, and you have the collision shape. And in 3D, most of the time from the mesh, you want
to create a collision shape. That's the easiest and most reliable way. Now, before we finish,
I don't want to have this static body. That was just for demonstration purposes. Let's get rid of it.
And the player shouldn't actually be able to move forward and backward. You see later how the system is going to
work, but the player in the actual game can only move left and right. Which in our game is very easily implemented. We
simply want to get rid of velocity.z. If I now run the game again, we can still go left and right, but we can't go
forwards or backwards anymore. Which works pretty well. To keep things a bit more organized for
all of that, direction doesn't need to be a vector two. It can be a float. And then, we don't need get vector.
Instead, I want to have get axis with only left and right. That way, we can use direction.speed
right away for velocity.x. The end result should be the same. And it is. Perfect.
Besides that, to make things just a bit more interesting, I want to rotate the model a bit when
we're going left and right. In other words, I want to change rotation.z. If I change this value, for example, to
a two, then you can see the ship is going to be rotated quite a bit.
We are working on radians, so a two is actually quite a bit. Now, in my case, I want to use move
toward. That way, we're moving towards a value. We are starting from a value and then
going to a value over a certain duration, which is called delta.
Our starting point is going to be the current Z rotation of the model. So, rotation.z.
The target is going to be the direction, which is simply going to be a value from zero to one.
Finally, we need a speed, which can just be delta. And for that, there shouldn't be an underscore before delta.
If I now run this thing again, you can see that the ship is rotating along the Z axis, although in the wrong
way. To fix that, all we need is negative direction.
Then we can try the entire thing again. And now, the ship is rotating in the right way,
although a bit too strong. All we have to do for that is divide this by 2.0.
Make sure you are using a float here, so you don't accidentally get an integer. Next attempt.
And now, the ship is rotating when we're going left and right in the right way. That is working really nicely.
Finally, I want to make the ship hover along the Y axis. Or in other words, it should go very slowly up and down.
Something like this. Makes the entire thing look much more realistic.
All we need for that, before move and slide, is update velocity.y. If I set this to 0.1, we will go up
very, very slowly. And this works. Although, we have to come down again as
well. The best way to approach all of that is to use the sign function. The sign curve
looks like this. We're going up and down with the highest value being a one and the lowest value being a negative one.
From this curve, we can sample. We could, for example, get this point or this point or this point or this point
or this point and so on. We just have to figure out where we are on the X axis.
Which in our case can simply be the time since the start of the project. That way, if time goes forward, we start
somewhere here. We are going all the way to one. And then, as time passes forward, we are
going to negative one, then we go back up to one, and so on. By that, simply by using the time, we can alternate between
one and negative one. To get the time, all you need is time.
get ticks in milliseconds. If I try all of that, we should be
seeing something, although it does look a bit jittery. Good start, though. To change the
strength of this effect, you can either multiply the time that you pass in as an argument, or you can change the entire
thing by multiplying it with some kind of value. For example, if you divide the entire thing by 500.0,
make sure you always use a float in here, then you are going to move a lot, lot
slower. And if you divide the entire thing by, let's say, 4.0,
then we aren't going to move as much anymore. And that looks pretty all right.
Although, at the moment, if you look at it, it looks very, very repetitive. If you want to make this look a bit more
organic, you can simply add different sign curves together. For example,
I can copy all of this, add it to the first one, and then simply make these values a
little bit different. Let's say, divided by 600 and divided by 10. That way, we are combining different
sign curves, which makes the whole thing look much less predictable.
As a result, we are getting something that looks a bit more organic. Quite hard to see, but in practice, this
is going to make a difference. But anyway, I think this part has gotten quite long, so let's finish it here.
To get an actual game, we are going to need lasers and meteors. That's going to be the next major step.
And well, at this point, we can basically do all of this already. So, let's work right away in Godot. Here we
are once again. I want to create a new scene where the root node is going to be an
area 3D that I can rename to laser. Save all of that inside of the scenes folder. Save.
Then we will need the actual laser graphic. This you can find inside of the files
under red.glb. If you drag this in there, you can see the laser.
And just as before, click on make local. That way, you're getting the mesh itself, which feels a bit more
organized. Make sure that under transform, all of this is set to zero. Although, that
being said, if the rotation is set to zero, this thing is pointing upwards, which is
really not ideal. In our case, the ship is facing down the negative Z axis, and the laser should
look in the same direction. Meaning, we want to rotate it 90° in that way, so it's facing down this way,
away from the blue arrow. If you are looking in a different direction, go with that.
Finally, for laser, we are going to need a collision shape.
This can be done in a couple of ways. You can either click on mesh, create a new collision mesh, sibling trimesh, and
that way, we are getting a very nice mesh. That I think would be totally
serviceable. Alternatively, you could get rid of it, and then create a very basic collision
shape 3D with, I guess, a sphere shape that you can make just a bit larger,
and then move it to the front of the projectile, roughly here, and then you would get a
similar result. There are different ways of doing collision shapes. I think I am going to
go with the first version, mesh, create new collision shape, and then sibling trimesh.
That one is definitely the most precise one. But in many cases, you don't need that
much precision, especially for a simple game like this. Next up, inside of the game, to keep
things a bit organized, I want to add a node 3D for lasers. All of the lasers that we are going to
create will be children of this node. And lasers are going to be created when the player presses a button. Which
means, inside of project, project settings, the input map, we will need a laser button. Could also
be called shoot, it doesn't really matter. In my case, I want to look for the space button.
There we go, space physical. That's all we need. After that, we have to work inside of
the player character. Inside of physics process, we can simply check if input is action just pressed
or laser. Then we want to do something. Really important at this point,
we want to have the lasers inside of the game scene. But currently, we're checking for input inside of the player
scene. Meaning we have to connect these two scenes.
I think the easiest way to do all of that is via a signal. Shoot {underscore} laser with one
argument, the position. Which always needs to be a vector three. If the player presses the laser button, I want
to shoot laser and emit with the global position of the spaceship. That way we make sure
that inside of the game, we always get the right position for this spaceship. Next up, we can work inside of the game
scene. First of all, we will have to attach a script to it. As always, the defaults here are totally
fine. And most importantly, I will need a var laser {underscore}
scene. That always needs to be a packed scene by preloading
laser.tscn. Besides that, we want to get the player, go to node, and shoot laser.
And connect it to the game scene. On player shoot laser, sounds fine. Connect.
Now we can check when the player shoots a laser inside of the game scene. Just for testing purposes, let's print
the position that we are getting and run the entire thing. I can press space and we're getting some
kind of position. Now we don't just want to print a position.
Instead, I want to create one instance of the laser scene. I'm just going to call it laser.
And this we create via laser scene.instantiate. As an added bonus, I want to instantiate
it as an area 3D. That way we're getting the auto-correct stuff, which can be handy.
Just make sure that your laser is an area 3D as well. If you don't have that, you're going to
get some weird results. Once we have the laser scene, we have to give it a position and add it to the
scene tree. To add it to the scene tree, I want to get the laser's Node 3D and add a child,
which is going to be the laser that we just created. This should already do something. Let me
run the entire thing. And now if I press space, we are getting the laser. Although if I press it multiple times,
nothing is going to happen on the surface, but if you look under remote, game, and lasers,
we're getting a whole bunch of lasers. They just happen to be in the same position, so you can only really see
one. Things are working though. The final thing that we have to do is
update the position of the laser by changing the position to the pos that we're getting from the parameter.
Once we have that, I can press space. We're getting a laser. We're getting another laser. And
that looks pretty good. With that, we can create lasers. That's working pretty well. Next up, the
laser has to move. That, we want to give the laser a script.
As always, the defaults here are totally fine. And then for the laser, I want to have
func {underscore} physics process. All we need for this one is to move it
down one of the axes. We are still pointing the negative Z direction. Which means we want to update
position.z and then subtract values. For now, let's go with 15 * delta.
Run the scene. And now the laser should be moving in the right direction, and it is. That
looks pretty good. To keep things more organized, we do want to have var speed, which is going
to be 15, and it should always be an integer. That way we're getting this speed here.
And things are a bit more understandable. All of this is giving us a reasonable laser, but by default, this
just doesn't look very good because the lasers are on top of the player and they're just way too big.
To fix that, I want to scale them up when they are being created. Which means I want to have func
{underscore} ready and then set a default scale of vector 3.0.
That way the lasers are not going to be visible by default, but we can scale them up using a tween in just a second.
Although, first of all, if I run the game and create a laser, Godot is going to be
really, really unhappy. That is because we're getting invert condition that is equal to zero is true.
That happens when you try to scale a physics object to a complete zero. If you set this instead to 0.1, 0.1, and
0.1, this error is not going to happen. Let's run it again. And now we're getting a very, very, very small laser.
Good enough for our purposes. I want to create a tween via create tween
and then tween.tween property with self.
I want to target the scale. The target value is going to be vector 3.1.
And the duration can be 0.4 seconds. The default scaling of the laser is going to be 0.1, but afterwards, via
tween, we are scaling it up to a size of one over duration of 0.4 seconds. That is going to make the entire thing
feel much, much nicer. What you could also do inside of the game, when you're setting the position,
you can add just a bit of an offset by simply adding a vector three. Where we have to set X, Y, and Z.
Once again, you want to make sure you are on the right axis. We want to offset this laser by some
negative amount on the Z axis. X is going to be zero, Y is going to be zero, and Z is going to be for something
strong, -10. That's going to be way too much. But you can see we are at least going in
the right direction. If I change this to a negative one, we have a much more reasonable offset.
I guess this could be a negative two. Possibly a bit too much now. Yeah, definitely too much.
But play around. This could be -1.2. That way looks a bit better.
We are actually nearly done with the lasers. The one final thing I want to do for
them is that they should disappear at some point. Because at the moment, we can
create lasers indefinitely and they would never disappear again. If the game is running too long, this
means we have more and more objects. Not great. A very simple way of fixing that would
be to check if position.z is greater or equal to let's say for now so we can see
something, 15. If that is the case, we want to get rid of the laser.
If I now, inside of the game, shoot a laser, the laser should disappear at some point.
And it does not because the Z values are getting smaller and smaller. So instead of being greater
than, we want to check if they're smaller than -15. Next attempt.
And now the laser disappears roughly around this point. The negative numbers here do get a bit
confusing. If you want to have this more organized in your game, you might want to rotate
all of this around the Z or the X axis. I think for our purposes, this is still good enough.
Although this number needs to be much larger than -15, or well, much smaller. So let's go with -200.
So it's going to take a while for the laser to disappear and the player is not going to see it.
Perfect. That covers the lasers. Next up, we have to create the meteors. This part is going to be your exercise.
I want you to create the meteors and spawn them via a timer. Every time the timer times out, create a
new meteor and then move that meteor towards the player. You can add some randomness to it, like
a bit of a random direction or rotation. Just see what looks good. You can find the meteor graphic inside
of the resource folder and the root node should be an area 3D, like the laser. Pause the video now and see how far you
get. First of all, I want to create a new scene where the root node is an area 3D.
Let's rename this right away to meteor. As a child to it, you could look for meteor inside of the
resource folder. It is there. We have meteor and meteor detailed along with meteor half. Although, I only want to
use the meteor itself. You could add this right away or
you could attach a child scene. If you look for meteor, you can find meteor.glb,
the imported graphic. Both will work. There we have a meteor and just as
before, I want to make this thing local, attach it to the root node, get rid of the Node 3Ds,
and then reset the transforms. That way we are getting a meteor. Although, I want this to be in the perfect center.
Which I have done by changing the transform Y to -0.3. After that, we will need a collision
shape. And I guess we can do the usual thing, go to mesh, create collision shape, sibling, try mesh.
And that looks really really solid. Although keep in mind here, the collision is basically a sphere.
You could get away with much fewer points, which would be good for performance.
Not an issue for our game because it's so simple, performance will not matter. But for a more complex game, you would
want to keep as few vertices as possible. The fewer you have, the easier your game will be to run.
Once we have all of that, save the scene as meteor.tscn and then we can work with it. To
actually use it for now, I want to attach one instance of the meteor and make sure you have meteor.tscn, not
meteor.glb. That is the wrong one. I want to add meteor.tscn. And then,
we want to move this thing just a bit forward so we can see it inside of the game.
Now we can run the whole thing. And we are getting a meteor. That looks perfect.
Next up, to animate this meteor, we want to give it a script. The defaults here are fine, just as usual.
And for the most basic effect, I want to create func physics process
where we are updating position.z. We want to move towards the player. So, I think this should be plus equal some
kind of speed. Let's go with two for now. Yes, we have to multiply with delta.
After that, in the game, we're getting the meteor moving towards the player. That is working pretty well.
For better organization, I want to create var speed with a default value of two. And I want
to have var direction_x. That way, we are going slightly left or right when the meteor is moving.
And this should be some kind of float that we will set in just a second whenever this meteor scene is ready.
I want to update direction.x and set a random value, which we get via randf
range where I want to go from -0.1 to positive 0.1.
A very small amount here is totally fine. After that, this value should actually
be used. In my case, position.x plus equal
direction.x multiplied with delta. That way, we are very very slowly going
left or right on the x-axis. Let's try. And we can't really see very much. These values might be a bit too small. Let's
go with -1 to 1. And that is looking much more random. One more attempt.
That works. While you're here, you can also do things like rotate.x
with some kind of speed. Let's go with 1.5 * delta. That way, this thing is going to rotate.
Makes it feel much more organic. You can do the same thing for rotate.z. And that way, we're getting a lot more
rotation. These are fairly small things. If you want to customize them, just play around
a bit more. But for now, they're not too important. What actually matters is that inside of
the game, we're spawning meteors when the timer times out. For that, I want to add a basic node,
rename it to timers and then attach a timer node. That should be called meteor timer.
The duration for this one is going to be 0.25. It should auto start and it should not
be one shot. We want this thing to run over and over again. I want to connect the timeout signal to
the game scene. Connect, that is totally fine. And then do pretty much the same thing
that we have seen for the laser. var meteor_scene We want to create a packed scene
and then preload the meteor.tscn scene. Once again, make sure that you're
getting scenes/meteor.tscn. You do not want to get the glb file. After that,
once the timer times out, I want to create var meteor via meteor_scene.instantiate.
[Music] After that, we have to find some kind of parent container for all of the meteors.
And I suppose what we can do is add another node 3D with the meteors.
You could actually have the same parent container for the lasers and for the meteors. Would be fine. I guess we can
actually do that. Let's get rid of the meteors and rename lasers to projectiles.
Make sure then you are adding the lasers to the projectiles like so. They shouldn't be lasers anymore.
And then we can use projectiles once again and add a child that is going to be a
meteor this time. Keeps things just a bit more organized. Then we can get rid of the meteor that
we have just added. Run the scene again. And now, we're getting a whole bunch of meteors whenever the timer times out.
Good start, although we might have to update the position of the meteors when they are being spawned, just a bit.
When a meteor is ready, I want to set position.z to some large negative value.
Let's try -30 for now. That way, should already look a lot better inside of the game. Now the
meteors start from much further away. We're going to fine-tune all of this later on, so don't worry too much about
it for now. I guess so we can see something, let's adjust to -10.
That way, we can actually tell what's going on right away. And that looks okay.
Besides the position, I also want to change the scale to some slightly more random value.
Before we're getting to the scale, I want to create var random scale which we can get via randf range
with a value that goes from 1 to 3. Then the actual scale is going to be a vector 3
with the random scale for x, y, and z. So, random scale, random scale, and random scale.
That is going to make the meteors look much more different. And now, we are getting different scales.
That is going to look quite a bit nicer. I suppose the final thing that we can change is the speed. This one should
also be a bit more random. Speed is going to be randf range. Let's say between three and five. And
for that to work, this speed doesn't need a default value of two for the integer. Instead,
I just want to have a float with a default value of zero. We're overriding all of this anyway in
the ready function, so the default value doesn't really matter. Final attempt.
And now the meteors should move at different speeds and they do. That looks fairly all right.
And you could randomize the rotation a bit, but I'm not too concerned. I think we have all of the important
parts. So now we have the meteors and we have the lasers. Lots of progress. Next up, we are going
to work on collisions between the player and the meteors and the lasers and the meteors.
While doing that, we are also going to work on a very basic flash shader effect. Let's get started right away.
Here we are in Godot and for now, I want to check the collision between the lasers and the meteors.
Both of these are area 3Ds. So, one of them can be used via the signals check for
area entered. That way, we can connect the two fairly easily. Although, first of all, we should go to
project, project settings, 3D physics, and then set some physics layers. I want
to have terrain layer lasers and meteors.
That way, all of this is much more organized. The player is going to be on collision layer two, I think. Yep.
And can see the terrain and the meteors. Nothing else needs to be visible.
The lasers are going to be on the laser layer, obviously. And they can only see the meteors.
The other way around for the meteors, they are on the meteor layer and they can only see the lasers.
Like so. That way, we don't get any accidental collisions, which is quite nice. Next up, I am going to keep the
logic for collisions organized inside of the meteors. Meaning in there, I I to have a signal for area enter.
Click on connect and then we can write some code. For now, when an area entered, I want to simply print the area
to check if this is working in the first place. Let's run the game and if I shoot a
laser at a meteor, we don't get anything. So, something went wrong. Although, we are getting a debugger
warning, soft static collisions between two concave shapes are not supported. In other words, the current shape of the
meteor and the laser don't work together. Godot simply cannot check a collision here.
Which arguably is a bit annoying, but the way to fix that is for the collision shape.
We have a concave polygon shape. Same for the meteor, where we also have a concave polygon
shape. And for some reason, these two cannot be checked for collisions.
The way around that is for the meteors, in our case, to get rid of the shape and then create a new shape for sphere,
which we can just move up a bit and then scale it down a tiny bit, so it fits
just right. Like so. If you do the same thing now, I can
shoot the laser and we get the expected result. That looks pretty good. For these kind of issues, you really
want to read the debugger. That way, you know what's going wrong. Otherwise, this is going to be super confusing.
And I realized I forgot one thing for the meteors. The meteors should see the lasers and
the player, obviously. Now, fundamentally, what we want to do once there's a collision,
we want to queue free the meteor and we want to queue free the laser, which is going to be the area
free D. If I try this now, I can shoot at the meteors and stuff
disappears. Although, it's not really satisfying. I guess it kind of works. To make this a bit more interesting, we
can add a timer. For that, I want to first of all stop the movement
once this function has run, which we can do by adding another variable, can
{underscore} move, which is going to be a boolean with a default value of true.
After that, inside of physics process, we want to all of this only if the meteor can move.
And once an area has entered, can move should be false.
And for now, let's focus on the meteor. This one should disappear, but it should only disappear after a short amount of
time. For that, we can either create a timer or we can use a wait {underscore} free
and then create a timer in GDScript. The one argument in here is going to be the duration, which I want to set to
0.4. Then we have to wait for the signal that is called timeout.
That way, we are waiting 0.4 seconds to run this line. After that,
I can wait for a meteor, shoot a laser, the meteor stop moving and after about half a second, they disappear.
That looks pretty all right. Although, the laser should also stop moving. To keep that all organized,
let's create a function inside of the laser. Which means area, which is going to be
the laser, let's call it destroy. That means inside of the laser, we will need func destroy.
No need for parameters and fundamentally, we want to do the same thing that we
have seen inside of the meteor. Basically, all of this. Let's paste it in there.
And we have to create can move. Var can {underscore} move, a boolean, default is true.
And then the movement should only happen if can move is the case. Like so.
If I now run all of this, I can shoot the laser. We get an error that we try to call the
function destroy in base previously freed on a null instance. That happened because of a silly
mistake. Just think about these four lines of code and I think you might be able to
see it already. Let's go for it step by step. Number one, we set can move to false, so none
of this happens anymore. That part is totally fine. Then we wait about half a second,
destroy the node and then call a function on another node.
There are two issues here that are pretty bad, actually. Number one, the most obvious one, is
that we are destroying this node on this line. Meaning, nothing else afterwards is
really going to be run because the node has been destroyed. And that, by the way, is the error message here.
This one should definitely come before queue free. That way, we are calling a function on an existing node, which is
what we have to do. But on top of that, this line should come before a wait.
We want to stop the laser right away. We don't want to wait half a second. That would be kind of weird.
But once we have both of those, I can run the code again, shoot at the meteor and now we can destroy meteors just
fine. And if there's a bit more space between them, this would also look better. We'll
work on that in just a bit. But now, I'm reasonably happy. Although, to make all of this look much nicer, I
want to add a shader to make the meteor and the laser flash in white once there's a collision.
For that, we have to work on shaders, specifically inside of the mesh. That's the only visible part of the scene.
And when it comes to free D stuff, there are multiple ways to add a shader, actually. If you click on the mesh,
there you have surface and there you have a material. To this material, you could add a new shader material.
That would be one way. Besides that, if you look under surface material override, you can add a surface
material override. The name explains itself. Finally, instead of geometry, you have a
material override and a material overlay. I think the difference between them makes sense. The override overrides
the current surface. The overlay doesn't get rid of it, but it lays something else on top of it.
Which is actually what we want to do. So, in material overlay, I want to create a new shader material.
Then we get the usual stuff. We want to create a new shader. We want to create a base shader. Now,
the mode has to be spatial and then we need a name. The name should be flash shader.
Because this shader should be applied to the meteor and to the laser. Also, I guess what we can do
is create another folder with shaders. Not necessarily needed, but I think it's good practice. After we have that,
create and then the meteor becomes pure white, which is
the expected behavior. So, all good so far. After that, we are getting the usual
stuff. We have a vertex shader and a fragment shader. But now, we don't need the vertex shader
whatsoever. We can get rid of it entirely. We also don't need the light shader. We
only want to work inside of fragment. Unfortunately, 2D shaders and 3D shaders are quite different. For example, in a
2D shader, you can simply set the color, which doesn't work in free D. For that, for the color, you want to target
albedo. Which wants to have a vector free with some kind of value. If you set this to
one, zero and zero, then you are going to get a red color. We have the full amount of red, no
amount of green and no amount of blue. If you want to look more into this kind of shader thing,
the best way to get started, I think, is to look at the material and then you basically get the names
that you want to target from all of these parameters, like albedo, metallic, roughness and so on.
Speaking of which, the next part that we want to target is going to be alpha, which is simply called alpha.
This value is a very simple float. If you set this to 0.0, don't forget the semicolon,
then we get the base color. But this only applies because we are adding a material overlay.
If you have material override, the entire thing would be invisible because we are hiding the actual texture.
But now, we are only hiding the overlay. So, I hope that part makes sense. Anyway, for this shader, what I really
want to do is to create a uniform float progress with a hint range of zero
to one with a default of 0.0. This value is then going to give us the
alpha value, so progress in here, and the actual color that I want is going to be 1.0 for R, G and B.
Meaning, we are getting a pure white. Once we have that, inside of the shader, we have shader parameters, progress
and with that, we can toggle a white flashy kind of thing. Which we can then use when there's a
collision, so on area entered, before we are awaiting the timer, I want to get the meteor. On the meteor,
I want to get the material overlay. Really important, don't get override. And in there, we can set shader
parameter of progress to 1.0. That should be it. If I now shoot a
laser, we are getting a white flash. Although now we are making all of the meteors
white. Not exactly ideal. To fix that, all we have to do is under resources local to scene,
and then this should be fixed. Next attempt. Now some meteors become white, but
the others keep the same color. Perfect. With that, we have the basic logic for the meteors.
The same thing I want to do for the lasers. They should also flash up from a shader.
That part is going to be your exercise. Apply the flash shader to the laser and trigger it when there's a collision,
like you have seen for the meteor. Pause the video now and see how far you get. Inside of the laser, first of all, we
have to apply the shader to this mesh, which is called red. I suppose we could rename it to something a bit more
descriptive. Let's call it mesh. Fairly minor part, the actually important bit is under geometry, we have
material override. In there, we want to create a new shader material, and for the shader, we want to quick load the
flash shader. That way, under shader parameters, we can toggle progress and change the color
super efficiently. That looks good. That's all we need in here. Next up, inside of the laser in the destroy
method, before wait, we want to do the same thing we have
done for the meteor, which means we can copy the line, and then
call it like so, except meteor should be mesh. That's pretty much it. Let's see if this
works. And that looks pretty good. The game is slowly coming together.
The meteors need to spread out quite a bit, but that we're going to work on in just a bit.
Next up, I also want to check if there's a collision between the ship and the meteors.
If that is the case, we're going to shut down the game. That way we don't have to worry about
UIs or resetting the game. That keeps things a lot more simple. Inside of the meteor,
we want to check the signals and then look for body entered. This we can connect to the script
at the bottom, and well, basically there's only one body that could be entering, which
should be the player. Let's try. Print body. And now, once there's a collision
between the player and the meteors, we should get something printed out, and we do.
That is working beautifully. We should be renaming this parameter to player, and we are expecting a character
body 3D. Makes the code just a bit clearer. That being said, we don't actually need any
of this. All we want to do inside of this function is end the game, which we can do by a get free and
quit. Next attempt. Let's wait just a second.
And there we go. The game ended. With that, we have the very very basic setup. There are a few major parts that
we have to work on. Number one, the camera needs to look at a different angle. Number two, the meteors need to
spread out quite a bit. And number three, we can add a couple of things to the ground, so we have some
obstacles. Makes everything look just a bit better. These are also going to be collidable,
so basically we have a few more projectiles. Back inside of the game.
Most importantly for now, the camera needs to look much further down, so we can't see the horizon.
For that, we want to look at transform, and first of all, move the camera up quite a bit.
I went with 6.5. That means we can see the horizon quite a bit more, so we need to rotate this
thing a lot more. For the exportation, I went with -45. And that way, this is already looking
quite a bit better. To make this look just a bit more interesting, we can rotate the camera around the Y axis
to something like this. Let's see how that actually feels inside of the game, and I think that is working reasonably
well. You can also change the Z rotation a tiny bit.
That's the blue line, so you get just a bit of a very slight rotation there. This should be a very low number,
something like 0. 5. Otherwise, it's going to feel very very
strange. Let's try. And didn't do very much, which I guess
is fine. For the most part, you want to work with the X and the Y rotation.
Also, while looking at this, we do have these corners here, but we don't have a background.
That is quite easily fixable. We just have to move this thing a bit forward, then look at the camera.
And nearly there. Move it over a bit here. And well, I guess we can make it a bit
larger. 100 and 30 in both dimensions. That should cover it. Not quite.
A bit over here. There we go. Now we have covered the entire floor.
And that is working pretty well. Obviously now, the entire floor looks incredibly boring, but that we can fix
as well. First of all though, the meteors need a better start position. Right now, they
start in this one point, which looks really really silly. At the moment, we are spawning the
meteors, and they're always spawning in the same position. That shouldn't be the case, and for that, we can work inside
of the meteor, specifically in the ready function. We are setting a Z position of -10, and that's the start point.
This number should be quite a bit lower. Let's go with -40. That way they should spawn outside of
the window. That looks about right. And on top of that, we want to give them a random X position.
Position.x is going to be randf range between -10 and 10. Let's see how that looks.
And now, we have a good bit more spread. This could even be more spread out. Let's say negative 20
to 15. [Music] And that is looking reasonably okay.
Obviously, you can fine-tune all of this quite a bit, but I am happy with that. Once the game is running for a bit, the
player is not going to notice. So, this is pretty all right. And now, if you hit individual meteors,
you get a much better effect. Before continuing, I forgot one thing. At the moment, we keep on creating new
meteors, and the meteors never disappear. That shouldn't be the case. We could fix that like we have fixed the
lasers. Or in other words, we can check if we're crossing some kind of position, and then
call queue free. Alternatively, for the meteors, once they leave the screen, we can also
destroy them. For that, we can add a visible on screen notifier 3D.
The eight. This thing is going to have a shape. Doesn't need to be too precise, but we
do want to cover the meteor reasonably well, something like this.
And on this node, we can get screen entered and screen exited.
Once the meteor leaves the screen, we can, let's for now simply print that this is working.
I want to print meteor exits. Getting a result from this one is going
to take a second. I am probably going to speed up the video just a bit. Righty. This meteor should trigger the
first signal, and there we go. Now we start to get lots of meteor exits. This is working.
Which means all we have to do in here is call queue free, and that way the meteor is going to disappear
as soon as it leaves the screen. Different ways of doing that, both would be fine. We could have done the same to
the lasers. Would be a bit cleaner, I guess, but for this kind of game, it just really doesn't matter.
And all right, that is already cleaning up the game a lot. Next up, the game at the moment looks
incredibly empty. That we have to work on. And for that, I want to create obstacles.
For this scene, the root node is going to be an area 3D. Let's rename it to obstacle
right away. Save the entire thing. And then we have to give it a collision
shape 3D, so Godot stops complaining. For now, I want to work with a box shape.
We're going to refine this in just a second. First of all though, the actually important bit is going to be
that we want to pick a whole bunch of graphics and show one of them at random. So, for example, we want to have a
couple of rocks, a rover, a satellite dish, and a few more objects inside of this scene, and then once the scene is
ready, only show one of them. For that, I want to add a node 3D
with the actual meshes. The shapes that I went with, and the easiest way to get them is by using the
filter. I want to have a rock. Let's simply drag it in there and make sure it's
reasonably centered. This one looks all right, although it should be part of meshes, like so.
Besides that, we have a rock crystals. Drag this one in there, and then we're getting basically the same skin except
with crystals. Which should be in the origin as well, vaguely here. This doesn't have to be
too precise. Although, make sure that you only move it on the X and the Z axis. It shouldn't go up or down.
Besides that, we have the rover. This one should be part of the mesh as well, like so.
Move it to the origin point, vaguely. Next up, I want to have a satellite dish, and there are quite a few. Let's
go with large. That looks pretty nice. Once again, needs to go to the origin point.
Finally, to add one more mesh, you can, instead of dragging stuff in, simply instantiate a child scene.
The one that I'm looking for is called turret double. Drag this one into the center as
well, and there we go. Now, we have a whole bunch of objects that we can select quite easily
from the meshes. We just have to get the children. Although, first of all, the collision
shape should be at least vaguely fitting here. This part isn't going to be too
important, so I'm not going to be too precise here. But, what you can do is first of all, get the front view,
and then move the collision shape to roughly fit all of these shapes. Make sure that this shape is fairly
small. The bottom part doesn't actually matter, and for the top part, we can put it vaguely here.
Besides that, we can look from the right and make sure that this thing isn't too long.
Put it to something like so. That way, we have a reasonable shape that mostly covers the area. Obviously,
for real game, you want to have a shape for every single one of these meshes that works a lot better. But, that would
take too long for this tutorial. Instead, I want to spawn a whole bunch of these obstacle on the map to make the
entire thing look much more populated. This should happen on the tick of a timer, which means I want to add another
timer. Let's call this one the obstacle timer. It's going to have a wait time of 2
seconds, and it should auto start. I suppose we can connect the timeout signal right away to game.
Most importantly, we need to have this scene ready inside of the game scene. Which means, like we've seen for the
meteor and for the laser, I want to have an obstacle scene.
Which we get via scenes and obstacle. After that, once the timer times out, I want to create a random amount of these
obstacles. That we can do via a for loop. For I in rand I range,
I want to create between three and six objects. This we can do in the usual way. We have
obstacle is going to be obstacle scene. {dot} instantiate. Next up, we have to add that to a node,
meaning we have to find a parent. And once again, we can add all of this to projectiles, because effectively, those
are projectiles. Maybe not the greatest name, but it's good enough. So, to that, we want to add
a child, which will be the obstacle. If we now run the whole thing, let's see what happens.
We are getting obstacles right on top of the player. That is at least something.
And also make sure that for the obstacle timers, auto start and wait time are enabled, so you get this kind of thing
constantly. Now, at the moment, this isn't too visible, because we always spawn things
in the same place. Not great. For that, we want to go back to obstacle and then add a script to it, so we can randomize
things a bit. As always, the defaults here are totally fine. Create. And then, once the scene is ready, func
{underscore} ready, I want to do a couple of things. Let me add pass for now. Number one, I want to randomize the
scale between one and three. Meaning, we can have the default scale
or scale up by a factor of three, and find some kind of float between those values.
Then I want to have a random rotation around the Y axis. That one should be straightforward. And
finally, as a separate function, I want to set the XZ position, with X and Z as an argument.
And this we are going to call from the game class after we created one of the obstacles.
That's going to become important later on. But, for now, there are three things that we need to
do. This is going to be your exercise. Randomize the scale and the rotation of the obstacle once the scene is ready,
and create a function for the X and Z position. Pause the video now and see how far you
get. Let's start with the really easy part. The function to set the XZ position.
And I should be a bit more organized here. For both data types, we are expecting a float, and nothing else
should be accepted. All we have to do in here is position.x is going to be X, and position.z
is going to be Z. That way, from inside of the game class, we can call
obstacle.set_xz position with some kind of value.
Let's say for now zero and negative five. That way, all of the obstacles should be
spawned a bit further in front of the player. And if you write the name of the function properly, this is also going to
work. Let's try again. And now, we get the obstacle a bit further in front of the player. That
worked. Besides that, for the ready function, I want to update the scale, and this has
to be a vector three. Well, we want to have values for X, Y, and Z, and all three of those should be
the same. I think the easiest way to approach that is to create another variable. Let's
call it scale random, which we create with randf_range, where we go from one to three.
This value we then plug into X, Y, and Z, like so. That way, we keep the scaling proportional.
That should actually be all we need. Let's try. So, now we should be getting obstacles
that are different in scale, and that definitely worked. The first one was really large, so it's
a bit hard to see, but this worked. That covers the first part. Next up, a random rotation around the Y axis.
All we need for that is rotate_y, and then a random range. Once again, randf_range,
and the values here go from zero to tau. Tau is simply two times pi, and randf_range is working in radians,
meaning the full range goes from zero to two times pi. In other words, this covers the full rotation.
And finally, one thing I forgot to mention is that at the moment, we have five children, and we only want
to make one visible. This could be an extra challenge. If you want to figure that one out, pause the
video now and try to only make one of those visible once the scene is ready. I want to get all of the meshes,
and then get the children. This is going to return an array with
all of the children, from which we can pick one at random. And this we want to show.
For that to work though, we have to make sure that by default, all of them are invisible, like so.
With that, inside of the game, we should only be getting one object, and this kind of worked.
The reason why it looks weird is that inside of the game, we are spawning between three and six objects at the
same time. Meaning, even if we only have one of those visible, we still see multiple.
We're going to fix that in just a second. But, for now, inside of the obstacle class,
we have nearly all that we need. The one additional thing that I want to do
inside of the game, the floor should be a bit further down. At the moment, in my case, the Y
position of the floor is 0.9 or -0.9 to be specific. The floor is a bit further down compared to the player.
This, I think, should be lower. Let's go with -2. That way there's a bit more space between the player and the floor.
This, however, means at the moment for the obstacles that they are floating. This we should really fix. And for that,
we will need position.y. And this should be -2 or whatever value you set for the floor of the game. And
by the way, this mesh instance should just be the floor. Makes a bit more sense.
Let's try all of this now. And now the obstacles shouldn't be floating anymore, and they're not. That
looks all right. With that, finally, we can randomize the position of the obstacles
by our set XZ position. In my case, for that, I have created another variable called
game_size. And this tracks the scale of the game, which is going to be a dictionary.
In there, we have the left side. We are going to have the right side. Then we have front, and finally, we have
back. The left side, in my case, is -15. The right side is positive 15.
The front is -2, and finally, the back is -40. Effectively, inside of the game, this
area is all of this bit here. And we want to spawn an obstacle inside of this area.
Although, we do have to be careful. These obstacles should be, like the meteors,
at the top end of the window. In other words, first of all, I want to cover the X part of the function. I want to have
randf range where we go from the game size.left.
And then game size and right. That way we are going left and right. This we can duplicate for the Z values.
And let's say, just to get started, I want to go from the front to the back. That way, we are spawning an obstacle in
the entire available range, which isn't actually what we want to do. But let's try.
Now we're getting some obstacles here, we're getting some there. That looks pretty all right.
The problem is these shouldn't just pop in and start existing randomly inside of the game.
They should start at the top of the game and then move down, just like the meteors. I want to have game size and
back for the first value, for the second value,
game size.back plus five. That way, they all spawn in the back, which at the moment isn't ideal because
they should also be moving. Cuz remember, the spaceship itself doesn't actually move. We are simulating
movement by having the meteors and the obstacles moving in this direction. Which means the obstacles will also need
a func_physics_process in which we are updating the position. Specifically, position.z.
And let's keep it simple, plus equal two times delta. That way, they should be moving. And
let's try all of that. Now we should be having obstacles coming towards the player, and that looks okay.
Once we have a bit more stuff here, this is also going to look a bit more realistic, but I think this is working
decently well. To make all of this feel even better, what we can do, once the game scene is ready, and let's do this
all the way at the top, func_ready, I want to create loads of obstacles. We can simply copy this for loop, like
so, and then spawn between, let's say, 50 and 60 obstacles.
And those should cover the entire available range, meaning we want to go from the front to the back. That way, if
I run the whole thing again, we are getting, right at the start, a whole bunch of stuff, and that way the
game looks a bit less empty. And that is working pretty well. Cool. Not perfect, but I think for a
basic game, this is good enough. The last minor thing that I want to do is, if the player happens to collide
with an obstacle, the game should still end. Which means, first of all, for the
obstacles, we have terrain, and for the master player. If that is correct, we can check for
body entered. And if that is the case, we can simply get the tree and quit the game.
Now this is going to be quite rare because all of these meshes are fairly far down, but the player should still be
a little bit careful. So we are nearly done. In the next part, we have to add sounds and work on a very
small shader, but other than that, we are pretty much good to go. It is time to finish up the game.
There are three major things that we need to do. Number one, we have to add sound. Number two, we have to clean up
some very small things. And number three, I want to add a floor shader. That way we get a proper terrain, at
least somewhat. All of this we can start with an exercise right away. I want you guys to add the sounds to the game.
There are three that you want to add, for lasers, collisions, and the background. This is going to work like
in a 2D game. Just keep in mind, an audio stream player 2D will become an audio stream player 3D. Other than that,
nothing really changes. So pause the video now and see how far you get.
Here we are in the project, and if you look at the audio folder, we have three files, explosion, laser eight, and
spaceship. These we want to play at specific instances. For example, when we are
shooting a laser, I want to play the laser sound. Which we can do very easily by adding an audio stream player 3D.
And then we get the usual stuff, where we want to play an audio stream. This should be laser eight.
And if you play it, you get a laser sound. You can also change the volume.
It's pretty straightforward. To play this thing inside of the script, when we are creating the laser, so
inside of ready, we also want to get the audio stream player 3D and play it. It's literally
all we have to do. If I now run the entire game and shoot a laser,
it works pretty well. When the laser hits a meteor, I want to play explosion.wav.
Means to the meteor, I want to add an audio stream player with explosion. Let's see how that sounds.
Pretty loud. Let's make it a bit less loud, but once again, experiment and see how this works for you.
When a laser area entered, we want to play that sound. Which means, before we're doing anything
else, I want to get the audio stream player and play it. Once we have that, I can shoot the
laser. Let's hope I hit something important. And that works. Perfect.
Finally, inside of the game, I want to have the background music. For that, I want to have a plain audio
stream player. The background music doesn't care about the distance to the player. Which, as a reminder, is why we
have an audio stream player 2D, 3D, and a plain one. 2D and 3D care about the position. The further away the sound is
from the camera, the quieter it will be. The audio stream player, on the other hand, doesn't care about that. This is
what we want to get. And for that, I want to have spaceship.wav.
Let's see how that sounds. Background music. That is definitely background music. And on top of that,
when you are importing the file, inside of the loop mode, this is set to forward.
If it is disabled or set to ping pong or backward, you are going to get some weird behavior. This should be forward.
If it's set to disabled for you, press forward, and then click on reimport. I don't have to do it because I already
had that setting. And this we want to auto play. That way, we are getting looping background music,
which usually should be a bit quieter, -20. Let's try all of this in the game.
And that's coming together reasonably well. With that, we have sound. Next up, I want to fix a couple of
smaller things. The first one is the debugger is unhappy with us because in on body entered for the obstacle, we
don't use the body. So there should be an underscore. That was a very easy fix. The more
important one is, there are actually two things we have to work on. Number one, inside of the game, the player can move
to the right and simply leave the screen, which does get a bit weird. To fix that, what we can do inside of
the game, we can simply add a static body 3D along with a collision shape 3D.
Where we just need a box shape, and this is going to be a pretty basic wall. That needs to cover just about the
player. Something like this is fine. And this you have to move to the right, so you get
something reasonably large. To test this inside of the game, you want to go to debug and then visible
collision shapes, so we can see the collision shapes. Let's see how that looks.
You can see a faint blue outline. That's the collision shape. And at the moment, I guess that's
basically all right. Then we can duplicate the collision body, move it to the left.
And this is quite hard to see, but we do have a collision shape here. I guess if I disable the floor,
this is much easier to see. I guess we can go somewhere here-ish. Let's try the game now.
And I guess that works. Also okay. Cool.
That way the player is constrained to the actual game. And that makes the entire thing feel
much better. Besides that, for the meteors, once they leave the
screen, they still exist, meaning we are creating more and meteors. For that, I want to have a visibility
on screen notifier 3D. If you add that, you get a collision shape that is a basic rectangle, and it
should roughly be the size of the meteor. Doesn't have to be too precise. The
basics here are totally fine. Something like this. This node simply checks if the node is,
well, on the screen or not. Inside of the signals, we can check if this node entered the screen or exited
it. In our case, we want to check if it exited the window. Click on connect, and if that is the case,
I want to queue free that node. That way, the meteors disappear once they go past the player.
A similar system might be good for the laser as well, but you can implement that on your own.
Those were the two smaller parts that I want to work on. Finally, the last major bit is
at the moment, if you look at the floor, this, well, doesn't really look like a floor. It's perfectly flat, and that
just doesn't look good. To fix that, we can write a basic shader.
Specifically, we want to create a vertex shader. For that, if you click on the mesh, at the moment, this thing has a
size of 130 and a subdivision for width and depth of zero, which means inside of perspective,
we are getting basically just these vertices, which isn't really ideal. Via the shader, we
want to move some of these vertices up and some down. That way, we're getting a random
terrain, but that we can't do at the moment because we don't have that many vertices.
To fix that, we want to change the subdivision to, let's say, 100 for X and Y.
That way, you can already see we're getting a lot more points. And these points we can change via a shader.
You can actually do something interesting. If you click on the arrow on the material, you can convert the
material to a shader material. If you do that, then first of all, nothing happens. But
if you now click on this thing again, you have a shader, which already has a ton of stuff,
which we don't really care about. All that we want to do is add a bit of stuff to the vertex shader.
Although all of this is a pretty good way to learn about shaders in terms of what happens under the hood,
but not our problem at the moment. Instead, what I want to do via the vertex property
inside of the vertex function, you can target the position of every single vertex inside of this mesh.
This is going to be a vector free that you can read and override. So, for example, what you can do is
vertex plus equal vector free, and then you have X, Y, and Z. Meaning, if you want to move up all of
this by two units, you simply need a vector free with zero to zero, and then all of this is going
to be moved up a tiny bit. There you can see it. Especially around the corner.
This is the original mesh, and we're moving all of this up by two units because of this number. If I change it
to a one, then we're going down a bit, and if I change it to a 10,
then we're going up by quite a lot. There you go. That's the basic idea.
But this we have to randomize, and for that, we will need a random texture. The way you are creating that is, first
of all, you want to have a uniform, so you can change it outside of the shader, and then the data type is a sampler 2D.
By that, you can create textures inside of a shader. The texture that we want to create is a noise texture.
You'll see in a second where this is going. If you add a semicolon, then nothing really happens. But if you look
at shader parameters, first of all, we get a lot of stuff that we don't care about. The only thing that we care about
is noise texture at the bottom. And there, we can create a noise texture 2D.
If you click on that, you get a few more options that we also don't care about. But then, once again at the bottom,
under noise, we can create a new fast noise light. Click on that, and then you are getting a random noise.
That is actually what we need. This is effectively a 2D texture where we have lots of random black and white
values with lots of gray in between. For example, in here, we have a pure white color, and here we have a pure
black color. The range always goes from zero to one, with zero being black and the white bits
having a value of one. Or to be a bit more specific, we are working with RGB values, where white color has an RGB
value of one, one, and one, and the black color has an RGB value of zero, zero, and zero. All of this you can
fine-tune as well. There are, for example, different kinds of noise. And if you look at the bottom options,
you can change a few more things. So, you can create some pretty funky behavior.
In my case, though, I want to have simplex. That way, we're getting something.
And via the frequency, you can scale in and out. This really doesn't have to be too precise.
From this texture, we want to sample the colors, which we do via inside of the vertex
function, I want to create a float noise sample.
To sample a color, we need first of all the texture function, and this wants to have two arguments. Number one is the
sampler. This is the thing that holds the texture. That is very easy. We want to have a noise texture.
Then we need the UV coordinates. Those tell us where we are on the texture.
In practice, all you need in here is UV, and then you are getting something. Effectively via this, we're getting all
of the points on this texture. But it returns a vector four, so RGB and A, but we need a float. The way you are
converting that is simply by looking at one of these values. R, G, or B are all going to be fine. Let's go with red.
Grayscale values like this texture, R, G, and B are going to be the same, so it doesn't matter which one you choose.
But effectively now, for every single pixel on this mesh, we get one associated value on this
texture. For example, the top left point would vaguely be somewhere up here. Or if you are vaguely here, then you might
be on this point, which for the red value would be basically zero. If you are somewhere down here, which would be
vaguely here, then you would get an R value of close to one. And that we can use simply by
adding the noise sample to the vertex position. If you're doing
that, and you look from the side, you can see that now we are getting a bit more of a terrain-y look. It's quite
hard to see. To make this more visible, we can multiply the noise sample by a factor of
10, and this is going to be really visible. And there you go. This looks much, much
better. And I hope you understand the logic here. Basically, we are laying a texture
on top of all of this. This texture here. And then via the position, we are sampling one of these grayscale values
and get a value between zero and one. If the value is zero, we are somewhere here,
meaning we didn't move up the vertices by a lot. But if we are on this bit, then we get a higher value. This would
be roughly here in the texture. And all combined, we're getting a pretty cool effect. If you go back to display
normal, this is arguably a bit less visible because we don't have a lot of lighting.
And also, this shouldn't be that strong. Let's change it to 0.5. This really shouldn't be that tall, and somewhere in
there we have the player. There we go. And right now, you can't really see
that. Let's change this a bit more, so we are getting something.
Always make sure you're multiplying this with 2.0. Otherwise, Godot is going to complain.
And now, this looks a bit more realistic. But, this isn't going to be too strong in the game. It might be just
about visible. And I guess we're getting a slight effect.
This is the kind of thing that you want to play around with a lot. Just to make sure we are not breaking
the rest of the game, I'm going to set this to 0.5, which makes it barely visible.
But, at the very least, it's going to do something. [Music]
And all right, with that, we have the finished game. Make sure on the debug, the visible
collision shapes is disabled, so it doesn't look too weird. But, other than that, you have learned
about the basics of 3D game development. You can either continue with the course, where you're going to learn about the 3D
platformer and a first-person shooter. If you don't really care about the course, I've also made a Zelda game. It
goes into a lot more detail about 3D game development. This one is entirely on YouTube, so just check it out if
you're interested. In there, I cover a lot of animation logic, a bit more on shaders, enemy behavior, and honestly, a
lot of stuff. The video is quite long.
The course explains Godot's scene and node architecture in detail, covering scene trees, node hierarchy, parenting, and how to organize game elements effectively to build scalable and maintainable projects.
The course covers GDScript fundamentals such as variables, data types, functions, conditional statements, loops, and event-driven programming using signals, enabling you to script engaging game behaviors.
The course transitions into 3D by teaching you about 3D nodes, mesh and camera setup, lighting, importing 3D models, player movement with CharacterBody3D, shooting mechanics, and advanced visual effects with materials and shaders.
No prior coding or game development experience is required. The course is designed for complete beginners, guiding you step-by-step from basic concepts to creating complex 2D and 3D games using Godot.
You will create seven diverse games including a runner, platformer, farming simulator, monster battle, 3D space shooter, 3D platformer, and a 3D shooter, giving you experience across various genres and gameplay mechanics.
You will engage in hands-on projects like creating a stick figure, implementing sprite animations with position constraints, coding a Frogga-style game, a Metroid-style platformer with enemies and animations, and a 3D space shooter with dynamic obstacles.
Yes, the course emphasizes organizing scenes and nodes cleanly, using Godot's documentation and editor tools, managing collisions efficiently, employing delta time for smooth physics, leveraging tweening for animations, and customizing shaders for improved visuals.
Heads up!
This summary and transcript were automatically generated using AI with the Free YouTube Transcript Summary Tool by LunaNotes.
Generate a summary for freeRelated Summaries
Unlocking the Power of Go: A Comprehensive Programming Course for Beginners
Learn Go programming with our comprehensive course for beginners. Master the fundamentals and build real-world projects!
Complete JavaScript Beginner Course: Projects, Fundamentals, and API Integration
Learn JavaScript from scratch with practical projects including a digital clock, calculator, game, image slider, and a weather app fetching live data from APIs. This comprehensive course covers essential concepts like variables, functions, loops, objects, DOM manipulation, asynchronous programming, and more to build interactive web applications.
Complete TypeScript Course: From Basics to Advanced React Integration
Master TypeScript with this comprehensive course covering fundamentals, advanced types, object-oriented programming, decorators, modules, integration with JavaScript, and building React applications. Gain practical skills to build robust, scalable, and maintainable applications using real-world examples and best practices.
Comprehensive Python Course: From Basics to Advanced Mega Projects
This extensive Python course covers everything from fundamental programming concepts, data types, and control flow to advanced topics like OOP, file handling, virtual environments, and AI integration. Featuring practical projects including a Jarvis assistant and chatbot, it equips learners with hands-on skills for professional growth and job readiness.
Creating Game Designs with Stable Diffusion and Photoshop: A Comprehensive Guide to Jungle Piics
Learn how to design a match-three game using Stable Diffusion and Photoshop, from logo creation to game assets.
Most Viewed Summaries
A Comprehensive Guide to Using Stable Diffusion Forge UI
Explore the Stable Diffusion Forge UI, customizable settings, models, and more to enhance your image generation experience.
Kolonyalismo at Imperyalismo: Ang Kasaysayan ng Pagsakop sa Pilipinas
Tuklasin ang kasaysayan ng kolonyalismo at imperyalismo sa Pilipinas sa pamamagitan ni Ferdinand Magellan.
Mastering Inpainting with Stable Diffusion: Fix Mistakes and Enhance Your Images
Learn to fix mistakes and enhance images with Stable Diffusion's inpainting features effectively.
Pamamaraan at Patakarang Kolonyal ng mga Espanyol sa Pilipinas
Tuklasin ang mga pamamaraan at patakaran ng mga Espanyol sa Pilipinas, at ang epekto nito sa mga Pilipino.
How to Install and Configure Forge: A New Stable Diffusion Web UI
Learn to install and configure the new Forge web UI for Stable Diffusion, with tips on models and settings.

