Did you always dreamed of
making a multiplayer FPS game? Today's your lucky day, because we'll
learn how to make these dreams come true. Hey guys, Lucas here and
Welcome to Busy Weasel Games. I hope you're all doing great. And today we'll start a new series on
how to make a multiplayer first-person shooter game using Godot engine. In this first part, we'll
create a project, a map and the basic movement of our player. So without any further ado, let's begin. Open Godot to see the project manager
here, we can create and open projects Click on new projects and then type the
name you want. I will name it FPS tutorial. Now we need to choose a folder
to save our project in. Select one, by clicking on browse, and
then choose the folder you want. Now click on create folder and Godot
will create a new folder for our project. Just make sure you have open GL3 checked
and finally, click on create and edit to open up our brand new project. Before we begin coding. Let me explain briefly, one of Godot's
main features: Scenes and nodes. If you already know what they are,
feel free to jump ahead to the next part, using the timestamps below. To explain better. What scenes and nodes are. Let me show you an example.
Let's make a player, okay? What do we need to make
a player in a game? We need a model. Then we need something to
collide with the world. And we also need a camera, so
we can see what's going on. Looking at this. We can tell that there is a
well-defined structure formed by different nodes. And while we can
define each individual part as a node. The whole structure is a scene. Basically, a node is an object that is
responsible for one, or just a few things. The model, for example, is only the
players's visual representation, and the collider only exists to
provide collision information. And as you may have guessed, the
only purpose of the camera is to show us what's on the screen. The root node player is also
a node, but it's parenting every other node in the scene. With that, we can tell that a
scene is a collective of nodes. We can build many different things
by grouping different nodes together, and we can even put a scene inside
another scene. Take our player here. We can add a weapon to it, but
the weapon is responsible for too many things to be a node, as it
has its own visual representation a magazine and can shoot bullets. So we can break the weapon
apart in different nodes. Therefore making it a scene too. See? The system is really powerful
and adds a lot of flexibility. If you're struggling a bit here, don't worry. This concept will become crystal
clear as we go further in this series. But that's enough of theory for now. Let's make something happen. The first thing we will create is a map. After all, our player needs somewhere to live. What do we need to create? You guess it right. We need to create a new scene. And since we are creating a 3D
game, click on 3D scene. Nothing has changed much yet, but that's okay. First off, rename this node here to map.
With capital M. And by the way this is a very basic note called Spatial
node. That handles some basic stuff in the 3D world and is an excellent
option to our map's root node. But how about the ground? Let's create a new node for it. To do this, you have two options. Either you click here, on this plus icon
or press Ctrl+A or Cmd+A and the create new node window will open. Well, that's nice, but which node
should I use to create the ground? Since we're at the beginning of our game
development, we will focus on prototyping everything first and Godot has a nice
feature to help hasten this process a bit. If you type in the search field
CSG, you'll find these nodes here. They are primitives that we can
simply put on our map, fast and easy. They have nice, more
advanced features too. But we talk about them later for now. Just choose CSG box and click create. We have a nice little cube in our map.
When you're using CSG meshes, you can change their dimensions by clicking
and dragging these orange dots here. Like this. Alternatively, we can type the dimensions
we want directly in the inspector. Let's make our ground 30 units
in width, one unit in height and 30 units in depth as well. To
get a better view of the scene. We need to know how to
navigate the 3D view. First let's zoom out a bit. Scroll down the middle
mouse button to zoom out. That's better. To rotate around the
selected object, click and hold the middle mouse button and move it. If you hold Shift, while the holding
the middle button, you will pan the view. Like this. By holding Ctrl
or Cmd, you can zoom in and out as well, but more precisely. Finally, if you click and hold the right
mouse button, you will rotate the view, but around the point, your camera is. Like this. Nice, but we have one problem. If you look closely, our ground is a
bit above the zero line on the world. To make things easier. Let's click on this magnet
icon to activate snap, and then transform, configure snap. Just type 0.5 in the
first field and hit okay. What this will do is to snap everything.
We move in half units increments. Now we just need to move down
a bit and our ground is now precisely aligned with the world. We already have a ground for our
player, but I say we also need two more things, so we can test everything
properly. A box and some kind of slope. The box will help us to measure our
player's jump height and the slope. Well, moving on slope is a tricky thing. So we'll create one too. First, select the map node, as we want to create a node child
of it, and click on this plus icon here, or press Ctrl+A or
Cmd+A to open up this window. Create another CSG box node. Cool. We want to use this box to measure
the player's jump height, right? But what's the height we want? The right answer to this question
will depend on a lot of factors, but since I know that our player's height
will be 2.5 units, I've decided it's jump height should be three units. So, in inspector up here type in
three in width, height, and depth. Done. Move the box up a bit
and position it as you desire. Good. Now we can move on to the slope. Again, select the map's root node and
let's create a new node. Type CSG again, but now we'll choose the CSG polygon node. It looks like the same CSG box as before. But if you'll notice the orange
circles are placed differently. To create the slope simply click
and drag this bottom circle to here, like this, and this one to
the same place as the last one. Now, take the circle up
and here should be fine. But we need to make the
slope thicker. To do this, go to the inspector, here
in depth type three. Nice. Position it whatever
you fancy, and done. We now have a nice little map with almost
everything we need to play our game. Cool. We have a ground, a box, and a
slope, but they look pretty ugly, don't they? We can make them prettier
by adding materials to them. Let's do this. I've already downloaded some
textures from the excellent Kenny's prototype texture pack. You can find the link to these
textures in the description. After you've downloaded the
textures, choose three of them. All PNG files. Now to go to the project folder,
you can easily do this by right clicking on the res folder here
and selecting open in file manager. Now let's make our project well structured.
Create a new folder and name it, assets, all small caps, and then inside it,
create another new folder and name it. textures, all small caps again, Just
paste the three chosen textures in here, and we're good to go. You may need to rename
some of them though. With all this taken care of,
we can now select our ground. You can do it either by clicking
on it here in the viewport, or by clicking on our ground node here,
let's change some names, by the way. Double click, slope. Right-click and
rename box, and finally press F2, ground to make it prettier go to the inspector. Here in material, new spatial
material, click on it again, and you should see this panel. There is a lot of parameters that you
can change here, but we'll focus solely on changing the texture. To do this, go to
Albedo and then click and drag the texture you want on our ground to the texture slot here.
This already looks nice, but we can make it even better by going to UV1, setting
triplanar to on, and changing the scale to 0.5. We can now repeat everything
with the box and the slope. Just choosing different textures. Great. Now our game is a lot easier on the eyes. We've already achieved a lot of things. So it's wise to save our progress. To do this, you can click here on Scene, save
Scene, or press Ctrl+S, or Cmd+S. As we did with the textures, we
should create a folder for our map right-click and select new folder. Name it maps, again, all small
caps. Confirm and save the map. Now we can finally test our game
for the first time, but before, go to the project settings and scroll
down to display, window and change the width to 1920 and height to 1080. This will set our game
screen resolution to full HD. Also, change the test width to
1920 divided by 1.5 and the height to 1080 divided by 1.5. Yes, you can do math operations in every
numerical field in Godot. Pretty handy. This last step will affect our
resolution only while testing. Close the project settings and click play here. Godot will ask for the main scene. We should select the map scene and done. Obviously, there is something wrong. The problem is that we didn't
assign any camera to the game yet. So we staring at this ugly gray screen. We will do this, but let's
create our player first. This time we will not choose the basic 3D
Scene as the root node for our player. Godot has a specific node that is
especially suited for what we need. Click on other node then type body. There are three main
types of bodies in Godot. The static body, which you should
use to well, static objects. Rigid body, which is a good
option when your character would be constantly affected by physics
like the birds, in angry birds and a kinematic body, which is what we want. It handles collisions, and
can be controlled by code. Okay. Create a kinematic body and rename
it to player with capital P. You get this yellow warning here. If you hover the mouse over it,
you will see that it is complaining about not having a collision shape,
but before creating one let's create a visual representation for
the player. Create a new node and this time search for mesh instance. Rename it to body. Yeah. You know it, capital B. Maybe more often seen than Blender's default cube is a capsule shape in game dev tutorials and we'll not break the pattern here.
With the body node selected, go to the inspector, click on mesh, new capsule mesh. Cool. You already have a chubby dead player. Let's bring it to life by
clicking on the capsule here and changing it's mid height to 2.5. and rotate it in the X axis by 90 degrees. Now our player is alive and well,
but it's burrowed in the ground go to transform configure snap, but this
time set the translate step to 0.25. And now move the player up until
it aligns with the world. To make it easier to us to know which
direction the player is facing you add another node for the player's eyes. Create a new mesh instance node,
child of the body node, name it, eyes and in mesh, select new cube mesh. That's a hell of an eye. Let's make it smaller. In the cube mesh, in size, set the X value
to 1, Y to 0.5, and Z to 0.5 as well move it out of the player body a bit and up. Perfect. Now to make things look prettier, with the body node selected,
go to material, new spatial material, albedo and select a nice
looking color for our player. I'll be picking green. The eyes are looking good already. I'll let them be. Good. Let's tackle this yellow warning now. With the player node selected,
create a new node and this time search for collision. What do we want here is
the collision shape node. Cool. The warning didn't disappear
but it has moved. So that's still some progress. With the collision shape node selected. Go to the inspector, click on
shape and then new capsule shape. You will create this little
wire frame mess here. That's okay. Don't panic. Go to the recently created capsule
shape and set its height to 2.5. The same as our player mesh. Rotate it 90 degrees in the
X-axis and move it up until it aligns with the player's body. Perfect. We still need one more thing though. A camera. And like almost everything in Godot. A camera has its own node. Create one as a child of the player's
node, move it up and to the side. In a place you consider good
for the player's eyes to be. See this pink wire frame? This is the direction the camera is facing. So fix it, if it needs. Check current. And we are done with the player for now. Let's save it. Ctrl+S or Cmd+S. Go back to the root folder and
create a new folder called actors. Inside it, a folder called player.
And save it. Good. Now let's add our player to the map
scene, click here on the map scene and the viewport will change to our map. To add a scene you've already
created inside another scene, you should add an instance of it. To do that, click here in this chain
icon, or press Ctrl+Shift+A or Cmd+Shift+A and then select the player scene. Excellent. Position the player somewhere and
let's test our game once more. We should now see what's
the player is seeing. Let's see, hit play and perfect. This is looking great, but it's really boring. We need to add some
functionality to our game. In Godot, you can add functionality
to a node by attaching a script to it. With our player scene opened, select
the root node player, and either click this, this scroll icon here or right click the note and select attach script. A new window will open up, and for
now set the template to empty and change the path by clicking this folder icon. Select the player folder and save
it as player.gd. All small caps. You will be taken to the script
editor view, where you can write your code inside Godot, without the
need of any third-party application. This first line dictates which object this script extents. In this case, the player is a kinematic body. Great, now that we have a script attached to our player you are ready to add some fun to our game. Before we start coding our player,
please let me talk about some methods Godot kindly provides to us. These methods will certainly be present
in most of your future scripts. They are.
Ready. Process. Physics process. Input.
And unhandled input. The ready method is called
just after the node and all its children have entered the scene tree. You should put the code in this
method when you want it to run when the scene is just loaded, and
ready to run. The process method is called every frame. If you need a large to run every frame
and do not mess with physics, you should put in the process method. The parameter delta is the difference, in
time, between the current frame and the last one. The physics process is similar
to the process method being called every frame but it has a big difference. It is synced to the physics
engine, which means that while the frame rate of the process
method may fluctuate while we are playing. The frame rate of the physics
engine is much more stable. Use the physics process if you need
to change something in physics like moving the player. For input, we have two important methods. The first is the input method that
receives all input Godot reads like keyboard, mouse, joysticks, et cetera. And there's also the unhandled
input method, which is only called when the input event triggering it has not been captured by
any other input method. Usually, I prefer to handle input in the
unhandled input method, rather than the regular method With this little break out of the way Let's focus on implementing our first functionality
We'll make the player be able to look around using the mouse. As usual, we have a problem that needs solving. Let me show it. To look around, we rotate
our player right and left But what about up and down? We can't rotate the player up and down. Otherwise, its body will also rotate. To solve this, we will create another
spatial node child of player and we'll call it Head. Move it to where the camera should be place the camera inside the head node and go to the inspector
and reset the camera's transform. Now, if you want to rotate the player
sideways, we can rotate its body. But if you want to look him up and
down, we will rotate the head node instead. In this way, the player's body
will rotate to the side as expected but not up and down. Now that we're set to move on,
which method do you think is better to read mouse movement? You're right if you answered
the unhandled input method, Add it to the first line. To read an input inside this method We should use the event parameter
but it's receiving every type of event that Godot can reads. So we need to check if it is of the type
you want to handle at the moment. This line will check if the event
is of the type input event mouse motion which has the information of
the mouse movement and position Write handle camera rotation,
passing the event variable and then two lines later create the method. We will put all the logic for rotating
the camera inside this method. The underscore is used to signalize
that this method is private and it should not be accessible
outside this script. Let's first rotate our player to the sides If you look in the viewport The axis we need to rotate to
the sides is the Y-axis. Gladly, the kinematic body has an
easy method to do this and it's called rotate Y. The rotate Y method asks for an angle And we can get one by using deg2rad which converts an angle from degrees to radians. What does line does is getting the
difference between the mouse last position and its current position,
and then converts to radians. But if you try the game
now, you'll see two problems. First, the rotation speed is too
high, making it almost impossible to control it properly. And second When the mouse gets outside the
game window, the rotation stops. Let's fix this issue. Little disclaimer If your camera is inverted, just
remove the minus sign before event.relative. To slow down the rotation speed we will need to multiply the angle we get by some small number. We see this number being used in
many FPS games, and it's usually called camera or mouse sensitivity. So let's create a variable, called
camera sensitivity, of the type float with the default value of 0.05. Have you noticed the export word here? You can put it before variables to
make it visible in the inspector. Look, if you select the player's root note,
there is a camera sensitivity property now you can change it however
you want here, but won't change the variable's default value. This is handy when you want to tweak
things without going back to the script. Or when you work with a level
designer that doesn't want or shouldn't change your code directly. Continuing, let's multiply the angle by our
camera's sensitivity variable that we just have declared and it should be okay now. But before we test the game again,
create a ready method before the unhandled input event and write this. What this line is doing, is capturing the mouse making the cursor invisible,
and limiting its movement. to only inside our game window. Now press F5 to test the game again. See? Now the camera's rotating much smootly. And we can move the mouse
sideways forever and the camera will keep
rotating normally. Press Alt+F4 to close the game. Nice. Now we need to rotate the head
node up and down To do this, we use the method rotate X, but to the
head node instead of the player's To change something in the head node, we
need first to get some reference to it. And it is dead simple to do.
Leaving two blank lines above the ready function Write onready, var, head, of the type
of spatial, equals dollar sign head. What this line does, is saving a reference
to the head node inside this variable Back to our camera method. We now can write this line of code,
which does the same thing as the line above it. But instead, changes the X rotation of
the head node taking into consideration the
mouse position in the Y axis But why the axes are different you ask?
Let me explain. The camera lives in a 3D environment. So their position, rotation, and scale
are all vector3 values but the mouse position corresponds to its
position on the screen, which is a two-dimensional plane or vector2. So, when we want to get the mouse,
moving to the sides, we need to choose the X axis, but up and
down movement, we use the Y axis. In contrast, if we look again at
our player, the X axis corresponds to the vertical rotation, while the Y
axis to the horizontal rotation. There's one last thing we
needed to fix before we wrap this up. If we play the game now,
everything should look fine. But if we continue to rotate the camera
down, down, and down, you eventually become the girl in the Exorcist,
and that's not cool. To fix this, we need to assure that the camera
rotation is limited to a specific range. First, let's declare two constants.
Just below the first line add: Minimum camera angle of type int equals minus 60,
then add another line Max camera angle of type int equals 70. We will use these constants to constraint the
camera's vertical rotation to a minimum of minus 60 degrees up to a maximum of
70 degrees Inside our camera method, add one last line. What this clamp function does is to make sure that the value
of the first parameter, head rotation X will be between the second
parameter, and the third parameter. And by using the constants we've just declared We will make sure that the
camera rotates in the Y axis only inside the range we want. Let's see. Cool, cool, cool. Now we have a smooth and precise camera
movement controlled by the mouse. Also, don't forget to save our progress. We can look around, but we
need to move around as well. Don't you think? Me too. But as always, before we start coding,
we need to do some things first. First, we need to
set up the physics layers. Basically, we can set up objects
to "live" (air quotes) inside a specific physics layers. And then later on, when setting up
collisions, we can define how these layers interact with each other. To name your physics layers Go to project.
Project settings. Scroll down until you find layer
names and select 3D physics. Here, you have 20 layers to name,
but we need only two for now. Set the first to world, and the second
to characters. Back to the map scene and to make our lives easier,
select the root node and add a new child node of the type CSG combiner. Then grab all the CSG meshes
we've made and dragged them into it. Now that they're all children of the
CSG combiner, we can change collisions in it, and it will be
passed down automatically with the CSG combiner selected,
go to the inspector, check use collision make sure the first square in collision layer is selected
and none squares are selected in collision mask. If you stop the mouse
over the squares for a bit. A tooltip will show up, telling you
which layer a square represents. The layers you check in collision layer
makes the object belong to the layer while the layers you check in collision mask,
tell you which layers this object will interact with. In this case, the ground, the box,
and the slope exist in the world layer. But don't interact with any layers. Roughly, what this means, is that these
objects will not check for collisions with any layers, but other objects
may check collisions with them. Let's change the player's collision
settings as well. Go to the player scene and with the root node selected, which
is a KinematicBody, which extends from PhysicsBody, that can handle collisions
and then change the collision layer to characters, removing it from the world layer. Since the player exists in the
characters layers only. And finally set the mask to both
world and characters layers. Since the player can interact both
with the world and other players. With the physics layers and collisions
already set up, we can now move on to implementing player movement. The first thing we need to do is set up
the input map, go to project, project settings, and then select the input map tab. Here lies every action that Godot
will listen to With its corresponding key, button, or other input. Let's create a new action by typing in
the action field here forward, in small caps press enter, or click add to confirm.
Create every other action we'll need. Left, right, backward, and jump. Now, scroll down to the bottom
of the list and you shall see the actions we've just created. Here, in front of forward,
click the plus icon and then key. Press the W key and click Ok. Do the same thing to the remaining actions
I'll use the standard WASD for movement and the space bar for the jump. You can choose whatever key you want. You can set multiple inputs to one event. For example, we can add the
arrow keys to the movement actions, with the same procedure. Now that our input map is set,
we're finally ready to start coding our player movement. Back to the player Scene, open up
the player script by clicking this script icon and then press this icon here to enter
the script editor full-screen mode. The player movement
involves physics, right? So we'll need to handle input for it
inside of the physics process method. But how the hell do we get input
events outside an input method? Luckily for us, Godot likes us.
Let me show you. Create a physics process method, and
then call for a get movement direction method again with an underscore.
And you know, why. Below the camera rotation method,
which we can fold it by the way by clicking on this little arrow, create
the get movement direction method. First of all, we need a
variable to store a direction our input is pointing. We set it to vector3 down,
which is our gravity direction. Now, we need a bunch of ifs. Each one, checking one direction
that a player may want to go. As you can see, using the
input method is action pressed We can check if said action is
being pressed, even outside an input method. Let's dissect all
these transform basis stuff. Go back to the 3D view and
select our player root node. Now take a look at the gizmo. There are three colored arrows, right? Each one corresponds to an axis.
The green one to the Y axis, the blue to the Z axis, and the red to the X axis. With that in mind, which axis you
should change to move the player forward? The Z axis right? Now, take a look at the bottom left of the screen,
when I move the player forward. It decreases.
So we can safely say that the direction to move the player forward is negative Z. Which leads then, to being positive
Z the direction to move the player backwards. Using the same experiment, we can see that the direction to
move the player to the left is negative X, while to the right is
positive X. Back to the script, just return the direction variable. Since we're returning the duration
variable in our get moving direction. We can retrieve it in the physics
process method, saving it to another variable called movement. And before we continue, declare a variable
called velocity of type vector3 with a default value of vector3 zero,
which stands for zero on all three axes. Again, in the physics process method, where we
should handle all player movement give it some space and write these two lines. Lastly, use move and slide
to apply the movement to the player. This is a KinematicBody built-in method that
handles all the physics calculations regarding the movement itself. Pass the velocity. And we're good to go.
Play the game again. We're moving, but
we are terribly slow. No one would like to
play a game like this. Let's fix it. Applying the same principle as we did
with the camera's sensitivity, we should multiply the movement by a number. Create an export variable called speed
of type float, with a default value of 10 Now multiply the movement
by the speed and test the game again. That's much better.
Change the speed value as you see fit. The player movement is good,
but we can make it even better. If you play it again, you can
notice that as soon as I pressed the W key, the player starts moving. And as soon as I let go of the key,
the player stops. The movement is happening too suddenly for my taste. In real life, people slowly accelerate to maximum speed,
and then, slowly decelerates to zero. To achieve this, we will need to
interpolate the velocity between the last value and the new value by a
defining weight. Declare a new exported variable called acceleration,
of type float, with a default value of six then change the velocity calculation in the
physics process to this new lines of code. Here we use Godot's lerp method, which asks
for a current value, a target value, and the weight, to slowly blend the
old velocity to the new one. To make it more clear, the first parameter
is the velocity current value. Then we pass the new velocity
we want to reach. Then a value for our acceleration.
Multiplying the acceleration by delta We make sure that the weight will
compensate fluctuation in FPS. Play the game again, and you should
see a much smoother movement. The player slowly accelerates
and decelerates. Much better. Now let's implement the player jump. First we'll need two variables,
one for the gravity and one for the impulse that we'll apply to our player
when the jump key is pressed Declare another constant called gravity,
of type int, and a default value of minus 20. If you noticed, constant names
are written in all capital letters. This is used across many languages and
basically tells you that if you see a variable named like this, you should
not change its value in runtime. Next, declare a new exported
variable called jump impulse of type float, with a default value of 12. Good. Now we have everything we
need to make our player jump. Go to the, get movement direction method,
and add the check for the jump action. When the jump key is pressed, we want
to set the Y velocity to the jump impulse. Why the Y velocity?
If you go back to the 3D view and select the player node, you'll see that the green
arrow pointing up is the Y axis. If we would test the game right now,
as soon as you hit the jump key, the player would jump in the air and go to
the infinite and beyond, never returning. We don't want that. Do we? Nooooooooo. Well, let's save this poor man's soul.
Back to the player script, go to the physics process method and add a new line
before the move and slide In this line, you add the gravity,
eased it by the delta value, each frame. So, in every frame, a force, will be trying
to pull the player down, as in real life. Let's try it game again.
Way better, but not so funny anymore. We still need to address two problems. Firstly, if press two keys at the
same time, for example forward, and right, you see that the
play speed is higher than normal. That's because we didn't normalize
the direction we want to go. Let's fixed this. Back to the player script, go to the
get movement direction method again and add dot normalized to the direction. Easy. What this does, is assures that each
of these vector values are contained between negative one and one.
Test the game again, and you'll see that everything is working fine now,
but if you think the player is too slow just change the speed value. Now, to the second problem. If you jump and try to jump
again, when midair, you will jump and you can jump forever, that's
pretty fun, but not what we want. In the get movement direction,
add a condition if the player is on the floor. But if you would test the game now,
you wouldn't even be able to jump that's because we didn't tell Godot,
which direction our ground is facing. So it will think that we're never on the floor. Gladly, this has an easy fix. Go to the physics process
method and in the move and slide add a second parameter vector up. What this is doing, is telling Godot
that the ground's normal is a vector pointing up. Test the game now. We can't jump in the air anymore. Perfect. You can make your player
do a double jump if you want creating a variable counting
how many times the player has jumped and adding a new condition
when checking if the jump has been pressed. By the way, homework for you.
Make your player capable of double jumping. And in the next video,
I will show you how I would do it. This movement implementation
works, but it's rather simple. And while it can be enough for
several games, this won't work for us. For example, test the game again,
go to the slope and stop on it. You see that the player slowly slides down. This problem would be fixed easily by
using the snap within the move and slide method. But since we're using interpolation
to accelerate and decelerate, simply setting snap to true won't work in
our case. I will not fix this issue by now because in the next video, we will learn
how to implement a finite state machine and a better player controller,
in line with more complex games. And that will fix everything. If you want to learn how to implement
a finite state machine for an FPS controller from the ground up. Stay tuned to my next video. That's it guys we've reached
the end of this video. I hope you liked watching it
as much as I enjoyed making it. It was a lot of work, but
it was totally worth it. Maybe in the next videos, I won't
write a 15-page script and take four days to record everything. While I'm taking too long to
record it and edit videos I will be posting new videos
on the first Friday of every month. Hopefully, as I get better in this YouTube
thing, I would release new videos more frequently. Please comment below what's your thoughts about this tutorial. Give me some feedback on
where I could improve. I initially thought to make this series
extremely beginner-friendly, but if you think the pace is too slow, the video is too long.
Anything really. Let me know. If you liked the content, hit the like button subscribe and activate the bell icon. So you get notified when I release new
videos, I'm also on Twitter and Instagram and of course, check my games on Itch.io,
including capture wars which was made with many of these techniques,
I will teach you in this series. If you want to help me with my game
dev journey, please consider paying any amount on my games on Itch.io. Consider reviewing them too.
It will immensely help my games reach more people. Thank you. Have a nice day. Cheers.