The game creation process starts with a simple concept or idea. Usually this is an original concept created within the company, but it happens that the idea is taken from outside. For my midterm project, I was inspired by different “Catching games”.
For example: http://learningprocessing.com/examples/chp10/example-10-10-rain-catcher-game
This is both a simple and very complex game that captures the user. I chose this game because I add a lot of my own stuff along the way and it’s very easy to interpret my ideas into this project.
At the moment I am planning to do something like a game where the player will collect different flowers and make his own bouquet. I will choose a suitable character (maybe there will be several). Also, I plan to choose flowers, approximately it will be lilacs.
At the moment, I have done research on creating the base of the game, the so-called “skeleton”. Next, I need to completely decorate the game, add win and lose screens, add pictures, and adjust the colors. Also, think about what conditions the game will have.
The midterm of this course required us to implement everything we had learned, ranging from the simplicity of shapes to the complexity of object-oriented programming. I intended to provide the skills I learned throughout these past seven weeks while also implementing my touch of art and creativity. That being said, this required me to code a playable game and make it aesthetically pleasing in its own art style. I chose to aim towards a retro-pixelated black-and-white art style that would make the user delve into a nostalgic experience when playing the game. I decided to get inspired by the most prominent game in video game history, snake. However, in my game (germ-worm), the player is in a black-and-white world where they control a worm in need of collecting germs to reach the highest possible score while exploring the setting available to play, making it a more complex or more manageable experience.
code:
// HAMAD ALSHAMSI
// GERM WORM: MIDTERM PROJECT
// code
//game state variable
var mode = 0;
//set object/player variables
let worm = []; //variables for worm and player
let player;
let germ;
//set game's rules variables
let gameExtent = 20;
let edgeExtent = 1;
let collectibles = 10;
let playState = true;
let direction, frameChange, scoreCount;
let portionValue, collectiblesValue, frameValue;
let portionSlider, lengthSlider, multiplierSlider, speedSlider;
//classify worm portions and
class Portion {
constructor(x, y) {
this.x = x;
this.y = y;
this.wormLength = gameExtent - edgeExtent * 2;
}
//used to test if player is offscreen
testEdges() {
return this.x < 0 || this.x > width || this.y < 0 || this.y > height;
}
//used to test player self-collision
testWorm() {
for (let portio of worm) {
if (this.x == portio.x && this.y == portio.y && direction) return true;
}
return false;
}
//used to test player germ-collision
testGerm() {
return this.x == germ.x && this.y == germ.y;
}
//update position to player's direction input
update() {
switch (direction) {
case "up":
player.y -= gameExtent;
break;
case "right":
player.x += gameExtent;
break;
case "down":
player.y += gameExtent;
break;
case "left":
player.x -= gameExtent;
break;
}
//test offscreen or self-collision, if so, lose. else test germ-collision
if (this.testEdges() || this.testWorm()) {
//implemet pixelated font
textFont(fontPixel);
fill(60);
push();
translate(width / 2, height / 2);
textSize(52);
text("GAME OVER", -150, 0);
textSize(18);
text("press ' SPACE ' to restart", -134, 30);
pop();
playState = false;
} else if (this.testGerm()) germ.consume();
}
//first worm section drawn using image asset alongside a white background
create() {
noStroke();
fill(255);
image(wormIMG, this.x, this.y, this.wormLength, this.wormLength);
}
}
//classify germs
class Germ {
constructor(x, y) {
this.x = x;
this.y = y;
this.wormLength = gameExtent - edgeExtent * 2;
this.collectibles = 2;
}
//when worm collides with germ, increase length. then pick random spot for new germ
consume() {
collectibles += this.collectibles;
this.x = floor(random(width / gameExtent)) * gameExtent + edgeExtent;
this.y = floor(random(height / gameExtent)) * gameExtent + edgeExtent;
}
//draw germ in new spot using image from asset
create() {
image(germIMG, this.x, this.y, this.wormLength, this.wormLength);
}
}
//load images from asset file
function preload() {
menuIMG = loadImage("assets/menuImage.png");
germIMG = loadImage("assets/germImage.png");
wormIMG = loadImage("assets/wormImage.png");
fontPixel = loadFont("assets/PixelDigivolve-mOm9.ttf");
}
function setup() {
//define default game state
mode = 0;
//center shapes and images
rectMode(CENTER);
imageMode(CENTER);
//set canvas size
createCanvas(400, 600);
//create new germ
let x = floor(random(width / gameExtent)) * gameExtent + edgeExtent;
let y = floor(random(height / gameExtent)) * gameExtent + edgeExtent;
germ = new Germ(x, y);
//view score counter
scoreCount = createP(
" S  C  O  R  E  :  1  "
);
scoreCount.style("color", "white");
scoreCount.style("font-size", "15px");
scoreCount.style("background-color", "#5c5c5c");
scoreCount.position(5, 560);
//worm size slider
portionValue = createP("size: 20");
portionValue.position(50, 610);
portionValue.style("font-size", "15px");
portionValue.style("margin:2px");
portionValue.style("color", "#5c5c5c");
portionSlider = createSlider(10, 50, 20, 10);
portionSlider.position(10, 630);
//worm length slider
lengthP = createP("length: 0");
lengthP.position(50, 660);
lengthP.style("font-size", "15px");
lengthP.style("margin:2px");
lengthP.style("color", "#3c3c3c");
lengthSlider = createSlider(1, 30, 1, 1);
lengthSlider.position(10, 680);
//score per germ slider
collectiblesValue = createP("multiplier: 1");
collectiblesValue.position(290, 610);
lengthP.style("font-size", "15px");
collectiblesValue.style("margin:2px");
collectiblesValue.style("color", "#3c3c3c");
multiplierSlider = createSlider(1, 10, 1, 1);
multiplierSlider.position(270, 630);
//frames per second slider
frameValue = createP("speed: 8");
frameValue.position(300, 660);
lengthP.style("font-size", "15px");
frameValue.style("margin:2px");
frameValue.style("color", "#3c3c3c");
speedSlider = createSlider(1, 20, 8, 1);
speedSlider.position(270, 680);
//set default framerate
frameRate(8);
//prnt rules in console.log
print(
"arrow keys --> MOVE. \nspace --> RESTART. \nclick --> START."
);
}
function keyPressed() {
//different arrows correspond to different directions
if (frameChange) {
switch (key) {
case "ArrowUp":
if (direction != "down") direction = "up";
break;
case "ArrowRight":
if (direction != "left") direction = "right";
break;
case "ArrowDown":
if (direction != "up") direction = "down";
break;
case "ArrowLeft":
if (direction != "right") direction = "left";
break;
case " ":
player = new Portion(width / 2 + edgeExtent, height / 2 + edgeExtent);
germ.x = floor(random(width / gameExtent)) * gameExtent + edgeExtent;
germ.y = floor(random(height / gameExtent)) * gameExtent + edgeExtent;
//display score
scoreCount.html(
" S  C  O  R  E  :  1  "
);
direction = undefined;
worm = [];
playState = true;
break;
}
frameChange = false;
}
}
//game state display
function draw() {
if (mode == 0) {
menuGame();
} else if (mode == 1) {
startGame();
}
}
//command lines responsible before the game starts
function menuGame() {
background(255);
push();
translate(width / 2, height / 2);
image(menuIMG, 0, 0);
pop();
}
//command lines responsible for when the game starts
function startGame() {
frameChange = true;
//update information on sliders accordingly
portionValue.html(
portionValue.html().split(" ")[0] + " " + portionSlider.value()
);
lengthP.html(lengthP.html().split(" ")[0] + " " + lengthSlider.value());
collectiblesValue.html(
collectiblesValue.html().split(" ")[0] + " " + multiplierSlider.value()
);
frameValue.html(frameValue.html().split(" ")[0] + " " + speedSlider.value());
if (!direction) {
//if game not run, update using sliders info
background(255);
gameExtent = portionSlider.value();
edgeExtent = gameExtent / 20;
collectibles = lengthSlider.value() - 1;
frameRate(speedSlider.value());
player = new Portion(
floor(width / gameExtent / 2) * gameExtent + edgeExtent,
floor(height / gameExtent / 2) * gameExtent + edgeExtent
);
germ.x = floor(random(width / gameExtent)) * gameExtent + edgeExtent;
germ.y = floor(random(height / gameExtent)) * gameExtent + edgeExtent;
germ.wormLength = gameExtent - edgeExtent * 2;
germ.collectibles = multiplierSlider.value();
player.create();
} else if (playState) {
background(255);
//copies player into a worm array and replaces index to show worm moving motion
worm.push(new Portion(player.x, player.y));
//if germ is present, erase it. else kill worm
if (collectibles) collectibles--;
else worm.shift();
//update game scores and display
player.update();
player.create();
germ.create();
for (let portio of worm) portio.create();
scoreCount.html(
" S  C  O  R  E  :  " +
(worm.length + 1) +
"  "
);
}
}
//launch game by clicking
function mousePressed() {
if (mode == 0) {
launchGame();
}
}
function launchGame() {
mode = 1;
}
// references
// game rule variables inspired from "GeoCrafter57" on YouTube
// color palette from "https://colorhunt.co/palettes/grey"
// images from "https://www.alamy.com/stock-photo/star-pixel-video-game-play.html"
method:
Initially, having an object, a worm, move through the screen in a manner that is up, down, right, and left was an essential aspect of the game. Then, it was necessary to include a collectible germ that would also serve as a point system. These goals were managed by classifying both the ‘worm’ and ‘germ’ and implementing them into arrays and indexes to stylize further the motion presenting an illusion of a worm-like movement. Then, adding a difficulty aspect where the user can freely adjust the difficulty level was an enjoyable feature, as it required sliders, a concept never delved into before. Finally, adding menu and end screens containing instructions would be the icing on the cake. To do that, I fully customized an entire image in Photoshop that compliments the pixelated theme present in the game. These images possessed easy-to-follow instructions such as an icon of arrows with “move,” a space bar with “restart, and a click with “start” phrases next to them.
sketch:
The following pictures preview the menu, game, and end screens of the game. Additionally, images of the gameplay with the various rules are previewed to present the difficulty range offered.
menu screen
gameplay screen
end screen
future improvements:
Presenting a worm-customization aspect allowing the user to customize the shape of the worm would be a fun and interactive feature. Additionally, adding a high-score element to possibly implement a competitive aspect to the game would be extremely fun.
The idea I had for my game came to me while I was watching a skit by a stand-up comedian on the absurdity of big tech companies. he likened the monopolies that the big tech firms have on our lives to the level of mastery a chess grandmaster has. At the same time, we, normal human beings, are trapped as newbies (“noobs”). this gave me the idea to make a satirical chess game where the capitalist big tech companies have complete, overarching control while providing the player the illusion of choice. Still, whatever happens, the big tech always wins. or you pay. the concept at first was to make a fully-fledged chess game with tweaked rules which allowed an eventual win for the capitalist side. but that was beyond the scope of my project so instead, I modified it to a simpler and more absurd version.
Process
To start, I drew a storyboard kind of thing on my iPad to make sure I had all my plans noted down. I decided to include two different game states; the start screen and the game screen. I went about populating the game screen first, using some maths to make a chessboard. after that, I made the start screen and added buttons and text. then I added the game state conditional statement which allowed the console to load the screen procedurally. the images came next. At first, I was going to make an array and generate the chess pieces by push() and pop() but then a friend suggest I add only queen and king pieces for the capitalist side and give only 3 pieces to the player. For this, I instead chose to make the opposite side into a single png and load in the player pieces separately.
After this, I chose to generate popups with promotional messages. to achieve this, i created a class for all the messages. i also created a class to make the highlighted areas for the chess pieces. the final aspect was to create the death animation for the king. i took inspiration from youtube and tried adding an animation for the king to be sliced in half. but instead i decided on an explosion gif image and an explosion sound for added effect.
Initially, I wanted to add a separate game state for the ending screen with more cheeky ‘buy our stuff’ remarks from the capitalist side but decided instead for a simple, crude game over. partially because I wanted to reduce the complexity of the code and partially because I appreciated the crude, absurdly low quality aesthetic that it presented juxtaposed with the seemingly shiny image that people have of big tech companies.
let mode = 0;
let startButton;
let imgCapitalist;
let imgPlayerPawn1;
let imgPlayerPawn2;
let HighlightPosX;
let pawn1y = 470;
let KillShotX = 252.5;
let KillShotY = 27.5;
let collision = false;
let explosion;
//loading all the image assets
function preload() {
imgCapitalistSide = loadImage("CapitalistSide.png");
imgPlayerPawn1 = loadImage("pawn1.png");
imgPlayerPawn2 = loadImage("pawn1.png");
imgPlayerKing = loadImage("king1.png");
imgExplosion = loadImage("explosion.gif");
explosion = loadSound('explosion.mp3')
}
//seting up the intitial screen
function setup() {
print("Use Arrow Keys to Move the Pawn Piece");
createCanvas(600, 600);
background(149, 53, 83);
textSize(20);
text("Please Select Difficulty Level:", 168, 370);
textSize(50);
text("CAPITALISM CHESS", 70, 100);
startButton = createButton("Please Press to Begin");
startButton.position(200, 200);
startButton.size(200, 100);
startButton.mousePressed(updatemode);
startButton.style("font-size", "25px");
startButton.style("background-color", "white");
DifficultyButton1 = createButton("Easy (selected)");
DifficultyButton1.position(100, 400);
DifficultyButton1.size(100, 60);
DifficultyButton1.mousePressed(eraseMessage);
DifficultyButton1.style("font-size", "15px");
DifficultyButton1.style("background-color", "white");
DifficultyButton2 = createButton("Easy Premium 🔒");
DifficultyButton2.position(250, 400);
DifficultyButton2.size(100, 60);
DifficultyButton2.mousePressed(promoMessage);
DifficultyButton2.style("font-size", "15px");
DifficultyButton2.style("background-color", "white");
DifficultyButton3 = createButton("Easy Premium Plus+ 🔒");
DifficultyButton3.position(400, 400);
DifficultyButton3.size(100, 60);
DifficultyButton3.mousePressed(promoMessage);
DifficultyButton3.style("font-size", "13px");
DifficultyButton3.style("background-color", "white");
highlight = new highlightedarea();
DisplayMessage = new PromoMessage();
}
//this function will display the message for the locked modes
function promoMessage() {
DisplayMessage.createMessage2();
}
function eraseMessage() {
DisplayMessage.eraseMessage();
}
function draw() {
//this if statement is based on the condition mode = 1, the game mode is start mode
if (mode == 1) {
noStroke();
startButton.hide();
DifficultyButton1.hide();
DifficultyButton2.hide();
DifficultyButton3.hide();
let SquareSize = width / 8;
let squareNum = 0;
for (let columny = 0; columny < height; columny += SquareSize) {
squareNum++;
//by adding squarenum+1 here i can add the value for the boxes in the y-axis
for (let rowx = 0; rowx < width; rowx += SquareSize) {
squareNum++;
//by adding squarenum+1 i was able to add the box values in the x-axis
if (squareNum % 2 == 0) {
fill(224, 255, 255);
} else {
fill(0, 139, 139);
}
rect(rowx, columny, SquareSize, SquareSize);
}
}
//here we can add all our sprites and maths because elsewhere doesnt work lol because it is being overwritten by the grid-making function
image(imgCapitalistSide, 0, 0);
image(imgPlayerPawn1, 242, pawn1y);
image(imgPlayerPawn2, 315, 470);
image(imgPlayerKing, 244, 545);
if (
mouseIsPressed == true &&
mouseX > 225 &&
mouseX < 300 &&
mouseY > 75 * 6 &&
mouseY < 75 * 7
) {
HighlightPosX = 225;
highlight.create(24);
// HighlightState= !HighlightState ;
} else {
if (
mouseIsPressed == true &&
mouseX > 300 &&
mouseX < 375 &&
mouseY > 75 * 6 &&
mouseY < 75 * 7
) {
HighlightPosX = 300;
highlight.create();
} else {
if (mouseIsPressed == true && mouseY > 450) {
DisplayMessage.createMessage1();
}
}
}
// these statements deal with teh mechanics of the bomb released at the opponent side
if (pawn1y < 470) {
fill(204, 85, 0);
circle(KillShotX, KillShotY, 20);
if (collision == false) {
KillShotX = KillShotX - 5;
KillShotY = KillShotY + 5;
}
}
if (KillShotX < 0) {
collision = true;
}
if (collision == true) {
KillShotX = KillShotX + 5;
KillShotY = KillShotY + 5;
}
if (KillShotX > 252 && KillShotY > 552) {
image(imgExplosion, 165, 450);
fill(110, 38, 14);
rect(100, 200, 400, 200);
fill(250, 250, 250);
textSize(60);
text("GAME OVER", 115, 310);
explosion.play()
}
}
}
//making classes for the messages in the pop up windows/rectangles
class PromoMessage {
constructor() {
this.a = mouseX;
this.b = mouseY;
}
createMessage1() {
fill(0, 0, 0);
rect(mouseX, mouseY, 350, 60);
fill(300, 300, 300);
textSize(20);
text("Join Chess Premium for More Pieces", mouseX + 7, mouseY + 40);
}
createMessage2() {
fill(0, 0, 0);
rect(45, 480, 510, 100);
fill(300, 300, 300);
textSize(23);
text("You Are Not Subscribed to our Premium Plan", 70, 510);
textSize(24);
text("Join Now to Get 2% Off for the Next 2 Months", 55, 550);
}
eraseMessage() {
noStroke();
fill(150);
rect(44, 479, 515, 105);
}
}
//class was made for the highlights that pop up to show legal moves available
class highlightedarea {
constructor(HighlightPosX) {
this.x = HighlightPosX;
}
create() {
fill(0, 139, 250);
rect(HighlightPosX, 300, 75, 150);
}
}
//function added to make the pawn move to intended position via arrow keys. it can go forward and backwards
function keyPressed() {
if (keyCode == UP_ARROW) {
pawn1y = pawn1y - 75;
} else {
if (keyCode == DOWN_ARROW) pawn1y = 470;
}
}
//this function updates the game mode from the intro screen to game screen
function updatemode() {
mode++;
}
Problems
As expected I ran into many problems while working on this project. so many that I would probably end up running out of space if I tried listing them here:,(
but here are the three main ones:
the first problem I ran into was with the initialization of the board itself. I could not manage to make the boxes render in a grid pattern, instead, they were rendering in columns which weren’t ideal. I realized it was because I had been not accounting for the fact that after every row, the values were resetting and the same values were being put again into the loop causing it to render the same pattern over and over again. to work around this I simply added a digit to the value of the subsequent row to offset the answer by 1. it caused the whole pattern to shift by one to the left and made a checkered pattern that I was looking for
the second problem I ran into was that the highlights for the possible moves for the player were not showing up. I tried creating it by rendering a rectangle with a mouse click after the game started but that would reset the background. to combat this I decided to create a class for the highlight as it was not only easier to assign it to different positions but also allowed me to render it on top of the checkered background
another problem I faced was that the kill shot element in the last portion of the game would not bounce but stick instead to the wall and fall to -y infinity. the problem was caused by the if statement I used which stated if x was less than 0 (the edge of the canvas) it would start adding 5 to the x variable instead of subtracting it. what I did not realize was that it sent the loop into an endless repetition since by adding 5 it ended the loop and subtracted 5 again and entered the loop again. to combat this I added a conditional statement that split the event into two states (one in which the condition was false and once when it was true). this allowed the loop to run separately and exhibit bouncing behavior
Reflection:
despite it being a tedious game to make, I really enjoyed creating a little narrative piece with absurdist elements. the coding aspect allowed me to explore way outside of what we had covered in class and forced me to think out of the box to solve problems that I had not experienced before. for this assignment, I forced myself to take as little coding input as I could from outside sources (as in using other people’s solutions) and create my workaround. it caused me to take much more time and made it harder but gave me the freedom to experiment on my own. in the future I would like to make a similar game, with a similar theme, but more streamlined and with more functionality. I feel more confident in my ability to make such projects thanks to the experience I gained from this exercise.
So, my game did not turn out exactly like I wanted it to be which I think is OK. the concept was to have a much more complicated game with different levels, but I don’t think I was able to achieve that. However, I was able to produce a game that I was really proud of I had a maze where the character had to walk through it and I was able to create a character which was able to walk and give the animation of it walking right to left and back and forward which is something I didn’t think was possible prior to this class. I was also able to have a game that is based on luck which was my initial idea the coin is what decides whether you wins or loses and the whole game depends on it so there is no skill involved which was something that I wanted to do with the game since the project was announced.
Inspiration
My inspiration came from the idea of randomness that we talked about during our first couple of classes. being a social science student, my background is in legal studies and in the law everything is relatively a fact so you have to prove the events and you have to make the assumption that nothing is really random. computer languages however depend a lot on randomness especially in the field of I.M. we see a lot of interesting artworks that have created been created from randomness and they all look beautiful in their own sense. And my game I wanted to create the idea of winning being incorporated with such randomness.
code
var Mazeb;
var x1 = 0;
var x2;
var scrollSpeed = 2;
let gameState = "start";
let spritesheet;
let sprites = [];
let direction = 1; // 0 up
let step = 0;
let x;
let y;
let speedx = 3;
let spritesheetintialx = 20;
let spritesheetintialy = 70;
function preload() {
soundFormats("mp3", "ogg");
spritesheet = loadImage("walking.png");
Mazeb = loadImage("Mazebackground.png");
castle= loadImage("castle.jpeg");
Flip = loadSound("Coin flip.mp3");
}
function setup() {
//fullscreen(true);
// print(Mazeb.width, Mazeb.height);
createCanvas(700, 600);
restartGame();
x2 = width;
// 12 images across, 4 down, in the spritesheet
let w = int(spritesheet.width / 12);
let h = int(spritesheet.height / 4);
for (let y = 0; y < 4; y++) {
sprites[y] = [];
for (let x = 0; x < 12; x++) {
sprites[y][x] = spritesheet.get(x * w, y * h, w, h);
} // iterate over rows
} // iterate over columns
x = 20;
y = 70;
imageMode(CENTER);
// Display first sprite
image(sprites[direction][step], x, y);
//Flip.play()
}
function restartGame() {
print("Welcome to the dungon");
print("help me escape");
y = spritesheetintialy;
x = spritesheetintialx;
wonGame = false;
gameState = "start";
}
function draw() {
if (gameState == "start") {
drawInstructions();
} else if (gameState == "playing") {
drawGame();
} else if (gameState == "end") {
drawEndScreen();
}
//print(mouseX, mouseY);
}
function drawInstructions() {
//print('drawing instructions');
background('#95a8b0');
// white
text("Welcome to my dugon", 300, 300);
text("you have to escape", 300, 320);
text("Press any key to start", 300, 340);
}
// nothing to do here because all the action
// happens in the keyPressed() callback
//coin
var Coinx = 430;
var Coiny = 380;
var Coinwidth = 70;
var Coinheight = 80;
// rectangle 1
var rect1x = 100;
var rect1y = 0;
var rect1width = 90;
var rect1height = 250;
// rectangle 2
var rect2x = 100;
var rect2y = 250;
var rect2width = 250;
var rect2height = 60;
function drawGame() {
fill(255,0,0)
text("Press the space bar",589,200);
text("GO",370,381)
text("Press L", 420,560 )
noFill()
fill("#d8572a");
// Maze
rect(rect1x, rect1y, rect1width, rect1height);
rect(rect2x, rect2y, rect2width, rect2height);
rect(100, 450, 90, 600);
rect(100, 450, 250, 60);
rect(500, 250, 200, 600);
rect(260, 115, 90, 136);
rect(260, 75, 600, 40);
noFill();
text("GO",430,380)
// coin
fill("#fdc500");
ellipse(Coinx, Coiny, Coinwidth, Coinheight);
noFill();
if (y > height) {
// Lost the game!
wonGame = false;
// Change to end game state
gameState = "end";
}
}
function drawEndScreen() {
if (wonGame) {
background("#4cc9f0");
fill('#ffd60a')
text("You have escaped", 300, 300);
text("Press any key to restart", 300, 320);
noFill()
}
if (lostGame){
background("#d90429");
fill('#000814')
text("You have not made it out.", 300, 300);
text("Press any key to restart", 300, 320);
noFill()
}
}
function mouseClicked() {
if (
mouseX > Coinx &&
mouseX < Coinx + Coinwidth &&
mouseY > Coiny &&
mouseY < Coiny + Coinheight
) {
Flip.play();
}
if (random() < 0.5){
print("up");
} else {
print("down");
}
}
//function frameDifference(){
// capture.loadPixels(255);
//}
function keyPressed() {
imageMode(CORNER);
image(Mazeb, x1, 0, width, height);
image(Mazeb, x2, 0, width, height);
x1 -= scrollSpeed;
x2 -= scrollSpeed;
if (x1 < -width) {
x1 = width;
}
if (x2 < -width) {
x2 = width;
}
// look at sprite sheet to determine
// which direction is which row
// if (
// mouseX > Coinx &&
// mouseX < Coinx + Coinwidth &&
// mouseY > Coiny &&
// mouseY < Coiny + Coinheight
// ) {
// Flip.play();
// }
if (keyCode === DOWN_ARROW) {
direction = 0;
y += speedx;
}
if (keyCode === LEFT_ARROW) {
direction = 1;
x -= speedx;
}
if (keyCode === RIGHT_ARROW) {
direction = 2;
x += speedx;
// if(x=>100){
// x=45
// }
// if(y=rect1height){
// x+= speedx
// }
}
if (keyCode === UP_ARROW) {
direction = 3;
y -= speedx;
}
// Every so often
// advance to the next sprite
if (frameCount % speedx == 0) {
step = (step + 1) % 12;
}
// Finally draw paint the sprite
image(sprites[direction][step], x, y);
if (gameState == 'start') {
// Start the game
gameState = 'playing';
} else if (gameState == 'playing') {
if (key == ' ') {
// Won game!
wonGame = true;
gameState = 'end';
}
} if (key == 'l') {
// lost game!
lostGame = true;
gameState = 'end';
}
return false;
}
At least one shape.
I created multiple shapes through the rectangles that create the maze and the coin that I made by using an ellipse.
At least one image
I use this sprite sheet with the different images to create The Walking character that goes through the maze. And I create the background with an image of a dungeon that moves along with the advancement of the game
At least one sound
I create a sound that you hear once you click on the coin to simulate the coin flip that decides on random whether you should go up or down
At least one on-screen text
I had many on screen text that had the instructions and the winning and ending commands while I also had the commands that tell you what actions to make once you win or lose
Object Oriented Programming
I created this game to simulate the maze with his story where you should escape from the dungeon
Problems
Why writing my code I run into many problems that I had to adapt with throughout the way. I wasn’t able to make the borders set so that the character would bump into them and not be able to go through them I try to use either color dedicated blocking, but I wasn’t able to figure out how that worked 100% and I was able to block the character at a certain point but it would go through the whole X axis. anyway, I also had issues with creating the instructions page that I later found out solutions to once I kept rereading my code over 20 times. we just I would have liked the character to either win or lose once they go through either screen instead of having a press command check but I came up with that idea later when I in my project and I didn’t have enough time to write about and implement the code.
My idea for my midterm project has changed more times than I count. What was first supposed to be a snowball fight became a volleyball game, then a tennis rally, and then finally settled into a ping pong streak game. Because I kept facing challenges and had to make adjustments to my plans and codes, this project took SUCH a long time even though the end result seems to be (a little bit too) simple. I decided to make the game in such a way so that the way to win is to get 5 points under the time constraint of 20 seconds.
Process/Coding:
Because my pong streak game was inspired by/is an alteration of the pong game, I decided to play the actual online pong game in order to get a grasp of what kind of elements I should add in my coding. Then, I searched up simulations/coding process of the pong game that others have posted, and I found this video to be extremely helpful in terms of what kind of objects that I will need to include in my tennis game as well as what point of the game I can start from, such as “paddles” and the pong ball.
One technique that came in handy was the “function preload()” skill, which I used to upload an image, audio file, and a font style! Here’s the code that I used with this function:
function preload() {
font = loadFont("JetBrainsMono-ExtraBold.ttf");
song = loadSound("media_doorbell.mp3");
photo = loadImage("cat.webp");
}
Then, I used the mouseX function for the pong bar/paddle so that it will freely move by the player’s intentions.
Once I was done with the paddle, I moved onto creating the title of the game, as well as the tennis ball itself. As for inserting the title “PONG STREAK,” I used the skills that I’ve learned from last time with our text assignment; I downloaded my desired text file from this website and inserted it into p5.js, and was able to use the “loadFont” function to load the text into the canvas, which produced the following title font:
Now that I had the basics such as the title, background, canvas, etc. done, it was time to code the details – the first and the biggest part of my code for this project was called “class pongBall.” Because I had to add many details for the ball in order to make it move, I decided that creating a class might be easier, which made it easier for me to include everything such as “move,” “draw,” and “barCollisions” under a single class. Here’s the code that I used for my pong ball:
It was after this part of my progress when things started to get really confusing. Having to use completely new functions such as “gameState” with different sub-actions such as “start” and “play” or creating new functions such as “startScreen” and “playScreen” and having my game run through these different stages smoothly took a long, LONG time.
And because I wanted to make my game include time constraint as well as an outcome (win/lose), having to figure these out was a tricky process. Thankfully, I was able to use this p5.js resource to guide me through the timer/countdown code process, which was a lot more simple compared to other methods that I found online. Here’s the code highlight for the countdown function:
//countdown
textAlign(CENTER, CENTER);
fill("white");
textSize(50);
text(timer, width / 2, height / 2);
if (frameCount % 60 == 0 && timer > 0) {
// if the frameCount is divisible by 60, then a second has passed. it will stop at 0
timer--;
}
As for the win/lose outcome, I ended up just combining it with the countdown function instead of separating it, so that it just continued without a break.
I was pretty happy with myself by this point, where my project looked something like this when you reached the end of the game – I especially liked the little kitten’s grin, which was an image I got from this site:
At this point, I felt like I was on the verge of going absolute bonkers because I have been spending 3 days on this project; and while I really tried to make my game reset/restart without closing the program, I never succeeded in making it work in the end. Here’s the code that I used, and I hope I’ll be able to identify the problem later on when I have more time in my hands to do so.
function draw() {
if (gameState == "start") {
startScreen();
}
if (gameState == "play") {
playScreen();
}
if (gameState == "end") {
restartScreen();
}
}
function restartScreen() {
if (keyCode == ENTER) {
gameState = "start";
}
}
Final Product:
Despite the ups-and-downs that I’ve had with this project, I’m pretty proud of my super-simple, very quick game:
Reflection:
This was undoubtedly the most challenging yet the most eye-opening/fulfilling project that I’ve had so far in this course! While it consisted of multiple mental breakdowns and panic attacks, it also really pushed me to understand all the skills I’ve learned throughout this semester because it really tested my knowledge that I’ve gained till now. It was both satisfying yet disappointing to see my skills so plainly through this project because while I did make progress in coding from my initial “absolutely no knowledge in coding whatsoever” stage, I also realized just how much there is to learn still. It was disheartening to see that none of my creative ideas and anticipations for exploring couldn’t come alive because of my limitations in coding. Something that I definitely enjoyed was installing sound effects as well as learning how to add points each time the ping pong ball hit the paddle/bar, because it was really satisfying to see how the points accumulated! It was cool to see how I was actually trying out all the back-end details that went behind the video games that I so casually played without giving thought about what kind of coding was necessary for those games to work. Next time I’ll definitely figure out how to make the game reset without having to refresh the page again, because that was a limitation that I couldn’t overcome for this project; I’d also love to create something with more color, photos, and a story rather than just creating a basic game like this one!
While playing Mario Party earlier this month, I was struck with the idea to recreate one of the game’s many minigames. After some thought, I settled on one named Goomba Spotting, in which players attempt to accurately tally the number of Goombas—a species of mushroom-like beings from the Mario series—that run across the screen. This is what it looks like:
While it would certainly be a massive challenge unlike anything I had done before, it was one I felt I could tackle at my current level. After mentally dissecting the game into a number of key components, I set out to work.
Challenges and Highlights
I first prepared the images that would be used in the game: one for the background and one for the Goombas. One decision I had made from the start was to have NYUAD be the setting for the game: the premise would be that a Goomba invasion had happened in our beloved institute, and that the two players were “Goomba Spotters” tasked with tallying the Goombas. I edited an image of NYUAD as seen from its entrance into two separate images, a foreground and a background—the Goombas would pass in between. For the Goomba itself, I selected a sprite online and edited the background away.
With the stage now in place, I decided to immediately take on the biggest challenge of coding the Goombas. How would they move? How many should there be? With questions brewing in my head, I created a class specifically for the Goombas and their movement. By adding new functions and adjusting values as needed, I was able to settle on a comprehensive block of code that worked.
One particular highlight from this process was figuring out a way to “stall” the Goombas so that they would come out one by one, instead of all at once. This is the function used to achieve this:
//Create Class for Goombas
class Goomba {
constructor(){
this.xPos = 0;
this.yPos = 492;
this.xSpeed = random(5, 10);
this.ySpeed = 0;
this.check = false;
}
//Used to "Stall" Each Goomba to Make Them Come Out in Order
stall(order){
if((frameCount - frameCheck) / (25 * (order)) == 1){
this.check = true;
}
}
//Changes Goomba's Position Based on Speed
move(){
if(this.check == true){
this.xPos += this.xSpeed;
this.yPos += this.ySpeed;
}
}
//Draws Goomba
draw(){
image(photo, this.xPos, this.yPos, 60, 70);
}
//There is more code in the class, but it is truncated here to keep things short.
}
This function effectively staggers the Goombas’ initial movement speeds by 25 frames each; in other words, the next Goomba in line will start moving 25 frames after the one before it has started moving.
(frameCount – frameCheck) is used to calculate the number of frames that have passed since the actual game has started (frameCheck is a variable that I made—it is defined as the frameCount at the time the gameState is set to ‘playing‘); said number is then divided by (25 * order), in which order is a parameter that corresponds to the “order” of the Goomba (calculated as i + 1).
The result of this division is then checked if it is equal to 1—and if so, this.check is set to true. As seen in the move() function, this.check has to be true in order for the Goomba to be able to move. A small self-congratulatory round of applause was in order.
Another major part of the code is a separate class I created for calculating tallies and results. Each click of a player’s assigned key (A for Player 1 and L for Player 2) is one tally, and this is kept track through this block of code:
//Create a Class for Tally
class Tally {
constructor(){
this.p1Number = 0;
this.p2Number = 0;
}
//Keeps Track of Each Press; One Press = One Tally
checkP1Click(){
this.p1Number ++;
}
checkP2Click(){
this.p2Number ++;
}
//There is more code in the class, but it is truncated here to keep things short.
}
//Function for Checking Key Presses
function keyPressed() {
//Player 1: A Key
if (keyCode === 65) {
tally.checkP1Click();
}
//Player 2: L Key
if (keyCode === 76) {
tally.checkP2Click();
}
}
65 and 76 are respective key codes for the A and L keys—so when pressed, the functions checkP1Click() and checkP2Click() are activated. This respectively adds 1 to this.p1Number and this.p2Number, which are the variables used to track the players’ tallies. These tallies are then compared to the actual number of Goombas at the end of the game to calculate the results (these are done through functions that are part of the truncated code mentioned above).
These two classes of Goomba() and Tally() are central to the game, which is in itself brought forth using a function named gameScreen(), which is called when gameState == ‘playing.’ On that note, it must be mentioned that the variable gameState is pivotal to this project, as it is used to transition between the start screen, game screen, and end screen. These three scenes are represented by three separate functions: startScreen(), gameScreen(), and endScreen(), which contain the code needed to bring each screen to life. These functions work in tandem with gameState, which has three values it alternates between: ‘start,’ ‘playing,’ and ‘end.’ The value of gameState is continually checked in the draw() function, and either startScreen(), gameScreen(), or endScreen() is called depending on the respective value. The code for this looks like:
//Draw Function: Displays Different Screen Based on gameState
function draw() {
//For Start
if (gameState == 'start') {
startScreen();
} //For Playing
else if (gameState == 'playing') {
gameScreen();
} //For End
else if (gameState == 'end') {
endScreen();
}
}
//Code has been abridged for convenience.
The start screen and end screen share an identical template (the same background and gray box is used) with different text. Another coding highlight is present within the background, a gradient made with an interesting function called lerpColor() that I learned anew for this specific task. lerpColor() blends two colors and finds a third color between the two, and the coder can also control how close the interpolated color is to either of the two colors. Using lerpColor() within a for() loop, I painted in thin rectangles that start near the first color (orange) and grow closer to the second color (light blue) with each iteration of the loop, giving off the impression of a gradient:
//Make Gradient Background
let from = color(255, 135, 0);
let to = color(0, 240, 255);
noStroke();
for (let i = 0; i < 664; i = i + 6.64){
fill(lerpColor(from, to, 0 + i * 0.0015))
rect(0, i, 820, 7);
}
Other highlights include figuring out ways to 1) vary the dialogue in the end screen based on the results (by using variables that functions in Tally() can alter to contain different text depending on the results), 2) change the number of Goombas in each game (by using a variable named randI, the value of which is reset every time the game is played), 3) make the Finish button appear after the game has actually ended (by tying the button to a variable named gameEnd that relies on the position of the last Goomba), and more.
Finally, here is the completed game:
Reflections
Whoa, what a project. Undoubtedly the biggest and toughest one yet—but I made it through! I feel pride in how I was able to achieve what I set out to do without making any major compromises. I admittedly felt incredibly stuck and confused at many points, unable to pull off certain effects I was aiming to implement (successfully stalling the movement of Goombas was especially challenging). Despite said difficulties, I was ultimately able to find a solution through each and every one of the major challenges I set out to tackle.
It’s been quite the journey! I started the class with a nearly blank slate, having only minimal coding experience (none with Javascript). Looking back, I’ve come quite far; it was a magical experience to be able to make something of my own that I personally envisioned and designed. And this will certainly not be the last.
For my Midterm project I was inspired by the idea of catching games. In fact, this time I thought long enough about the idea of my project, because there were so many options. Once I chose a game that I want to create, I began to describe the concept and in the process of research found other interesting games that captivated me more. One of the interesting sites I found https://codeguppy.com/, there I came across many impressive concepts, moreover, on this site I looked tutorials to create the game (collision, score counter, etc.).
The point of the game is that the player needs to collect 11 flowers to make your bouquet. The complexity of the game is that each time the speed of falling flower increases, and at the end of the player must be careful to quickly collect the falling flowers. If one flower falls and the player does not have time to catch it, the game is lost.
To build the Garden Walk game, I was inspired by the cartoon “Sofia the First”, a cute cartoon about a girl who unexpectedly becomes a princess, and characteristic of every princess, she lives in harmony with the flora and fauna around her. As for the painting in the background of the main game I chose (I have no idea why) a painting by Claude Monet. I chose as my theme and in the process just picked out pictures that I liked. For the audio I chose a quiet music for background sound and short sounds to announce the winning/losing.
Basically I liked the way the totally random images looked together and the way everything looked in the game. I’m also particularly proud of the work done on the game screen functions, where I was initially confused.
In the process, I ran into all kinds of problems that would eventually affect the outcome of the work. It took me a long time to figure out the variables and the gameplay steps, which led me to get confused with my own data. However, thanks to some useful tutorials on the internet, I was able to solve these problems as well. Furthermore, in the process, I wanted to change the player’s movement. I spent some time exploring different options, and achieved small results with the use of OOP. In this link you can see another outcome of my attempts. here
Also, here are some useful links that helped me get ahead in my work:
In general, I am happy with the result of my work. I believe I still have a lot to work on, yet it was a good start. In the future, I would like to eliminate past mistakes and add to my game:
* Many types of flowers so that there is variety.
* Change Sofia’s movement so that she moves all over the canvas and can catch flowers at any point.
* Make the game more difficult with the addition of “trash” So if the player caught trash, his game will be lost.
* Create a unified design for the entire game
My inspiration for this game was my puzzle assignments from Intro to CS that involved matrices calculations and boards (tik tac to, connect 4, etc.) and also this artwork in the Arts Center hallway:
The idea of the game is very simple. Just like the frame above, there is only one empty tile (randomly placed as a result of shuffling). Click on any empty tile’s adjacent cell to swap the two. Continue till all the pieces are in place!
Implementation:
Two (huge) classes Puzzle and Tile, seven methods, eight functions, 292 lines, and lots of debugging prints in between.
The basic idea is how you think about any puzzle; we need to create a board, divide it into cells (that can be replaced or swapped), assign each tile a pattern in such a way that the whole board represents a complete photo, then shuffle the cells and try to solve the puzzle. Each puzzle has a different set of rules; in this puzzle, you have one empty cell and are allowed to swap it with an adjacent cell.
The tiles:
It starts with a single tile. The tile class creates an object Tile with the appropriate properties. Each tile object has a specific position on the board tile[row][column]:
Each tile object is also assigned an image index ranging from 0-15, and the way we are displaying it is by multiplying the tile’s row and column number by its size (defined at the beginning of the program):
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
display_tile () {
if (this.img_index != (this.numRows*this.numCols) - 1) {
// print("image index is: ", this.img_index)
image(this.img, this.c*my_width, this.r*my_height, my_width, my_height);
}
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The board (matrix):
We are now ready to populate an array of arrays with our tile objects. The Puzzle class has a createBoard() method that loops through the range of numRows and numCols (passed as arguments in the Puzzle object constructor). A temporary array is created for each row in numRows, and then numCols arrays (numbers of columns) are pushed into the temp array. Then a new Tile object is created for each cell. Then we push this temporary list to the board (every tile is in the correct place at this point). Now before you exit the function, you shuffle all the tiles.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
createBoard () {
this.board = [];
for (var r=0; r<this.numRows; r++) {
// for each row create a temp list
var tempList = [];
// add numCols tiles to the list
for (var c=0; c<this.numCols; c++) {
// r*c is the image index
var new_tile = new Tile(r, c, r*this.numCols+c, this.numRows, this.numCols);
tempList.push(new_tile);
}
// push the list to the board
this.board.push(tempList);
}
this.shuffle_tiles();
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Shuffling tiles:
Picking random values for any tile’s [r] and [c] values (ranging from 0 to 4). Identify its neighbors -> [[0,-1], [1,0], [0,1], [-1,0]]. Swap the tile with a neighbor from this list (also random).
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
shuffle_tiles () {
// var current_r = this.numRows-1;
// var current_c = this.numCols-1;
var current_r = int(random(0,4)); // initially 0-3, left most cells or right most?
var current_c = int(random(0,4));
var neighbors = [[0,-1], [1,0], [0,1], [-1,0]];
// increase s to have a more standard shuffle - was 20 initially
for (var s=0; s<10; s++) {
var empty_tile = this.board[current_r][current_c];
var shuffling = random(neighbors);
var destination_r = current_r + shuffling [0];
var destination_c = current_c + shuffling [1];
while (destination_r < 0 || destination_c < 0 || destination_r > this.numRows-1 || destination_c > this.numCols-1) {
shuffling = random(neighbors);
destination_r = current_r + shuffling [0];
destination_c = current_c + shuffling [1];
}
var new_tile = this.board[destination_r][destination_c];
// print("Before swap: " + str(this.board [current_r][current_c].img_index) + ", " + str(this.board [destination_r][destination_c].img_index));
var temp = empty_tile.img_index;
empty_tile.img_index = new_tile.img_index;
new_tile.img_index = temp;
// print("After swap: " + str(this.board [current_r][current_c].img_index) + ", " + str(this.board [destination_r][destination_c].img_index));
current_r = destination_r;
current_c = destination_c;
for (var r=0; r<this.numRows; r++) {
for (var c=0; c<this.numCols; c++) {
var the_tile = this.board[r][c];
the_tile.img = loadImage ('resources/'+str(the_tile.img_index)+'.png');
}
}
}
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Swapping tiles:
Method tile_empty_adj() returns two important values: the current empty tile (empty tile has a fixed index of [3][3], but it moves around as a result of shuffle), and also a list of empty tile’s neighbors.
We record mouse[x][y] when a click happens on the empty cell’s neighbors and swap their img_index so at the next iteration of draw() when the display() method is called, the tiles would be switched and now you have a new empty cell. Repeat process.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
swap(nonempty, empty) {
// takes coordinates of two tiles, swap their index
var empty_tile = this.board[empty[0]][empty[1]];
// print("this is empty tile & img index: ", empty_tile, empty_tile.img_index);
var new_tile = this.board [nonempty[0]][nonempty[1]];
// print("this is new tile & img index: ", new_tile, new_tile.img_index);
var temp = empty_tile.img_index;
empty_tile.img_index = new_tile.img_index;
new_tile.img_index = temp;
var tmp=new_tile.img;
new_tile.img = empty_tile.img;
// loadImage ('resources/'+str(new_tile.img_index)+'.png');
empty_tile.img = tmp;
// loadImage ('resources/'+str(empty_tile.img_index)+'.png');
// print("after swap: ", empty_tile, empty_tile.img_index);
// print("after swap: ", new_tile, new_tile.img_index);
for (var r=0; r<this.numRows; r++) {
for (var c=0; c<this.numCols; c++) {
// print(r,c, this.board[r][c].img_index);
if (this.board[r][c].img_index != r*4 + c) {
return false;
}
}
}
this.win = true;
print ("won game!");
this.win_sound.play();
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function mouseClicked () {
var empty, adj, empty_adj;
empty_adj = my_puzzle.tile_empty_adj();
empty = empty_adj[0];
adj = empty_adj[1];
// print("adjacent cells: ", adj);
mouse = [int(mouseY/my_height), int(mouseX/my_width)];
// print(mouse);
for(var i=0; i<adj.length;i++) {
if(mouse[0] == adj[i][0] && mouse[1] == adj[i][1]) {
// print("we are here in the swap call\n");
// switch the empty and non-empty slots
my_puzzle.swap(mouse, empty);
// my_puzzle.display();
}
}
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Win conditions:
You win if ALL the tiles in the board have the img_index initially assigned to them e.g. tile[0][0] -> img_index 0, tile[2][3] -> img_index 12 and so on. The empty cell belongs to the last tile (tile[4][4]) and that’s where it should end up for the win condition to be true.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
for (var r=0; r<this.numRows; r++) {
for (var c=0; c<this.numCols; c++) {
// print(r,c, this.board[r][c].img_index);
if (this.board[r][c].img_index != r*4 + c) {
return false;
}
}
}
this.win = true;
print ("won game!");
this.win_sound.play();
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Gameplay:
I tried to keep the instructions very minimal:
1) User is presented with an instructions screen that they can go back to at any point in the game by pressing i (i = instructions)
2) User can start the game by pressing p (p = puzzle)
3) User has the choice to play the minions’ music or mute it while they solve the puzzle (it has a low amplitude anyway). (m = mute, u= unmute)
4) In some rounds the shuffling is very complicated (due to randomness) and the user might not want to spend a lot of time on solving it (e.g. for playtesting purposes). The user has the option to reshuffle the board by pressing s (s = shuffle).
Embedded sketch:
Improvements:
Currently, I’m working on writing a function that slices any image into X equal parts (very useful for puzzle purposes). This can extend the current program from a minion puzzle to any puzzle of the user’s choosing. While this function is easy to implement, the next step that involves saving each slice (of image) back to the assets folder is a bit tricky and needs some research. But overall I think the createBoard()/swap()/shuffle()/display() methods are very extendible and are basic foundations for many board games.
There used to be this game called Purble Place by Oberon Games. This game was available on windows operating systems by was removed from the newer ones. The game has a collection of smaller games and the one I was most inspired by was called comfy cakes.
The goal of the game is to try and mimic the cake displayed in the top left corner while the conveyer belt moves the plate(papers in this case). The game has buttons to move the conveyer belt however the cake orders would keep coming so you would need to work fast.
what my version has:
I looked at the game version and picked the aspects I want to recreate and the ones I wanted to change. I watched a few videos of the game, and looked at pictures since I couldn’t play the game because it’s no longer available. I decided to simplify the graphics so I can focus on the code aspect. (Picture of notes on the side, not sure why it’s blurry)
I also had to decide on a game logic of how the players win and lose and so on. This would also help me decide on what classes would control which part. This is the logic I decided on:
the player would first be presented with the main page the move to the instructions page after-which the player can start the game
the game it self would consist of a conveyer belt that moves the plates. once the cake is under one the dispensers the player must click the dispenser to add the cake part.
The player’s goal is to add as many cake parts correctly before running out of orders.
The player wins if the player gets at scores 80%.
things implemented successfully:
a new cakes are being automatically recreated once the current one leaves the window
two classes are created (
cake creator class
keeps track of the total score
number of cakes
creates new cakes
draws the cakes
single cake class
creates only one cake
adds parts to said cake
keeps track of only it’s score
check if said cake is under (within a certain range of) the dispenser
the cake parts are added once the user clicks on the dispensers
Features I didn’t plan to implement but would be cool:
a score board (learned how to send and req data from APIs in connections lab, so I can use a google sheets API to store the user’s data and display them)
Different difficulty levels
add more cake features to make it more challenging
Design aspects
I also wanted to work on the design aspect and make sure that the design of each page is intuitive and also has a consistent theme.
The main page looks like this. I looked for free graphics online and cropped the ones I liked. the cake slices are rotated and “float”(move) a round a bit to make the page less static. The background has sprinkles which are randomly drawn lines.
there is a button that says start. if the user hovers over it it darkens to make it clearer that it is a button. the cursor also changes to a pointer.
the instruction page has the same sprinkles but has a translucent rectangle in front of it to make sure that the text is readable. this page also has a button
This is the game page. it shows the score and the number of orders left. It also shows what the cake should look like. It has an audio that indicates that a new cakes is created and another that plays when the dispensers are used.
One thing I would change is the way the dispensers look. I wanted to get better graphics for them and include the text better did not have time to do that.
This is the end game page. It offers the user the option to go to the home page or to restart the game. It also displays if the user won or lost and their score (the score here is there was only one cake order, I was only testing here)
The page also has the same translucent layer as the instructions page but here I added some more graphics.
aspects that I am proud of:
I think that the code was neater than I expected it would turn out. I anticipating that this project would be a long one and that things might not be as intuitive looking back a few days later I tried to neaten the code as I went, keep the variable names descriptive, and create functions for things that I expect to use multiple times (like the buttons). But I usually say I’d do that and end up being lazy and picking unintuitive names for variables, but I stuck to it this time which I am proud of. I think that the code could still benefit from being spread on multiple js files (if that is possible).
challenges:
one of the challenges was handling all the different components of the game. The code got really long (like I assumed it would), I tried to prepare for that by making sure that my code is modular and by using many drafts with clearly stating the functions each draft achieves. Those two things made it easier for me to go back to previous versions, and also ensured that if a feature did break, the code would be minimally affected and recovering would be easier. I changed the cursor when hovering over the navigation buttons, added an instructions page, and tried to keep the design simple. However, I believe that there are still some things that I didn’t anticipate the user would need. I’ll fix this gap by letting someone interact with the game and noting down where they struggled and what they didn’t understand on their own. I think that this would give me more insight into the user’s experience however I did not have the time to do this for this project.
some things that were helpful in p5js:
you can use textWidth(“Text”) to get the width of the text. this helps when centering text
you can do random(list_name) to get a variable from the list instead of doing random( list_name[random(range)) and using a function to get random integers to get an index.
For my Midterm Project, I decided I wanted to create a sidescroller game similar to Super Mario and Geometry Dash. After contemplating the idea of the game, I chose to create a floor lava game. The user starts on the left side of the map with the aim of getting to the finish line (right side of the map). The user must jump on platforms to avoid falling into the ‘lava pit’ and dying. There is a title screen displaying the name, levels (Easy, Medium, and Hard), and instructions. The user can pick any level to start off with. If the user falls into the ‘lava’ a screen will be displayed informing them that they lost and they can go back to the menu. If they successfully reach the other side, a screen will be displayed informing them that they have won and that they can go back to the menu. There is a continuous lava sound playing in the background of the game. Link to game: https://editor.p5js.org/aishaalketbi_/sketches/LH21rAmDW
How the Game Works:
With the help of Chris Courses on Youtube (Mario Game Tutorial with JavaScript and HTML Canvas), I managed to get things working. I first created a Class of the Player and Ground to test whether scrolling, collision, and movement would work. I gave my Player a constant velocity. For collision, I created a for loop that detected If the player was on top of the ground it would not fall through. For scrolling the ground, I coded that once the player reaches 450 on the X axis, the ground X axis would be decreased to the left to create the illusion of scrolling. Once I managed to get that working I added the platform as well. Here is the code for scrolling:
if (player.x < 450) {
player.x += player.xg;
player.xg += player.v;
ground.forEach((ground) => {
//moves the ground to the left to create the illusion of scrolling
ground.x -= 10;
scrollOffset += 5;
});
platforms.forEach((platforms) => {
//moves the ground to the left to create the illusion of scrolling
platforms.x -= 10;
});
}
Furthermore, with the help of Mangs code, I allowed the background to scroll continuously.
The function keypressed is essential to my code as it not only allows the user to jump with the use of gravity but also allows the user to navigate between different levels and to the menu. Here is a photo of the title screen:
Good Game Design:
Originally I was only going to put platforms (the shorter platform) for the user to jump on however, I decided to add both longer (the ground) and shorter platforms to make the game more difficult for the user. The key pressed-up function also is a good game design as the user doesn’t have to worry about moving the ball itself, all they have to do is jump.
Challenges:
There were a few challenges I came across. Firstly, I wasn’t sure how to use a for loop for the platforms and the grounds. It didn’t seem to work when it came to changing the position. So to tackle this, I used Chris Courses code which made it much easier and simpler for me. This is the code and it is the one I’m most proud of: