17. DPCM sound

This one is a bit tricky. DMC files are huge. You might want to avoid them altogether. Most games don’t use them.

However, you might want to put in a sound effect that sounds like a real thing, a voice saying one word “fight”, or a dog barking, or maybe some more realistic drums, or cool bass line.

Try to keep them very short, like 0.2 seconds. You can / should edit them down with a sound editor like Audacity. I’m using some royalty free samples.

The faster the frequency of the DMC sample, the more bytes of ROM it uses. Faster is higher quality… below rate A, the sound quality is terrible. Maybe split the difference and use C or D. I actually used rate F for mine, but I might have lowered that if I ran out of ROM space.

Famitracker can import the sample, and map them to a note. With famitone2, you must put the samples on instrument zero, and use note range C-1..D-6. Also, the instrument zero must have a volume sequence, even if you don’t use it (just a quirk of famitone2). That’s under 2a03 setting, instrument settings. Put a check mark next to volume (this is true for all instruments).

19_FT2

Even if you only want to use the sample as a stand alone sound effect, and not in the song, it must be included in the song like this.

Now, when you export the text it will have the dmc file defined. and when you use text2data it will output a dmc file. Include that in crt0.s in a “dmc” or “sample” or whatever, segment. I called it “SAMPLES”.

This is the tricky part. DMC samples must go between $c000 and $ffc0. preferably as far to the end as possible. Subtract the byte size of the samples from ffc0 and round down to the next xx40. In the cfg file, define the SAMPLE segment to start there.

Now, also, go to the top of crt0.s and define FT_DPCM_OFF to start at the same value.

The .dmc file is 5,248 bytes long. That’s rather large for a NES game. It will be ok for a simple example code, though.

Now, if the samples are in the music, they should play with the music.

But, if you wanted them to be sound effects, you have to call them in your code, like

