Jump to content
IGNORED

Giant Dynamic Map (Test)


Recommended Posts

Hello everyone,

 

You might remember one of my previous posts about extreme usage of Video RAM (VRAM) as extra RAM for the game and I decided to do a simple test to see if it worths more investigation. The idea is to use a variation of Graphic Mode I ( 1 charset, 32 pairs of colors for the entire charset, and a limited number of sprites) in a way to free up as much consecutive VRAM as possible for other purpose.

 

DAY 1

 

After an entire day thinking and calculcating, I came up with the following potentialy interesting VRAM is structure. With a very limited sprites usage, a dynamic map of 96x128 tiles can be stored in 12K of the 16K available. Only 14x12 tiles can be shown on screen at once, each tile rendered as 2x2 characters (32x32 pixels).

 

post-3651-0-97170000-1375064715_thumb.png

 

DAY 2

 

The following ROM file is a demonstration of the scrolling effect based I've imaged for this test. Depending on where the player is on the map and where (s)he is going, the window scroll or not accordingly allowing to reach every tile of the map. The player's movements are always completed to stop on a tile, never partial tiles, which may feel a bit odd the first time you try but it's an important part of the game mechanics I've in mind.

 

day2.zip

Link to comment
Share on other sites

In order to give the player an even bigger map can you implement meta tiles? Something like NES games where one element in the level data is equal to 2x2 tiles on the map?

 

Thank you for continuing your Coleco efforts!

 

Short answer :

 

A different rendering strategy has to be considered in order to use 32x32 pixels per tile without using more RAM space or a lot more computations.

 

Long answer :

 

Extending the map with 32x32 pixels per tile is possible of course but it will results into different considerations and computations. By considering using only a single byte for X and a single byte for Y coordinates, the 8 pixels movements on 16x16 pixels tiles on a 96x128 tiles map provide the possibility of 191x255 coordinates for a 16x16 pixels entity. This way bytes for X and Y coordinates are used fully for all entities including the player giving an elegant and optimal RAM usage and assembly coding.

 

My rendering strategy with 16x16 pixels per tile imply to show always the same number of tiles regardless if they are partialy visible or not, and to render all the tiles in RAM before loading them on screen. By using this strategy but for 32x32 pixels per tile instead of 16x16 pixels per tile will imply to show only 7x5 tiles on screen at once in a 200x136 pixels area instead of 14x10 tiles in a 216x152 pixels area. Note : screen resolution is 256x192 pixels. You might argue that we can have also 232x168 pixels area on screen and still use the same rendering strategy but in that case the size of the buffer in RAM will be 768 bytes long which is unacceptable considering that only 1K of RAM is available for everything including sound table, various informations for the player and other entities, etc.

 

So, it's possible to use more pixels per tile but it's a different case that needs a different approach.

Link to comment
Share on other sites

Speaking of giant maps, I made a techdemo for what was intended to become part of a T&T game years ago:

 

http://atariage.com/forums/topic/118543-colecovision-tt-rpg-very-early-techdemo/

 

No VDP magic there, just procedural map generation. The map size is 32768 x 32768 tiles, tile size is 8 x 8 pixels.

 

I wish I was a productive as Daniel, and would be able to complete games more often; but I just don't find much time for ColecoVision programming these days. At least prgramming in C instead of asm helps a lot.

 

Philipp

Link to comment
Share on other sites

Zombie Near has a map of 132x66 tiles of 16x16 pixels (264x132 characters)

 

It was not so easy to compress it in 32K, but I've used Huffman coding for each room (12x6) and each room is uncompressed as player visits it.

 

Besides for each room there are 4 bits to signal dynamic modifications to room (no key, no scientist, no life)

 

And the ROM contains three maps inside

Link to comment
Share on other sites

There is always a trade off between : complexity of the map, algorithm complexity, size of memory and processing time.

 

The map usually has to be not only good looking, but interested in it's feature to offer a good game play. The algorithm written to compress the data should tend to stay simple but take the advantage of the feature of the map.

 

