Retro Development — sega games
Pushing the Sega Genesis to Its Limits
All of this information is getting pulled from the following series of videos. If you are planning on doing anything along these lines, it is a great idea to not only read this document but also watch the videos that are being referenced. They break down how to do these effects piece by piece in order to achieve these impressive effects: https://www.youtube.com/playlist?list=PLi29TNPrdbwJLiB-VcWSSg-3iNTGJnn_L
2-Color 60FPS Full Screen Animations
Doing a full-screen animation, even at two colors, at 60 frames per second would obviously be way too large to fit on a Genesis game. Even with compression, it would end up being larger than the max size. In order to get around that, the creators of Sonic 3D Blast took a very interesting approach to achieve the effect.
This is a single frame pulled out of the animation. The way that they were able to create the fluid motion is by making the animation itself ¼ of the perceived amount of frames and then overlaying a handful of frames on top of each other with different palette indexes. They then set palette index0 to a dark color and then the appropriate indexes for the first “subframe” to a light color. Finally, all other indexes are set to the same color as index0. Then they simply cycled the palette between frame changes to animate all of the subframes to create the smooth animation. So while it looks like a smooth 60fps animation, it’s really a very choppy 15fps animation with a ton of palette cycling going on in it. What that means for an artist is that they have to set up each frame to have a series of frames overlaid on top of each other. Each of these subframes needs to be a unique color, as does any area where subframes overlap like in the above image.
3 Dimensional Curvature
This is an interesting trick that makes use of horizontal scan line interrupts and a lot of math to create a 3 Dimensional perspective with a slight curvature to it. The background itself is composed of a tileable portion that is mirrored and then offset in a way to hide the seam where it is mirrored.
Those are the mirrored segments lined up to show that they are indeed mirrored, but when they are placed next to each other, you can see that the artist did it in a way that the mirroring would be hidden.
The way the perspective curve is achieved is entirely through using horizontal interrupts to remove select horizontal lines from rendering, and doing it in a curve in order to make it shrink the higher you go up the screen.
The resulting combination will give you a full image that looks like this:
In motion with a vertical scroll, it gives off a pretty great sense of perspective. There are about a million different ways this concept can be used as well other than just perspective tricks. Using this method you should be able to actually squish and stretch entire background layers vertically for example.
Full on FMVs in a Genesis game
The first thing that was done to save a little bit of space to make a fully rendered video actually fit into a full-featured game was the make the video slightly shorter than the native screen resolution, as well as only rendering it at 16 colors (as opposed to using the four available palettes). The video itself is played back at 15fps to further save space. Then finally they used RNC compression on the entire thing to shrink it down even further. The problem with RNC compression though is that it can’t process fast enough for a nearly full-screen 15pfs screen animation. This is where they yet again used horizontal scan-line interrupts.
This is what the video looks like in the game when you are playing it.
This is what the image that is being rendered actually is. They took the frames of the video and sliced a bunch of horizontal lines out of it to produce an extremely shortened version of the image. You’ll also notice all of the vertical lines running through the image. RNC compression handles vertical lines a little better than dithering patterns. Those lines will be dealt with shortly though.
In order to make the image fill the screen, they used horizontal interrupts to duplicate each line of the image, stretching it back out to fill the screen up. That is how they went from the short image above to a nearly full-screen image in the final product.
In order to change the horizontal lines into a dithering pattern, they offset every other scanline by one pixel, creating the checkerboard pattern that you would expect. They went back and offset the odd scan-lines instead of the even ones and alternated between the two at a constant 60fps. Doing this took what was originally just a series of vertical lines and convincingly turned it into some extra colors. In the end, the entire 12.5 second FMV only ends up weighing roughly 660kb, which is only a small portion of the available 4MB of the total ROM.
Enemy Design 201 - Lethal Wedding
We Got Dungeons - Dev Log 5
Happy Holidays and welcome back to the dev log for our upcoming game We Got Dungeons!
Today we'll talk a little about the AI of the NPCs.
We talked about many different ways of approaching the AI in our game. Through our discussions, we realized that what we wanted is a couple of things from our enemies.
First and foremost they should be consistent. This might run counter to intuition but ultimately, what good AI should do is not "outsmart" the player, it should instead give the player a sense of accomplishment and strategic mastery. This cannot be achieved if the enemies act randomly. On top of that random AI generates unpredictability which is a tactical game that can lead to frustration as all your master strategies are thwarted not by something that's "smart" but by something random.
The next thing we wanted to be was for the enemies to be complimentary and varied in their behavior. This creates the potential for emergent game-play and keeps every encounter interesting and enjoyable. Chess wouldn't be much fun if all the pieces moved like the peons.
Ultimately the AI should give character to the enemies. Furtive hit and run AI conveys a much different character than one that actively hunts you down.
These principles work together to create a combat system that is fun and engaging and also acts as a puzzle of sorts. The player can predict the enemy's behavior, plan accordingly, and formulate a winning strategy. And that always feels awesome.
Let's look at an example AI
They [VHS enemies] will attempt to attack in melee and will use their constrict attack once every three turns. They will attack if a player is adjacent to them, but they will always try to target the player with the least HP.
You can see how this simple and predictable behavior instantly gives character and strategic depth to the opponents in combat.
Now let's talk about the implementation. Our good friends, the function pointers, make an appearance once again here.
In our general-purpose creature struct, we have a pointer to a void function that takes a game object (itself) as a parameter.
Next, we declare the enemy data as a constant which means it is stored in the ROM and we just set the pointer of the function to the according to AI module function.
This approach allows us the easy making of AI modules that are completely independent of the rest of the code base, which in turn speeds up the debugging and tweaking one of the most fickle aspects of game development. One can easily see how bad AI can be just switched off (Stop Dave...) and quickly repaired.
That concludes our chat on enemy AI. Join us next time when we'll be revealing even more details about We Got Dungeons.