Midterm Project: Interactive Fantasy Game

For this project, I decided to transform a short fantasy story I wrote in primary school into an interactive game using p5.js. This game is played by a single player who guides the protagonist, Eden, on her quest to slay the dargon and save her village. The game has multiple choices that affect the storyline, but in the end, all paths lead to the same outcome. There are a total of 5 key decision points and 14 different screens in the game.

How it works:

The game’s structure is built around a series of screens, each representing a different part of Eden’s journey. The transitions between screens are managed efficiently through the mousePressed() function and buttons. The game utilizes object-oriented programming (OOP) for button management.

Proud Achievements and Technical Decisions
One of the most significant achievements in this project is the implementation of the OOP approach for button management. Initially, built-in functions were used, but switching to OOP proved to be a more flexible and maintainable solution. This decision allows for easier customization of button behavior and appearance across different screens.

Another achievment is the overall structure of thegame, with its multiple screens and decision points. This took a long time and required  careful planning and implementation to ensure smooth transitions and logical flow of the story.

The integration of background images and music also adds to the immersive experience of the game. These elements help bring the fantasy world of EverLand to life for the player.

The buttons code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
//Creating the buttons class
class GameButton {
//initializing the state of the button
constructor(label) {
this.label = label;
this.x = 0;
this.y = 0;
this.width = 105;
this.height = 30;
this.visible = false;
}
//method to show button at specified coordinates
show(x, y) {
this.x = x;
this.y = y;
this.visible = true;
fill("white");
noStroke();
rect(this.x, this.y, this.width, this.height);
fill("black");
textSize(13);
textAlign(CENTER, CENTER);
text(this.label, this.x + this.width / 2, this.y + this.height / 2);
fill("white") //so trhat the text itself is white
textSize(15); //so that the text itself has a size of 15
}
//method to hide button
hide() {
this.visible = false;
}
//method to identify if mouse is hovering over button
isMouseOver() {
return (
this.visible &&
mouseX > this.x &&
mouseX < this.x + this.width &&
mouseY > this.y &&
mouseY < this.y + this.height
);
}
}
//Creating the buttons class class GameButton { //initializing the state of the button constructor(label) { this.label = label; this.x = 0; this.y = 0; this.width = 105; this.height = 30; this.visible = false; } //method to show button at specified coordinates show(x, y) { this.x = x; this.y = y; this.visible = true; fill("white"); noStroke(); rect(this.x, this.y, this.width, this.height); fill("black"); textSize(13); textAlign(CENTER, CENTER); text(this.label, this.x + this.width / 2, this.y + this.height / 2); fill("white") //so trhat the text itself is white textSize(15); //so that the text itself has a size of 15 } //method to hide button hide() { this.visible = false; } //method to identify if mouse is hovering over button isMouseOver() { return ( this.visible && mouseX > this.x && mouseX < this.x + this.width && mouseY > this.y && mouseY < this.y + this.height ); } }
//Creating the buttons class
class GameButton {
  //initializing the state of the button
  constructor(label) {
    this.label = label;
    this.x = 0;
    this.y = 0;
    this.width = 105;
    this.height = 30;
    this.visible = false;
  }
  //method to show button at specified coordinates
  show(x, y) {
    this.x = x;
    this.y = y;
    this.visible = true;
    fill("white");
    noStroke();
    rect(this.x, this.y, this.width, this.height);
    fill("black");
    textSize(13);
    textAlign(CENTER, CENTER);
    text(this.label, this.x + this.width / 2, this.y + this.height / 2);
    fill("white") //so trhat the text itself is white
    textSize(15); //so that the text itself has a size of 15
  }
  //method to hide button
  hide() {
    this.visible = false;
  }

  //method to identify if mouse is hovering over button
  isMouseOver() {
    return (
      this.visible &&
      mouseX > this.x &&
      mouseX < this.x + this.width &&
      mouseY > this.y &&
      mouseY < this.y + this.height
    );
  }
}

