This is the beginning of a snake game using a combination of elements you have learned about in class. This activity uses Processing and all code should allow you to copy and paste.
Step 1: Create the Play Space and Player
Create a 600 x 600 size canvas that has a black background and a 25 x 25 square at coordinates (50, 50).
voidsetup() {size(600,600); // 600 x 600 pixel canvasrectMode(CENTER); // Puts the pivot of rectangles and squares in the center}voiddraw() {background(0); // Black backgroundsquare(50,50,25); // 25 x 25 pixel square at coords (50, 50)}
Let's look at the functions already used:
setup()and draw()
For each of these functions, they start with "void" meaning there's no return value or output. They exist to just run code. The parentheses are empty meaning there are no arguments or input needed for the function to run correctly. Here, we are creating these functions and Processing knows these specific functions have specific actions (setup runs once, draw runs on every frame).
size()or size(float width, float height)
Here, we are "calling" or "invoking" a built-in function. The two values we provide in the parentheses are "arguments" or the input for the width and height. Without these values, the function will not work. However, there is a default size for Processing.
rectMode()- takes a mode to know how to handle rectangles and squares
background() or
background(float grayscale)or
background(float red, float green, float blue) or
background(float red, float green, float blue, float alpha)
This is a good example of how four different functions can have the same name and run different code based on the number of arguments. One argument will provide the grayscale or the equivalent of having all three colors the same value. The ones with one or three arguments all have a default alpha of 1. If you use all four arguments, the colors are 0-255 and the alpha is 0-1.
square() or square(float x, float y, float size)
This is actually more like an object in Processing, but it can be seen as a function as well with three arguments - x coordinate, y coordinate, and the size.
Step 2: Move Values to Variables
float x =50;float y =50;float size =25;voidsetup() {size(600,600); // 600 x 600 pixel canvasrectMode(CENTER); // Puts the pivot of rectangles and squares in the center}voiddraw() {background(0); // Black backgroundsquare(x, y, size); // 25 x 25 pixel square at coords (50, 50)}
Output:
Step 3: Add Movement
In the past, we've just added a speed variable to one of the coordinates:
float x =50;float y =50;float size =25;float speed =1;voidsetup() {size(600,600); rectMode(CENTER); }voiddraw() {background(0); x += speed;// Draw your squares lastsquare(x, y, size);}
However, this creates a smooth movement:
We want it to be jumpier like the old snake game, so let's add a delay between the movements.
This delay is basically a timer that resets at zero and triggers a set of code at zero, leaving the delay as long as the delay reset in frames.
Create a delay and delayReset variable. Don't give delay an initial value.
Have delay count down and have the square's x value update only when zero. Don't forget to always reset the delay at the end of the delay if statement.
Instead of speed, have x increase by the size of the square.
voiddraw() {background(0); // counts down delay delay -=1;// Run the code at zeroif (delay <=0) { x += size;// Reset the delay delay = delayReset; }// Draw your squares lastsquare(x, y, size);}
Output when played:
Step 4: Add Other Directions and Keyboard Input
To help organize values, let's have an integer variable keep track of the direction the square is headed. That way, we only have to update the value when we check the keypress and check the value to say which direction to go.
int direction =0; // 0 = right, 1 = down, 2 = left, 3 = up
Check the direction in the delay if statement and add code to update the appropriate coordinate.
...delay -=1;if (delay <=0) {// If going rightif (direction ==0) { x += size; }// If going downelseif (direction ==1) { y += size; }// If going leftelseif (direction ==2) { x -= size; }// If going upelse { y -= size; } delay = delayReset;}...
Add the keyPressed() function. Like setup() and draw(), you are creating a function, but Processing handles the specifically named function to provide information. The one we make will not have a return value or take any arguments.
voidkeyPressed() {}
Check for the arrow buttons using keycodes and update the direction. This way, the direction updates when the user presses the button.
voidkeyPressed() {if (keyCode == RIGHT) { direction =0; }if (keyCode == DOWN) { direction =1; }if (keyCode == LEFT) { direction =2; }if (keyCode == UP) { direction =3; }}
Full code so far:
float x =50;float y =50;float size =25;float delay;float delayReset =30;int direction =0; // 0 = right, 1 = down, 2 = left, 3 = upvoidsetup() {size(600,600); rectMode(CENTER); delay = delayReset;}voiddraw() {background(0); delay -=1;if (delay <=0) {// If going rightif (direction ==0) { x += size; }// If going downelseif (direction ==1) { y += size; }// If going leftelseif (direction ==2) { x -= size; }// If going upelse { y -= size; } delay = delayReset; }square(x, y, size);}voidkeyPressed() {if (keyCode == RIGHT) { direction =0; }if (keyCode == DOWN) { direction =1; }if (keyCode == LEFT) { direction =2; }if (keyCode == UP) { direction =3; }}
Output when played and pressing arrow keys:
Step 5: Add a Target
Add another square as a target with variables to hold its x and y coordinates (but don't set them!). Let's also make the player white and the target red.
We want to set the target coordinates to be random when the game starts and whenever the player touches it. To make it easier to reset often, let's make a function to handle it.
voidresetTarget() {// size/2 offsets the target so it stays within the canvasfloat offset = size/2; targetX =random(0+ offset, width - offset); targetY =random(0+ offset, height - offset);}
It doesn't need to output any info and no information is needed since size is a "global" variable (listed at the top of the script). The variable offset here is an example of a "local" variable - created and only usable within this function.
Now, call this function in the setup() function so the target has a start location.
Using a technique to determine the edges of a square, let's create a function that takes in information about the location of the player square and determines if it's interacting with the target. It will simply return true if it is and false if it is not.
playerX and playerY are parameters here - needed information for the function to run. The information given as arguments should match the data types and playerX and playerY serve as temporary variables within the function.
Now, we can use it in the draw() function to check if the player is touching the target and when it is, the target coordinates update.
if (touchingTarget(x, y)==true) {resetTarget();}
Full code so far:
float x =50;float y =50;float size =25;float targetX;float targetY;float delay;float delayReset =30;int direction =0; // 0 = right, 1 = down, 2 = left, 3 = upvoidsetup() {size(600,600); rectMode(CENTER); delay = delayReset;resetTarget();}voiddraw() {background(0); delay -=1;if (delay <=0) {// If going rightif (direction ==0) { x += size; }// If going downelseif (direction ==1) { y += size; }// If going leftelseif (direction ==2) { x -= size; }// If going upelse { y -= size; } delay = delayReset; }if (touchingTarget(x, y)==true) {resetTarget(); }// Playerstroke(255);fill(255); // Whitesquare(x, y, size);// Targetstroke(255,0,0);fill(255,0,0); // Redsquare(targetX, targetY, size);}voidkeyPressed() {if (keyCode == RIGHT) { direction =0; }if (keyCode == DOWN) { direction =1; }if (keyCode == LEFT) { direction =2; }if (keyCode == UP) { direction =3; }}voidresetTarget() {// size/2 offsets the target so it stays within the canvasfloat offset = size/2; targetX =random(0+ offset, width - offset); targetY =random(0+ offset, height - offset);}booleantouchingTarget(float playerX,float playerY) {float offset = size/2;if (playerX + offset >= targetX - offset && playerX - offset <= targetX + offset && playerY + offset >= targetY - offset && playerY - offset <= targetY + offset) {// If touching, return/output truereturntrue; }else {// otherwise, return/output falsereturnfalse; }}
Output when played:
Step 7: Making It Snake
When the player square hits the target, we want it to add a square to the player's body. As the player goes around, the longer snake tails should appear to snake around. This can be done by having the last square update to the second to last and so on until the first/main square gets told which direction to go.
Let's first set up x and y to be arrays so we can have multiple squares to loop through.
float[] x = { 50 };float[] y = { 50 };
In our if statements, update x and y to x[0] and y[0]. We will be changing the direction of the front square only.
Now, we want to add the current value of the last square in the snake as the next value to add a square to the snake.
...if (touchingTarget(x[0], y[0])==true) { x =append(x, x[0]); y =append(y, y[0]);resetTarget();}...
To see the squares, we need to draw them with a loop toward the end of draw()
This replaces the player square.
for (int i =0; i <x.length; i++) {square(x[i], y[i], size);}
Now, we should update the snake using a loop before the first square changes direction or position. Remember: We are starting with the last square and updating each one in reverse order.
for (int i =x.length-1; i >0; i--) { x[i] = x[i -1]; y[i] = y[i -1];}
This is just the beginning - see if you can code boundaries to show "Game Over" when the player hits the edge of the canvas or its own tail. Also, see if you can code the target to not appear on the snake.