Modifiable scenarios using cc65

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
User avatar
Diskover
Posts: 219
Joined: Thu Nov 24, 2011 7:16 am
Contact:

Modifiable scenarios using cc65

Post by Diskover »

Imagine a platform video game.

I am currently trying to create a level made up of 5 screens, using a scroll that allows me to move from left to right.

I can put objects (for example, coins in the air) as part of the background, collide with them, and they disappear.

The problem is that when I keep going forward and go back, the coins keep appearing.

How would you solve this problem? I've been trying things for days and I can't get anything satisfactory.
User avatar
Jarhmander
Formerly ~J-@D!~
Posts: 569
Joined: Sun Mar 12, 2006 12:36 am
Location: Rive nord de Montréal

Re: Modifiable scenarios using cc65

Post by Jarhmander »

The simple answer is: you need to memorize what was collected.

One simple solution is to do the Super Mario Bros. 3 way: have extra memory in the cartridge (EXRAM, 8kB), load level data into that memory. When you destroy blocks or collect coins, that memory gets changed so it remembers which blocks and which coins are collected. In a sense, a coin is not much different of a block that you destroy: it's something from the background that can disappear once you interact with it. Even if the coin is a sprite, the principle is largely the same. When you scroll in the level, what gets loaded to screen and what object are loaded into memory now depends on the state of that region of the RAM that is related to the part of the level you currently are.

If you have only one kind of object that you need to remember if they are collected or not, and with careful level design, you could probably store that information in internal RAM (IRAM, 2KB), with only one bit per object.
((λ (x) (x x)) (λ (x) (x x)))
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Modifiable scenarios using cc65

Post by tokumaru »

Most games don't define object positions using level map blocks, because that introduces a bunch of limitations, such as:
  • No way to place objects in front of background decorations, since each space in the map is either an object or a background tile, never both.
  • No way to position objects with pixel precision;
  • No way to include extra information about an object (such as health points, speed, direction), since all the space you have for it is that one byte;
  • Can't permanently kill objects if the level map is in ROM;
If you really insist in placing objects that way, and some of these limitations don't bother you, there are a few possible solutions:

1- Put the level map in RAM:

Like Jarhmander said. Copy the level map from ROM to RAM when the level starts, and use that copy for everything. When objects are collected/destroyed, you erase them from the map. On the NES, this approach pretty much requires extra RAM on the cartridge, unless your levels are only 1 or 2 screens large.

2- Have the level map point to specific object instances:

If you don't have enough RAM for the level map, you can have cells in the level map point to specific object instances, as opposed to object types like you're currently doing. For example, if a cell in the level map is < $80 (bit 7 clear), it represents a block that you can draw and/or collide with, but if it's >= $80 (bit 7 set), the lower 7 bits indicate the specific object instance that goes in that spot. This way you can have up to 128 objects per level, conveniently numbered from 0 to 127, and with that number you can access extra data about each object stored separately from the level map itself.

If all you need to know about each object is its type and whether it's alive or dead, you can just have a 128-byte list of object types in ROM (for each level), and a 128-byte list of dead/alive states in RAM (that you reset to "alive" when each level begins), and you access both lists using the 7-bit index you read from the level map.

If 128 objects per level is not enough, or 128 background block types is not enough, you can dedicate a smaller range of values to represent objects in the level map (say, 0 to 239 are blocks, 240 to 255 are objects), and have a separate list in ROM containing the number of the first object in each screen, so that in order to find the number of an object you have to look up the number of the first object in the screen where it is, then add the 4-bit value you read from the map cell. This way you'd be limited to 16 objects per screen, but could have a total of 256 per level.
User avatar
Diskover
Posts: 219
Joined: Thu Nov 24, 2011 7:16 am
Contact:

Re: Modifiable scenarios using cc65

Post by Diskover »

Ok, the things you explain I had already thought about and in some cases, tried.

For example, putting the entire level into RAM. It was useless because it only reached me to store little more than an entire screen. What I was doing worked, but if it was just on one screen, it didn't work for me.

I also thought about making an array where it is stored if something exists or does not exist in a level. Right now I am at that point, although getting the correct memory address where the object was, storing it, and making it work, is proving too complex for me. Even so, I will insist to see if this week I solve it.