As a rule of thumb for optimization, I use the one which said... to first look to what is the "biggest" (for compression) or "slower" (for processing time).

 

I like the fractal approach where tiny stuff (pixels) form bigger objects (tiles) to form even bigger objects (meta-tiles), to form bigger objects (screen), to form.. [a couple of iteration later....] the Universe! :)

 

Otherwise, I like the idea of using the Video RAM for something else than storing video... so, storing uncompressed data is cool! :thumbsup:

Link to comment
Share on other sites

But slow where VRAM I/O is concerned. :)

 

Yup, that make it even more challenging...

I think it's mostly the Read which are slower than the Write (as the read need to query the byte and return it to the video chip, which place it to the bus for the cpu). While the write, goes only in one direction.

Link to comment
Share on other sites

There is no magical answer about making giant maps for Coleco games except that it is possible to find ways to accomplish it.

 

All the ideas are pretty good, giving some solutions to think about in order to answer this question "how to make huge maps for ColecoVision games?" ... which is not easy to answer unless you involve special cartridges that can hold even more ROM and/or RAM.

 

Using bigger tiles per byte is a clever idea to get a bigger map. Unfortunately, it forces the game to display only predetermined parts of rooms and formations of blocks. This method can be used for scrolling games without worrying too much about rendering speed.

 

Using compressed data instead of uncompressed involve to keep track of where to look at in the compressed data to decompress it right on screen... something that you have to be very clever or don't care about cpu time to be used in a game as if it's uncompressed. This idea is pretty good for non-scrolling games but not really for scrolling ones.

Link to comment
Share on other sites

DAY 3

 

After coding some more to actually get data from the 12K VRAM to be rendered on screen as tiles, I did this quite simple "doodle" program where you can move around and press FIRE#1 to fill up a square and press FIRE#2 to empty one. Note : if you press both, you'll simply empty the square like if only FIRE #2 is pressed. If I wanted to do this doodle software I would have choosen only 1 bit (not an entire byte) to represent each tile, expending by 8 the size of the map... but this is just a test before the real game project starts.

 

Now it's time for me to consider coding the game itself I've in mind which involves some collision detections, some more tiles and entities of course, and so on.

 

I hope you enjoy this thread as much I do!

 

day3.zip

 

And the source code for the programmers interested into Z80 coding and also into how I coded this ROM file.

 

; gridtest.s
.module gridtest

; INTERNAL VARIABLES
.globl _player
.globl window
; INTERNAL FUNCTIONS
.globl _init_gamegraphics
.globl _update_window
.globl _output_window
.globl _tile0
.globl _tile1

;; TABLE OF VARIABLES (IN RAM)
.area _DATA
_player::
.ds 2 ; player (Y,X);
window::
.ds 2 ; window (Y,X);
playerspr_tmp::
.ds 2 ; playerspr_tmp (Y,X);

buffered_tiles::
.ds 560 ; RAM buffer ( 28x20 = 560 );