sample_play(sample#)

How do we know what the sample number is? It depends on what note you mapped them to in your famitracker file. If you look at the output file from text2data (mine is “DMCmusic.s”), you see a samples area. You can tell by the non-zero lengths at the right side, that our samples are at 25, 27, 29, 30 and 32. It counts up from C1, so if we had mapped it to C1, it would be sample_play(1). But the sample I want is at G3, so we need sample_play(32).

I put this with a new jump. It’s just my voice, saying “jump”, to be silly. 🙂

Hope this makes sense.

https://github.com/nesdoug/20_DPCM/blob/master/platformer4.c

https://github.com/nesdoug/20_DPCM

 

Advertisements

16. Sound effects

Even if you have no music talent, you might be able to make some cool sound effects. Music is nice, but sound effects make if feel like a real game.

Again, open famitracker. You can use mostly all the fx and any notes. Put all the sound effects in 1 file, each as its own “song”. Add a song in module properties…

18_FT

Try to write the music so pulse channel 1 or triangle play the main melody. And then use pulse channel 2 (or noise channel) for sound effects. That way they don’t collide. If you are having problems with sfx cutting out, you could also try to make the sound effects volume louder than the music (at least a little bit). I actually made my sfx quieter than the music, but louder than the Square 2 channel, where most of the sfx play.

They should be rather short. End every effect with C00, and put the C00 on its own line.

Save. Now, export to NSF file.

19_FT

Open the command prompt and use nsf2data with the -ca65 option. I’m using famitone 2 v1.15. The sound effect code changed in the last update, so use 1.15 to make the data.

Include the sounds data in crt0.s under sounds_data:, and make sure FT_SFX_ENABLE is set at the top, and also that FT_SFX_STREAMS is 1. The init code will initialize our sound effects. Having 1 stream means that only 1 sfx can play at once.

We just need to call the sound effects like…
sfx_play(sfx,channel). Channel means stream, use channel = 0, since we have only 1 activated.

If you want 2-4 streams defined, you could set a channel to 1,2,3. Higher having higher priority. I would caution against having too many, they might conflict. I have used 2 before.

I made it so that jump calls sfx_play(SFX_JUMP, 0). B button plays a noise sfx, and Select button plays a “ding” sfx. I’m going to use that for coin collecting, later.

Start still changes the song.

https://github.com/nesdoug/19_SFX/blob/master/platformer3.c

https://github.com/nesdoug/19_SFX

 

15. Music

I wrote a page a while back about the NES Audio.

https://nesdoug.com/2015/12/02/14-intro-to-sound/

But, mostly you shouldn’t have to know that much about the sound registers. We are going to use Famitracker to write the music and Famitone 2 v1.15 to play the music. I have been including the famitone code in every example, so that it’s already in place for you. All you have to do is to include the music data file in crt0.s right below music_data:

I’m not the best famitracker musician, so maybe you should watch a tutorial about using it, like this one.

 

I like this tool chain, but famitone2 has some limitations.

  1. No volume column.
  2. Allowed notes are C-1..D-6, Note Cut
  3. Only Volume, Arpeggio, and Pitch sequences
  4. All instruments should have a volume envelope assigned
  5. no Duty sequence
  6. 64 instruments max
  7. no fx except Tempo Fxx, Looping backwards Bxx, and ending the frame early D00
  8. Up to 17 sub songs in a file

Because there is no volume column, you might want to make a few instruments of different max volume.

And a warning, don’t set ROWS = 256. The text2data program won’t work right.

Put every song into the same file, use “module properties” to add a song.

18_FT

Once everything is done, export a txt file. You need to process this file with famitone2 program text2data.exe. Use the command line, and add the -ca65 switch so that our assembler won’t have any problems reading it. I left all the files in /MUSIC if you want to see what they look like.

18_FT2

I wrote 2 songs and imported them into the platformer game.

music_play(0) plays the first song.

music_play(1) plays the second song.

Press “start” to switch between the songs.

The neslib code automatically updates the audio registers to match the song. At the end of the nmi code, FamiToneUpdate is called, once per frame.

If you need to, you can pause the song music_pause(1) and unpause the song music_pause(0). And you can stop the song altogether music_stop().

Oh, and one more side note. I wrote a function that allows you to change the speed of the music with your code. Normally, you could only set the speed inside the famitracker file. But, what if you wanted to speed it up for a boss fight, or slow it down if you are in some stunned state? Well, now you can easily do that with this function…

set_music_speed()

Lower numbers mean faster, bigger means slower. 6 is the default.

https://github.com/nesdoug/18_Music

.

.

On a side note, there are other music drivers.

Pently, has been used in a few games, and might be good for you if you like to write your music with a sheet music tool. It uses a descriptive language. Music Macro Language (MML) or LilyPond can be used.

https://github.com/pinobatch/pently

.

ggsound is another options. NESmaker is using it. I am not familiar enough to give details.

https://github.com/gradualgames/ggsound

.

The full famitracker driver is another option, but it’s very large, and a bit slower than other drivers. You can get it from shiru’s example file.

https://shiru.untergrund.net/files/src/cc65_neslib_famitracker.zip

Or you could look at cppchriscpp’s example code, which uses it, I think (?).

https://github.com/cppchriscpp/nes-starter-kit/tree/master/source/neslib_asm/ft_drv

.

I also wrote 2 unofficial updates to famitone, which I talk about here.

https://nesdoug.com/2018/09/05/links-and-misc/

 

 

 

14. Platformer

Believe it or not, this is basically the same as the scrolling right code. What makes a platform game different? Gravity. And we need a 16 bit X and Y speed and position. The lower byte represents sub pixel positions. I believe it’s called fixed point math. (I even splurged, and used a signed int or 2, gasp!).

Anyway, for gravity, we add a little to the Y speed every frame. Not too fast, or we might fall right through a floor. So we have a max allowable speed.

And moving left and right I’m slowly accelerating, up to a maximum speed. These define statements in the .h file control all this physics. It should be easy to fine tune now. These are 16 bit values, where the low byte is sub-pixel. So the max speed is really 2.25.

#define ACCEL 0x20
#define GRAVITY 0x50
#define MAX_SPEED 0x240
#define JUMP_VEL -0x600
#define MAX_RIGHT 0xb000

And I didn’t want all the platforms to act like solid brick walls. I wanted to be able to jump upward into the platform, but it would catch your feet. So, I added a collision array, which defines how each metatile behaves.

#define COL_DOWN 0x80
#define COL_ALL 0x40

const unsigned char is_solid[]={
0,
COL_DOWN,
COL_ALL+COL_DOWN,
COL_DOWN,
COL_DOWN,
COL_DOWN,
0,
0,
0,
0,
0,
0
};

I had to hand edit these, which is a bit awkward.

The BG collision code parses the collision bits, and sends a collision signal, depending on the direction. The main movement code ignores the feet collision if Y speed is negative (rising)

if(BoxGuy1.vel_y > 0) {
BoxGuy1.vel_y = 0;
}

And only sets feet collisions if the feet are just barely inside the block.

if((temp3 & 0x0f) > 3) collision_D = 0;

This is why it’s important that Y speed down can’t exceed 3 pixels per frame. Any faster, and you might miss the floor hit.

So I made some new blocks in NES Screen Tool, and screenshot to GIMP, save as metatiles.png, import that as a tileset to Tiled. Make some rooms. Export to .csv. Convert to C array. Import to the C code. (easy, right?)

17_Tiled

Now it’s starting to feel like a real game.

17_platformer

https://github.com/nesdoug/17_Platformer

 

13. Scrolling up

This was much harder than scrolling right. If I had chosen to scroll downward, it might have been easier, because scrolling up is like going backwards in the code.

Skipping over the y values of 0xf0-ff wasn’t that bad as long as you use my functions add_scroll_y() and sub_scroll_y() that handles this for you. But, I also had to add them to the BG collision code, since you can be half in one nametable and half in another. And, the code assumes that Y position is between 0 and $ef.

Since scrolling up is going backwards, I’m starting at the maximum scroll position, and going backwards to 0.

I had to build the array of room arrays backwards too.

const unsigned char * const Rooms[]= {
Room5, Room4, Room3, Room2, Room1
};

Anyway, I made 5 more rooms, in Tiled, and exported 5 more csv files, converted to C arrays, and imported those into the code.

16_Tiled

And after a few days of debugging (yikes) it finally works. I have to give some due respect to the guys who made games like Kid Icarus, or Metroid, or Ice Climber, because scrolling upward is not easy.

16_scroll_up

https://github.com/nesdoug/16_Scroll_Up/blob/master/scroll_up.c

https://github.com/nesdoug/16_Scroll_Up

 

12. Scrolling right

Ok, THIS is where having an automatic vram buffer system that does updates during NMI really starts to become useful.

There are so many ppu updates in scrolling continuously, it helps to have an auto update system. You want to update on the opposite nametable that you are on so the user can’t see it change. And, only a little at a time (not too many to overload the system). Let’s look at it frame by frame as I’m scrolling to the right. I’m doing buffer_4_mt() twice per frame, which draws 8 blocks per frame.

update1

update2

update3

update4

So, I don’t want to do too many PPU updates in 1 frame, that’s why I break it up into 4 frames of updates.

.

Level design in Tiled.

15_Tiled

So, I made 5 rooms worth of levels (easily expandable). Once you scroll right, it automatically updates just off the screen to the right, where you can see. It’s reading data from the collision map, like before, but I have to reload new maps as we move.

There are now 2 collision maps of 16×15 metatiles (240 bytes each). Every time you cross into another room, it loads the next collision map just off the screen to the right. We could have read directly from the ROM for collisions, but the advantage of having it in RAM means it is destructable / modifiable. Like when Mario breaks a block, or when Link (Zelda) pushes a block to the side.

I’m still making the rooms 1 room at a time (see the previous metatile page). The Tiled .csv files are processed with a py script into a c array. I import those to the code, and added an array of pointers to these arrays.

Note, if you expand this, you need to also change the max scroll constant.

I decided that 1 pixel per frame seemed a bit slow, so I modified all the movement and collision code to handle variable speeds bigger than 1 pixel, and also collision ejection bigger than 1 pixel. It seems to work ok now, but I didn’t test speeds over 2 pixels per frame.

And it also works across the room edges, which is the tricky part, but that complicates the math. When it checks 4 points, each of those points might be in a different room, so I accounted for that. The bg_collision() function is generalized, so it should work for enemy objects checking collisions too, when we add those later on.

.

15_scroll_r

https://github.com/nesdoug/15_Scroll_R/blob/master/scroll_r.c

https://github.com/nesdoug/15_Scroll_R

 

11. Metatiles

What’s a metatile? Just a fancy word for a block of tiles. 2×2 in my case. Why use them? It’s a form of compression. And it simplifies the BG. And the BG palette table (attribute table) can’t modify each tile’s palette selection, only a 2×2 block of tiles, at minimum.

Instead of thinking of a room as 32×30 tiles (960 bytes), the code will treat it like 16×15 metatiles (an array of 240 bytes). We only need 4 bits from X and 4 bits from Y to calculate the offset into a collision array.

index = (y & 0xf0) + (x >> 4);

But, I take it one step further. The collision map can double as an array to draw the BG. I use a system, which allows me to use Tiled Map Editor to draw my levels. Rooms of size 16×15.

But we have to jump through some hoops to make this happen. We can’t draw our graphics in Tiled. The game graphics need to be 2bpp binary CHR files. We could make our graphics in a tile editor. I still recommend YY CHR to start. Then, import the graphics to NES Screen Tool. Then make some 16×16 blocks (2 tiles x 2 tiles) this is a good size because this is the size of the attribute table squares. And, importantly, choosing a palette for each block. If have 2 identical blocks of different color, I make them into 2 different metatiles (see the last 2 blocks… identical tiles, but different palette).

meta_st

Go left to right from the top row making metatile blocks. My system can only handle 51 metatiles, because it assumes a data set under 256 bytes, and each metatile takes 5 bytes for definition. 51×5 = 255. NES Screen Tool can’t save the background as a picture file, so I take a screenshot, and paste it into GIMP, and crop it, and save a metatiles.png. This can be imported into Tiled, as a tileset (I had to make the tile size in Tiled 32×32 because of how NES Screen Tool doubles the pixel dimensions).

We’re not done with NES Screen Tool yet. Save as a nametable with attributes, uncompressed binary .nam file. Use my meta.py to convert it Into a c array. After the 0th one, meta.py will interpret the first 0,0,0,0 metatile as the end of the data. Here’s what it output…

const unsigned char metatiles[]={
2, 2, 2, 2, 3,
4, 4, 4, 4, 1,
9, 9, 9, 9, 2,
5, 6, 8, 7, 1,
5, 6, 8, 7, 0
};

4 tiles + 1 palette = 5 bytes per metatile. I copy this array into the code.

Now, to Tiled, and make my level data. It’s a nice GUI for designing video game levels. We imported the metatiles.png as the tileset. And drew our level.

13_Tiled

Make a level. Save it. Then, export to csv file. you could easily turn that into a c array, but I made a .py file to automate that step, csv2c.py, and turn our csv file to a C array, “Room1.c”. Import that into the game.

Ok. we have our metatile data. we have our level data. How does the metatile system work?

So my metatile system is an extension of the vram buffer system. It can run while the screen is on, but in this example it writes while the screen is off. Instead of waiting for NMI to push our data to the ppu, we’re going to speed up everything by immediately pushing the vram buffer to ppu with flush_vram_update_nmi(). This is the same function as flush_vram_update() in some neslib.s but doesn’t require a pointer as an argument.

I already set up a pointer to the vram buffer… set_vram_buffer(). Also, we need to set a pointer to the metatile definitions set_mt_pointer(metatiles1), and set a pointer to the room data set_data_pointer(Room1). Now we can use the metatile functions.

I made 2 functions for pushing metatiles to the buffer.

buffer_1_mt(address, metatile); // push 1 metatile, no palette changes. (this doesn’t need a pointer to the room data).

buffer_4_mt(address, index); // push 4 metatiles (2×2 block) and set their palettes. Index is the index into the room data of the data. It actually finds 4 blocks, and pushes each to the vram buffer, gets the palette info from each, and combines them into 1 attribute byte, and pushes that to the vram buffer.

And the room loader code does a loop, and goes to every 32 x 32 pixel block, and converts all the data to metatiles, and prints them to the screen.

for(y=0; ;y+=0x20){
	for(x=0; ;x+=0x20){
		clear_vram_buffer(); // do each frame, and before putting anything in the buffer
		address = get_ppu_addr(0, x, y);
		index = (y & 0xf0) + (x >> 4);
		buffer_4_mt(address, index); // ppu_address, index to the data
		flush_vram_update_nmi();
		if (x == 0xe0) break;
	}
	if (y == 0xe0) break;
}

It was important to clear the buffer each loop. Flush immediately pushes to the ppu, so the screen needs to be off.

Some of the movement code is a little crazy. Shifting back and forth from using char BoxGuy1.x and int hero_x. This probably should be refactored. I made his hitbox 13×13 instead of 15×15, and shifted his sprite positions 1 up and 1 left, so he can move into tight spaces easier. And borrowed the collision code from another project, which allows for speeds > 1 pixel per frame, so it’s a little more complicated than it needs to be.

We are still colliding with the c_map. Every non zero metatile is solid. You could change that, if you need more blocks the be non collision.

Here’s what the game looks like, and he’s colliding nicely.

13_metatiles

https://github.com/nesdoug/13_Metatiles/blob/master/metatiles.c

https://github.com/nesdoug/13_Metatiles

.

.

.

And then I made another one, because I wanted to show how buffer_1_mt() worked. It’s basically the same code as the last time… I did have to change the main BG color to green to get this to work how I wanted.

buffer_1_mt() is supposed to be for changing a metatile mid-game. But it doesn’t affect palettes. Changing just 1 metatiles worth of palettes would require changing just 2 bits of a byte of the attribute table, and is quite annoyingly complicated.

buffer_1_mt() requires that you turn on the vram system, set_vram_buffer(), and it need a pointer set for the metatile definitions set_mt_pointer(metatiles). You just need to give it an address, and tell it which metatile you want to print there.

This was the level data.

.14_Tiled

And I added just 1 more metatile using buffer_1_mt() at the top left.

buffer_1_mt(NTADR_A(4,4),1);

The right parameter (1) means put the #1 metatile (brick), with it’s top left tile starting at 4th tile from the left and 4th tile from the top.

Notice its palette is wrong. buffer_1_mt() doesn’t change attribute bytes. You fix this, if you knew which bits to send. I don’t want to get into that, but if you uncomment these lines in the code, it would color that block correctly.

address = get_at_addr(0, 32, 32); // tile 4,4 = pixels 32,32
one_vram_buffer(0x01,address); // top left = palette 1

14_metatiles2

I gave the little guy a stick, which pokes to the right if you press A or B. When the stick is out, it checks the collision map to see if there is a block there, and replaces it with blank blocks using the one_vram_buffer() function. (and also changes the collision map byte to zero, so you can walk through it).

void break_wall(void){
	temp1 = BoxGuy1.x + 0x16;
	temp2 = BoxGuy1.y + 5;
	coordinates = (temp1>>4) + (temp2 & 0xf0);
	if(c_map[coordinates] == 1){ // if brick
		c_map[coordinates] = 0; // can walk through it
		address = get_ppu_addr(0, temp1, temp2);
		buffer_1_mt(address, 0); // put metatile #0 here = blank grass
	}
}

So the system is a bit complicated, but as you can see, the code is pretty straightforward.

https://github.com/nesdoug/14_Metatiles2/blob/master/metatiles2.c

https://github.com/nesdoug/14_Metatiles2

.

We could easily make a non-scrolling game, with just this. You would just need to make room data for each room, and call the room loader when you walk to the edge of the screen. Lot’s of games work like that.

But, I decided to make some scrolling examples, because ultimately, I want to make a platformer. Next time, we add scrolling.