As a student at NYUAD, I really love our campus cat, Caramel. She makes being here even better. I enjoy graphic design, and it’s fun for me to draw. I’m also getting into coding, and I want to learn more about it. So, I had this idea for a project I call “Catsaway.” It’s a game inspired by Caramel. In the game, she can fly around the campus without crashing into the pointy palm tree corners. Instead, she gracefully glides through the soft palm leaves. To make her fly, you just press the spacebar. It’s a way to enjoy a little adventure through our campus. This project lets me combine my love for art and my interest in coding, turning my fondness for Caramel into a fun game.
My Game starts with my landing page that have 2 options either to start the game directly or taking you into the instructions page.
- how my project works and what parts you’re proud of (e.g. good technical decisions, good game design)
I’m happy with how I brought Caramel to life in the game. Players get to control Caramel as she gracefully flies around our campus, avoiding obstacles like pointy palm trees. I’ve also made sure the game feels like NYUAD by adding familiar buildings and fluffy clouds.
I’m proud of the technical choices I made. The game has different parts, like the start page and instructions, to make it easy to play. I even added sound effects, like a cheerful jump sound when Caramel takes off. Players can restart the game or go back to the start page with simple key presses.
- Some areas for improvement and problems that you ran into (resolved or otherwise)
During the development of my project, I encountered a few challenges and areas for improvement. One major issue I faced was optimizing the game’s performance. As the game got more complex with added elements, like the buildings and clouds, I noticed some slowdowns. I had to spend time fine-tuning the code to make sure the game runs smoothly, especially on lower-end devices. This was a valuable lesson in optimizing game design.
I had to work on enhancing the game’s instructions. I realized that players might need clearer guidance on how to play, so I made improvements to ensure that the instructions are more user-friendly.
Another challenge was making sure that the game’s look and feel truly capture the essence of NYUAD and Caramel. It required some iterations to get the graphics just right and create that immersive atmosphere.
While these challenges arose during development, I’m happy to say that I addressed them, resulting in a game I’m proud to share. The process of tackling these issues taught me valuable lessons in game development and design.
- Future plans:
- Play on Phones: Right now, you can play the game on a computer P5.js, but it would be great to play it on your phone or tablet. I need to make sure it works well on all kinds of devices.
- High Score List: Having a list that shows who has the highest scores can make the game competitive. People will want to beat each other’s scores and share their achievements.
- Listening to Players: I’ll pay attention to what players say and try to make the game better based on their ideas. They’re the ones playing it, so their feedback is important.
- Making Money: If I want to earn money from the game, I can think about ways like letting people buy things in the game or showing ads. But I’ll be careful not to make the game annoying with too many ads 🙂
My own checklist of the requirements:
- At least one shape
The background was coded the exact same way as the first-ever assignment, The NYUAD building, clouds, highline, and trees. I’ve used plenty of shapes
- At least one image
The Cat is an imported image sketched by Hand using Procreate. as well as the NYUAD logo
- At least one sound
When the cat is flying the moment you hit on the Up Arrow you will hear a jumping sound
- At least one on-screen text
The Score and the final score is a screen on text
- Object Oriented Programming
The game is entirely made using OOP
- The experience must start with a screen giving instructions and wait for user input (button / key / mouse / etc.) before starting
The landing page has 2 options either wait for the user input to click on the space bar to start playing immediately or press on the letter “i” (stands for instructions) to access the instructions page.
- After the experience is completed, there must be a way to start a new session (without restarting the sketch)
Again there are 2 options either to press on the space bar to replay or press on the letter B (stands for begin) to take you to the landing page again
Code:
let pipes = []; // An array to store information about the pipes in the game. let score = 0; // Keep track of the player's score. let gameOver = false; // A flag to check if the game is over. let backgroundImage; // Not used in the code, possibly intended for a background image. let backgroundX = 0; // The horizontal position of the background image. let gameStarted = false; // Indicates whether the game has started. let speed = 10; // The speed of the game elements, adjustable. let gameState = 'landing'; // Tracks the current state of the game. function preload() { // Loading images and sounds used in the game. // backgroundImage = loadImage('11.png'); landing = loadImage('Catsaway(1).png'); instruction = loadImage('instructions.png'); caramel = loadImage("caramel.PNG"); palm1 = loadImage("palm2-1.PNG"); palm2 = loadImage("palm2 -2.PNG"); ending = loadImage("score2.png"); NYUAD = loadImage("NYUAD.png"); // Load the jump sound jumpSound = loadSound('sfx_point.mp3'); // hitSound = loadSound('sfx_hit.mp3'); } function setup() { // Set up the initial canvas for the game and create a bird object. createCanvas(800, 450); bird = new Bird(); } function draw() { if (gameState === 'game') { // Code for the game state // Drawing buildings, clouds, and other elements on the canvas // Pipes are also created, and the game logic is implemented background(156,207,216,255); fill(207,207,207,255); rect(150,150, 500, 300); ellipse(400,150, 490, 100) // strokes for the building fill(169, 169, 169) rect(150, 220, 500, 10); rect(150, 235, 500, 10); rect(150, 250, 500, 10); rect(150, 265, 500, 10); rect(150, 280, 500, 10); rect(150, 295, 500, 10); // rect(200, 220, 10, 100); rect(260, 220, 10, 100); rect(320, 220, 10, 100); rect(380, 220, 10, 100); rect(440, 220, 10, 100); rect(500, 220, 10, 100); image(NYUAD, 55, 0); fill(171,109,51,255); rect(150, 300, 510, 100); fill(121,68,19,255); rect(150, 310, 500, 5); rect(150, 317, 500, 5); rect(150, 324, 500, 5); rect(150, 331, 500, 5); rect(150, 390, 500, 20); // the 2 buildings fill(115,115,115,255); noStroke(); square(0, 250, 200); square(600, 250, 200); // windows fill(207,207,207,255); rect(20,260, 40, 50) rect(70,260, 40, 50) rect(120, 260, 40, 50) rect(640, 260, 40, 50) rect(690, 260, 40, 50) rect(740, 260, 40, 50) noStroke(); fill(255); // First Cloud ellipse(200, 100, 80, 60); ellipse(240, 100, 100, 80); ellipse(290, 100, 80, 60); ellipse(220, 80, 70, 50); ellipse(260, 80, 90, 70); // Second Cloud ellipse(400, 80, 60, 40); ellipse(440, 80, 80, 60); ellipse(490, 80, 60, 40); ellipse(420, 60, 50, 30); ellipse(460, 60, 70, 50); // Third Cloud ellipse(600, 120, 90, 70); ellipse(640, 120, 110, 90); ellipse(690, 120, 90, 70); ellipse(630, 100, 80, 60); ellipse(670, 100, 100, 80); ellipse(0, 80, 60, 40); ellipse(40, 80, 80, 60); ellipse(90, 100, 60, 40); ellipse(140, 150, 50, 30); fill(15,138,70,255); ellipse(100, 420, 90, 70); ellipse(60, 450, 90, 70); ellipse(140, 420, 110, 90); ellipse(190, 420, 90, 70); ellipse(130, 500, 80, 60); ellipse(170, 500, 100, 80); ellipse(600, 420, 90, 70); ellipse(640, 420, 110, 90); ellipse(690, 420, 90, 70); ellipse(630, 500, 80, 60); ellipse(670, 500, 100, 80); fill(0,166,81,255); ellipse(0, 420, 90, 70); ellipse(40, 420, 110, 90); ellipse(90, 420, 90, 70); ellipse(30, 500, 80, 60); ellipse(70, 500, 100, 80); ellipse(670, 420, 90, 70); ellipse(700, 420, 110, 90); ellipse(740, 420, 90, 70); ellipse(750, 500, 80, 60); ellipse(760, 500, 100, 80); if (!gameOver) { bird.update(); bird.show(); for (let i = pipes.length - 1; i >= 0; i--) { pipes[i].show(); pipes[i].update(); if (pipes[i].hits(bird)) { gameOver = true; } if (pipes[i].offscreen()) { pipes.splice(i, 1); } } if (frameCount % 15 === 0) { pipes.push(new Pipe()); } textSize(32); fill(255); text(score, 100, 30); for (let i = pipes.length - 1; i >= 0; i--) { if (pipes[i].pass(bird)) { score++; } } } else { image(ending, 0, 0, width, height); textSize(64); fill(255, 0, 0); text("", 200, height / 2 - 32); textSize(50); fill(0); text("" + score, 450, height / 2); // Provide a restart option // Check for restart key press if (keyIsDown(32)) { // SPACE key restart(); } else if (keyIsDown(66)) { // 'B' key gameState = 'landing'; } } } else if (gameState === 'landing') { // Code for the landing screen state // Displays the landing image and checks for keypress to start the game background(0); image(landing, 0, 0, width, height); textSize(32); fill(255); textAlign(CENTER, CENTER); text("", width / 2, height / 2); // Check for start key press (SPACE key) or 'i' key press for instructions if (keyIsDown(32)) { // SPACE key startGame(); } else if (keyIsDown(73)) { // 'i' key gameState = 'instruction'; } } else if (gameState === 'instruction') { // Code for the instruction screen state // Displays instructions and checks for keypress to start the game background(0); image(instruction, 0, 0, width, height); textSize(32); fill(255); textAlign(CENTER, CENTER); if (keyIsDown(32)) { // Check for SPACE key press startGame(); } } } function startGame() { // Function to start the game // Resets game variables and sets the game state to 'game' gameState = 'game'; gameStarted = true; bird = new Bird(); pipes = []; score = 0; gameOver = false; } function restart() { // Function to restart the game // Calls the startGame function to reset the game startGame(); } function keyPressed() { // When the UP_ARROW key is pressed, the bird jumps (if the game is not over) // Plays a jump sound when the bird jumps if (keyIsDown(UP_ARROW) && !gameOver && gameState === 'game') { bird.jump(); jumpSound.play(); } } function restartGame() { bird = new Bird(); pipes = []; score = 0; gameOver = false; } class Bird { // Bird class to handle bird-related functionality constructor() { this.y = height / 2; this.x = 64; this.gravity = 0.6; this.lift = -15; this.velocity = 0; } show() { noFill(); ellipse(this.x, this.y, 32, 32); image(caramel, this.x - 70, this.y - 30, 150, 90); } update() { this.velocity += this.gravity; this.velocity *= 0.9; this.y += this.velocity; if (this.y > height) { this.y = height; this.velocity = 0; } if (this.y < 0) { this.y = 0; this.velocity = 0; } } jump() { this.velocity += this.lift; } } class Pipe { // Pipe class to handle pipe-related functionality constructor() { this.top = random(height / 2.5); this.bottom = random(height / 2); this.x = width; this.w = 20; this.speed = speed; // Adjust the speed here as well this.highlight = false; } show() { fill(106, 69, 46, 255); if (this.highlight) { fill(106, 69, 46, 255); noStroke(); } rect(this.x - 8, 0, this.w + 6, this.top); rect(this.x, height - this.bottom, this.w + 2, this.bottom); const palmX = this.x - 82; const palmYTop = this.top - 250; const palmYBottom = height - this.bottom - 120; image(palm2, palmX - 20, palmYTop + 195, 200, 200); image(palm1, palmX, palmYBottom, 200, 200); } update() { this.x -= this.speed; } offscreen() { return this.x < -this.w; } hits(bird) { if (bird.y < this.top || bird.y > height - this.bottom) { if (bird.x > this.x && bird.x < this.x + this.w) { this.highlight = true; return true; } } this.highlight = false; return false; } pass(bird) { if (bird.x > this.x && !this.highlight) { return true; } return false; } }