1. p5js Sketch
If the experience looks static, please click on the ‘sketch’ version and run it. If you do not see the sketch to the entire width and height, please use the ‘sketch’ version instead.
2. Overall Concept
In my midterm project, the user takes on the role of a beta-tester of a 2D RPG casual adventure game “Tales of Avella” played on a fictional, futuristic ‘Playstation 9’ device. ‘Playstation 9’ is meant to be a much more state-of-the-art video game console than the currently available ‘Playstation 5’ (see Fig. 1). In “Tales of Avella,” the user has the opportunity to explore the world and help someone in need. Upon beginning the experience, the Playstation 9 apparently “turns on” and “loads” the game. The user is greeted with a welcome message from fictional PixelBloom Studios developers, as well as informed with background story for the game and instructions for controlling the user’s character, “Luke.” The background story of the game goes as follows: Luke has just moved into the charming village of Avella and is ready to discover what makes this town so special. The user can control “Luke” to explore the world in “Tales of Avella.” using the arrow keys (left, right, up, down). Depending on the user’s actions, the user could enter new areas and meet a non-playable character (NPC) farmer with a quest to harvest forty-five carrots. Upon completion of the quest, a congratulatory message is displayed, followed by a ‘memory’ snapshot of a gratitude dinner with the farmer and his daughter Lily is displayed. Finally, the user faces an option to restart the game, by pressing ‘Y’ for yes and ‘N’ for no.


The user has the opportunity to interact with the world in ways more than moving Luke around. The user could use mouse clicks to progress through the conversation between the farmer and Luke. At some point in the conversation, and if the user decides to accept the farmer’s quest, the farmer’s name is revealed from an initially mysterious title “???”. The user could have Luke grab a tool in Luke’s vicinity by pressing ‘i’. The user could also have a shovel-handling Luke harvest some carrots by pressing ‘i’ and replant some carrots by pressing ‘r.’
3. How Project Works & Good Parts
Good, major game design aspects can be found in project concept, project structure, dialogue flowchart, assets (video, image, audio), character design, story design, quest progress possibilities, quest progress feedback, as well as particular details of the game mechanics. Firstly, the project concept is an attempt on an innovative twist on a simply general game played on a browser on the PC – by simulating the experience of playing a game on a futuristic device, and creating a story for the user as a beta-tester of a game. Thus, there is a frame narrative entailing a story within a story; the character “Luke” within the game “Tales of Avella” has a story that can be played by a “beta-tester” that has a story in supporting the developers at a fictional PixelBloom Studios Inc. The juxtaposition of a pixelated game played in a futuristic device is another game design choice with the intention of bringing an interesting effect – whether it is raising questions, emphasizing contrast in technological development vs historical game interest, or something else. Second, the project has been structured in six stages from 0-5, namely: (0) Opening with Background Story and Instructions; (1) Game Landing Scene (Inside Luke’s House); (2) Game Scene 2 – Luke’s Neighbourhood Outside His House; (3) Game Scene 3 – Farm; (4) Game Scene 4 – Inside Farm Shed; (5) Quest Completion Congratulatory Message with Dinner Memory Fragment and Option to Restart. A gallery containing snapshots of the game progress may be seen below. Third, considering the complex nature of a dialogue between a quest-giving farmer and Luke, I decided to create a flowchart (see flowchart image below the gallery). Fourth, videos were personally designed through Canva, taking into consideration adherence to the concept of the futuristic device interface and visual appeal through positioning of text, etc. I also browsed through audio, considering its appropriateness to the scene at hand; ambient music when the Playstation 9 opens up, relaxing acoustic guitar for the dinner memory, and game atmosphere sounds based on my past experience with Playstation 4/5. Character spritesheets were taken from Spriter’s Resource, chosen based on their closeness to the ideas of the characters I had in mind: a young adult entering a new town and a farmer. Fifth, I spent time on character design (see character traits table below flowchart). Sixth, I designed the message from Pixelbloom Studios Team to the player. Seventh, quest progress possibilities were considered and quest progress feedback was implemented for enhanced user experience. Since the quest is on harvesting an exact amount of forty-five carrots, if the user harvested less than forty-five carrots, there is a prompt from the farmer to pull more carrots. If the user harvested more than forty-five carrots, the farmer “frowns” and tells Luke, “Over fourty-five. Go back.” At some point in development, I realized that only having the possibility to harvest carrots would impede the user from being able to complete the quest if the user has harvested more than forty-five carrots by accident, so I decided to add the option to replant some carrots – this is one of the changes in my project plan. Last but not least, I find certain little details in the game mechanics are crucial. For example, in Stage 3, the farmer NPC always “faces” Luke, whether Luke is to his right or to his left, mimicking real dialogue.


