This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

"Stylistic" question in NES programming

"Stylistic" question in NES programming
by on (#117789)
The other thread that I opened was about the hard facts of NES programming, especially with the CC65. Like "What is a cfg file for?" etc.

In this thread here I'd like to discuss some questions about the programming style. I.e. common ways to achieve something, best practice etc. Things that aren't objective facts, but more certain styles that established themselves.


My first question is: How is the level structure for the game logic usually saved?


Possibility 1:

There are some general variables that define how the level looks like and where the player can walk. From these variables, the background in the nametable is created. But during the algorithm that decides if the player can walk a certain way, only these general variables are used.

Imagine a simple maze game where there are blocks/walls of 16 x 16 pixels that the player can't pass and the rest is the floor that the player can walk along.
Since the screen is 256 x 240 pixels and every block is 16 x 16 pixels, we have a playfield of 16 x 15 = 240 squares.
That means, the whole level structure (from a purely logical point of view, I'm not talking about the actual graphic tiles in the PPU yet) can be put into standard variables (or constants) of the size of 240 bits = 30 bytes. Each bit stands either for "there is a wall in the way" (1) or "the way is free to walk" (0).
In the beginning, the nametable is built up from these values: You loop through the bits and bytes of the variables and if there's a 1, you put the 2 x 2 wall tiles in the corresponding place and if there's a 0, you put the 2 x 2 floor tiles there.
Later, when the game runs, you don't look at the nametable anymore. All you do is take the x and y coordinate of the sprite, and through calculation, you extract the correct bits from your 30 bytes generic variables to see if the way that the character is about to go is free.


Possibility 2:

You somehow create the background in the nametable (for example through some compressed ROM data that is extracted for this one action and then discarded again). And during gameplay, you actually literally read the values from the PPU itself. If the player wants to go up, you check the nametable at the corresponding location. If there is one of the wall tiles set, movement isn't possible, if one of the floor tiles is set, movement is possible.


In the first version, the level structure is abstracted away. Both, the nametable and the game logic use generic variables. To define if a character can walk somewhere, the PPU is never read, only the customly-defined variables that are either stored in the ROM or in the CPU's RAM.
In the second version, the level structure is directly defined by what is written in the nametable. The nametable, and therefore the PPU's RAM, is constantly read to see if the player is in a position to move.


How do real NES games do it?

For example in "Donkey Kong", can Mario climb a ladder because some variable tells the game that in this and that location there is supposed to be a ladder? Or does the game literally read the nametable at that location and recognizes that there is the ladder tile?

So, if I manipulate the nametable in the PPU during a running game on an emulator, but I don't change anything in the regular CPU or the ROM data, could I change the gameplay, i.e. create working walls and ladders etc.? Or would the change only be visual while the movements of the player wouldn't be influenced, i.e. there would be a discrepancy between the gameplay's level structure and the visual representation on the screen?


What's the common practice for NES games here? In a normal PC game, I would of course put the level structure into a two-dimensional integer or byte or boolean array and only work from that array. I would never actually read the background graphics to decide what's to do in the game logic. Is this the same on the NES? Or are game logic conditions directly linked to the nametable contents?
Re: "Stylistic" question in NES programming
by on (#117792)
Because the NES (unlike the 2600) doesn't provide (almost) any PPU-assisted clipping calculations, NES games usually have better model-view-controller separation.

(Exception: the NES has two readable bits in the register at $2002 pertaining to sprite behavior. These are usually used for UI, not for gameplay.)

Also, because access to the nametables is restricted to only during vblank, doing such logic would require it being minimal enough to fit into the minimal amount of time (21/262 of the time) and so significantly restricts how much you can do.
Re: "Stylistic" question in NES programming
by on (#117798)
Alright, so adding ladders in the nametable in "Donkey Kong" won't allow me to climb the platforms from any desired place, right?
I even find this better. I wouldn't have wanted to program against the graphics objects. I prefer cheking against abstract representations of the screen objects, saved in regular variables.

By the way, is there an emulator that allows you to manipulate the nametable data at runtime? In FCEUX I can only see the data, but I didn't find a way to change it on the fly.


Now, how is the level data usually saved during gameplay? (I'm not talking about the initial data in the ROM chip which might be RLE compressed or whatever. I'm talking about the variables during live gameplay.)

Let's take this here:
Image

In this situation, would the data be saved in a bunch of variables that are all located right after another in the memory, having the below values?
(Four bits stand for one 16 x 16 big object. 0 is floor, 1 is fixed block, 2 is breakable block, 3, 4 and 5 would be the items and so on. The outer wall isn't saved since it's constant anyway.)

Line 1: $00, $20, $00, $22, $20, $02, $20
Line 2: $01, $01, $01, $21, $21, $01, $20
etc.
(The last half-byte per line is unused because the squares have an uneven number.)

Is this how it works?
Re: "Stylistic" question in NES programming
by on (#117803)
DRW wrote:
In FCEUX I can only see the data, but I didn't find a way to change it on the fly.

I'm pretty sure you can edit it in FCEUX's hex editor (I think it shows the CPU memory by default, but you can select PPU, ROM, etc.), but you don't have any visual feedback there. Maybe if you have both the name table viewer and the hex editor open you can see the tiles changing as you type them in.

Quote:
Now, how is the level data usually saved during gameplay? (I'm not talking about the initial data in the ROM chip which might be RLE compressed or whatever. I'm talking about the variables during live gameplay.)

That will depend on the game, since every programmer is free to implement this as they see fit. Most games with large maps use metatiles of some kind. There are compression schemes that, unlike RLE or LZ, can be decompressed on the fly from the ROM. For example, just by using 32x32 metatiles (like Mega Man) levels become 16 times smaller than the raw name table data.

Quote:
In this situation, would the data be saved in a bunch of variables that are all located right after another in the memory, having the below values?

Blocky maps like this are often stored in 2-dimensional arrays.

Quote:
Is this how it works?

I haven't looked at the game's code, but it could be. When you program a game it's important that you find the solution that works for your particular case, and you appear to have the right idea.
Re: "Stylistic" question in NES programming
by on (#117805)
2k of RAM is enough to store about two screens worth of level data packed into a 2d byte array. That's the simplest solution and so the most widely used. Sometimes bit flags are used in those bytes for things like walkable/not walkable instead of being literal tile or metatile numbers.

Games which need to scroll continuously in more than one direction will usually either use 8k of PRG-RAM and decompress an entire level worth of data into it at once, store one screen at a time with extra bytes for flags for enemies that can be killed or objects that can change state, or use some sort of display list format for the level data in memory.
Re: "Stylistic" question in NES programming
by on (#117808)
Quote:
Now, how is the level data usually saved during gameplay? (I'm not talking about the initial data in the ROM chip which might be RLE compressed or whatever. I'm talking about the variables during live gameplay.)
I don't think I can really say there's a consistent way people store the live copy of a level. Looking at a few homebrew:

Driar stores the game state as a 224-byte array. Incrementing by one byte goes right, incrementing by 16 bytes goes down. Each byte specifies the corresponding metatile on screen. The 32s bit specifies whether the tile is foreground or background.
IsharaComix's Ludum Dare competition entry stores the map as a 120-byte array, one bit per tile.
A horizontally-scrolling game such as SMB1might store the game state as a series of vertical strips of game (i.e. increment pointer by one byte = go down)

Generically, it's easiest if you can keep your entire map state to less than 256 bytes, because then you can just index a single array. Obviously you can exceed that.

Quote:
(Four bits stand for one 16 x 16 big object.
The 6502 doesn't make indexing nybbles particularly easy. Unless I were in danger of running out of RAM, I'd probably stick with one byte per metatile. (Metatiles are most often 16x16 pixels, but not always)
Re: "Stylistic" question in NES programming
by on (#117858)
Grapeshot wrote:
2k of RAM is enough to store about two screens worth of level data packed into a 2d byte array. That's the simplest solution and so the most widely used. Sometimes bit flags are used in those bytes for things like walkable/not walkable instead of being literal tile or metatile numbers.

Games which need to scroll continuously in more than one direction will usually either use 8k of PRG-RAM and decompress an entire level worth of data into it at once, store one screen at a time with extra bytes for flags for enemies that can be killed or objects that can change state, or use some sort of display list format for the level data in memory.


Did you mean to say 2 pages of RAM? I just ask because my levels are made of 16x16 pixel metatiles, which I decompress into RAM, two screens at a time. This takes exactly 2 pages of RAM.

I would not recommend relying on actual tile numbers and their position on the name table to do collision detection. It's an alternative solution if you're running out of RAM, I guess, but you sacrifice flexibility, and often end up with redundant tiles in the CHR table (the same 8x8 pixel graphic can't serve more than 1 purpose unless you create a duplicate tile). It's really nice to have a separate collision map in RAM. It's typically much easier to work with, and can be formatted however you want.
Re: "Stylistic" question in NES programming
by on (#117860)
Grapeshot wrote:
2k of RAM is enough to store about two screens worth of level data packed into a 2d byte array. That's the simplest solution and so the most widely used.

If you're saying the most widely used solution is to store the unpacked nametable (1024 bytes worth) in (CPU) RAM, you're simply wrong. Even for a game that has 8 KB WRAM that would be terribly wasteful.

And there are plenty of games that don't have any extra RAM but still support multi-directional scrolling (e.g. Battletoads).
Re: "Stylistic" question in NES programming
by on (#117862)
I did actually mean 2 pages of RAM there.
Re: "Stylistic" question in NES programming
by on (#117863)
SMB1 uses a 32x13-metatile sliding window, divided into a 16x13 array for nametable $2000 and a 16x13 array for nametable $2400.

My current project uses a 32x24-byte array because it doesn't scroll (sorry, tokumaru) and there are 24 rows of 30 8x8 pixel cells.
Re: "Stylistic" question in NES programming
by on (#117871)
tokumaru wrote:
I'm pretty sure you can edit it in FCEUX's hex editor

Thanks. That works. I will have to play around with it a bit later.

tokumaru wrote:
Blocky maps like this are often stored in 2-dimensional arrays.

Assemply doesn't really have two-domensional arrays, does it? I assume it's a bunch of variables that are placed in memory right after another and where the current variable is calculated by
startLocation + y * width + x
Is this correct?

lidnariq wrote:
The 6502 doesn't make indexing nybbles particularly easy.

Why not? If I have a byte that includes two distinct values, one per half-byte, I would do the following:
If I want to read the value of the upper half-byte, I would shift the variable four bits to the right.
And if I want to read the value of the lower half-byte, I would AND-connect it with $0F.

Celius wrote:
I would not recommend relying on actual tile numbers and their position on the name table to do collision detection.

Yeah, as I said, I wouldn't have wanted to do this anyway. That's why I was asking if this is common practice in NES programming. Good that it isn't.

I guess I will save the status of my background during gameplay in the following way:
No background object in my game will be smaller than 16 x 16 pixels. So, I would have to save a maximum of 16 x 15 statuses per screen. Since the gameplay will probably not happen on the whole screen because of the area that a TV might cut off, the gameplay might end up at 14 x 12 "squares".

Now, per square I don't have to save the ID of the actual meta-tile:
0: Street
1: Grass
2: Tree
3: House
4: Spikes
5: Hole
6: ...

But only the status of the influence the square would have on the player:
0: Walkable (street, grass)
1: Wall (tree, house)
2: Walkable, but taking damage (spikes)
3: Instant death (hole)

This way, each square would only require two bits (four possible statuses). That means:
14 x 12 squares = 168 squares
168 x 2 bits = 336 bits
336 bits / 8 = 42 bytes

And that's it: For one screen, I need 42 bytes of background data and it includes everything that the game logic needs to communicate with the status values of the sprites. If there are more possible statuses for each square, I make it four bits instead and have 84 bytes.

What would you say about it? (By the way, my game doesn't scroll, so I don't have to care for that.)
Re: "Stylistic" question in NES programming
by on (#117881)
tepples wrote:
(sorry, tokumaru)

No, you suck! Hehe just kidding. I think I'm past the whole "games need to SCROOOOOOLL!!!" thing. I guess there's much more to a game than how technically complex it is if the idea is good.
Re: "Stylistic" question in NES programming
by on (#117887)
DRW wrote:
Assemply doesn't really have two-domensional arrays, does it? I assume it's a bunch of variables that are placed in memory right after another and where the current variable is calculated by
startLocation + y * width + x
Is this correct?

Yup, you're right. In the past, I have pre-calculated the y * width part to create a table of pointers to each row, to make things faster. It was for a map made from really big blocks though, so there weren't that many pointers/rows and 256 horizontal blocks made a pretty long level.

Quote:
If I have a byte that includes two distinct values, one per half-byte, I would do the following:
If I want to read the value of the upper half-byte, I would shift the variable four bits to the right.
And if I want to read the value of the lower half-byte, I would AND-connect it with $0F.

It surely is possible, just not very efficient. If you do have the time necessary for the extra processing and you really don't need the extra bits, it's OK to use nibbles.

Quote:
What would you say about it?

Sounds good. You will probably change this design later, that always happens... maybe because the packed bits are too hard to work with, maybe because you overlooked some types of blocks you might need... but you'll only know that when you start coding the engine.
Re: "Stylistic" question in NES programming
by on (#117985)
Another style question:

Imagine we have a game character of 16 x 32 pixels (for example Mario). So, it consists of 2 x 4 actual sprite objects.

If the character is mirrored, then it's of course not enough to just mirror every sprite object. Otherwise this:
Image

would not become this:
Image

but this:
Image

So, they also have to be re-arranged.

Now, what's the common standard?
Do I reposition the eight sprite objects (i.e. sprite 1 switches places with sprite 2, sprite 3 switches places with 4, 5 with 6 and 7 with 8)?
Or do I change the tiles of each sprite object (i.e. sprite 1 switches tiles with sprite 2 etc.)?

What's the value that is re-assigned to each sprite object when the whole character has to be mirrored? The location or the tile number?
Re: "Stylistic" question in NES programming
by on (#117989)
DRW wrote:
Now, what's the common standard?

Meta-sprites. Just like with level maps, it's good practice to separate the game entities from the display hardware. In other words, don't hardcode OAM entries to your objects and manipulate them directly. The idea is is to have your objects modeled in RAM, and every frame use their coordinates to render meta-sprites to the OAM (this also helps with sprite cycling, because you can shuffle the objects/sprites around every frame). The routine that renders the meta-sprites scans lists of sprites and takes care of the mirroring, adjusting the coordinates and the step value based on the object's state (i.e. the x step can be 8 or -8 depending on the flipping). Some people prefer to simplify things by making 2 sets of meta-sprites, one facing left and the other facing right, so they don't have to deal with this. I guess this works fine if you have few sprites.
Re: "Stylistic" question in NES programming
by on (#117992)
Two metasprite sets not only simpler, but also faster, and has consistent speed (with software flipping flip code will work faster for one side and slower for other). The main drawback is doubled memory use, but it would only make major difference if there are many large sprites in the game.
Re: "Stylistic" question in NES programming
by on (#117995)
There are ways to get consistent speed without two sets of metasprites.
  1. The first applies to engines that store as a set of horizontal strips of tiles. The pen advance can be set to +8 or -8 when rendering each sprite. You can see this in action in my simple sprite demo: in main.s, search for x_add.
  2. The second applies to engines that store the X offset for each sprite. Store $00 for unflipped or $FF for flipped, and EOR all the X coordinates in the metasprite with that value.
Re: "Stylistic" question in NES programming
by on (#118017)
I checked my metasprite routine, and it looks like (if I counted correctly):

One time penalty per metasprite:

- a penalty of either 9 or 15 cycles if flipped horizontally (depends if clipping needs adjustment)
- a penalty of either 7 or 13 cycles if flipped vertically

- additional penalties of 3 cycles per hardware tile if flipped (6 per tile if both flipped)

There might be a few more cycles, since I am not counting the fact that there are branches for both modes that would not be needed without supporting flipping, but I don't think it is a big deal. I use a left to right, top to bottom pattern in my algorithm and reverse it if the sprite is flipped either way. (Edit: actually the impact is slightly less when the Y position of the tiles doesn't change.)
Re: "Stylistic" question in NES programming
by on (#118027)
I didn't know that flipping costs time, so I guess I will indeed make two groups of sprites.
Re: "Stylistic" question in NES programming
by on (#118242)
I'm personally not a fan of the pre-flipped sprite maps approach. It can waste a ton of ROM space, and also increase the complexity of other code. I imagine you have to have some conditional code that says "If this object is facing right, use sprite map X. Otherwise, use sprite map Y." You would need a way to identify the locations of those individual maps.

I took a different approach. I don't really care so much about vertical flipping, so objects can either be normal, or flipped horizontally. The metasprite drawing code looks at an object's flip status, and decides to go to one of two routines: DrawNormal, or DrawFlipped. These two routines are hardcoded to flip or not flip the sprites in a metasprite. The main time saver is that the DrawFlipped routine subtracts the sub-coordinates of each 8x8 sprite from the object coordinates instead of adding them. This ends up saving a lot of time.

I used to have one routine that would handle both flipped and unflipped metasprites. It would always add individual sprite sub-coordinates to the main object coordinates, but do some XORing when objects were flipped to add negative values instead of positive ones. This ended up wasting several scanlines alone.
Re: "Stylistic" question in NES programming
by on (#118244)
Celius wrote:
I imagine you have to have some conditional code that says "If this object is facing right, use sprite map X. Otherwise, use sprite map Y." You would need a way to identify the locations of those individual maps.


In my top-down adventure game, (almost) every actor has at least four directions they can face. To keep processing to a minimum, I store all four animations separately and in order of direction (#0=up, #1=down, ...). To change an animation to a certain direction:

Code:
   ; X = obj id#
   lda Object_Direction, X
   clc
   adc #ANIMID
   jmp Func_ChangeAnim


If an actor moves in eight directions, but only has four animations:

Code:
   ; X = obj id#
   lda Object_Direction, X
   tay
   lda anim_8wTo4w, Y
   clc
   adc #ANIMID
   jmp Func_ChangeAnim


"anim_8wTo4w" is a table that converts the diagonals to cardinal directions. Usually diagonals just drop the left and right, so UR and UL use the UP animation, DR and DL use DOWN.

It does "waste" a ton of space, but I decided early on for this project that cpu cycles > ram > rom. Since most of the actors are 16x16, a large amount of frames and some animations can be reused for unrelated actors that share the same tile ids, but are in different tilesets.
Re: "Stylistic" question in NES programming
by on (#118245)
I have build a system which allows both flipping entiere metasprites (saving space) or storing left/right versions of them separatedly (allowing for details, such as weapons being always in the correct hand).

Since I have 4 pointers for all 4 directions, and that they all points to a metasprite which is stored in ROM, it's compulsory that they point at the $8000-$ffff range, which makes the 15th bit of the pointer useless to store (since it'll always be set). I then use this bit for a flip bit, if set, it means the sprite pointed at that adress (OR-ed with $8000) is to be flipped before being displayed.

I hope my explanation makes sense.
Re: "Stylistic" question in NES programming
by on (#118291)
Another question: If a game does not scroll, but use both screens to flip between them, does it make a practical difference if the game is set to horizontal or vertical mirroring?
Re: "Stylistic" question in NES programming
by on (#118293)
Not at all, it depends whenever you prefer to flip bit 0 or bit 1 of $2000 (or if you prefer to write to $2400 or to $2800).

The only situcation where it would matter is if you would use scrolling for special effect (like shaking the screen, or do an iris-fade-in or fade out).
Re: "Stylistic" question in NES programming
by on (#118295)
Then, how come all the non-scrolling games still use both versions instead of defaulting to one of them? "Donkey Kong" is horizontal, "Donkey Kong Jr." is vertical. Same company, same series, even same release date. Who decided that both will have a different mirroring? Did they throw a coin?
Re: "Stylistic" question in NES programming
by on (#118296)
And Donkey Kong Classics has both games on a single board without any switchable mirroring support. As long as your game uses only the nametables at $2000 and $2C00, it will work without changes on either mirroring type.
Re: "Stylistic" question in NES programming
by on (#118328)
DRW wrote:
Then, how come all the non-scrolling games still use both versions instead of defaulting to one of them? "Donkey Kong" is horizontal, "Donkey Kong Jr." is vertical. Same company, same series, even same release date. Who decided that both will have a different mirroring? Did they throw a coin?

The PCBs still have solder pads, so at some point someone has to decide which mirroring to use, even if this choice doesn't make any difference. Did you get this info from .nes ROMs? Those values might even have been selected by the people who dumped the games, and not reflect how the solder pads in the actual games are configured... who knows?
Re: "Stylistic" question in NES programming
by on (#118336)
On BootGod's database, it seems many versions of Donkey Kong and Donkey Kong Jr. doesn't even have solder pads (they use globs board with no solder pads, or HVC-HROM which is hardwired to vertical mirroring).

But it seems this guy's right, Donkey Kong uses horizontal mirroring and Donkey Kong Jr. vertical mirroring. Are these games really not scrolling at all ?
Re: "Stylistic" question in NES programming
by on (#118339)
Bregalad wrote:
I have build a system which allows both flipping entiere metasprites (saving space) or storing left/right versions of them separatedly (allowing for details, such as weapons being always in the correct hand).

Since I have 4 pointers for all 4 directions, and that they all points to a metasprite which is stored in ROM, it's compulsory that they point at the $8000-$ffff range, which makes the 15th bit of the pointer useless to store (since it'll always be set). I then use this bit for a flip bit, if set, it means the sprite pointed at that adress (OR-ed with $8000) is to be flipped before being displayed.

I hope my explanation makes sense.


That is a very good workaround!

My game just has the main character holding the weapon in the other hand. True, it isn't very logical, but I didn't care enough for this particular game.

never-obsolete wrote:
It does "waste" a ton of space, but I decided early on for this project that cpu cycles > ram > rom. Since most of the actors are 16x16, a large amount of frames and some animations can be reused for unrelated actors that share the same tile ids, but are in different tilesets.


I suppose it definitely makes a difference in a top-down environment. You're not just facing left or right, but also up/down. Plus, if you're like Bregalad, you might want to show objects holding things in the appropriate hands when flipped.

My project was intended to be an NROM platformer. For this reason, I need to be cautious about space being used. But being a scrolling platformer, pretty much everything that happens takes up a lot of CPU time. It's hard to say which is more important in my case: CPU time, or ROM space. I really didn't want to sacrifice either. Thankfully, my solution sacrificed a fixed amount of ROM space (for the hardcoded flipped drawing code), and a tiny amount of time per object.