Beginner Godot Tutorial - How to Make Pong with AI

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
originally released in 1972 by Atari Pong is one of the earliest arcade video games it was initially intended to be a warmup project for a new engineer Alan alorn to familiarize him with game development he went on to create the Prototype connected it to a black and white TV and the game was born to recreate the game in gdom first of all go into project settings then into the display window size and make sure that it's set to 1152 for the width and 648 for the height I then create a Sprite 2D node and this is going to be my background I'll rename it to Main and then I need to load over my image into the texture property I've got this background image here so I drag it over into the texture I then go into the offset and I untake centered to position it correctly if I zoom in on this line you can see the edges of it are actually a little bit blurry I go into the texture down here and change the filter to KNE wrest and that sharpens up the image for me and I then create the player paddle I'm going to create a static body 2D node and rename it to player this node then needs a couple of child nodes the first one is the color wct and the second one is going to be a collision shape 2D node and that gives me this little rectangle up here I'm just going to change the size of it so I go into the transform property and I set the width to 20 and the height to 120 pixels it's currently being positioned from the top left so to change that I set the anchor pre set to Center left to be able to detect collisions I need to use this Collision shape 2D node I'll Define a rectangular shape and then I just need to adjust the size of it to match my paddle it's easier to do this if you turn on this grid snapping option or rather the smart snapping option that just allows me to snap to the edges of the piece once done I can position the whole paddle correctly I go into the transform and change the position to 50 by 324 and now it's sitting nicely in the middle of the left hand side of the screen I then select these three nodes and go into duplicate I'll rename this one to CPU and this is going to be the AI controlled paddle I'll need to make sure that it's repositioned so I go into the transform property and change the X position to 1082 so now this paddle is on the opposite side from the player paddle for the ball I'm going to use a different Noe type I create a child node of character body 2D similar to the paddles this also needs a color wck node and it needs a collision shape 2D node I'll rename this node to ball and I can then go into the color rect node and update the size of it inside layout and transform it's too big right now so I'm going to set it to 10x 10 pixels and then I'm going to set the anchor preset to Center so now the ball is going to be positioned from its Center Point this also needs a collision shape assigned so I'm going to use another rectangle here and just resize it to match the shape of this ball now I want this ball to be positioned in the middle of the screen so I go into the ball node and inside of the transform property I can set the x coordinate to 576 which is half of the screen width and 324 which is half of the screen height so the ball looks like it's disappeared but it's actually just sitting there exactly inside of that middle divider line now I'm going to set up borders around of my game board for this I'll create another child node which will be a static body 2D same as the paddles there's going to be two Collision areas one above and one below so I need to create two Collision shaped 2D nodes as child elements of this node I'll rename these nodes accordingly as borders then as top and the second one as bottom and now just the same as before I'm going to create rectangle Collision shapes for them I'll turn on this grid snapping so I can see a little bit easier what I'm doing and I will just extend this to be the width of my game board and to just start right at the outer edge of it then I do the same with the bottom border create a rectangle shape drag it down here and then make sure that it just starts at the limit of the board and maybe extends just one grid size it doesn't really need to go too far as long as it has enough space to detect the ball passing through these top and bottom Collision shapes will mean that the ball is able to bounce off of them the left and right is going to be a little bit different I don't want the ball to bounce off I want the ball to go off the screen and then to score a point for the opposite player these will be created slightly differently I'll set up another child node of my main scene and this will be an area 2D node inside of that I need to add in a collision shape just like I did previously I'll rename this one to score left and then I can create a rectangular Collision shape just like I did with the top and bottom borders so I'll extend it down here here and it's going to be just on the left hand side of my game board we need to do the same thing on the right hand side so as an exercise pause it here and try and do this yourself for practice you should try and set these up manually but I'm just going to duplicate these nodes with contrl d and I'm going to change this one to score right then I just need to move the Collision shape over from the left hand side over to the right hand side like so it can be a little picity so I'm just going to make sure I've got it exactly right and none of the areas are overlapping or missing oh yeah I've got a little bit missing here so I just to tidy all those edges up okay so now all four of the borders are covered with some kind of collision shape since these borders are actually touching in the corners we potentially will get some Collision detection between the two we only want collision between the ball in the borders not the borders themselves so to fix that we can move them onto different layers so if I go into my borders and I go into the Collision set here I can move them onto Layer Two instead of layer one then I go into the ball go into its Collision properties and make sure that under mask I'm selecting one and two so that is colliding with everything that's on layers one and layers two that way it collides with everything that's on layer one and Layer Two next I'm going to set up a heads up display that's going to show the scores so I can create a new node as a child which is going to be a canvas layer node I'll rename this canvas layer to a heads up display and add a child node which is going to be a label and I'll rename this one to player score I'll set some initial text here as zero this will be the starting score and then change the alignment to Center for both horizontal and vertical then I want to change the font type so if I go into my theme overrides and into fonts I can load in this pixel font that I found online so if I drag it over that will will change the style of font the font is a little bit small so I go into font sizes and I'm going to set that to 50 so now the font is much bigger now I can position it correctly so if I go into layout and transform I'll set the x coordinate to 456 and Y to five just to position it to slightly to the left of this dividing line that takes care of one score counter now I duplicate this for the second score counter I'll rename that to CPU score and it's going to be exactly the same I just need to move that node over so I go into its layout transform and I change the x coordinate to 648 the last node that I need to add is a timer so I'll create another child node set up as a timer and I'll rename this to ball timer in the properties I'm going to leave the waight time to 1 second I'll set it to a one shot timer and I will let it auto start as soon as it's loaded that's all of the noes set up so I'm going to save this scene inside of the scene's folder fer and then press F5 to run it click select current and we should be able to see the game coming up so everything looks good and now we can start on the scripting to actually add in physics and movement I'll begin by adding a script to my main node this is a Sprite 2D node so the first line is automatically populated to extend it then I'm going to define a couple of variables the first one is the score I want to track the score for both the player and the CPU inside of an array so the first index is going to be the player the second index is the CPU and that's what this note here explains then I'm going to define a constant variable which will be the paddle speed and I will set that to 500 that's how fast each of the two paddles will be able to move I can save the script and I can now add another one to the player the player extends static body 2D and it comes with a ready and a process function which we'll get to shortly for now I just need to begin by defining a couple of variables the first one is going to be the win height variable which will track the height of the game window the second variable is p height and this records the height of the paddle inside of the ready function I can assign values into both of these variables the first one is going to be the window height to get the window height I first need to get the viewport rectangle using this built-in GD function then I get its overall size and then I take the Y property of that size so it's just the height to get the paddle height I can do the similar thing so my p height variable is equal to my color wct node which is a child node of my player this node here then I get its size using this built-in gdau function and I take just the Y property of that so that gives me just the height now I can move on to the process function in which I'm going to handle player input the input I want to look for first of all is when we press the up key on the keyboard so if that action is pressed then I want to do something and that something is that I want to move the paddle up moving the paddle up and down means I need to adjust its y position so I will decrease the Y position by that paddle speed variable which is going to be paddle speed multiplied by Delta Delta as this comment up here explains is the elapse time since the previous frame using this Delta variable means that the game runs at the same speed regardless of your frame rate or how powerful the computer is but here I'm getting an error that says that identifier paddle speed is not declared in the current scope well the paddle speed variable is created inside of this main script but that is a different script that is a script that's inside of the parent node of the node that I'm trying to work on right now so I need to access that variable by looking inside of the parent node and to do that I can add a prefix here which is getor parent this looks into the parent node and picks up any variables that are inside it such as paddle speed this will allow me to move the paddle up the way I'll do the same thing for moving the paddle down using an L if statement this time I'm checking for the action UI down which is the down key on the keyboard the line below is almost the same the difference is that we're now adding rather than subtracting I could run this code now and if I press the up and down keys I can move the paddle what I need to do next is limit its movement so that it can't go off the top and bottom of the screen I will do that inside the process function just underneath these inputs add a commment to say limit padal movement to the window and then I use the clamp function to clamp the Y position of the paddle between these two values here now if I run it again I can only move as far as the edges of the screen I can't go below the top and the bottom and that's all that we need for this paddle so I can save the script and I can close it down now I can begin working on the ball so I'll go in here and add another script to the ball node this extends the character body Tod node this time we'll Begin by defining a few variables the first one being the window size which is similar to one of the other scripts we created already next I Define three speed related variables the first two are constants this one is our start Speed this is how fast the ball will begin every time we play the game and then we have an acceleration so that as the game progresses the ball moves faster and faster this last variable speed is the current speed we will use both of the constants above to adjust our current speed in addition to speed we also need to know the direction and that's what this variable here is for this script came without a ready function but we need one so I'm going to add in ready function which will be called whenever the node enters the scene tree for the first time which basically means as soon as this ball is created we run this ready function inside of it I'm going to assign a value to this wind size variable I'll use this built-in gdau function again get viewport direct and I will take its size another function I'm going to Define is a new ball function the purpose of this function will be to pick a position and a direction for each ball that we begin the game with in reality we only ever actually use a single ball we only have one note for it and we never delete it but whenever I run this new ball function I'm going to reposition it at the beginning and give it a new Direction so that's what I'll add to begin with is a comment to explain what we're going to do the x coordinate is not going to be randomized that's always going to be half of the window size so I want the ball always to start right in the middle of the game the y coordinate will be randomized though I will use this Rand ey range function to give me a random integer inside of a range this range will limit the ball's y-coordinate to anywhere between 200 pixels from the top or 200 pixels from the bottom of the game screen then I will Define the speed variable which is just going to be the start Speed next I need to pick a direction and to start with I just want a random Direction I'm going to have to create a function for this which is going to be random Direction so I'm going to call this function but I get an error because it doesn't actually exist yet I'll go down here and I'll Define this new function to pick a random Direction I'll first of all create a local variable called New Direction and this will be a vector 2 variable the X Direction can only be left or right it can only go one way or the other so for that I'm going to create an array which contains one and minus one and I will use the pick random function to just pick one of those two values the y direction though will have a lot more flexibility so for this I'm going to pick a random float from this range of -1 to + one now that I've assigned X and Y properties to this this I can return that new Direction variable however because it's a vector 2D I need to make sure that I normalize it this will mean that no matter which direction it's facing in the length of that Vector is always the same at this point we've got a position and a direction for the ball however we're not actually handling movement this needs to be done inside of another function which is physics process just like the process function from the other scripts we created this is called every frame and it contains a Delta variable to make the game frame rate inp dependent now we can move the ball using this built-in move and Collide function inside here I need to pass in the direction multiplied by the speed and multiplied by the Delta variable we've got most of the building blocks in for the ball however if I run it still nothing happens and that's because this new ball function never actually gets called we want this function to be called based on this ball timer so I can go into that node go into the node tab at the top and you'll see it has a signal here called timeout I and double click that and connect it into my main node this will add an additional function into my main script so what I can do is add some functionality inside of this function that will only happen whenever this bow timer takes over the 1 second wait time and the functionality I want is to run that new ball function from my ball node but that function is inside of a different node so how do I get that ball here if I click on the ball node and drag it over I can now access the functions inside of it so I can call newcore ball if I run the game now and wait 1 second the ball fires out in a random direction we still need to add some code into this ball script but I'm going to come back to that first I'm going to add some functionality into the CPU node I will create another script that will attach to that node and like the other scripts I'm going to begin by defining some variables the first one is the ball position when we play the game we can see where the ball is and we move the paddle to intercept it so the AI needs to do a similar thing he needs to know where that ball is actually positioned to be able to move towards it and that's what this ball PA variable is going to store next I create a variable called distance which will be the vertical distance between the AI paddle and the ball then I have a variable called move by which will Define how far the AI paddle should move and lastly a couple of variables that we're already familiar with from the other scripts these are the window height and the paddle height inside my ready function I'm going to get my window height and and paddle height assigned so this code is exactly the same as what I used in the previous script so I'm not going to go into that again and then we can jump over into the process function which is where the paddle is going to move allow the commment to say move the paddle towards the ball the first thing I need to do is work out where the ball is currently positioned the ball node is another child node so the CPU and the ball are both child nodes of the main node to access the variables from that I can again just drag it over like this and this will give me the functions as well as the variables that are inside of that ball node so what I actually want to do is take the position of that ball and assign it into the ball pause variable then I need to work out how far away the ball is from the paddle and that's what this distance variable is going to do I'll take the current paddle position and I'll subtract the ball position from it now that I have these variables I can move the paddle so first I need to calculate the move by variable which I'll do in a similar way as as I did with the player paddle I get my parents paddle speed variable and I multiply it by Delta with that done I can update the paddle y-coordinate using this move by variable and then I remember from the other paddle I need to restrict the movement to the top and bottom edges of the screen using this clamp function if I run this game now the paddle always moves to the top of the screen regardless of which direction the ball is actually going in so each time whether it's going up or down the paddle moves up the way and that's because although the computer knows the distance to the ball it doesn't know whether the ball is above or below the paddle so it doesn't know which direction to move by all it knows is the distance that it has to move by so the position variable is always being reduced by this move by and that's why it always goes up but I need to know whether this distance variable is negative or positive meaning if the ball is above or below the paddle so how do I get the sign of this how do I know if this is a positive or A negative value well for that I'll use a little math trick here where I will divide the distance variable by the absolute value of itself the absolute value is always positive so if the distance variable starts off positive then I take a positive divided by a positive the answer is also positive but if the distance value is negative then this is a negative divided by a positive so it keeps a negative sign this little bit here allows me to give a sign to my move by variable if I run this game now I get a divide by zero error and because the ball and the paddle are at the same y-coordinate so the distance variable is zero I'm going to add a temporary line of code here that will say if the absolute value of distance is greater than zero then we want to run this code down here and if I run this now the paddle doesn't begin moving until the ball is created as soon as it is the paddle moves towards it so this kind of works but there's a little bit of an issue which you can see here the paddle is jittery It's Quickly moving up and down and the reason for that is because it has a fixed speed so every time it moves towards the ball it overshoots and then it has to correct itself on the next frame overshooting again so it gets stuck in the cycle of constantly going back and forth because it misses its Target this move by section needs to be adapted a little bit I'm going to swap out this first line and now I'm going to be saying that instead of just checking if this absolute distance is greater than zero I check whether it's greater than the maximum speed that I'm able to actually move at if it is then this is okay we'll move just as normal but if it isn't then I have an lse statement here and my move by is just the distance itself so it's not going to be the maximum speed anymore it's just the remaining distance that I need to move by to reach the ball if I run it now the result is slightly different you can see the paddle moves much more smoothly towards the ball position and then it stops it doesn't keep jittering up and down and that's us now done with the CPU script as well so I can close that one down and go back into the ball because while we worked on the up and down movement you notice that there's Collision here that isn't really working we're detecting the Collision but then there's no bounce back back inside of the ball script where we have this move and Collide function I can actually return a value from it which is going to be the Collision so whenever it detects the Collision it returns an object from that using the Collision I can then extract a collider I'll create a variable for that to store the value when we get it first I need to check if a collision has actually been detected if it has then I can take the collider object from that Collision using the built-in get collider function and then I can check what it's collided with the first check is whether I've hit one of the paddles and that's done by comparing the collider to one of these nodes to get the node over here you can either type it out manually or just like I did previously you can drag over the node that you're interested in and it will add in the node name like this for you if the ball has hit one of the paddles then I need to do two things the first thing is I need to increase the speed by the acceleration value the second second thing is that I need to bounce off of the paddle and for that we have a bounce function built into Gom which will take the current Vector as well as the collision and it will work out the new direction that we need to move in once we bounce off the paddle that takes care of the ball hitting the paddles so the other option is that if it hits one of the walls we add an else statement down here and I add the same code from above to bounce off of the wall if I run the game now I should be able to bounce these balls back and forth so you can see it's bouncing correctly from the top the bottom as well as both of the paddles and each time we hit the ball back it actually speeds up a little bit based on the acceleration so everything there works pretty well however the way the ball bounces from the paddles is a little bit predictable in the actual pong game the angle of return depends on which part of the paddle was hit by the ball this means it doesn't matter what angle the ball came in at if the ball hits the paddle right in the middle then it bounces off perpendicularly but a collision further from the center bounces back with a greater angle so so I'm going to create a new function that will take care of this new Direction calculation I'll Define a function called New Direction which will take the collider as an argument then I need to work out how far along the paddle the ball hit for that I need to know the position of the ball and I also need to know the position of the paddle I then calculate the distance between the two and create a local variable to store this new Direction inside of the first part when we hit the paddle is to flip the x coordinate because now if it was going left it goes right and if it was going right it goes left it just bounces off the paddle completely and that can be done with this little block of code it says if the Direction X property is moving to the right a is greater than zero then we just set it to minus one but if it's moving to the left then we set it to plus one the Y variable is a little bit more tricky we take the distance variable which is how far the ball center is from the paddle Center and then we divide it by the height of the paddle this gives us a ratio the greater this distance I.E the farther that the ball is from the center the greater this value is going to be I need to then Define what the maximum value could be and that's going to be an additional constant right at the top this is going to be the max y Vector that we can bounce off at and I'm going to set this to 0.6 I can then use this as the final value in this calculation so once we get this ratio we multiply it by the max y Vector once we have the new Direction X and Y properties we have to normalize that vector and return it back from the function to apply this change we go into this section here where we check for collision with one of the two paddles and now instead of just directly bouncing off we call that new Direction function and if I was to test this out now every time the ball hits the CPU paddle it's kind of bouncing off in the middle because the CPU panel matches up nicely but if I was to hit one of the edges of my paddle you can see that it bounces off at an angle same with the top and the bottom and that makes the game a lot more unpredictable that's the ball script complete as well so so we can close this one down and the only thing actually remaining is to take care of the scoring because we've got all of the Collision the ball bounces off correctly from the top to bottom as well as the paddles but when the ball goes off the left and right edges right now nothing happens there was a reason why I set these up as area 2D nodes if we select on one of them and go into the node here we can see that it has a number of different signals the one that I'm interested in is body entered what I can do here is link the signal into my main node just like I did previously with the ball timer and this creates a new function for me I can do the same with my score right node as well and Link the body entered signal into here now I can populate these functions this first one means that the ball has gone through the left hand side and that means that the CPU is the one that's scored so I increase the score for that player if I go back to this variable up here I store the score as an array the first value is the player score the second value is the computer score so that's why I'm increasing the second value by one that will increase the variable but I also want to update this label up here I can access it by going into my heads up display and dragging over the CPU score node I can then go into its text property and set it to the string of this score value once the score is updated I also want to restart that ball timer so that we get another ball generated and now I can do the same for the other function this one is going to record the score for the player so it's going to be the first index at index zero additionally it's no longer the CPU score label it's now the player score label so if I test this out I can try and get a couple of points so if I missed the first one now the CPU has scored it's actually very difficult to beat the AI on this so I'm very unlikely to score anything but you can see that the scoring is working correctly and once the score is done we generate a new ball at a random Direction and now you have a fully functioning pong game complete with some AI if you want to take this further then you could try adding s effects or replacing the CPU with a second controllable player I hope you found this video useful and I'll see you in the next one
Info
Channel: Coding With Russ
Views: 15,943
Rating: undefined out of 5
Keywords: godot, godot tutorial, godot beginner tutorial, godot project, godot beginner project, godot starter project, gd script, godot buttons, godot signal, code, game dev, learn to code, godot 4.0, godot 4, godot 4.0 tutorial, godot engine, pong, collision, ai
Id: Xq9AyhX8HUc
Channel Id: undefined
Length: 27min 4sec (1624 seconds)
Published: Sun Nov 12 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.