Good technical decisions include setting a game state for which actions happen, structuring characters and objects with behavior (namely Luke, Farmer Giuseppe, message box between Luke and Farmer Giuseppe, shovel, carrot) into classes, structuring code into functions, using arrays to store huge amounts of carrots, designing the game mechanics of the dialogue using if and else if statements, and useful variable names. The game state is crucial because depending on game state, the farmer, carrots and videos may or may not be displayed. Structuring Luke and the Farmer using OOP has been very helpful for organization and readability, and therefore aids in my problem-solving process. Using arrays to store huge amounts of carrots does not only save time, but also helps me control the behavior of certain carrots – whether it’s status is “picked”, “unpicked” or “replanted” based on user behavior. The dialogue is a particularly complex piece of code, thus I paste the snippets below. The first snippet shows the constructor of the message box, which contains x position, y position, image profiles (on the left part of message box to help indicate who is speaking), dialogue_states array, and more arrays containing the speech text of the character(s) for the relevant dialogue_state. The second snippet is a clever way of reducing lines and reusing code as I need to display the profiles as often as I display a dialogue message. The third snippet reveals part of a long function performing actions based on the current dialogue_state. For the “Character Introduction” state, as with many other states, translation has been applied to the message box image and setTimeout is used to have a 200ms cooldown between advances to prevent accidental skips/rapid firing.
class Farmer_Giuseppe_And_Luke_Message_Box { constructor(luke_profile, farmer_profile) { this.x = 15; this.y = 500; this.luke_profile = luke_profile; this.farmer_profile = farmer_profile; this.farmer_quest_progress = "not started"; this.dialogue_states = [ "Character Introduction", "Quest Introduction", "Player Choice Point", "Repeated Quest Request", "Quest Further Details", "Quest Progress", "Quest Completed", "After Quest", ]; this.dialogue_current_state = this.dialogue_states[0]; this.intro_messages = [...]; this.current_message_index = 0; this.input_blocked_cooldown = false; this.reveal_title_flag = false; this.quest_intro_messages = [...]; this.player_choice_yes = [...]; this.player_choice_no = [...]; this.quest_further_details = [...]; this.quest_progress_messages = [...]; this.quest_thanks = [...]; }
display_profiles(person_speaking) { if (person_speaking === "???" || person_speaking === "Farmer Giuseppe") { push(); translate(110, 550); image(this.farmer_profile, 0, 0, 130, 100); pop(); } else if (person_speaking === "Luke") { push(); translate(110, 550); image(this.luke_profile, 0, 0, 100, 130); pop(); } }
display_message_box_and_text(farmer_giuseppe_x, farmer_giuseppe_y, luke_x, luke_y, farmer_title, carrots) { if ( dist(farmer_giuseppe_x, farmer_giuseppe_y, luke_x, luke_y) <= width / 8 ) { textAlign(LEFT); fill(0); //--------------------------------------------------// if (this.dialogue_current_state === "Character Introduction") { push(); // Center horizontally using message_box width translate(width / 2, 550); image(message_box, 0, 0); pop(); // Show current message let person_speaking = this.current_message_index % 2 === 0 ? "Luke" : farmer_title; this.display_profiles(person_speaking); textSize(17); text(person_speaking, 200, 500, 85, 120); text( this.intro_messages[this.current_message_index], 300, 500, 300, 120 ); // Show continuation prompt if (this.current_message_index <= this.intro_messages.length - 1) { text("[Click to continue]", 300, 590, 600, 20); } // Advance dialogue on mouse press if ( mouseIsPressed && !this.input_blocked_cooldown && frameCount % 10 === 0 ) { if (this.current_message_index < this.intro_messages.length - 1) { this.current_message_index++; } else { this.dialogue_current_state = "Quest Introduction"; this.current_message_index = 0; } this.input_blocked_cooldown = true; // 200ms cooldown between advances // Prevents accidental skips/rapid firing setTimeout(() => (this.input_blocked_cooldown = false), 200); } } ... }
4. Problems Encountered & Areas for Improvement
Throughout the course of the project, I encountered many problems which allowed me to improve in my debugging and coding skills. Often, p5js gives feedback on code errors, which point to a line number. I refer to the line number and check the code around it and code associated to objects in that line. I think this has helped me solve many errors, like simple syntax errors involving a missing closing bracket (which can be easy to do when there are nested if conditions within a function within a class), to slightly more complex errors involving something “undefined” (which could be because I forgot to update the path to video/image in preload function). Personally, the very challenging problems are when the game runs without any error p5js throws out, but does not function as intended. These are logical errors. For example, I encountered this problem: the message box is displayed initially when Luke is in the vicinity of the farmer, and after a certain dialogue state is complete, the message box displays when Luke is farther away from the farmer. My intention was to have the message box display only when Luke is near the farmer. I think that this problem was resolved by being even more specific with my if conditions. One of the most challenging unresolved problems for me was that after the second video called in draw() plays, the video in the next chain of states won’t play. I tried to research online, use AI, try different ways (such as using image() in draw but this produced a static frame, using onended() functions, setting an if condition if the time of the video exceeds the duration-1 second of the video) but these all did not work. It was very difficult, and I may have tried to resolve this for about more than five hours, before deciding to give this idea up, and simply having videos that don’t play in consecutive game states.
A key area for improvement include expanding the affordances of the experience through multiple ways such as adding more characters. Another way is to add objects that are not simply part of the background but can interact with the user. By this, I mean objects similar to the tool that can be “grabbed” by the user and used to do something, such as pull carrots. Perhaps, add a painting that can be clicked on and zoomed in, which depicts a painting of the farmer and his family, along with text on their family history in Avella. A third way to expand affordances is to program the code to not only explore new places, but also go back to previously explored places.