Page 1 of 1

Making object-based map format?

Posted: Thu Dec 22, 2016 6:43 pm
by OscarRichard
My NES project will be similar to SMB1 and I actually need to compress the levels. So while searching around here, I've seen people that recomends to make the map data to object-based format. So how do I do object-based? Any ideas, tutorials, or other suggestions? P.S: It's about a side scrolling game.

Re: Making object-based map format?

Posted: Thu Dec 22, 2016 7:02 pm
by tokumaru
I believe that object based maps can't be easily drawn to the screen or checked for collisions directly, so you'll most likely need to have a grid based buffer in RAM where to decompress your maps ahead of time, and then use that buffer for drawing and for collisions.

As for how the maps are stored, that's ultimately up to you. Each screen could be defined by a list of objects, each with an ID (which could be used to look up entries in a table containing addresses of routines that handle each type of object) followed by the parameters necessary to define that object (x and Y coordinates, width, height, color, and so on).

Re: Making object-based map format?

Posted: Fri Dec 23, 2016 2:54 pm
by dullahan
SMB1 disassemblies may be helpful:

* http://www.romhacking.net/documents/344/
* https://gist.github.com/1wErt3r/4048722

For an example, here is an encoded level from my game Nebs & Debs which uses a similar format to SMB1. This is generated from a Tiled tmx file via a ruby script. Objects are like SMB1 objects and the metasprites are actors and powerups, etc:

Code: Select all

.code
.include "../src/objects/objects.h"
.include "../src/tiles/tiles.h"
.include "../src/palettes/palettes.h"

.segment "RODATA"
.proc mines1_1
  ; sprite palette
  .byte palette_names::hero_pal
  
  ; background palette
  .byte palette_names::background_pal
  
	; default tile ID
	.byte object_names::sky
  
	; scene width
	.byte 224

	; trigger count
	.byte 0

	; object count
	.byte 84

	; object IDs
	.word object_ids

	; object column positions
	.word object_cols

	; object row and length bytes
	.word object_row_lens

	; object width and collision bytes
	.word object_width_cols
    
  ; metasprite count
  .byte 46
    
  ; metasprite IDs
	.word metasprite_ids

	; metasprite column positions
	.word metasprite_cols

	; metasprite row and length bytes
	.word metasprite_rows

	object_ids: 
		.byte object_names::floor, object_names::plants, object_names::floor_shadow, object_names::floor, object_names::plants, object_names::floor, object_names::plants, object_names::floor, object_names::plants, object_names::floor_shadow, object_names::plants, object_names::floor, object_names::floor, object_names::plants, object_names::plants, object_names::floor, object_names::floor, object_names::floor, object_names::plants, object_names::plants, object_names::floor, object_names::floor_shadow, object_names::floor, object_names::plants, object_names::floor, object_names::floor_shadow, object_names::floor, object_names::plants, object_names::abyss, object_names::floor, object_names::plants, object_names::floor, object_names::plants, object_names::floor, object_names::floor_shadow, object_names::plants, object_names::floor, object_names::plants, object_names::abyss, object_names::floor, object_names::floor, object_names::plants, object_names::floor, object_names::plants, object_names::abyss, object_names::floor_shadow, object_names::floor, object_names::plants, object_names::floor_shadow, object_names::plants, object_names::floor, object_names::floor_shadow, object_names::floor, object_names::plants, object_names::abyss, object_names::floor, object_names::floor, object_names::plants, object_names::abyss, object_names::floor, object_names::floor, object_names::abyss, object_names::plants, object_names::floor_shadow, object_names::floor, object_names::floor, object_names::floor_shadow, object_names::floor, object_names::plants, object_names::floor, object_names::plants, object_names::floor_shadow, object_names::plants, object_names::floor, object_names::plants, object_names::floor_shadow, object_names::floor, object_names::plants, object_names::floor, object_names::plants, object_names::floor, object_names::abyss, object_names::plants, object_names::floor

	object_cols:
		.byte 0, 0, 9, 9, 9, 15, 15, 21, 21, 21, 21, 21, 28, 28, 31, 31, 37, 38, 39, 43, 43, 43, 43, 43, 47, 47, 48, 48, 49, 52, 52, 54, 55, 55, 55, 56, 58, 58, 64, 68, 71, 72, 76, 76, 80, 83, 83, 83, 89, 89, 89, 93, 93, 93, 96, 96, 98, 100, 105, 108, 109, 110, 111, 111, 111, 121, 123, 123, 123, 138, 138, 146, 146, 146, 156, 156, 156, 164, 164, 171, 171, 178, 181, 181

	object_row_lens:
		.byte 60, 27, 26, 25, 24, 45, 28, 60, 23, 25, 27, 24, 90, 25, 28, 45, 60, 27, 27, 28, 45, 26, 25, 24, 23, 24, 22, 21, 30, 45, 28, 28, 23, 24, 25, 28, 60, 27, 45, 60, 42, 27, 42, 25, 45, 28, 57, 24, 23, 21, 22, 23, 22, 21, 30, 59, 21, 21, 29, 44, 36, 45, 21, 28, 42, 21, 27, 41, 24, 105, 24, 23, 21, 22, 21, 23, 22, 21, 150, 18, 195, 45, 27, 60

	object_width_cols:
		.byte 143, 143, 133, 133, 133, 134, 134, 135, 133, 133, 135, 133, 131, 131, 134, 134, 134, 129, 132, 134, 134, 131, 131, 131, 131, 131, 130, 130, 131, 134, 130, 130, 131, 131, 131, 130, 134, 134, 132, 140, 129, 132, 132, 132, 144, 134, 134, 134, 130, 130, 130, 158, 158, 133, 142, 137, 130, 137, 131, 130, 130, 156, 138, 134, 134, 130, 141, 141, 141, 154, 154, 133, 133, 133, 133, 133, 133, 135, 135, 135, 135, 131, 154, 154

  metasprite_ids: 
		.byte metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::z65, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::z65, metasprite_names::z65, metasprite_names::z65, metasprite_names::balloon, metasprite_names::z65, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::z65, metasprite_names::z65, metasprite_names::balloon, metasprite_names::z65, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::z65, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::z65, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon, metasprite_names::balloon

	metasprite_cols:
		.byte 10, 12, 17, 21, 22, 24, 25, 29, 31, 38, 38, 41, 41, 47, 51, 53, 55, 61, 66, 73, 75, 95, 100, 101, 101, 103, 104, 106, 113, 117, 118, 121, 132, 134, 149, 151, 155, 160, 165, 169, 179, 179, 179, 179, 179, 182

	metasprite_rows:
		.byte 6, 6, 6, 5, 4, 4, 5, 6, 6, 9, 7, 9, 7, 12, 5, 5, 6, 6, 6, 11, 11, 4, 9, 5, 8, 8, 9, 5, 5, 8, 5, 8, 6, 6, 5, 4, 4, 8, 3, 1, 7, 9, 11, 3, 5, 11