The screens code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function draw() {
//calling the function that displays the screen based on the screen assignment which happens when the mouse is pressed
if (screen === 0) {
showStartScreen();
} else if (screen === 1) {
showBirthdayScreen();
} else if (screen === 11) {
showSuppliesScreen();
} else if (screen === 12) {
showWeaponScreen();
} else if (screen === 111 || screen === 121) {
showNightScreen();
} else if (screen === 112 || screen === 122) {
showMorningScreen();
} else if (screen === 1111 || screen === 1121 || screen === 1211 || screen === 1221) {
showRiverScreen();
} else if (screen === 1112 || screen === 1122 || screen === 1212 || screen === 1222) {
showForestScreen();
} else if (screen === 11000 || screen === 12000 || screen === 21000 || screen === 22000) {
showNextScreen();
} else if (screen === 5000) {
showDragonCaveScreen();
} else if (screen === 5001) {
showInsideScreen();
} else if (screen === 5002) {
showOutsideScreen();
} else if (screen === 5003) {
showTrapScreen();
} else if (screen === 262626) {
showFinalScreen();
}
}
//function to hide all buttons which i will use when switching screens so that the previous buttons don't appear on the screen
function hideAllButtons() {
enterButton.hide();
suppliesButton.hide();
weaponButton.hide();
nightButton.hide();
morningButton.hide();
riverButton.hide();
forestButton.hide();
fishButton.hide();
riverspiritsButton.hide();
next1Button.hide();
insideButton.hide();
outsideButton.hide();
trapButton.hide();
next2Button.hide();
forestspiritsButton.hide();
firefliesButton.hide();
playButton.hide();
quitButton.hide();
}
//assigns screen with next number based on the previous screen and the button pressed and also hides all buttons so that they aren't layered on top of one another
function mousePressed() {
if (screen === 0 && enterButton.isMouseOver()) {
screen = 1;
hideAllButtons();
} else if (screen === 1) {
if (suppliesButton.isMouseOver()) {
screen = 11;
hideAllButtons();
}else if (weaponButton.isMouseOver()) {
screen = 12;
hideAllButtons();
}
} else if (screen === 11 || screen === 12) {
if (nightButton.isMouseOver()) {
screen = screen * 10 + 1;
hideAllButtons();
}else if (morningButton.isMouseOver()) {
screen = screen * 10 + 2;
hideAllButtons();
}
}else if (screen === 111 || screen === 112 || screen === 121 || screen === 122) {
if (riverButton.isMouseOver()) {
screen = screen * 10 + 1;
hideAllButtons();
} else if (forestButton.isMouseOver()) {
screen = screen * 10 + 2;
hideAllButtons();
}
} else if (screen === 1111 || screen === 1121 || screen === 1211 || screen === 1221) {
if (fishButton.isMouseOver()) {
screen = 11000;
hideAllButtons();
} else if (riverspiritsButton.isMouseOver()) {
screen = 12000;
hideAllButtons();
}
} else if (screen === 1112 || screen === 1122 || screen === 1212 || screen === 1222) {
if (firefliesButton.isMouseOver()) {
screen = 21000;
hideAllButtons();
} else if (forestspiritsButton.isMouseOver()) {
screen = 22000;
hideAllButtons();
}
} else if (screen === 11000 || screen === 12000 || screen === 21000 || screen === 22000) {
if (next1Button.isMouseOver()) {
screen = 5000;
hideAllButtons();
}
} else if (screen === 5000) {
if (insideButton.isMouseOver()) {
screen = 5001;
hideAllButtons();
} else if (outsideButton.isMouseOver()) {
screen = 5002;
hideAllButtons();
} else if (trapButton.isMouseOver()) {
screen = 5003;
hideAllButtons();
}
} else if (screen === 5001 || screen === 5002 || screen === 5003) {
if (next2Button.isMouseOver()) {
screen = 262626;
hideAllButtons();
}
} else if (screen === 262626) {
if (playButton.isMouseOver()) {
restartGame();
} else if (quitButton.isMouseOver()) {
quitGame();
}
}
}
function draw() { //calling the function that displays the screen based on the screen assignment which happens when the mouse is pressed if (screen === 0) { showStartScreen(); } else if (screen === 1) { showBirthdayScreen(); } else if (screen === 11) { showSuppliesScreen(); } else if (screen === 12) { showWeaponScreen(); } else if (screen === 111 || screen === 121) { showNightScreen(); } else if (screen === 112 || screen === 122) { showMorningScreen(); } else if (screen === 1111 || screen === 1121 || screen === 1211 || screen === 1221) { showRiverScreen(); } else if (screen === 1112 || screen === 1122 || screen === 1212 || screen === 1222) { showForestScreen(); } else if (screen === 11000 || screen === 12000 || screen === 21000 || screen === 22000) { showNextScreen(); } else if (screen === 5000) { showDragonCaveScreen(); } else if (screen === 5001) { showInsideScreen(); } else if (screen === 5002) { showOutsideScreen(); } else if (screen === 5003) { showTrapScreen(); } else if (screen === 262626) { showFinalScreen(); } } //function to hide all buttons which i will use when switching screens so that the previous buttons don't appear on the screen function hideAllButtons() { enterButton.hide(); suppliesButton.hide(); weaponButton.hide(); nightButton.hide(); morningButton.hide(); riverButton.hide(); forestButton.hide(); fishButton.hide(); riverspiritsButton.hide(); next1Button.hide(); insideButton.hide(); outsideButton.hide(); trapButton.hide(); next2Button.hide(); forestspiritsButton.hide(); firefliesButton.hide(); playButton.hide(); quitButton.hide(); } //assigns screen with next number based on the previous screen and the button pressed and also hides all buttons so that they aren't layered on top of one another function mousePressed() { if (screen === 0 && enterButton.isMouseOver()) { screen = 1; hideAllButtons(); } else if (screen === 1) { if (suppliesButton.isMouseOver()) { screen = 11; hideAllButtons(); }else if (weaponButton.isMouseOver()) { screen = 12; hideAllButtons(); } } else if (screen === 11 || screen === 12) { if (nightButton.isMouseOver()) { screen = screen * 10 + 1; hideAllButtons(); }else if (morningButton.isMouseOver()) { screen = screen * 10 + 2; hideAllButtons(); } }else if (screen === 111 || screen === 112 || screen === 121 || screen === 122) { if (riverButton.isMouseOver()) { screen = screen * 10 + 1; hideAllButtons(); } else if (forestButton.isMouseOver()) { screen = screen * 10 + 2; hideAllButtons(); } } else if (screen === 1111 || screen === 1121 || screen === 1211 || screen === 1221) { if (fishButton.isMouseOver()) { screen = 11000; hideAllButtons(); } else if (riverspiritsButton.isMouseOver()) { screen = 12000; hideAllButtons(); } } else if (screen === 1112 || screen === 1122 || screen === 1212 || screen === 1222) { if (firefliesButton.isMouseOver()) { screen = 21000; hideAllButtons(); } else if (forestspiritsButton.isMouseOver()) { screen = 22000; hideAllButtons(); } } else if (screen === 11000 || screen === 12000 || screen === 21000 || screen === 22000) { if (next1Button.isMouseOver()) { screen = 5000; hideAllButtons(); } } else if (screen === 5000) { if (insideButton.isMouseOver()) { screen = 5001; hideAllButtons(); } else if (outsideButton.isMouseOver()) { screen = 5002; hideAllButtons(); } else if (trapButton.isMouseOver()) { screen = 5003; hideAllButtons(); } } else if (screen === 5001 || screen === 5002 || screen === 5003) { if (next2Button.isMouseOver()) { screen = 262626; hideAllButtons(); } } else if (screen === 262626) { if (playButton.isMouseOver()) { restartGame(); } else if (quitButton.isMouseOver()) { quitGame(); } } }
function draw() {
  //calling the function that displays the screen based on the screen assignment which happens when the mouse is pressed
  if (screen === 0) {
    showStartScreen();
  } else if (screen === 1) {
    showBirthdayScreen();
  } else if (screen === 11) {
    showSuppliesScreen();
  } else if (screen === 12) {
    showWeaponScreen();
  } else if (screen === 111 || screen === 121) {
    showNightScreen();
  } else if (screen === 112 || screen === 122) {
    showMorningScreen();
  } else if (screen === 1111 || screen === 1121 || screen === 1211 || screen === 1221) {
    showRiverScreen();
  } else if (screen === 1112 || screen === 1122 || screen === 1212 || screen === 1222) {
    showForestScreen();
  } else if (screen === 11000 || screen === 12000 || screen === 21000 || screen === 22000) {
    showNextScreen();
  } else if (screen === 5000) {
    showDragonCaveScreen();
  } else if (screen === 5001) {
    showInsideScreen();
  } else if (screen === 5002) {
    showOutsideScreen();
  } else if (screen === 5003) {
    showTrapScreen();
  } else if (screen === 262626) {
    showFinalScreen();
  }
}

//function to hide all buttons which i will use when switching screens so that the previous buttons don't appear on the screen
function hideAllButtons() {
  enterButton.hide();
  suppliesButton.hide();
  weaponButton.hide();
  nightButton.hide();
  morningButton.hide();
  riverButton.hide();
  forestButton.hide();
  fishButton.hide();
  riverspiritsButton.hide();
  next1Button.hide();
  insideButton.hide();
  outsideButton.hide();
  trapButton.hide();
  next2Button.hide();
  forestspiritsButton.hide();
  firefliesButton.hide();
  playButton.hide();
  quitButton.hide();
}

//assigns screen with next number based on the previous screen and the button pressed and also hides all buttons so that they aren't layered on top of one another
function mousePressed() {
  if (screen === 0 && enterButton.isMouseOver()) {
    screen = 1;
    hideAllButtons();
  } else if (screen === 1) {
    if (suppliesButton.isMouseOver()) {
      screen = 11;
      hideAllButtons();
    }else if (weaponButton.isMouseOver()) {
      screen = 12;
      hideAllButtons();
    }
  } else if (screen === 11 || screen === 12) {
    if (nightButton.isMouseOver()) {
      screen = screen * 10 + 1;
      hideAllButtons();
    }else if (morningButton.isMouseOver()) {
      screen = screen * 10 + 2;
      hideAllButtons();
    }
  }else if (screen === 111 || screen === 112 || screen === 121 || screen === 122) {
    if (riverButton.isMouseOver()) {
      screen = screen * 10 + 1;
      hideAllButtons();
    } else if (forestButton.isMouseOver()) {
      screen = screen * 10 + 2;
      hideAllButtons();
    }
  } else if (screen === 1111 || screen === 1121 || screen === 1211 || screen === 1221) {
    if (fishButton.isMouseOver()) {
      screen = 11000;
      hideAllButtons();
    } else if (riverspiritsButton.isMouseOver()) {
      screen = 12000;
      hideAllButtons();
    }
  } else if (screen === 1112 || screen === 1122 || screen === 1212 || screen === 1222) {
    if (firefliesButton.isMouseOver()) {
      screen = 21000;
      hideAllButtons();
    } else if (forestspiritsButton.isMouseOver()) {
      screen = 22000;
      hideAllButtons();
    }
  } else if (screen === 11000 || screen === 12000 || screen === 21000 || screen === 22000) {
    if (next1Button.isMouseOver()) {
      screen = 5000;
      hideAllButtons();
    }
  } else if (screen === 5000) {
    if (insideButton.isMouseOver()) {
      screen = 5001;
      hideAllButtons();
    } else if (outsideButton.isMouseOver()) {
      screen = 5002;
      hideAllButtons();
    } else if (trapButton.isMouseOver()) {
      screen = 5003;
      hideAllButtons();
    }
  } else if (screen === 5001 || screen === 5002 || screen === 5003) {
    if (next2Button.isMouseOver()) {
      screen = 262626;
      hideAllButtons();
    }
  } else if (screen === 262626) {
    if (playButton.isMouseOver()) {
      restartGame();
    } else if (quitButton.isMouseOver()) {
      quitGame();
    }
  }
}

Areas for Improvement:
While the current version of the game is functional, there are several areas for potential improvement:

Expanding the storyline: Adding more options and choices would increase replayability and make the game more interactive. For example, allowing players to pick specific weapons or have more detailed interactions with characters like the spirits could add depth to the game.

Enhanced interactivity: Implementing features like dialogue systems for conversations with spirits or other characters could make the game more engaging. This could involve creating a more complex dialogue management system.

Challenges:

One of the main challenges faced was transitioning from built-in button functions to the OOP approach. This required refactoring a significant portion of the code and it would’ve been so much easier if I just started with OOP.

Moving forward, the focus will be on enriching the game content and enhancing the player’s ability to influence the story through their choices.

The game sketch:

Leave a Reply