1. Concept
I was inspired by the ‘coffee shop expo’ project, where the user can explore a place and click on different buttons freely; but I wished for more room for exploration and especially more freedom, in the way that a user can control a sprite using keys to move around. Then, if this sprite lands on a specific place, a new place of discovery opens up.
I spent a considerable amount of time to develop the concept: a 2D RPG adventure, casual game in a pixelated world with a medieval setting, with story snapshots (as clear, not-so-pixelated images) appearing from time to time as a quest is completed. The user takes on a role as a character (represented with a sprite) that has migrated to a new city and wants a new job to get some money. There is a task board in a tavern villagers come to post various miscellaneous tasks. The user can pick a job from it, some examples below though my goal is to incorporate at least two from the following:
-
- fetch water from the well,
- harvest a number of carrots,
- doctor’s mission – fetch herbs for medicinal purposes,
- help me find my dog,
- blacksmithing job,
- help deliver letter.
I was wondering whether it would be strange to haveboth pixelated and non-pixelated graphics. After explaining my concept idea to my friend, my friend thought of a game that existed like that: “Omori.” Omori has pixelated sprites but clear CG scene snapshots, as well as iconic music – things to get inspired by – as well as a fan wiki for sprites which I could try to make good use of.
Interactive elements are to be present, such as a mouse click for opening a door and revealing a character’s speech. Audio is also heard for different story scenes, which should be chosen appropriately based on the atmosphere – eg. music with a sense of urgency on the task board, relaxing music in the village, music with a sense of triumph and excitement when the quest is completed, etc. On-screen text could be used to beckon and inform the user of narration, instructions and dialogue.
After the experience is completed, there must be a way to restart the experience again (without restarting the sketch).
2. Code Highlights
I think the main challenge would be in the OOP, specifically making the character classes. I found a useful resource for collecting sprites: Spriter’s Resource. In particular, I would like to use village sprites from Professor Layton and the Curious Village. Here are the Character Profiles. I selected the following from the various characters in Professor Layton and the Curious Village:
- Luke (user’s character)
- Franco (farmer who needs help with harvesting carrots),
- Ingrid (neighbour grandma who needs help with delivering a letter),
- Dahlia (noblewoman with a lost cat),
- Claudia (Dahlia’s cat),
- Lucy (young girl who needs help with getting herbs for her sick mother),
- Flora (mysterious herb seller).
Since the challenge lies in OOP, I would like to practise making an object, namely the user’s character “Luke.”
In the development of the code, I found it challenging to adapt the walk animation code (discussed in class with Professor Mang) it in two ways: (1) into an OOP format and (2) to adapt it to my spritesheet, which does not have different frames of walking and does not have different different directions that the sprite faces. With (1):
- I decided to have variables from the walk animation placed into the constructor as the class’s attributes.
- Instead of keyPressed() as in the walk animation, I used move() and display() since keyPressed() is not an allowed function within a constructor (probably it’s an issue because of it being local when it should be a global function)
class User_Luke { constructor() { this.sprites = []; this.direction = 1; // 1 = right, -1 = left this.step = 0; this.x = width/20; this.y = height/15; this.walkSpeed = 3; this.scaleFactor = 0.2; // Scaling factor // 12 images across, 4 down, in the spritesheet let w = int(luke_spritesheet.width / 6); let h = int(luke_spritesheet.height / 2); for (let y = 0; y < 2; y++) { this.sprites[y] = []; for (let x = 0; x < 6; x++) { this.sprites[y][x] = luke_spritesheet.get(x * w, y * h, w, h); } // iterate over rows } // iterate over columns this.x = width / 2; this.y = height / 2; imageMode(CENTER); // Display first sprite image(this.sprites[0][0], this.x, this.y); } move() { ... } display() { ... }
With (2), I set conditions if direction is 1 (representing right direction) or -1 (representing left direction). Since my spritesheet only shows the sprites in one direction, I used an image transformation:
class User_Luke { constructor() { ... } move() { ... } display() { let spriteWidth = this.sprites[0][0].width * this.scaleFactor; let spriteHeight = this.sprites[0][0].height * this.scaleFactor; // Finally draw the sprite // The transparent areas in the png are not // drawn over the background if(this.direction === -1) { image(this.sprites[0][0],this.x,this.y, spriteWidth, spriteHeight) } else if(this.direction === 1) { // We will use the scale() transformation to reverse the x-axis. // The push and pop functions save and reset the previous transformation. push(); // Scale -1, 1 means reverse the x axis, keep y the same. scale(-1, 1); // Because the x-axis is reversed, we need to draw at different x position. image(this.sprites[0][0], -this.x, this.y, spriteWidth, spriteHeight); pop(); } } }
I also noticed my sprite appear at a huge size – to deal with this, I used a scale factor in the spriteWidth and spriteHeight (already shown in above code).
3. Embedded Sketch
4. Reflection and Next Steps
I experienced multiple challenges along the way, but I gained valuable experience with OOP. I feel that making the next sprites won’t be so challenging, since I can use Luke’s code as reference and adapt it. I think it will be important for me to plan deadlines for me since there are big subtasks for this midterm project including:
- Finding background(s)
- Finding snapshots
- Coding all the sprites
- Interactive elements – door open animation, ‘choose a job’ buttons