.endproc

Re: Making object-based map format?

Posted: Fri Dec 23, 2016 3:43 pm
by dougeff
Here's a quick rundown of how SMB does level compression...

It gets 2 pointers to data, at the start of the level, one for BG objects, one for Sprite Objects (Enemies).

For greater compression, a byte may contain 2+ pieces of information...like the top 2 bits for, type of level, bottom 6 bits for which level, within that type.

The level type (water, castle, hills, underground) is used to set the palette, the music, etc.

There is a master room pattern, like...1 ceiling, 8 sky, 2 floor. This is often changed on the fly, for example, in the underground or in castles. Once it is set, it will automatically draw that pattern Left to Right.

Then, each data byte describes how to draw things...starting at X=3, Y=6, draw 3 bricks left to right. Starting at X=5, Y=7, draw 6 coins...starting at X=7, draw a gap in the floor.

And there's a bit for...wait until you cross into the next room, then draw object Z here...

And, like tokumaru said, there is a collision map in the RAM that is also drawn, as you scroll.

Re: Making object-based map format?

Posted: Sat Jan 28, 2017 11:16 pm
by OscarRichard
OK, as for the attributes what do you recommend? A separated array to store them, or to include them in the list of objects? In order to both load the level and scrolling. I use 16x16 pixel metatiles ( 4 groups of 8x8 pixel tiles squared ) and as far as I know in the attribute table, the areas are 32x32 pixels-sized. Any good ideas to align?

Re: Making object-based map format?

Posted: Sun Jan 29, 2017 5:57 am
by tokumaru
After deciding on an object based format to save space, wasting 64 bytes per screen on attributes hardly sounds like the most sensible solution, don't you think? It might be (much) easier, but so is using uncompressed 960-byte name tables.

Anyway, attributes on the NES aren't very fun to work with, but once you understand how you can use bitwise operations (AND, OR, XOR) to clear/set/toggle individual bits of a byte, you can easily manipulate the 16x16-pixel attribute areas.

DRW recently had the exact same problem: viewtopic.php?f=2&t=15396

Re: Making object-based map format?

Posted: Sun Feb 05, 2017 6:57 am
by na_th_an
I personally use a lookup table which tell me exactly which bits I should OR for each kind of 16x16 metatile when located on each of the 4 possible positions inside of an attribute, so printing new metatiles is easy and fast.