xxxxxxxxxx
491
let RESOLUTION = 500; // Setting canvas resolution
let ROWS = 20; // Number of rows in the grid
let COLS = 20; // Number of columns in the grid
let WIDTH = RESOLUTION / ROWS; // Width of each grid cell
let HEIGHT = RESOLUTION / COLS; // Height of each grid cell
let end_flag = false; // Flag to indicate the end of the game
let start_flag = false;
let bg_flag = 0;
let xVal = 0;
let yVal = 0;
let high_score = 0;
let head_up,
head_left,
apple,
banana,
game_background1,
gamebackground2,
game_font,
bite_sound,
level_up,
game_over,
background_music; // Loading images for background, snake head, apple, and banana; fonts; sounds
class SnakeElement {
constructor(x, y, element_num) {
// Snake element constructor
this.x = x; // X-coordinate of the element
this.y = y; // Y-coordinate of the element
this.element_num = element_num; // Identifier for the type of element
}
display() {
if (this.element_num === 1) {
// Displaying the head facing up
noFill();
stroke(250);
strokeWeight(2);
ellipse(this.x + WIDTH / 2, this.y + HEIGHT / 2, WIDTH + 2);
image(head_up, this.x, this.y, WIDTH, HEIGHT);
} else if (this.element_num === 2) {
// Displaying the head facing down (flipped vertically)
noFill();
stroke(250);
strokeWeight(2);
ellipse(this.x + WIDTH / 2, this.y + HEIGHT / 2, WIDTH + 2);
push(); // Save the current drawing state
scale(1, -1); // Flip vertically
image(head_up, this.x, -this.y - HEIGHT, WIDTH, HEIGHT);
pop(); // Restore the original drawing state
} else if (this.element_num === 3) {
// Displaying the head facing left
noFill();
stroke(250);
strokeWeight(2);
ellipse(this.x + WIDTH / 2, this.y + HEIGHT / 2, WIDTH + 2);
image(head_left, this.x, this.y, WIDTH, HEIGHT);
} else if (this.element_num === 4) {
// Displaying the head facing right (flipped horizontally)
noFill();
stroke(250);
strokeWeight(2);
ellipse(this.x + WIDTH / 2, this.y + HEIGHT / 2, WIDTH + 2);
push(); // Save the current drawing state
scale(-1, 1); // Flip horizontally
image(head_left, -this.x - WIDTH, this.y, WIDTH, HEIGHT);
pop(); // Restore the original drawing state
} else {
// Displaying a circle for the body elements
stroke(250);
strokeWeight(2);
if (this.element_num === 5) {
fill(120, 220, 20); // Green circle
} else if (this.element_num === 6) {
fill(200, 48, 32); // Red circle
} else if (this.element_num === 7) {
fill(251, 240, 76); // Yellow circle
}
ellipse(this.x + WIDTH / 2, this.y + HEIGHT / 2, WIDTH);
}
}
}
class Snake extends Array {
constructor() {
super();
// Initializing the snake with head and initial body elements
this.push(new SnakeElement(RESOLUTION / 2, RESOLUTION / 2, 1));
this.push(new SnakeElement(RESOLUTION / 2 - WIDTH, RESOLUTION / 2, 5));
this.push(new SnakeElement(RESOLUTION / 2 - WIDTH * 2, RESOLUTION / 2, 5));
this.full_flag = false; // Flag to check if the snake has filled the grid
}
display() {
// Displaying all snake elements
for (let element of this) {
element.display();
}
}
move(current_dir) {
// Controlling the movement of the snake
let head_x = this[0].x;
let head_y = this[0].y;
// Updating head position based on the current direction
if (current_dir === "UP") {
this[0].element_num = 1; // Updating element_num for head facing up
this[0].y -= WIDTH; // Moving up
} else if (current_dir === "DOWN") {
this[0].element_num = 2; // Updating element_num for head facing down
this[0].y += WIDTH; // Moving down
} else if (current_dir === "LEFT") {
this[0].element_num = 3; // Updating element_num for head facing left
this[0].x -= WIDTH; // Moving left
} else if (current_dir === "RIGHT") {
this[0].element_num = 4; // Updating element_num for head facing right
this[0].x += WIDTH; // Moving right
}
// Moving the body elements
for (let i = 1; i < this.length; i++) {
let temp_x = this[i].x;
let temp_y = this[i].y;
this[i].x = head_x;
this[i].y = head_y;
head_x = temp_x;
head_y = temp_y;
}
}
collide_self() {
// Checking if the snake collides with itself
for (let i = 1; i < this.length; i++) {
if (this[0].x === this[i].x && this[0].y === this[i].y) {
end_flag = true; // Collision occurred, end the game
game_over.play();
}
}
}
collide_walls() {
// Checking if the snake collides with the canvas borders
if (
this[0].x >= RESOLUTION ||
this[0].x < 0 ||
this[0].y < 0 ||
this[0].y >= RESOLUTION
) {
end_flag = true; // Snake has left the canvas, end the game
game_over.play();
}
}
board_full() {
// Checking if the snake has filled the entire grid
if (this.length === ROWS * COLS) {
end_flag = true; // Board is full, end the game
this.full_flag = true; // Player wins
}
}
}
class Fruit {
constructor() {
// Generating a random position for the fruit
this.x = Math.floor(Math.random() * ROWS) * WIDTH;
this.y = Math.floor(Math.random() * COLS) * HEIGHT;
this.fruit_num = Math.floor(Math.random() * 2); // Randomly choosing apple or banana
}
display() {
// Displaying the fruit based on its type
if (this.fruit_num === 0) {
image(apple, this.x, this.y, WIDTH, HEIGHT);
} else {
image(banana, this.x, this.y, WIDTH, HEIGHT);
}
}
}
class Game {
constructor() {
// Initializing the game with snake, fruit, and default direction
this.snake = new Snake();
this.fruit = new Fruit();
this.current_dir = "RIGHT";
this.score = 0; // Player's score
this.frames = 11;
this.eat_count = 0;
}
display() {
// Displaying the snake, checking for fruit collision, and displaying the score
this.snake.display();
let n = 0;
while (n < this.snake.length) {
// Checking if the fruit is at the same position as any snake element
if (
this.fruit.x === this.snake[n].x &&
this.fruit.y === this.snake[n].y
) {
this.fruit = new Fruit(); // Create a new fruit
}
n++;
}
this.fruit.display();
textSize(10);
fill(0);
text("Score: " + this.score, RESOLUTION - 100, 30);
}
move() {
// Moving the snake, checking for collisions
this.snake.move(this.current_dir);
this.snake.collide_self();
this.snake.collide_walls();
}
eat() {
// Checking if the snake eats the fruit
if (this.snake[0].x === this.fruit.x && this.snake[0].y === this.fruit.y) {
// Adding a new element to the snake based on the current direction
if (this.current_dir === "DOWN") {
this.snake.push(
new SnakeElement(
this.snake[this.snake.length - 1].x,
this.snake[this.snake.length - 1].y - HEIGHT,
6 + this.fruit.fruit_num
)
);
}
if (this.current_dir === "UP") {
this.snake.push(
new SnakeElement(
this.snake[this.snake.length - 1].x,
this.snake[this.snake.length - 1].y + HEIGHT,
6 + this.fruit.fruit_num
)
);
}
if (this.current_dir === "LEFT") {
this.snake.push(
new SnakeElement(
this.snake[this.snake.length - 1].x + WIDTH,
this.snake[this.snake.length - 1].y,
6 + this.fruit.fruit_num
)
);
}
if (this.current_dir === "RIGHT") {
this.snake.push(
new SnakeElement(
this.snake[this.snake.length - 1].x - WIDTH,
this.snake[this.snake.length - 1].y,
6 + this.fruit.fruit_num
)
);
}
this.fruit = new Fruit(); // Create a new fruit
this.score += 1; // Increase the score
if (this.score >= 20) {
bg_flag = 1;
}
frames -= 1;
this.eat_count += 1;
if (this.eat_count % 5 === 0 && this.eat_count <= 30) {
this.frames -= 1;
level_up.play();
}
bite_sound.play();
}
}
rungame() {
// Main method to be called in draw()
if (frameCount % this.frames === 0) {
if (!end_flag) {
// If the game hasn't ended
if (bg_flag === 0) {
image(game_background1, 0, 0, RESOLUTION, RESOLUTION);
} else if (bg_flag === 1) {
image(game_background2, 0, 0, RESOLUTION, RESOLUTION);
}
this.display();
this.move();
this.eat();
} else if (!this.snake.full_flag) {
// If the game has ended and the board is not full
if (bg_flag === 0) {
image(game_background1, 0, 0, RESOLUTION, RESOLUTION);
} else if (bg_flag === 1) {
image(game_background2, 0, 0, RESOLUTION, RESOLUTION);
}
textSize(30);
text("Game Over :(", RESOLUTION / 2 - 175, RESOLUTION / 2 - 15);
textSize(18);
text(
"Final Score: " + this.score,
RESOLUTION / 2 - 120,
RESOLUTION / 2 + 15
);
textSize(10);
text(
"Click the joystick to restart :D",
RESOLUTION / 2 - 150,
RESOLUTION - 40
);
if (this.score > high_score) {
high_score = this.score;
}
push();
textSize(18);
fill(255);
stroke(0);
text(
"High Score: " + high_score,
RESOLUTION / 2 - 110,
RESOLUTION / 2 + 40
);
pop();
} else {
// If the game has ended and the board is full
if (bg_flag === 0) {
image(game_background1, 0, 0, RESOLUTION, RESOLUTION);
} else if (bg_flag === 1) {
image(game_background2, 0, 0, RESOLUTION, RESOLUTION);
}
textSize(25);
text("You Win :D", RESOLUTION / 2 - 140, RESOLUTION / 2);
textSize(7.5);
text(
"Click anywhere to restart :D",
RESOLUTION / 2 - 100,
RESOLUTION - 50
);
}
}
}
}
let game;
function preload() {
// Loading images before setup()
head_up = loadImage("images/head_up.png");
head_left = loadImage("images/head_left.png");
apple = loadImage("images/apple.png");
banana = loadImage("images/banana.png");
game_background1 = loadImage("images/snake_game_background1.png");
game_background2 = loadImage("images/snake_game_background2.png");
game_font = loadFont("fonts/snake_game_font.ttf");
bite_sound = loadSound("sounds/bite.m4a");
level_up = loadSound("sounds/levelup.mp3");
game_over = loadSound("sounds/gameover.mp3");
background_music = loadSound("sounds/backgroundmusic.m4a");
}
function setup() {
// Setup function for creating the canvas and initializing the game
createCanvas(RESOLUTION, RESOLUTION);
game = new Game();
textFont(game_font);
}
function draw() {
// Draw function for running the game
if (xVal >= 1000) {
start_flag = true;
}
if (!background_music.isPlaying()) {
background_music.play();
}
if (start_flag === true) {
game.rungame();
} else {
image(game_background1, 0, 0, RESOLUTION, RESOLUTION);
push();
textSize(50);
text("SNAKE", RESOLUTION / 2 - 120, RESOLUTION / 2 + 20);
textSize(15);
strokeWeight(1.5);
text(
"the most original game ever!",
RESOLUTION / 2 - 205,
RESOLUTION / 2 + 40
);
pop();
}
if (!serialActive) {
stroke(255);
strokeWeight(2);
text("Press Space Bar to select Serial Port", 20, 30);
} else if (start_flag === true) {
text("Connected", 20, 30);
} else {
text("Connected. Press the joystick to \nstart playing!", 20, 30);
}
// changing the direction of the snake using values from arduino
if (xVal < 300) {
// joystick moved left
if (game.current_dir !== "RIGHT") {
game.current_dir = "LEFT";
}
} else if (xVal > 700 && xVal < 1000) {
// joystick moved right
if (game.current_dir !== "LEFT") {
game.current_dir = "RIGHT";
}
} else if (yVal < 300) {
// joystick moved down
if (game.current_dir !== "UP") {
game.current_dir = "DOWN";
}
} else if (yVal > 700) {
if (game.current_dir !== "DOWN") {
// joystick moved up
game.current_dir = "UP";
}
} else if (xVal >= 1000) {
// restart game when joystick pressed
if (end_flag === true) {
end_flag = false;
game = new Game();
bg_flag = 0;
}
}
}
function mousePressed() {
// Restart the game on mouse click
if (end_flag === true) {
start_flag = true;
end_flag = false;
game = new Game();
bg_flag = 0;
}
}
function keyPressed() {
// Change the direction of movement on arrow key press
if (keyCode === LEFT_ARROW) {
if (game.current_dir !== "RIGHT") {
game.current_dir = "LEFT";
}
} else if (keyCode === RIGHT_ARROW) {
if (game.current_dir !== "LEFT") {
game.current_dir = "RIGHT";
}
} else if (keyCode === UP_ARROW) {
if (game.current_dir !== "DOWN") {
game.current_dir = "UP";
}
} else if (keyCode === DOWN_ARROW) {
if (game.current_dir !== "UP") {
game.current_dir = "DOWN";
}
}
if (key == " ") {
// important to have in order to start the serial connection!!
setUpSerial();
}
}
// This function will be called by the web-serial library
// with each new *line* of data. The serial library reads
// the data until the newline and then gives it to us through
// this callback function
function readSerial(data) {
////////////////////////////////////
//READ FROM ARDUINO HERE
////////////////////////////////////
if (data != null) {
// make sure there is actually a message
// split the message
let fromArduino = split(trim(data), ",");
// if the right length, then proceed
if (fromArduino.length == 2) {
// only store values here
// do everything with those values in the main draw loop
xVal = int(fromArduino[0]);
yVal = int(fromArduino[1]);
}
}
}