Of course, I think the best solution is to base everything on sprites for these cases, but... for example, Super Mario Bros. 2, in a level X screens long, perfectly stores if something in the background was taken, destroyed, etc... So does this game use extra RAM for these things?
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Modifiable scenarios using cc65

Post by tokumaru »

Diskover wrote: Sun Feb 13, 2022 3:38 pmRight now I am at that point, although getting the correct memory address where the object was, storing it, and making it work, is proving too complex for me.
The way I suggested above is pretty straightforward... the value you read from the map cell does not represent the object type anymore, it's actually the index of a specific object instance. You can use this value directly, without any calculation whatsoever, to get the object's type (from an array in ROM) or its status (from an array in RAM).
Super Mario Bros. 2, in a level X screens long, perfectly stores if something in the background was taken, destroyed, etc... So does this game use extra RAM for these things?
IIRC, SMB2 does have extra RAM, so it's safe to assume it's used for level maps.
User avatar
Diskover
Posts: 219
Joined: Thu Nov 24, 2011 7:16 am
Contact:

Re: Modifiable scenarios using cc65

Post by Diskover »

tokumaru wrote: Sun Feb 13, 2022 4:04 pm
Diskover wrote: Sun Feb 13, 2022 3:38 pmRight now I am at that point, although getting the correct memory address where the object was, storing it, and making it work, is proving too complex for me.
The way I suggested above is pretty straightforward... the value you read from the map cell does not represent the object type anymore, it's actually the index of a specific object instance. You can use this value directly, without any calculation whatsoever, to get the object's type (from an array in ROM) or its status (from an array in RAM).
So... It's possible, using a NROM mapper, to get something like what I want, right?

When it's a static screen, I don't see much of a problem... but when there's scrolling involved, things get complicated.
User avatar
Hamtaro126
Posts: 818
Joined: Thu Jan 19, 2006 5:08 pm

Re: Modifiable scenarios using cc65

Post by Hamtaro126 »

Diskover wrote: Sun Feb 13, 2022 4:08 pm
tokumaru wrote: Sun Feb 13, 2022 4:04 pm
Diskover wrote: Sun Feb 13, 2022 3:38 pmRight now I am at that point, although getting the correct memory address where the object was, storing it, and making it work, is proving too complex for me.
The way I suggested above is pretty straightforward... the value you read from the map cell does not represent the object type anymore, it's actually the index of a specific object instance. You can use this value directly, without any calculation whatsoever, to get the object's type (from an array in ROM) or its status (from an array in RAM).
So... It's possible, using a NROM mapper, to get something like what I want, right?

When it's a static screen, I don't see much of a problem... but when there's scrolling involved, things get complicated.
Not really. The only mapper 0 using SRAM game is Family BASIC, and that is not really NROM, but needs to be a submapper of mapper 0 to add SRAM.

To add SRAM or WRAM to $6000-$7FFF, in reality, you need a cartridge that is built for it to work. closest one is MMC1 or MMC3.

Edit: Typo corrected.
Last edited by Hamtaro126 on Sun Feb 13, 2022 4:24 pm, edited 1 time in total.
AKA SmilyMZX/AtariHacker.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Modifiable scenarios using cc65

Post by tokumaru »

Diskover wrote: Sun Feb 13, 2022 4:08 pmSo... It's possible, using a NROM mapper, to get something like what I want, right?
Absolutely. Several games can remember object states without extra RAM.

Are 128 objects per level enough for you? If so, it doesn't get any simpler than what I suggested above.

EDIT: Hamtaro126's means you can't have extra RAM in an NROM cartridge, but like I said, you don't need extra RAM to keep track of basic object states.
User avatar
Hamtaro126
Posts: 818
Joined: Thu Jan 19, 2006 5:08 pm

Re: Modifiable scenarios using cc65

Post by Hamtaro126 »

tokumaru wrote: Sun Feb 13, 2022 4:19 pm Hamtaro126's means you can't have extra RAM in an NROM cartridge
That is correct. There is no SRAM/WRAM connection in the NROM cartridges because it is literally just the 8k CHR ROM and 32k or 16k PRG ROM as well as fixed mirroring (H or V).
AKA SmilyMZX/AtariHacker.
Post Reply