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
}
}