Link to the fullscreen version
Concept: My midterm project is a game I’ve named “Gold Rush”. It’s inspired from the Super Mario games’ design. The game features a character Super Onion boy that the player controls to jump over and walk to dodge enemies and collect coins to increase their score. The main character, an adventurer with a knack for treasure hunting, traverses a dynamically scrolling landscape filled with gold coins for him to collect. The game introduces a unique enemy, the “GOMBA,” a mysterious creature with a purple body, tentacles, and light-flickering eyes, adding a layer of intrigue and challenge to the player’s journey. Dodging the enemy gains the player +5 points and collecting each coin gains them +10 points, and the score system is displayed on top of the screen while the game is playing. If the player collides with the enemy GOMBA, the game over screen appears and to play again – the player needs to press SHIFT key and the game will restart.
User Experience: The game is structured around a loop where the player’s character is continuously moving forward, with the background scrolling to simulate progression through the game world. This effect is achieved through an implementation of looping background images that reset once they scroll past the view, creating an endless moving landscape. The characters, including the player, the coin, and the enemy, are animated using sprites, which are sequences of images that create the illusion of motion when displayed in rapid succession.
One aspect of the project I’m particularly proud of is the collision detection mechanism. It’s designed to be both efficient and accurate enough for the game’s scope, using simple geometric shapes to approximate the player, coins, and enemies’ positions. This simplicity ensures the game runs smoothly without compromising the gameplay experience. Another highlight is the sound integration, which enhances the immersive experience of the game. The careful selection and timing of sound effects for jumping, collecting coins, and colliding with enemies add a layer of polish that elevates the overall game.
Areas for Improvement and Challenges: One area for improvement involves enhancing the game’s replay value. Currently, the game offers a straightforward challenge with a single level that repeats. Introducing varied levels with increasing difficulty with speed, diverse environments, and additional enemy types could significantly enhance the player’s experience and engagement. Implementing a power-up system or special abilities for the character could also add depth to the gameplay.
During development, one challenge encountered was ensuring that the game remained engaging over time. Initially, if the player missed a coin, it wouldn’t reappear, which could’ve led to a monotonous gameplay loop. This issue was addressed by adjusting the coin’s reinitialization logic to ensure it reappears regardless of whether it’s collected or missed, maintaining the game’s pace and challenge. Another technical hurdle was balancing performance with the visual richness of the game, particularly with sprite animation and background scrolling. Also to integrate the sound loops perfectly was hard at first but after fixing the code logic in different ways the desired results were received.
Full code for the game:
//GOLD RUSH BY RAYA TABASSUM //CHARACTERS: SUPER ONION BOY, GOLD COIN, GOMBA THE ENEMY //5 POINTS FOR AVOIDING THE ENEMY, 10 POINTS FOR COLLECTING THE COIN // Preload function for loading assets before the game starts let img, game_over_bg, sf, coin_sound, jump_sound, enemy_sound, pixel_font; let xpos = 0, ypos = 0, x1 = 0, x2 = 1000, y = 0, x3 = 0, y2 = 0, score = 0, win = true; let player, enemy, coin; function preload() { img = loadImage("plt.png"); // Background image game_over_bg = loadImage("Gameover.png"); // Game over background image soundFormats('mp3', 'ogg'); // Specify the sound formats to use sf = loadSound("backgroundmusic.mp3"); // Background music coin_sound = loadSound("coin.mp3"); // Sound for collecting coins jump_sound = loadSound("jumping.mp3"); // Sound for jumping enemy_sound = loadSound("enemy_sound.mp3"); // Sound when colliding with an enemy pixel_font = loadFont("PixelFont.ttf"); // Custom font for text display } // Player class with properties and methods for the player character class Player { constructor() { this.playerYOnGround = 550; // Y position of the player on the ground this.playerSize = 60; // Size of the player character this.bgGroundHeight = 45; // Height of the ground this.animationSlowDown = 8; // Slows down the animation frame rate this.width = 1000; // Width of the canvas this.jumpHeight = 0; // Current jump height this.jumpStrength = 0; // Current strength of the jump this.jumpStrengthMax = 5; // Maximum strength of the jump this.gravity = 0.1; // Gravity affecting the jump this.jumping = false; // Is the player jumping? this.playerImg = []; // Array to hold player images for animation this.numberPlayerImg = 6; // Number of player images this.playerImgIndex = 0; // Current index of the player image for (let i = 1; i <= 3; i++) { this.playerImg.push(loadImage(`guy-${i}.png`)); // Load player images } } initPlayer() { xpos = (this.width * 0.5) - (this.playerSize * 0.5); // Initialize player's horizontal position ypos = this.playerYOnGround; // Initialize player's vertical position } animatePlayer() { // Handle jumping logic if (this.jumping) { this.jumpStrength = (this.jumpStrength * 0.99) - this.gravity; // Apply gravity this.jumpHeight += this.jumpStrength; // Update jump height if (this.jumpHeight <= 0) { // Reset jump parameters if player is back on ground this.jumping = false; this.jumpHeight = 0; this.jumpStrength = 0; } } ypos = this.playerYOnGround - this.jumpHeight; // Update player's vertical position based on jump // Display the player image, use jumping image if jumping or animate otherwise if (this.jumping) { image(this.playerImg[0], xpos, ypos); } else { image(this.playerImg[this.playerImgIndex], xpos, ypos); // Animate player images if (frameCount % this.animationSlowDown === 0) { this.playerImgIndex = (this.playerImgIndex + 1) % 3; } } } } // Enemy class, inherits from Player and represents the enemy character class Enemy extends Player { constructor() { super(); // Call the parent class constructor this.enemyImg = []; // Array to hold enemy images for animation for (let i = 1; i <= 3; i++) { this.enemyImg.push(loadImage(`enemy-${i}.png`)); // Load enemy images } this.enemyX = 800; // Initial horizontal position of the enemy this.enemyY = 460; // Initial vertical position of the enemy this.enemySize = 100; // Size of the enemy character this.enemyOnSky = random(250, 510); // Randomize enemy's vertical position this.enemyImgIndex = 0; // Current index of the enemy image } initEnemy() { this.enemyX -= 2; // Move the enemy horizontally this.enemyY = this.enemyOnSky; // Update the enemy's vertical position } animateEnemy() { // Display and animate the enemy character image(this.enemyImg[this.enemyImgIndex], this.enemyX, this.enemyY, this.enemySize, this.enemySize); this.enemyX -= 1; // Move the enemy horizontally this.initEnemy(); // Re-initialize the enemy position if (frameCount % this.animationSlowDown === 0) { this.enemyImgIndex = (this.enemyImgIndex + 1) % 3; // Animate enemy images } // Reset enemy position and increase score when it moves off-screen if (this.enemyX <= 0) { this.enemyX = 1000; this.enemyOnSky = random(250, 510); this.initEnemy(); score += 5; // Increase score } // Check for collision with the player if (dist(this.enemyX, this.enemyY, xpos, ypos) <= (this.playerSize / 2 + this.enemySize / 2)) { win = false; // End the game if collision detected if (!enemy_sound.isPlaying()) { if (sf.isPlaying()) { sf.stop(); // Stop the background music } enemy_sound.loop(); // Play the enemy sound on loop } } } } // Coin class, inherits from Player and represents collectible coins class Coin extends Player { constructor() { super(); // Call the parent class constructor this.coinImg = []; // Array to hold coin images for animation for (let i = 1; i <= 5; i++) { this.coinImg.push(loadImage(`coin-${i}.png`)); // Load coin images } this.coinX = 800; // Initial horizontal position of the coin this.coinY = 460; // Initial vertical position of the coin this.coinSize = 48; // Size of the coin this.coinImgIndex = 0; // Current index of the coin image } initCoin() { this.coinX = width; // Place the coin at the edge of the screen this.coinY = random(250, 510); // Randomize the coin's vertical position } animateCoin() { // Display and animate the coin image(this.coinImg[this.coinImgIndex], this.coinX, this.coinY, this.coinSize, this.coinSize); this.coinX -= 1; // Move the coin horizontally if (frameCount % this.animationSlowDown === 0) { this.coinImgIndex = (this.coinImgIndex + 1) % 5; // Animate coin images } // Check if the coin has been collected if (dist(this.coinX, this.coinY, xpos, ypos) <= (this.playerSize / 2 + this.coinSize / 2)) { this.initCoin(); // Re-initialize the coin position for it to reappear again score += 10; // Increase score if (!coin_sound.isPlaying()) { coin_sound.play(); // Play the coin collection sound } } // Check if the coin has moved off the left edge of the screen without being collected else if (this.coinX < 0) { this.initCoin(); // Re-initialize the coin position for it to reappear again } } } function setup() { createCanvas(1000, 750); // Set up the canvas if (!sf.isPlaying()) { sf.loop(); // Loop the background music if not already playing } player = new Player(); // Instantiate the player enemy = new Enemy(); // Instantiate the enemy coin = new Coin(); // Instantiate the coin player.initPlayer(); // Initialize the player coin.initCoin(); // Initialize the coin enemy.initEnemy(); // Initialize the enemy } function draw() { background(220); // Set the background color image(img, x1, y); // Draw the background image twice for a scrolling effect image(img, x2, y); x1 -= 1; // Move the background images to create a scrolling effect x2 -= 1; if (x1 <= -1000) { // Reset the background images positions for continuous scrolling x1 = 1000; } if (x2 <= -1000) { x2 = 1000; } // Animate player, coin, and enemy player.animatePlayer(); coin.animateCoin(); enemy.animateEnemy(); // Display the score and instructions textSize(25); textFont(pixel_font); fill(255, 215, 0); text(`Score: ${score}`, 450, 100); text("Use UP Key to Jump", 375, 25); textSize(20); text("Collect the coins and avoid hitting the enemy!", 250, 45); if (!win) { image(game_over_bg, x3, y2); // Display the game over background textSize(30); text("Press SHIFT Key to Play Again", 285, 25); //noLoop(); // Stop the draw loop } } function keyPressed() { // Jump when UP key is pressed if (keyCode === UP_ARROW && win) { player.jumping = true; // Enable jumping if (!jump_sound.isPlaying()) { jump_sound.play(); // Play jump sound } player.jumpStrength = player.jumpStrengthMax; // Set jump strength to maximum } // Reset game when SHIFT key is pressed after losing if (keyCode === SHIFT && !win) { win = true; // Reset game status score = 0; // Reset score if (enemy_sound.isPlaying()) { enemy_sound.stop(); // Stop the enemy sound if playing } loop(); // Resume the draw loop setup(); // Reinitialize game setup } }