Music, Crashes and Rotation
Hey there! Here's luksamuk with another devlog for Sonic XA.
The last couple of weeks have been very interesting for my little game. Lots of people playing it and enjoying it! I want to thank everyone who played the game and also the ones that sent me feedback. For those who published videos, know that I probably watched it (if I didn't, feel free to send me the video from anywhere you want -- YouTube, X, or even here!).
Since then, I've been trying to manage all the bugs, features and technical debts which I wanted to tackle through GitHub Projects, and also to have a more organized roadmap to SAGE 2025, which is months away, but there's still plenty of room for work. Mind you, I'm not treating this as a day job; everything is still being done on my free time, mostly at night or weekends.
Last week, there was at least one critical bug on the game that I had to fix one way or another, and other two somewhat big features that I worked on, so I wanted to discuss these with you. Brace yourselves: things are about to get very technical, and are only for the strong of heart!
Bug fixed: Game crashes when paused for too long
This is a bug that I must admit that I was kind of aware of. It happened to me before, but ever since I changed a few things on the code to prevent the game choking when music is switched, it didn't happen to me again (on my very short test sessions). So when someone reported that the game crashed after being paused for about 40 minutes -- and on real hardware! --, it was a surprise to me.
As I anticipated, it was related to how the engine was handling looping on XA music playback. The problem is this: I am encoding up to 3 songs on every .XA file (in different channels), and the number of CD sectors read during playback is always monitored with a CD event callback. When it reaches the final expected sector for that specific song (these sectors are defined by hand), the engine issues an asynchronous command to reposition the CD reader at the beginning of the song. But, for some reason, doing so within the event callback function frequently crashes the application!
The solution was simple delegation of behavior: The audio system has its own update function that is called once every frame, so when the final sector for the song is reached, a flag is set and the update function, upon seeing the flag, sets the CD reader at the position of the song, like before. As a guarantee, I also set up a critical section during this process to prevent any callbacks from being called while the CD reader repositioning event is issued.
Testing this one was the funny part. The only way to do that was by leaving the emulator running for a VERY LONG TIME. I was able to simulate the bug on Eggmanland (since it has a shorter song). The game would crash sometimes within two minutes, sometimes within half an hour. So after implementing the bugfix, I left the emulator running on my personal computer and worked my day job on my work computer... when I got back after eight hours, the game was still running with no problems. So that was a win. :)
But the real test would be to run it on real hardware. Now... I didn't want to wear off my disc reader which is already very fragile, so I had the game run there for one hour only. And... it did not crash also!
Enhancement: improved sprites and new animations
I also replaced Sonic's old sprites with new ones; the old ones weren't properly aligned so tile division was not entirely correct, and also there were palette inconsistencies.
This demanded a full rework of the Aseprite file I created to export the individual tiles for each frame, and I also took the opportunity to add new animations that were missing for too long (balancing on edges, gulping an air bubble, death and drowning animations that are still unused, and even a bonus of a "fast walk" animation that only appears when underwater!).
I didn't think it would change the game much, but some of these animations make all the difference while playing the game: though they are copied straight from Sonic CD, they still give Sonic XA more of a personality. You can see this difference for yourself on the video below.
Enhancement: Sprite rotation using the Geometry Transformation Engine (GTE)
This is a big one, and the discussion is very technical, but very interesting too.
I wanted to do this for a long time, but I was procrastinating because I thought it would be much harder and that I might hit a roadblock. Well, that didn't happen, and I was also able to finish it in a single day. :P
Some of you might already know this, but the PlayStation's GPU can only render 2D graphics. I know this is counterintuitive, but any 3D transformation is done through the PlayStation's CPU (processor), not by the GPU (graphics processor); more specifically, there is a CPU core specialized in matrix and vector operations, the Geometry Transformation Engine, also known as GTE, which I was not using during level gameplay until now.
The data sent to the GPU is just 2D meshes -- mostly triangles and quads. Add the fact that the GPU does not receive -- nor is able to process -- any depth information, and you'll get the PlayStation's famous rendering artifacts that we all know and love (or hate?) so much, such as texture warping, T-junctions, etc.
At the time of the last stable release (New Year '25 Demo), Sonic's sprite simply does not rotate. It does flip on the X axis, but this is achieved by flipping quad positions for every animation frame, and also by flipping UV coordinates for their textures so you get a flipped texture mapping.
I am going to explain how sprites are rotated, but this might be too abstract. So first I am going to drop a video of the new sprite test screen with rotation implemented, and explain it afterwards:
To rotate sprites, first I had to make the PlayStation render sprites using a "billboard" technique: imagine a rectangle that is always facing the camera from any perspective. It would after all be many quads rendered around an absolute center (the center of the screen), all at a fixed Z depth. This centered position would be encoded in a 3x3 matrix, which we call the "world matrix".
But that is not all. Also encoded on this matrix is also a sprite rotation around the Z axis (pointing towards the screen). If we still think of those quads rendered around the world center, we can also imagine the world rotating on this Z axis: all the small squares are still positioned relative to the center, but rotated around the center of the screen, as expected.
Finally, we also offset this "screen center" a little so we can have actual rendering relative to how the camera offsets from the player's position (not shown on the video above, but on actual gameplay below). So it's as if we were "pinning" the rotated sprite at the center, and offsetting it afterwards as if the combined parts were a single image.
Here's the sprite rotation working on a level. Notice that I also programmed the player's animation angle in such a way that it doesn't always rotate; instead, it only rotates on steep slopes and when Sonic is not rolling:
As you can see from both videos, this approach introduces artifacts when Sonic is rendered. You'll notice transparent lines where the frame tiles meet, some tiles are slightly offset, and the frames may appear somewhat distorted (which could not be prevented anyway).
Plus, once again I had to build upon the sprite changes I made before; tiles now have a 1-pixel gap between them so it doesn't look even worse than now. This means that Sonic sprites now use a texture bigger than the texture size limit for texture mapping. This is unfortunate: it forced me to do a clever trick to allow for a bigger texture, but that might prevent the inclusion of Tails in the game, since I'm now using the VRAM area I was saving for Tails. :/
The sprite distortions and artifacts can certainly be handled with some tricks, and the one I'm probably going for in the future is to have off-screen rendering. The idea is simple: Frames will not be distorted this badly when you render them with no rotation and facing right. I can render the sprite like this on another area of VRAM, then take this rendered image and apply any rotations/flipping that I want. Since the new image only takes a single quad to be rendered, I get no artifacts at all. Bingo!
But of course, since I'll need an unplanned chunk of VRAM also, I better work on other features that also need VRAM first, and afterwards I can think about optimizing VRAM usage so I can reserve space for this off-screen rendering. I still don't want to give up on including Tails either, but that will need more planning too.
Wrapping up
This is everything I wanted to say for now. I'll try to write more on this devlog as I update this game. Hope you enjoyed the technical discussion. See ya! :)
Get Sonic The Hedgehog XA
Sonic The Hedgehog XA
A Sonic fangame for the PlayStation 1
Status | In development |
Author | luksamuk |
Genre | Platformer |
Tags | 2D, Fangame, playstation, PSX (PlayStation), Retro, Singleplayer, sonic, sonic-the-hedgehog, Sprites |
Languages | English |
More posts
- New Year '25 Build21 days ago
Leave a comment
Log in with itch.io to leave a comment.