.area _CODE
_init_gamegraphics:
ld bc, #0x0000
call #0x1fd9
ld bc, #0x0182
call #0x1fd9
ld bc, #0x0202 ; SCREEN * 0400 = 0800
call #0x1fd9
ld bc, #0x032d ; COLOR * 0040 = 0B40
call #0x1fd9
ld bc, #0x0400 ; PATTERN * 0800 = 0000
call #0x1fd9
ld bc, #0x0516 ; SPRITE_ATTRIB * 0080 = 0B00
call #0x1fd9
ld bc, #0x0601 ; SPR_GEN * 0800 = 0800, but only 0B60-0FFF is usable
call #0x1fd9
ld bc, #0x0701
call #0x1fd9
;; CLEAR VRAM
ld hl, #0x0000 ; START
xor a ; BYTE TO COPY
ld de, #0x4000 ; COUNT
call #0x1f82 ; FILL VRAM
;; SPRITE PATTERN #0x6C : FILLED SQUARE
ld hl, #0x0B60 ; START
ld a, #0xff ; BYTE TO COPY
ld de, #0x0020 ; COUNT
call #0x1f82 ; FILL VRAM
;; INIT SPRITES ATTRIBUTES : (Y,X,ID,COLOR)
ld de, #0x0B00
ld hl,#spr_init
ld bc,#5
call #0x1fdf
;; GAME CHARSET
ld de, #0x0000
ld hl,#game_charset
ld bc,#6*8
call #0x1fdf
;; GAME COLOR
ld de, #0x0B40
ld hl,#game_color
ld bc,#1
call #0x1fdf
;; SCREEN ON
ld bc, #0x01e2
call #0x1fd9
ret
_tile0:
ld a,#0
jp set_tile
_tile1:
ld a,#1
set_tile:
push af
ld c,#0xbf ; 10 lines, port #BF
ld hl,(_player) ; HL = window position
srl h
srl h
ld a,l
rra
ld l,a			 ; HL = offset on map
ld de,#0x1000
add hl,de		 ; HL = offset in VRAM
out (c),l
ld a,#0x40
add a,h
out (c),a
dec c
pop af
out (c),a
ret
_update_window:
call #window_newxy
;; GET MAP DATA BASED ON WINDOW COORDINATES
ld bc,#0x0abf ; 10 lines, port #BF
ld hl,(window) ; HL = window position
srl h
srl h
ld a,l
rra
ld l,a			 ; HL = offset on map
ld de,#0x1000
add hl,de		 ; HL = offset in VRAM
ex de,hl		 ; DE = offset in VRAM
ld hl,#buffered_tiles+560-140 ; HL = offset in RAM
jp $4
$3:
ld a,e
add #0x80
ld e,a
adc a,d
sub e
ld d,a
$4:
out (c),e
nop
out (c),d
push bc
ld bc,#0xebe ; 14 bytes, port #BE
inir
pop bc
djnz $3
;;;;
ld de,#buffered_tiles ; DE = POINTER TO BUFFER FOR TILES TO BE ON SCREEN
ld hl,#buffered_tiles+560-140 ; HL = POINTER TO PARTIAL MAP IN RAM
ld b,#10 ; B = 10 lines
$5:
push bc
ld b,#14 ; B = 14 tiles per line
$6:
push bc
xor a
ld c,(hl)
inc hl
push hl
sla c
rla
sla c
rla
ld b,a
ld hl,#game_tiles
add hl,bc
ld bc,#0x0002
ldir
push de
push hl
ld hl,#0x001a
add hl,de
ex de,hl
pop hl
ld c,#2
ldir
pop de
pop hl
pop bc
djnz $6
ex de,hl
ld c,#0x1c
add hl,bc
ex de,hl
pop bc
djnz $5
ret
_output_window:
ld hl,#buffered_tiles
;; ADJUST WINDOW OFFSET
xor a
ld de,(window)
bit 0,d
jr z,$0
add #28
$0:
bit 0,e
jr z,$1
add #1
$1:
ld e,a
ld d,#0
add hl,de
;; OUTPUT TO VRAM
ld de,#0x0863
ld b,#19
$2:
push bc
ld bc,#27
push de
call #0x1fdf
pop de
inc hl
push hl
ld hl,#0x0020
add hl,de
ex de,hl
pop hl
pop bc
djnz $2
;; PLAYER SPRITE
ld hl,(_player)
ld de,(window)
ld a,h
sub d
sla a
sla a
sla a
add #23
ld (playerspr_tmp),a
ld a,l
sub e
sla a
sla a
sla a
add #24
ld (playerspr_tmp+1),a
ld de, #0x0B00
ld hl,#playerspr_tmp
ld bc,#2
call #0x1fdf
ret
window_newxy:
ld hl,(_player)
ld de,(window)
ld a,l
or a
sub #8
jr nc, $11
xor a
$11: sub #246-8
jr c, $12
xor a
$12: add #246-8
ld l,a
or a
sub #17-8
jr nc, $13
xor a
$13: cp e
jr c, $14
ld e,a
$14: ld a,l
or a
cp e
jr nc, $15
ld e,a
$15:
ld a,h
or a
sub #6
jr nc, $17
xor a
$17: sub #184-6
jr c, $18
xor a
$18: add #184-6
ld h,a
or a
sub #11-6
jr nc, $19
xor a
$19: cp d
jr c,$20
ld d,a
$20: ld a,h
or a
cp d
jr nc, $21
ld d,a
$21: ld (window),de
ret

