Awake Documentation
Table of Contents
MY PROJECT -- mode: org --
1. Project Overview
1.1. Core Gameplay Loop
- Explore
- Fight
1.2. Pillars
- Effective use of items
- Predictable but challenging and rewarding combat
- Dense and interactive enviornments
1.3. Art
1.4. System Mechanics
System mechanics are largely undecided as the engine is still in development.
1.5. Engine
As of writing this, the engine is capable of the following things:
- Display, animate, and move a player character.
- Display, animate, and move up to 3 npcs. (More can be added somewhat easily)
- Employ basic ai to allow each npc to perform actions.
- Load map data to display a new location, including collision map and npcs.
- Detect and rectify collisions between player, npcs, and map objects.
In the future I plan on writing more in depth explanations of how these things are achieved.
Next steps:
- Interrupt based timers
- Complex player behavior(attacks, jumps, etc)
1.6. Known Issues
The collision system produces unexpected results whenever two objects collide on the edge of two or more screen cells.
Two npcs can sometimes push eachother out of the screen space, and they will pop back out the other side.
The jump function allows you to jump into, and through, map collision boxes.
The timer interrupt handler increments the timer for every npc regardless of whether or not they are active. I might leave this how it is, just to avoid wasting cpu cycles on checking whether or not each npc is active.
The map loader reloads all map tiles for each map, even if many are shared with the previous map. In link’s awakening, tiles are divided into those that are always loaded in a specific area (like the mysterious forest), and those that are map specific. Then only the map specific tiles are loaded between maps, saving cpu cycles, but more importantly space in rom. It will be very important to implement something like this later in development.
2. Devlog
2.1. 03/02/2025
I just fixed the walking animation for npcs, meaning the rewrite is finally finished. I am considering also rewriting the player code because I had less experience when I wrote it, although it isn’t impossible to work with like the old npc code.
Now I am trying to map out a collision system. I have already created a format for storing collision maps. The collision system will prevent the player or npcs from crossing lines specified in the collision map. It will detect collisions between the player and npcs, and call a function that decides what is to be done in this scenario. It will also prevent the npcs from crossing the screen border. In the event the player crosses the screen border, it will call a function that decides which map to load, loads it, and then changes player coordinates to coorespond with new map.
All actions performed as a consequence of collision should be seperated from the collision system itself, to make it readable and modular. Most likely, I will create a file specifically for collision, and collision related functions.
The previous collision system was terrible, so I removed it entirely.
After some research I have come up with a plan. The screen will be divided into a uniform grid of cells. If movement occurs within a cell, collision will be checked for that cell only. This will reduce the number of processor cycles being spent on collision each frame The number of cells remains to be decided.
I will likely abandon my original plan of lines, and instead use axis-aligned bounding box collision.
The first thing I will have to do is add a column to the npc matrix for the cell value of each npc. Then if any npc moves, I will mark that cell for collision detection later in the frame cycle.
After mapping this all out, I have decided to do the following things first to make collision easier:
- Rewrite of player code.
- Velocity system for both player and npcs.
- Consolidate all npc actions to a new function to avoid clutter of the main loop.
Items 2 and 3 of the list have been completed, now working on the full rewrite. I will probably finish it tomorrow.
I have decided not to rewrite it entirely, it is good enough now.
2.2. 04/02/2025
I have implemented a function in global.h that takes coordinates, and outputs a cell value. I have also added values to the npc matrix and pippi array for cell numbers
There is now logic to check which cells have seen movement within them, and a collison function is called for each of these cells. I haven’t yet designed the collision function. Here I will outline my plans for it:
If player is located within the cell: check player pos against every bounding box specified in the collision map check player pos against every active npc in the cell
for every active, and currently moving npc within the cell: check npc pos against every bounding box specified in the collision map check npc pos against every active npc in the cell
2.3. 05/02/2025
The collision system is now functioning for player-npc collisions. I am still working on map collisions. Once i figure that out, npc-npc and npc-map collisions should be fairly trivial. This may not mean much because the function is only half done, but performance impact seems minimal.
Once everything is working, I will finish the functions that perform various actions after collisions.
The collision system is pretty much done. I will probably add logic that prevents the npcs from leaving the screen, but I could also just implement that with existing systems by changine collision map.
Now I need to work on the functions that decide what is to be done in the event of a collision. The player-npc collision function already works but it is unpolished and buggy. It moves the player coordinates away from the npc, and adds velocity in the same direction. The result is that moving into an npc will bounce you backwards in the opposite direction with significant force. It is possible to glitch yourself inside an npc and stay there for a second or two, this seems to happed mostly on cell borders. The main issue is that the bounce back sometimes acts unpredictably and sends you in an unexpected direction. It would still probably work fine in an actual gameplay setting the vast majority of the time.
For npc-npc collisions and map collision i will have to just move the coordinates of the affected objects instead of adding velocity, because this wouldn’t result in damage being taken in an actual game.
2.4. 06/02/2025
All of these functions are done. Now I will prevent npcs from moving off screen, and then I will be completely done with the collision system. The collision system is now completely finished. I may polish it in the future but functionality will probably not change significantly.
Next I will work on complex player behavior like attacks and jumps. I also thought of making interrupt based timer, but I haven’t found a great reason for doing this yet. Everything seems to be working ok without it.
Here I will outline my plans for the player action system:
Implement a system to model player behavior based off the player state. Implement player behavior functions for various actions.
First steps:
Relocate all player behavior to seperate function. - done Create art for attacks and jumps.
2.5. 07/02/2025
After learning how to actually implement interrupt handlers, and how they can be useful, I have decided to do that before attempting complex animations.
I am going to write an interrupt handler or the gameboy clock, and replace all frame based time currently used to rely on the timer. This will be a decent bit of work now, but should make everything much easier later in development.
This is unrelated but in the future I will likely need to use assembly, so I will store this link here for reference: https://www.chibiakumas.com/z80/Gameboy.php I found an assembler called rgbds https://gist.github.com/tobiasvl/664ec1b2dcdaf627d27bb0ba961429de https://gbdev.gg8.se/wiki/articles/Pan_Docs https://gbdev.io/gb-asm-tutorial/part1/hello_world.html
2.6. 09/02/2025
The interrupt handler is written, and sprite timers now rely on it. I tried to do the same for the player walk cycle timer, but it introduced a very strange bug so I reverted it. For some reason the player direction would change in seemingly random ways. This made no sense to me because there is only one function that changes the player direction, and I didn’t touch that logic.
Regardless of how it happened, I can live with a walk cycle timer that isn’t optimal.
I also learned a lot more about assembly and the gameboy itself, which will shape my approach to the project from now on. Here is a list of important things for future development:
C is not optimal for gameboy hardware. C relies heavily on the stack, constantly writing functions and variables to it. The gameboy is not designed to effeciently manipulate the stack, so this can make very slow code in some cases. To avoid performace issues with C, you have to avoid manipulation of the stack as much as possible.
Local variables should be avoided when possible because they are written to the stack instead of the heap. Never use recursion. If possible use macros in place of functions. Gameboy games can be larger than 32kb, but this will require banking. Banking means to seperate the rom into banks of 16kb. Bank 0 will hold the most important data, and is always loaded. The rest of the banks will be switched out depending on game state. I have heard this is difficult to implement in C alone, so assembly will likely be needed. I’m not sure how big this game is going to be, so I may not need to use banking at all.
GBDK-2020 comes with its own assembler, and the linker can easily mix the two langauges. This makes the process of implementing assembly easier, but their assembler has some issues. Its a modified version of the assembler that comes with SDCC. That assembler was designed to be modified for a multitude of hardware targets. There is no documentation for the gameboy assembler specifically, so I have to rely on the documentation for the original version.
GBDK comes with an example project called space which is written entirely in assembly. Most of what I have learned so far has come from studying this project.
The lcc linker can compile to assembly instead of a rom, using the -S argument.
2.7. 10/02/2025
I found a disassembled version of Link’s awakening. This will probably be a useful reference thorughout development. https://github.com/zladx/LADX-Disassembly Extra info: https://xkeeper.net/hacking/linksawakening/
The disassembled version uses the rgbds assembler, but it isn’t all that different from sdcc.
Actually learning assembly is proving to be extremely difficult. Part of the problem is that I simply don’t understand the syntax of the sdcc assembler. There is no tutorial, and no documentation outside of the universal sdcc docs.
It might be worth linking rgbds assembly as pre-compiled binaries, so I don’t have to work with sdcc assembler.
Found more extremely useful stuff: https://gbdk-2020.github.io/gbdk-2020/docs/api/docs_coding_guidelines.html https://github.com/gbdk-2020/GBTD_GBMB https://github.com/bbbbbr/gimp-tilemap-gb https://github.com/Rangi42/tilemap-studio/ https://github.com/pret/pokecrystal/wiki/Optimizing-assembly-code
I am going to re-structure includes soon
2.8. 12/02/2025
Based on the gbdk docs’ recommendations, I have restructured much of the project. Previously I was only compiling the main.c file, and then just including the other files within it. That is not the way you’re supposed to do that at all, but I didn’t understand how projects like this were structured when I started making it. I have changed this so that all of the files are compiled seperately, and are linked together before the rom is created. I learned to properly use the extern keyword to make all of this work properly.
I also used the const keyword on all arrays that were never meant to be changed, like sprites, and map data. This means they will actually by stored only on the rom, and not the ram. This will free up a significant amount of memory for later use.
Now I have to actually create the art for the complex behaviors, like I said I was going to 6 days ago. First however, I will look into the new art programs I found in the gbdk docs, linked in the previous log.
2.9. 14/02/2025
I added a jump function, which technically works. It is unpolished, and the animations aren’t implemented yet, but it is a good starting point. It properly utilises the new clock interrupt handler (tim).