HAILSTORM HAVOC
My game draws significant inspiration from the hailstorm that occurred in Abu Dhabi a few weeks ago.
I was captivated by the idea of creating a game where the player must dodge hail. I envisioned a game centered around a car avoiding the hail, however, this concept proved to be overly complex, and after trial and error, I was able to overcome this challenge. To play the game: players must move the car away from the falling hail using the mouse click function. Interestingly, the mouse click function in my game serves two purposes, a feature I was unaware of until Pi assisted me in rectifying a misunderstanding. This allowed me to control both the car and a sprite, adding a fascinating layer to the gameplay.
function mousePressed() { console.log("Mouse Press"); //just flipping between modes 0 and 1 in this example clouds.forEach((cloud) => cloud.startAnimation()); } function mouseReleased() { clouds.forEach((cloud) => cloud.stopAnimation()); }
How the game works:
-
- You must press Click to start
- To move the car you have to press the mouse
- The longer the press the further right the ball will go
- Dodge the hail (white balls) and avoid touching the red line as both are game over
- Press “space” to restart the game
This is my game:
If it doesn’t work, visit:
https://editor.p5js.org/Afrabinjerais/sketches/q0cF4mKOG
The game is straightforward yet incorporates all the coding components we have learned to date. Its design is uncomplicated, effectively conveying the intended message. I got inspired by this p5 sketch and, the link takes you to a simple game on P5, which has a similar concept to my game, where the objects are falling from the sky.
https://editor.p5js.org/jordanBlueshift/sketches/vSO_bzkaD
My favorite aspect of my game is the cloud sprites, which fidget whenever the mouse is clicked, creating the impression that the clouds are generating hail. On another hand, I also encountered a significant challenge when integrating sound effects to play each time the score increased, which was undoubtedly the most difficult part for me to implement.
time += 1; if (frameCount % 60 == 0) { score++; } function scoreUpdate() { // score += 10; if (score != prevScore) { scoreSound.play(); } prevScore = score; fill(128, 128, 128, 150); rect(width - 100, 5, 75, 20, 5); fill(255); text("SCORE: " + int(score), width - 65, 15); }
The issue stemmed from the points increasing too rapidly. By reducing the timer and implementing a modulo operation as suggested by the professor, I was able to resolve this problem. Looking ahead, or given more time, I would be eager to experiment with transforming the background to be interactive and making it move to visualize a street. Although my initial attempt at this modification was unsuccessful, I am keen on dedicating time to delve into unfamiliar areas of coding to make this feature a reality.
This is the code for the game:
let gameMode = 0; // Variable to store the current game mode let musicSound; // Variable to hold the music sound object let gameoverSound; // Variable to hold the game over sound object let scoreSound; // Variable to hold the score sound object var landscape; // Variable to store the landscape graphics var car_diameter = 15; // Diameter of the ball var bomb_diameter = 10; // Diameter of the bombs var xpoint; var ypoint; var zapperwidth = 6; // Width of the zapper var numofbombs = 10; // Number of bombs var bombposX = []; // Array to store X positions of bombs var bombposY = []; // Array to store Y positions of bombs var bombacceleration = []; // Array to store acceleration of each bomb var bombvelocity = []; // Array to store velocity of each bomb var time = 0; // Variable to track time, usage context not provided var timeperiod = 0; // Variable to store a time period, usage not clear without further context var score = 0; // Variable to store the current score var posX; // X position, usage context not provided var inMainMenu = true; // Boolean to check if the game is in the main menu var prevScore = 0; // Variable to store the previous score let font; // Variable to store font, usage context not provided //Cloud Variables let spritesheet; let oneDimensionarray = []; function preload() { spritesheet = loadImage("clouds.png"); musicSound = loadSound("sounds/song.mp3"); gameoverSound = loadSound("sounds/gameover.mp3"); scoreSound = loadSound("sounds/score.mp3"); glassbreak = loadSound("sounds/glassbreaking.wav"); font = loadFont("fonts/Inconsolata_Condensed-Light.ttf"); car = loadImage("car.png"); car2 = loadImage("car2.png"); } // Cloud class starts class Cloud { constructor(x, y, speed, stepSpeed, scale) { this.x = x; this.y = y; this.scale = scale; // Add scale property this.speed = speed; this.stepSpeed = stepSpeed; this.step = 0; this.facingRight = false; // Initially moving to the left this.animationTimer = null; } move() { if (this.facingRight) { this.x += this.speed; if (this.x > width + spritesheet.width / 4) { this.x = -spritesheet.width / 4; // Wrap around to the left side } } else { this.x -= this.speed; if (this.x < -spritesheet.width / 4) { this.x = width + spritesheet.width / 4; // Wrap around to the right side } } } display() { push(); if (!this.facingRight) { scale(-this.scale, this.scale); // Apply scale with horizontal flip image(oneDimensionarray[this.step], -this.x, this.y); } else { scale(this.scale, this.scale); // Apply scale image(oneDimensionarray[this.step], this.x, this.y); } pop(); } advanceStep() { this.step = (this.step + 1) % 8; } startAnimation() { this.facingRight = true; clearInterval(this.animationTimer); this.animationTimer = setInterval(() => this.advanceStep(), this.stepSpeed); } stopAnimation() { this.facingRight = false; clearInterval(this.animationTimer); } } let clouds = []; // Cloud class ends function setup() { createCanvas(640, 480); textAlign(CENTER); musicSound.play(); var temp00 = 0, temp01 = -20; // A while loop that increments temp01 based on temp00 until temp01 is less than the canvas height while (temp01 < height) { temp00 += 0.02; // Increment temp00 by 0.02 in each loop iteration temp01 += temp00; // Increment temp01 by the current value of temp00 timeperiod++; // Increment timeperiod in each iteration } // Calculate the initial position of posX based on zapperwidth and car_diameter posX = zapperwidth + 0.5 * car_diameter - 2; // Set xpoint and ypoint relative to the width and height of the canvas xpoint = 0.7 * width; // Set xpoint to 70% of the canvas width ypoint = height - 0.5 * car_diameter + 1; // Set ypoint based on the canvas height and car_diameter initbombpos(); // Call the initbombpos function (presumably initializes bomb positions) imageMode(CENTER); // Set the image mode to CENTER for drawing images centered at coordinates // Initialize variables for width and height based on sprite sheet dimensions divided by specific values let w = spritesheet.width / 2; let h = spritesheet.height / 4; // Nested for loops to extract sprite images from the sprite sheet and push them to oneDimensionarray for (let y = 0; y < 4; y++) { for (let x = 0; x < 2; x++) { oneDimensionarray.push(spritesheet.get(x * w, y * h, w, h)); // Get sub-images from spritesheet and add to oneDimensionarray } } // Create 3 clouds with horizontal offsets, different speeds and scales clouds.push(new Cloud(width / 8, height / 9, 0, 100, 0.9)); // First cloud clouds.push(new Cloud((2 * width) / 5, height / 9, 0, 100, 1.2)); // Second cloud clouds.push(new Cloud((2 * width) / 2, height / 9, 0, 200, 1.0)); // Third cloud } function draw() { background(58, 66, 94); if (gameMode == 0) { clouds.forEach((cloud) => { cloud.display(); }); textFont(font); fill(255); textSize(50); // Larger text size for the game title textAlign(CENTER, CENTER); // Align text to be centered text('HAILSTORM HAVOC', width / 2, height / 2 - 40); textSize(16); // Smaller text size for the directions // Draw the directions right below the game title text('DIRECTIONS:\n click mouse to dodge hail\n the longer the press the further right\n the car will go\n\n AVOID the red line - crossing it means game over', width / 2, (height / 2) + 50); textSize(20); text('Click to start!', width / 2, (height / 2) + 140); } else if (gameMode == 1) { clouds.forEach((cloud) => { cloud.move(); cloud.display(); }); fill(239, 58, 38); rect(0, 0, zapperwidth, height); scoreUpdate(); fill(255); noStroke(); for (var i = 0; i < numofbombs; i++) { ellipse(bombposX[i], bombposY[i], bomb_diameter, bomb_diameter); } updatebombpos(); // ellipse(xpoint, ypoint, car_diameter, car_diameter); image(car,xpoint, ypoint-30, car_diameter*5, car_diameter*5); xpoint -= 3; // Check if the mouse is pressed and the xpoint is within the canvas boundaries if (mouseIsPressed && xpoint + 0.5 * car_diameter < width) { xpoint += 6; // Move the xpoint to the right by 6 units } // Check if xpoint is less than or equal to posX or if a collision with a bomb has occurred if (xpoint <= posX || bombCollistonTest()) { gameover(); // Call the gameover function if either condition is true } // Increment the score every 60 frames time += 1; if (frameCount % 60 == 0) { score++; // Increase score by 1 } } } function updatebombpos() { // Iterate over each bomb for (var i = 0; i < numofbombs; i++) { bombvelocity[i] += bombacceleration[i]; // Update the velocity of the bomb by adding its acceleration bombposY[i] += bombvelocity[i]; // Update the Y position of the bomb based on its velocity } if (time > timeperiod) { initbombpos(); // Reinitialize the positions of the bombs by calling the initbombpos function time = 0; } } function initbombpos() { for (var i = 0; i < numofbombs; i++) { bombacceleration[i] = random(0.02, 0.03); bombvelocity[i] = random(0, 5); bombposX[i] = random(zapperwidth + 0.5 * car_diameter, width); bombposY[i] = random(-20, -0.5 * car_diameter) + 190; } } //This function initializes the position and motion properties of each bomb by assigning random values within specified ranges. function bombCollistonTest() { var temp = 0.5 * (car_diameter + bomb_diameter) - 2; var distance; // Iterate over each bomb to check for a collision for (var i = 0; i < numofbombs; i++) { distance = dist(xpoint, ypoint, bombposX[i], bombposY[i]); if (distance < temp) { return true; } } return false; } //This function checks for collisions between the player and each bomb by comparing the distance between them to a threshold. If any bomb is too close (within the threshold), it returns true (collision detected). Otherwise, it returns false. function gameover() { image(car2,xpoint, ypoint-30, car_diameter*5, car_diameter*5); musicSound.pause(); glassbreak.play(); gameoverSound.play(); textFont(font); fill(255); textSize(50); // Set the text size textAlign(CENTER, CENTER); // Align text to the center text("GAME OVER", width / 2, height / 2 - 20); // Center the text horizontally and vertically textSize(15); // Decreased text size for "press space to restart" text text("Press space to restart", width / 2, height / 2 + 20); // Positioned below "GAME OVER" text noLoop(); } function scoreUpdate() { // score += 10; if (score != prevScore) //// Play the scoring sound only if the current score has changed from the previous score { scoreSound.play(); } prevScore = score; fill(128, 128, 128, 150); rect(width - 100, 5, 75, 20, 5); fill(255); text("SCORE: " + int(score), width - 65, 15); } function keyPressed() { if (keyCode === 32) { // Check if the key pressed is space (keyCode 32) restartGame(); // Call the function to restart the game } } function mousePressed() { if (gameMode==0) gameMode=1; console.log("Mouse Press"); //just flipping between modes 0 and 1 clouds.forEach((cloud) => cloud.startAnimation()); } function mouseReleased() { clouds.forEach((cloud) => cloud.stopAnimation()); } function restartGame() { // Reset all game variables to their initial values musicSound.play(); gameoverSound.pause(); time = 0; score = 0; posX = zapperwidth + 0.5 * car_diameter - 2; xpoint = 0.5 * width; ypoint = height - 0.5 * car_diameter + 1; initbombpos(); // Restart the game loop loop(); } //This function resets the game environment and variables to their initial state, essentially restarting the game. It resumes background music, pauses any game over sound, resets score and time, repositions the player and bombs, and restarts the game loop.
Enjoy!
References of pictures: