I'm learning how to make art! In an effort to aid my singular artist with this massive task, my co-programmer @trotopype and I are learning Aseprite. It's really fun. I think we'll make a lot of cool art.
My drawings were not displaying correctly - it was color mode in Aseprite. I still don't exactly know what it does, but it causes the images to look correct so I'll never forget it.
I'm kind of tired this morning so as a way to get back into development let me talk about recent updates to the game.
Player Movement Rework
The player walks around via the D-Pad. This used to consist of fairly simple moveWithCollisions(x,y) calls. This resulted in continuous motion. As we developed the puzzles and began to implement the rules in code, we quickly discovered that enforcing discrete tile-based rules on a continuous game state would be far too complicated and may not even feel good to the player. Imagine trying to line up with a mirror and being a few pixels off, that would get annoying. We needed something other than just reacting to collisions after the 'move' has been enacted.
Tile-based Movement
Solution: tile-based movements. The player should only move up, down, left, or right and never diagonal. The player must always end up centered on a tile. This is natural in sokoban games, but given that we're making more of an adventure game, this was not obvious to us at first.
The screen has an invisible grid of 20px by 20px squares, which we call tiles. Think: chess board (discrete) vs a marbles circle (continuous). The game is composed of 'rooms' with a static camera, as shown in posts above. Each room that contains a puzzle has a very strict set of rules we need to follow as designers to make our puzzle-logic enforceable.
A short list of rules for example:
Everything must line up on tiles. And by this, I mean the border of then collision rectangle should be at least 1 pixel in from the tile boarder.
The player or statues (push blocks) must never be allowed into an occupied tile.
The player must be able to push a statue if the destination for the statue is unoccupied
These kinds of interactions are difficult if not impossible to implement purely using collisions. Partially because, by the time a collision occurs, we've got a problem. The simplest observation that illustrates this is the fundamental action: player pushes statue one tile.
Why It's Better
Janky Version
player presses right D-pad button
player begins movement
player collides with statue, and freezes partway between tiles
some functions run to try to now push the statue, then restart the player, then fix tile logic issues
New Version
player presses right D-pad button
game checks the state of the tile which the player is essentially requesting to go to
if tile is clear, player goes to that tile
if tile is a statue, we then do the same process for the statue, checking requested tiles before moving both the player and the statue in tandem
It became clear to me that this was superior because invalid moves simply do not begin. I don't have to chase the player around cleaning up tile-based issues.
We needed an editor. I had a process that I'd developed years ago for generating JSON from Google Sheets, and that is how we've been building out levels. There are approximately 80 rooms so far and maybe 30 of them have puzzles. That's about as much as I can take of inputting every single entity into a spreadsheet to place them in the world. I have briefly looked into solutions such as LDTk and the like, but nothing took.
For personal reasons and for the purpose of allowing for level design contributions from others, we have begun hand-rolling our own level editor. A few things mattered to me about how we make it:
It should be able to run entirely on the Playdate with no simulator functions needed
It should allow for not just placing and removing entities, but editing their internal parameters too
You should be able to drop the player in at any time on any tile to walk around and test the level
Being able to set up doors such that you can connect this room to the existing game world is critical
"Exporting" a room should be easy
There are more requirements but this is the flavor of thing I'm trying to make.
By making a level editor that runs on-device, we facilitate some cool things. I'd like to send the game out to testers and have them eventually contribute level ideas if they so choose. I don't know if/when/how we'll get access to the internet via the SDK, but we could have people create and post puzzles for others to download and try. The vanilla experience will be very thorough and is important to me that it happens unfettered, so maybe after game completion we unlock the editor for players or something.
Demo
I've talked enough. Here's a bit of the editor in action.
I was so down and tired yesterday that I felt apocalyptic about this game and the unwieldy task set out for @trotopype and I.
Today I feel a lot better. I know this isn't a diary, but like, isn't it?
Menu Updates
I have been working on placing doors in the level editor. It's kind of working, and entailed writing a lot more Menu class code to make menus with tons of options appear correctly and scroll. I have avoided using the SDK's grid class. I used it in the previous implementation of this game, and I remember it working rather well actually. But, so far at least, what I'm trying to accomplish is much simpler than what the grid class can do. So I'm just hand-rolling what I can where I can.
In this video you can see the menu scrolling i'm talking about. I intend to add in a "holding down" the navigation button causes auto-scroll. Currently tho, in addition to the up and down buttons, you can use the crank which can be quite fast at scrolling through long lists. I think the longest list in the game that will be scrolled with be the rooms in the editor, which most players will never see anyway.
Door Placement (In-Editor)
You can also see a Door being placed. I have collision rectangles highlighted because some doors are invisible. This should be familiar to most gamers. Sometimes you walk into a path or something on the edge of a map and get transported to the next area.
I place a door and then using a context menu (pressing B in the editor opens a context menu based on the tile the cursor is currently selecting) I select the destination. Then in the destination room I am selecting the 'destination tile' for the player to appear on when they use this door and go to that room. That does not work yet, I'm like halfway through coding that.
PS - our composer sent me this little diddy (in the video) this morning and it's got me very excited for continuing work on the music in the game.
Old way: Player moves and collides with walls to stop. Can be left between tiles. Relies on SDK collisions.
New way: Player requests to move to an adjacent tile, the tile is checked for properties such as "occupied" and if not then the player is granted permission to move. This upholds puzzle logic, such as not letting the player walk or teleport into occupied tiles.
The Editor Lives!
I'm going through existing levels and replacing the old walls with the new version of walls. The editor spits out JSON that I just replace the original source version of the room with. It's working!
Life Update
I'm getting married in August so I will likely slow down production, and pick back up in September.
It makes me nervous to put this in writing, but I'd like to release the game early 2025. I think that's doable, with some long coding days over this winter.
I'm married to the artist for this game now - life is crazy.
Clarity of Purpose
I had an experience during which I described the game to my closest friends. Complicated - was the feeling I kept giving myself. So since then I've been working to simplify and clarify the game.
It's a game about reflection.
I want about 80% of the puzzles to be optional - the reward being a mushroom. A symbol used in gaming for decades for growth, the humble mushroom will open up the world to the player.
You can give them to NPCs to unlock new dialogue based on them 'reflecting' on some topic.
This could unlock new rooms, new story lines, it could get interesting - but the mechanic will be as simple as possible. Collect mushrooms - give them away.
In What Way Exactly We Are Back
We've been working at a rapid pace to improve both the game itself and the tools we're using to build out the game. We've been working on our level editor, building out the story, and coding in the puzzle design.
Below you can see a quick walk through some rooms and the editor we're making to build out puzzles. You can see me whip up a simple mirror reflection puzzle and a door.
Example Walkabout
Editor In Action
The editor was misbehaving quite badly before recent changes. Placing and removing objects was strange because there are multiple places objects are 'known' about. Which, granted, may be a design mistake. But it's not absurdly complex, just a few lists I need to be aware of.
After some work on ensuring rooms save to JSON correctly, we can now make levels complete with doors linking them to other levels easily. We have not touched a spreadsheet in months. It feels great.
The entire editor is 'playable' on the Playdate as well. We're considering releasing it with the game. I don't want the design we create to be trivialized however, so I think we'll make the editor start locked and unlock upon completion or near-completion of the game.
"Completion of the game" meaning collected all of the mushrooms in the world and finished the story.
Got NPCs (adding, removing, and relocating) in the Editor working. This was not as hard as I thought it would be.
Can scroll NPCs, place them, and they're immediately ready for dialogue. Making the game is becoming so doable that I almost have no excuse not to make the game... Oh nooo..
Some might see this as a grand failure, saying "Oh no, so much wasted time and energy." But I don't see it that way. I'm happy with my ability to let go of what's not working.
Ultimately delivering a great experience is top priority. Strangely, part of that is maintaining the code base.
Small Main (said like Balmain)
Additionally, we've whittled our main.lua file down to nearly nothing - so that's fun to look at. Those three lines of code in the update are all that run the entire game - amazing.