spr_init:
.db #0xD0,#0x00,#0x6C,#0x0E,#0xD0
game_charset:
.db #0x00,#0x00,#0x00,#0x00,#0x00,#0x00,#0x00,#0x00
.db #0xFF,#0x80,#0x80,#0x80,#0x80,#0x80,#0x80,#0x80
.db #0xFF,#0x01,#0x01,#0x01,#0x01,#0x01,#0x01,#0x01
.db #0x80,#0x80,#0x80,#0x80,#0x80,#0x80,#0x80,#0xFF
.db #0x01,#0x01,#0x01,#0x01,#0x01,#0x01,#0x01,#0xFF
.db #0xFF,#0xFF,#0xFF,#0xFF,#0xFF,#0xFF,#0xFF,#0xFF
game_color:
.db #0x51
game_tiles:
.db #0x01,#0x02,#0x03,#0x04
.db #0x05,#0x05,#0x05,#0x05

 

#include <coleco.h>
#include <getput1.h>
extern void init_gamegraphics();
extern void update_window();
extern void output_window();
extern void tile0();
extern void tile1();
struct coord {
byte x;
byte y;
};
extern struct coord player;
char dy;
char dx;
void nmi(void) {}
void update_player(void)
{
if (dy!=0 || dx!=0) {
player.y+=dy; dy = 0;
player.x+=dx; dx = 0;
}
else {
if (joypad_1&FIRE1) tile1();
if (joypad_1&FIRE2) tile0();
if (player.x>0 && joypad_1&LEFT) dx = -1;
if (player.x<254 && joypad_1&RIGHT) dx = 1;
if (player.y>0 && joypad_1&UP) dy = -1;
if (player.y<190 && joypad_1&DOWN) dy = 1;
player.y+=dy;
player.x+=dx;
}
}
void main(void)
{
init_gamegraphics();
dy = 0;
dx = 0;
loop:
delay(1);
update_player();
update_window();
delay(1);
output_window();
goto loop;
}

Edited by newcoleco
  • Like 1
Link to comment
Share on other sites

DAY 3

 

After coding some more to actually get data from the 12K VRAM to be rendered on screen as tiles, I did this quite simple "doodle" program where you can move around and press FIRE#1 to fill up a square and press FIRE#2 to empty one. Note : if you press both, you'll simply empty the square like if only FIRE #2 is pressed.

 

Look like it could become a Level Editor feature for a game... :)

Link to comment
Share on other sites

Looking good.

 

For "Reckless Wizard" I use 24x24 pixel tiles, and I have 16x8 (128 in total) screens with 8x8 tiles. So that is 8k of maps uncompressed. Since I have about 40 tile types I use the top two bits to specify the run length (from 1 to 4) and the bottom 6 bits to specify the tile type. So RLE but not the conventional RLE compression. It works pretty good, with a 8k->3.2k compression. And it is super easy to decompress. So no pattern compression but easy stuff to decompress.

 

To make it a changable map, every tile has an "alternate tile". Then in video memory, there is 8 bytes (64 bits) to say if the main tile or the backup tile are active for each tile on that screen. It doesn't take much to read 8 bytes or write 8 bytes.

 

I don't see how writing to video ram is any different in speed than reading, unless you mean read-update-write is slow.

Edited by hardhat
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...