Space Saving Tricks

From Uzebox Wiki
Jump to navigation Jump to search

Space Saving Tricks

Alright, you're here because you want to know how to save the precious little flash available on the Uzebox. When you start coming towards the end of your game development cycle, things can really start getting ugly, especially if you are trying to fit with the bootloader. Here's a couple simple tips, no pictures or anything fancy. Really easy to do and they can really turn the tides leaving you with plenty of room to work with.


    • Special thanks to Uze who described mainly all these concepts, in different discussions around the forum. This tutorial is a convenient collection of that information.


Sound

Sound can take massive space, in fact I'd wager a couple games might use more space on it than graphics(which isn't bad). Here I will briefly describe the concepts, you will easily be able to do any of this.

  • Midi - your songs can take a lot of space. Likely the most rapid notes, therefore the most expensive, are the percussion. If your song sounds pretty good without percussion and you need the space, cut it out totally. I've found it's worth it much of the time, sometimes reducing the song by half.


  • "Mini Patterns" - A great idea for those songs with rapid percussion, there is still a way to save a ton of space. What you will do in this case is define an individual patch that plays a pattern corresponding to your percussion loops. Often times there will only be a couple different parts, and this avoids storing lots of redundant information for those drum beats. Check out this example [1] used in BC Dash. Notice that those patches are not describing a single "hit", but a sequence of hits with timing to correspond to the overall beat. Cover 4 full music bars with one patch if you want! Just trigger this patch once instead of repeating loops with individual notes, maybe trickier to synchronize but quite efficient. Keep in mind this could be applied to any pattern in a song, not just percussion. Though using it on channel 1/2 could cause problems with game sound effects clobbering whole patterns of the song.


  • Waves - Inside data\sounds.inc you will see all the default waves compiled with the kernel. These waves are referenced by your patches you use for sound effects and music. When you are done with all of that, determine which waves you are not using. Delete the unused elements and adjust the PC_WAVE parameters in your patches accordingly(so they are still using the same data). This is a REALLY big thing, you might notice I'm USING BIG LETTERS ON THIS! Each wave consumes 256 bytes, and probably you are only using 2-4 of them. In that example, you could get back up to 2K!! for no loss!! To use a custom wave set you will likely need to modify your makefile. To avoid issues with compiling , use Paul McPhee's hack(check out the makefile for Donkey Kong). The file path starts from inside the kernel, so change the patch to something appropriate for you situation.
## Escape spaces in mixer path (due to custom sounds.inc)
EMPTY:=
SPACE:= $(EMPTY) $(EMPTY)
SPACE_ESC:= \\$(SPACE)
MIX_PATH:= $(realpath ../data/sounds.inc)
MIX_PATH_ESC:= $(subst $(SPACE),$(SPACE_ESC),$(MIX_PATH))
KERNEL_OPTIONS = ...##your normal kernel options here and add the below line
KERNEL_OPTIONS  += -DMIXER_WAVES=\"$(MIX_PATH_ESC)\"

Code space

This is a complicated subject, because GCC often seems to have a mind of its own. Only consider this if you know you will be tight on space, personally I don't like veering from my style very much for any reason so just an idea here. Essentially if you are making a game that will never be at risk of running out of cycles, then code your game in such a manner. You will just have to be conscious about your decisions as your program them. For instance, when you have a bunch of if(...) statements that contain lots of code duplicated in nearby if statements, just execute that code no matter what(where applicable of course). Design your logic flow to avoid these situation, take the slow solution as opposed to the inline. It's hard to think of many specifics, but if you see several lines of code that are the same, does it have to be there? Function calls are very cheap compared to only a few lines of code. This of course is very dependent on your game, if neither space nor time is an issue then of course just code everything for easy reading(which is pretty subjective!). If you don't know the relative cost of things, rebuild your project after modifying lines of code to see what difference it makes. You might be suprised how fast things, you weren't thinking about, add up. I was. The items below you should definitely try.


  • Use this in your make file: CFLAGS += -mcall-prologues Uze passed this one onto me, and often times it will save significant amounts of space for free(but not always). What you will want to do is build your project without the option, then with it to make sure that it did not actually increase the size. All dependent on your program, but a good thing to try when you are getting tight. Saving 500 bytes is achievable.


  • Uze also recommended trying another flag that can shorten code size (perhaps at a slight expense to speed): CFLAGS += -fno-inline. This makes sure GCC doesn't automatically inline functions it considers small, therefore reducing duplicate code. Try it with and without, make sure it doesn't hurt any time sensitive places in your program.

Graphics

  • Don't do complicated effects with discrete tiles. Read Ram_Tile_Effects_Primer. Tons of things are possible and many of them are space savers if you need an effect.


  • If possible, design your tile set in a manner where it actually minimizes code space(by decreasing if(vram[i] >=...) etc. If NOT including a tile actually requires more code it might not be worth it. You might find some designs will actually use less space by using a few duplicated tiles. A tile costs 64 bytes, and you can eat that up in only a few lines of code to work with special cases.


  • Consider the real value of the resource you are thinking of including. I've caught myself using huge graphics and maps for something the player will only enjoy once. Apply cost-benefit analysis and common sense, simplify things that can be. Ram tiles can help here.

Fonts

A common situation occurs where you have a tile set for your GUI/Title screen, and another for your actual gameplay tiles. Often times you will need text for both tile sets, but since you can have only one at a time, you must waste space duplicating the font for each! If you aren't afraid of complication and are using Mode 3, check out some of my other tutorials on ram tile effects for compressed fonts. Otherwise a much cleaner solution, which also happens to fit more situations, is to include the font data BETWEEN the data for both tile sets. This way you can SetFontTilesIndex() to an appropriate value while using each tile set and use font without duplicating it!

Other Stuff

  • Use compression for level,demos, and anything else possible. Design is key here, it is worth the extra effort. Also read Compression_Tutorial if you are curious.


  • Every "feature" is expensive whether in code or graphics/sound, I can't seem to ever remember this though :) Focus on important stuff then see where you are at.

Questions

Anything I haven't described adequately, please post on the thread Weber's Rants(tutorials) so I can make this more complete.