Link to the Sketch: https://editor.p5js.org/izza.t/full/V9Mv_WERI
For my midterm, I decided to do a spin-off of a classic maze that is also heavily inspired by 2 other things I love: mystery and cats. The story of the game involves an archeologist exploring an abandoned tomb that is said to hold a large treasure with their cat. Suddenly, something spooks the cats and it runs off into the tomb. It is then revealed that the tomb is like a maze, and the user must navigate through it. The catch? It’s pitch black apart from the small light from their flashlight and there are booby traps in certain parts of the maze like a mummy, beetles, and a snake that will cause the player to have to restart if the player collides into them.
The game works by showing a start screen that explains the situation and what keys to use to navigate the game. The player uses the arrow keys to navigate up, down, left, and right through the maze and can use the ‘C’ key to call for their cat and hear it’s meow which expands their flashlight radius momentarily (almost like a power up). This feature does have a cooldown feature that is displayed at the top. The user must navigate through the maze to the end to win and find both their cat and the lost treasure. They must do so without triggering any of the booby traps and dealing with all of the dead ends in the maze.
The maze itself took the longest time to create, almost an entire day, as it all had to be hardcoded using an image I had found of tomb-like stone walls online. Then, creating the flashlight like circle around the player and making sure that only pieces of the maze that are within that circle are displayed required the use of masking. This required me to use a new function called drawingContext which unlocks more features of the canvas in p5js and allowed me to do that. The code for which I’m very proud of and can be seen below.
class Maze { constructor() { this.walls = [ // Outer boundaries (these are always visible) {x: 75, y: 0, w: 1200, h: 30}, // Top {x: 0, y: 570, w: 1200, h: 30}, // Bottom {x: 0, y: 0, w: 30, h: 600}, // Left {x: 970, y: 0, w: 30, h: 535}, // Right // Interior walls (these are only visible within the flashlight radius) {x: 75, y: 25, w: 10, h: 100}, {x: 75, y: 165, w: 10, h: 220}, {x: 75, y: 425, w: 10, h: 90}, {x: 145, y: 25, w: 10, h: 100}, {x: 195, y: 25, w: 10, h: 100}, {x: 195, y: 115, w: 75, h: 10}, {x: 75, y: 115, w: 75, h: 10}, {x: 75, y: 165, w: 200, h: 10}, {x: 265, y:115, w: 10, h: 55}, {x: 75, y: 375, w: 165, h: 10}, {x: 75, y: 425, w: 215, h: 10}, {x: 280, y: 295, w: 10, h: 140}, {x: 235, y: 355, w: 10, h: 30}, {x: 105, y: 345, w: 140, h: 10}, {x: 155, y: 295, w: 135, h: 10}, {x: 155, y: 255, w: 10, h: 50}, {x: 105, y: 205, w: 10, h: 150}, {x: 75, y: 515, w: 210, h: 10}, {x: 280, y: 515, w: 10, h: 60}, {x: 105, y: 490, w: 205, h: 10}, {x: 105, y: 450, w: 205, h: 10}, {x: 105, y: 450, w: 10, h: 50}, {x: 155, y: 255, w: 155, h: 10}, {x: 115, y: 205, w: 195, h: 10}, {x: 300, y:95, w: 10, h: 115}, {x: 235, y: 85, w: 75, h: 10}, {x: 300, y:255, w: 10, h: 205}, {x: 300, y:490, w: 10, h: 85}, {x: 225, y: 25, w: 10, h: 70}, {x: 345, y: 25, w: 10, h: 320}, {x: 345, y: 345, w: 225, h: 10}, {x: 345, y: 395, w: 60, h: 10}, {x: 465, y: 395, w: 60, h: 10}, {x: 345, y: 395, w: 10, h: 180}, {x: 515, y: 395, w: 10, h: 180}, {x: 565, y: 345, w: 10, h: 180}, {x: 565, y: 525, w: 50, h: 10}, {x: 605, y: 300, w: 10, h: 230}, {x: 655, y: 165, w: 10, h: 410}, {x: 405, y: 300, w: 205, h: 10}, {x: 405, y: 255, w: 205, h: 10}, {x: 405, y: 225, w: 205, h: 10}, {x: 605, y: 225, w: 10, h: 40}, {x: 405, y: 255, w: 10, h: 50}, {x: 405, y: 25, w: 10, h: 210}, {x: 465, y: 165, w: 195, h: 10}, {x: 465, y: 85, w: 10, h: 90}, {x: 465, y: 85, w: 225, h: 10}, {x: 685, y: 85, w: 10, h: 25}, {x: 685, y: 150, w: 10, h: 425}, {x: 745, y: 20, w: 10, h: 345}, {x: 745, y: 410, w: 10, h: 120}, {x: 745, y: 525, w: 70, h: 10}, {x: 805, y: 450, w: 10, h: 85}, {x: 865, y: 450, w: 10, h: 85}, {x: 805, y: 445, w: 70, h: 10}, {x: 865, y: 525, w: 140, h: 10}, {x: 745, y: 410, w: 145, h: 10}, {x: 935, y: 410, w: 40, h: 10}, {x: 745, y: 355, w: 170, h: 10}, {x: 905, y: 90, w: 10, h: 275}, {x: 845, y: 90, w: 60, h: 10}, {x: 845, y: 90, w: 10, h: 245}, {x: 785, y: 20, w: 10, h: 315}, {x: 785, y: 325, w: 60, h: 10}, ]; } display() { for (let i = 0; i < 4; i++) { let wall = this.walls[i]; image(stoneWall, wall.x, wall.y, wall.w, wall.h); } //creating the masking for (let i = 4; i < this.walls.length; i++) { let wall = this.walls[i]; // Create a mask for the wall drawingContext.save(); drawingContext.beginPath(); drawingContext.arc(player.x, player.y, lightRadius, 0, TWO_PI); drawingContext.clip(); image(stoneWall, wall.x, wall.y, wall.w, wall.h); drawingContext.restore(); } }
The outline of the maze itself can be seen in this image below which I took before I made the maze pitch black apart from the flashlight.
The flashlight itself is another thing I am very proud of as I wanted to make it look as natural as the way the light coming from a flashlight looks like. This means it has a bright center and a smooth gradient to transparent. For this effect, I had to learn how to use the blendMode in p5js which allowed me to create it in the easiest way. The code for this can be seen below.
function drawFlashlight() { fill(0); noStroke(); rect(0, 0, width, height); blendMode(SCREEN); drawingContext.globalCompositeOperation = 'lighter'; // Draw the flashlight gradient let gradient = drawingContext.createRadialGradient( player.x, player.y, lightRadius / 4, player.x, player.y, lightRadius ); gradient.addColorStop(0, 'rgba(255, 255, 255, 1)'); gradient.addColorStop(1, 'rgba(0, 0, 0, 0)'); drawingContext.fillStyle = gradient; drawingContext.beginPath(); drawingContext.arc(player.x, player.y, lightRadius, 0, TWO_PI); drawingContext.fill(); blendMode(BLEND); }
The result of this and the masking can be seen in the image below (with a peep of one the traps – the mummy):
The game proved to be more challenging than I anticipated, and I quickly realized I had set the bar a little to high for myself. Originally, I wanted the player to be a sprite of an archeologist and to have all the booby traps be animated and moving. However, with time constraints and animation struggles, I wasn’t able to do that (but would love to try and incorporate in the future). I also wanted for the cat to somehow appear at the edge of the flashlight’s radius when the ‘C’ key was pressed, the radius of the flashlight expanded, and the meow sound occurred.
Overall, I am still very proud of the game I ended up creating. It is fun, has an overarching Egyptian theme through carefully selected images and even the Papyrus font, and has a niche to it that sets it apart from other games. I learned a lot about p5js and how to code new things while working on this game, and I hope you enjoy playing it!