Snake: Make A Classic Game using Javascript & P5.js Framework | Saran Siri | Skillshare

Snake: Make A Classic Game using Javascript & P5.js Framework

Saran Siri, Instructor

Play Speed
  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x
9 Lessons (34m)
    • 1. Promo

      0:57
    • 2. Basics

      6:14
    • 3. Move Your Character

      6:59
    • 4. Add Tail

      3:10
    • 5. Add Apple

      3:09
    • 6. Check Collision

      2:56
    • 7. Add Score System

      3:11
    • 8. Share It

      0:53
    • 9. Challenges

      6:07

About This Class

This is my game tutorial series where I teach javascript and P5.js framework using classic games.

Check out other games

In this class, we will make a classic Pong game using free online P5.js editor

P5* editor => https://editor.p5js.org

Transcripts

1. Promo: Hello, my name is Saran, and thank you to click on this course. This course will help you get started on coding by making a classic game -- snake. We will be using JavaScript at the beginning level, along with the graphic framework - P5.js. There's also a free online editor running on the browser that you can learn from any device In the first part, we will take a look at the basic functions from P5.js, how the coordinate system works and how to draw and animate simple objects. Then we will be working on moving our snake, on the grid system, controlling its behaviour, adding his tail, adding an apple and then setting up the score system. You can follow me for more game tutorials. Now let's open the next lesson and get started. 2. Basics: In this first video, we will talk about the basics of P5 framework, open a browser and type in editor.p5js.org. In here, we have a coding panel on the left side, and a preview on the right side. When you hit Run, it reads all the code here, executes it and shows the preview of the game on the right side. I recommend creating an account right away, because after signing in, you can save all the projects and avoid accidentally refreshing the screen, which clears the code you have written. In the coding panel, we have two main functions. The setup function and the draw function. As the name implies, the setup function is for assigning initial values, like how the character would look like once the game starts and all the code inside its brackets will be run only once. Right now we have one method, called createCanvas, which sets the display screen to 400 by 400 pixels, 400 width and 400 height. We can change the width to 600 and hit run to see the change. Let's set it back to 400 by 400 for now, because that is the resolution we will use for the game. In a drawer function, we have background method, which clears the entire display screen using the grey value of 220, which is the value for a light grey colour. Change the number to 0, and you get a black colour, or 255 for a white colour. I will change it back to 220. For other colours, you can google -- RGB colour picker. Pick the colour you want, and used these 3 values for the background. I will pick the black colour, which is 0, 0, and 0. If all 3 numbers are the same, we can get rid of the two. Once all the code in the setup function is called, the program would then run the draw function repeatedly. That means, whatever inside the draw function, it will be called 60 times per second by default. For example, if I write a method print, parentheses, frameCount, and hit Run, you will see that it prints the frame count to the console window below, at the rate of 60 times per second, we can slow it down by calling frameRate method inside the setup function. Set it to 1 frame per second, and run it again. This time, the draw function is being called only one time per second. Next, let's take a look at the coordinate system. In most 2d game engines, including our P5 engine, the origin starts at the top left corner, which means, this point is X = 0, Y = 0. Moving to the right increases the X value on the X coordinate. Moving downwards increases the value on the Y coordinate, which is the opposite of what we have learned in the mass class. Let's play with the basic shapes that we will use in the snake game. The first one is a rectangle. A rectangle method needs 4 input parameters: X position, Y position, the width of a rectangle, and the height of a rectangle. To draw a 60 by 30 rectangle at the origin point, we write, rect, parentheses, X is 0, Y is 0, width is 60, and height is 30. To change the colour of the rectangle, we will use the fill method, fill method fills the colour in the area of the drawn shapes. We have to call it before drawing any shape, because the code runs from top to bottom. So if we want a red rectangle, we call fill, and put the RGB numbers on a red colour, which is red 255, green 0, and blue 0. Let's draw a blue rectangle at the centre, First, change the fill colour to blue, using fill method right after the last line fill, red 0, green 0, and blue 255. Then call rect, at 200, 200, width 100 and height 100. Even though they look like a still image, the draw function is being called repeatedly. Let's change the frameRate to 10, and change the position X to frameCount instead, as you can guess, frameCount goes up every time the draw function is called. If we use that for the X value, the rectangle will move to the right, and eventually travel out of the screen. Now that we cover the basics, in the next video, we will draw a snake and make it move. Thank you. 3. Move Your Character: For our classic snake game, we will use a rectangle as our snake. It could be 20 pixels in size. Since we set the size of the display screen to 400 by 400 pixels, The snake should be able to travel within 20 by 20 blocks, and move 20 pixels at a time to the next position. Let's make the constant variables on top first. var, number of blocks, equals 20. And each block is 20 pixels wide. So, var, the blockSize, equals 20. Then we need to create variables to track the position of the snake's head. We will call it headX and headY, var headX, equals 0, var, headY, equals 0. We will then, remove everything except the background for now. Next, we would draw a snake's head with a white rectangle. Change the few colour to white, so fill, 255 255, and 255. Or just fill, 255. Then draw the rectangle using rect method. The first perimeter is the X position, which is handX, times blockSize. Next, is Y position, which is headY, times blockSize. The last two parameters are for the width and height of the rectangle which are 20, or the blockSize, hit Run. And we can see the head at 0, 0. We test it by setting a new position in the setup function, move it to the centre of the screen, which is basically half the number of the blocks. headX, equals numOfBlocks/2, headY equals numOfBlocks/2. I will add a comment here, by starting a new line with //. Anything after that in that line will not count as the code. Now we can move the hand around. Next, we can detect keys when user presses, by adding keyPressed function, below the draw function enter a new line, function, keyPressed, parentheses, and brackets. The user will press WASD keys to move the head. If the user presses A, the snake's head should move to the left, with the constant horizontal. If the user, presses S, the head should move downwards, with the constant vertical speed. So we need 2 more variables, the horizontal speed, and the vertical speed. We'll call them speedX and speedY. Scroll up to the top, and add, var speedX equals 0, var speedY equals 0. As I mentioned in the last video, that X increases when moving to the right side, and Y increases when moving downwards. When the player hits W key to move upwards. headY should decrease. Let's add the logic in the keyPressed function. If a key, that the user is pressing, equals, W character. speedX should be 0, but the speedY should decrease. So set it to -1. If the user is not pressing W but press S, it should go in the opposite direction. speedX stays the same, but speed why should be 1 instead. When pressing A, the head should move to the left. SpeedX to be -1, SpeedY should be 0. And the last one For D key, speedX should be the opposite, speedY is the same as A key. Once we assign our keys, every time the draw function is called, we should update the position of the snake's head, according to the speed. The new headX equals the old headX + speedX. The same goes for the Y position, new headY, equals, old headY, + speedY Hit Run. Now we can move it around using keys. The problem is that the snake's head, can sometimes go out of the screen. We can make it loop back to the other side of the screen, by adding some conditions, saying, If headX is too far to the left, then teleport it to the far right. If headX, is negative, then the new headX equals 19, which is numOfBlocks, 20 - 1. The same when moving to the far right. Make it appear from the left instead. Do the same concept for the Y coordinate, And hit Run. In the next video, we will add the tail to the snake. Thank you. 4. Add Tail: To make it look like a snake, it needs its tail. We will attach the tail blocks to its head. First, define the length of the tail, by creating a variable called tailLength, and set it to 3. var tailLength equals to 3. Basically, tailBlocks will appear every time the head moves to the new position, we need to keep track of the old opposition the head has moved, to draw the tailBlocks. To do that, we will create an empty array called tailBlocks, var tailBlocks, equals, an empty array. When the head moves to the new position, add that position to the tailBlocks array. We will use push function, which pushes an object to the back of the array. So it will be tailBlocks, dot push. Inside the parameter, we will create a JSON object with 2 key pairs. keyX equals value headX. keyY equals headY. So once it moves away from the centre, the position X 10, Y 10, will be pushed to the tailBlocks. Then, we will draw all positions, inside the tailBlocks. for, let i, equals 0, i < tailBlocks dot length, i++ Then draw the rectangle using rect method. Get the position of X at, i, times blockSize. Get the position of Y at i, times blockSize. Then, blockSize. Next, we need to cut the tail when it's too long. Keep removing the oldest positions from the left side of the array, until the length of the array is 3, by using shift method. So before drawing the tailBlocks, ask, while the length of the array, tailBlocks dot length, is > the tailLength, which is 3. Then, tellBlocks dot shift. In the next video, we will feed the snake with an apple. Thank you. 5. Add Apple: Just like creating the snake's head, we need 2 variables to track apple's position. Let's create appleX and appleY, var appleX, equals 0, var appleY equals 0. Then, in the setup function, set the apple's position by randomly picking an integer number, between 0 and 19. Starting with appleX is equal to random number from 0 to less than numOfBlocks. Do the same thing for appleY. Since random method also gives you a decimal number like 1.5, we will wrap it with a floor method, in order to get rid of the decimal point. Do the same thing for appleY. Then draw the apple before drawing the head. We will first change the few colour to red colour. fill, red 255, green 0, blue 0. And then, draw the apple using rect function like drawing the head. Instead of headX and headY, change it to appleX and appleY. Run it. When the head hits the apple, the apple should randomly move to the new position, to make it look like the snake eats it, And the new one appears. Before the rect function, ask when the head and the tail are in the same position. If headX equals appleX, and headY equals appleY. If that is true, randomly pick a new apple position. Copy what we wrote in the setup function, and increase the tailLength's limit to have it more room for the tailBlocks array to grow. tailLength++, to increase it by 1. In the next video, we we detect the collision between the head and the tail. Thank you. 6. Check Collision: The game should end when our snake hits his own tail, meaning that we need to check whether the head position is equal to any position in the tailBlocks array. If they occupy the same position, the game ends. The snake should stop moving, and we should reset everything back to the starting point. Enter a new line at the end of background method. Ask if the snake is still moving, meaning that either one of the speech is not 0. If speedX is not 0, or speedY is not 0. We will wrap it, then check each block in the tailBlocks. for, let i equals 0, i < tailBlocks dot length, i++ Then if the positions are the same, the game ends here. If headX plus speedX, equals, tailBlocks at i dot x, and headY, plus speedY, equals tailBlocks at i dot y. When the game ends, move the head back to the centre. I will copy it here. Reset the speeds to 0. Reset the tail's values, and finally, randomize the apple's position. Let's try eating the apple. Also try eating his own trail. This also works when the snake changes the direction immediately from left to right. And now we got to core mechanics. In the next video, we will add the score system. 7. Add Score System: To finish off the game, let's create a score to track how many apples that snake has eaten and high score to track the best score. The score increases when the snake eats the apple, and if it dies, the score should reset to 0. First, create a new variable called score. Starting at zero. var score, equals 0. When the snake eats the apple, score increases by 1. In the if statement, when the head eats the apple, add, score plus plus. And when the game ends, the score should also be reset to 0. score equals 0. We should also have high score, so let's make another variable. Set it to 0. var hiscore, equals 0. When the game ends before the score resets, check if the current score is > the hiscore. If it is, the current score is a new high score. If, score, greater than, hiscore. If that is true, hiscore equals score. Finally, in the last line of the draw function, draw both score and hiscore using text method. Use fill to set the text colour to white, 255. To change the size of the text, use textSize method. textSize, make it 20, and then call the text method. The first parameter is the text itself. Let's say score, colon, and join it with the score's value. The second and the third parameters are X and Y position. Let's put it at 10, 30. For the hiscore, put it below at 10, 50. And that's it for this snake game, I encourage you to make adjustments and make your own version. The things you can improve are, adding a green apple that scores 2 instead of 1, from the right apple, adding an obstacle that ends the game when the snake hits it. Changing the colour of the snake, background and scoreboard. Thank you. 8. Share It: When you are happy with the result, let's chair in the final game. To get the sharable link, first, make sure that you logged in. You can change the name of the project. Save it. Then go to File -> Share, and use the full screen link, if you want others to see the code behind it. Use to the present link to get a stand alone game, or an embedded iFrame, to add to your website. To learn more about P5 framework, check out my latest courses in my profile, and let me know in a comment, which topics you want to learn more. Thank you. 9. Challenges: In this video, I will show things you can improve. First, to add another apple, we need 2 more variables. Let's call it greenAppleX and greenAppleY. var greenAppleX, equals 0. var greenAppleY equals 0, randomize the position just like the red apple, change apple to greenApple When the snake eats it, the score should increase by 2 instead of 1. The easiest way to do that, is to copy the if statement when the head eats the apple, and change apple to green apple, then increase the tailLength and score, by 2. Instead of using plus plus, change it to plus equals 2. Also, copy the draw method from Apple, and draw greenApple below. Change the colour to green, which is red 0, green 255, blue 0. Test it, next, to add an obstacle, we will add another array. to store wallBlocks. When the snake hits one of the war blocks, the game ends just like hitting his own tail. Starting with, var, wallBlocks, equals, an empty array, In setup function, let's build the wall, Push the initial positions to walkBlocks, wallBlocks, dot push, keyX equals 5, keyY equals 5, and make the wall vertical. When this snake hits it, it will restart the game just like hitting tailBlocks. I will wrap the code when the game ends, and create a new custom function. Outside the draw function, type function, restart, parentheses, brackets. Move the code here, and then when the game ends, just called the restart method instead. Test it. It works just like before. Next, we will copy how we check each block in tailBlocks. Paste it below, and change tailBlocks to wallBlocks. The same goes for drawing the wallBlocks. Let's fill the wall colour to light grey. fill, 220, test it. The last one, is to make it a bit more beautiful. Right now, the default outline colour is black. To change the outline colour, use stroke method, followed by the colour numbers. For example, stroke 0, 0, 255, to make it blue. To remove the outline, used noStroke method. I would try to match the style I use in the presentation. Remove noStroke method, and use stroke, 180. Change the background colour to absolute white, make the tail's colour different from the head's. fill, 240, made the wall a bit darker, 200, and use black colour for the text. stroke method also affects the text, and creates unwanted outline. To get rid of the extra layer, use noStroke method for the text. Thank you.