Tile Map Code

SGDK only sub forum

Moderator: Stef

matteus
Very interested
Posts: 336
Joined: Mon Feb 04, 2008 1:41 pm

Tile Map Code

Post by matteus » Tue Jan 09, 2018 4:51 pm

Hi all,
I just wanted to check you lot to ask if you can see anything obviously wrong with this code? I think it has a memory leak but I can't find where!

Code: Select all

int drawMap() {
    int DrawMapStatus = FALSE;
    u16 palette[32];
    int static DrawMapCurrentStage = 0;
    int static ItemTemp = 0;
    int static I = 0;
    int static Y = 0;
    int static X = 0;
    switch(DrawMapCurrentStage) {
    case 0:

        SYS_disableInts();
        VDP_setPalette(PAL0, palette_black);
        VDP_setPalette(PAL1, palette_black);
        SYS_enableInts();

        memcpy(&palette[0], layout_image.palette->data, 16 * 2);
        memcpy(&palette[16], map_tileset.palette->data, 16 * 2);

        strclr(CurrentGame.Global.MessageBoxLineStr1);
        sprintf(CurrentGame.Global.MessageBoxLineStr1, "%s %d", DraculasTombLevelStr, CurrentGame.User.Level);

        strclr(CurrentGame.Global.MessageBoxLineStr2);
        if (CurrentGame.User.Level > 0) {
            ItemTemp = CurrentGame.Global.RoomArray[CurrentGame.User.CurrentRoom];
            CurrentGame.Global.RoomArray[CurrentGame.User.CurrentRoom] = 1; // YOUR POSITION
            sprintf(CurrentGame.Global.MessageBoxLineStr2, "%s %s", YouLastMovedStr, Directions[CurrentGame.User.Moving]);
        } else {
            sprintf(CurrentGame.Global.MessageBoxLineStr2, "%s", StudyCarefullyStr);
        }

        CurrentGame.Global.TileIndex = TILE_USERINDEX;

        SYS_disableInts();
        
        VDP_loadTileSet(map_tileset.tileset, CurrentGame.Global.TileIndex, 1);
        CurrentGame.Global.TileIndex += map_tileset.tileset->numTile;
        
        VDP_drawText(CurrentGame.Global.MessageBoxLineStr1, 7, 2);
        VDP_drawText(CurrentGame.Global.MessageBoxLineStr2, 11, 24);
        
        for (X = 0; X <= 21; X++) {
            VDP_setTileMapXY(PLAN_B, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, CurrentGame.Global.TileIndex-10), X+9, 5);
            VDP_setTileMapXY(PLAN_B, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, CurrentGame.Global.TileIndex-10), X+9, 21);
        }
        
        for (Y = 0; Y <= 16; Y++) {             
            VDP_setTileMapXY(PLAN_B, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, CurrentGame.Global.TileIndex-10), 9, Y+5);
            VDP_setTileMapXY(PLAN_B, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, CurrentGame.Global.TileIndex-10), 30, Y+5);
        }
        
        if (CurrentGame.User.CurrentRoom == 0) {
            VDP_setTileMapXY(PLAN_B, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, CurrentGame.Global.TileIndex-3), 19, 21);
        }
        
        VDP_fadeIn(0, (2 * 16) - 1, palette, 30, FALSE);
        
        SYS_enableInts();
        
        strclr(CurrentGame.Global.MessageBoxLineStr1);
        
        strclr(CurrentGame.Global.MessageBoxLineStr2);

        Y = 20;
        X = 29;
        DrawMapCurrentStage = 1;
        break;
    case 1:
        if (Y >= 6) {
            if (X >= 10) {
                SYS_disableInts();
                VDP_setTileMapXY(PLAN_B, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, mapItemLookupInt(CurrentGame.Global.RoomArray[I])), X, Y);
                SYS_enableInts();
                X--;
                I++;
            } else {
                X = 29;
                Y--;
            }
        } else {
            Y = 20;
            X = 29;
            I = 0;
            CurrentGame.Global.RoomArray[CurrentGame.User.CurrentRoom] = ItemTemp;
            DrawMapCurrentStage = 2;
        }
        break;
    case 2:
        if (pause(MAP_TIMER) == TRUE) {
            DrawMapCurrentStage = 0;
            DrawMapStatus = TRUE;
            SYS_enableInts();
            VDP_fadeOut(0, (2 * 16) - 1, 30, FALSE);
            VDP_clearPlan(PLAN_B, 1);
            SYS_enableInts();
        }
        break;
    }
    return DrawMapStatus;
}

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Re: Tile Map Code

Post by Chilly Willy » Tue Jan 09, 2018 8:21 pm

Could be the strings - you have to be very careful about not running past the max length. I've run into that in MANY programs. For example, you should be using snprintf instead of sprintf. You should also check that strclr is not clearing past the max length of the space allotted the strings.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Tile Map Code

Post by Stef » Wed Jan 10, 2018 9:05 am

Definitely not easy to see where can be the problem from the code, it misses the variables declaration, we don't know how much you allocated for the string, we also don't know how much text you write to it. It can be the issue. Also strclr(..) is totally useless if you are just writing the string after it. strclr will basically does that : str[0] = 0;

matteus
Very interested
Posts: 336
Joined: Mon Feb 04, 2008 1:41 pm

Re: Tile Map Code

Post by matteus » Wed Jan 10, 2018 9:27 am

Hi Chilly,
Thanks for getting back to me! Yes the strings are a nightmare and one of the few things I figured could be causing the I (they always seem too!). The majority are declared as static char * but MessageBoxLineStr1 and MessageBoxLineStr2 are 37 in length which is more than likely where my problem stems from

I'm not sure the sgdk has an implementation of snprintf. I wish it did! As it would make my code a lot easier! :)
You should also check that strclr is not clearing past the max length of the space allotted the strings.
I wasn't aware it could do this :o How would I go about checking? Strclr in the SGDK seems to just assign a zero to position 0 in the array.

matteus
Very interested
Posts: 336
Joined: Mon Feb 04, 2008 1:41 pm

Re: Tile Map Code

Post by matteus » Wed Jan 10, 2018 9:29 am

Stef wrote:
Wed Jan 10, 2018 9:05 am
Definitely not easy to see where can be the problem from the code, it misses the variables declaration, we don't know how much you allocated for the string, we also don't know how much text you write to it. It can be the issue. Also strclr(..) is totally useless if you are just writing the string after it. strclr will basically does that : str[0] = 0;
Ha weird I just mentioned that in my response! It's definitely a string problem. Just discovered strclr is pretty pointless :)

matteus
Very interested
Posts: 336
Joined: Mon Feb 04, 2008 1:41 pm

Re: Tile Map Code

Post by matteus » Wed Jan 10, 2018 11:23 am

Is there something you'd recommend I use instead of strclr?

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Re: Tile Map Code

Post by Chilly Willy » Wed Jan 10, 2018 3:48 pm

matteus wrote:
Wed Jan 10, 2018 11:23 am
Is there something you'd recommend I use instead of strclr?
memset(string, 32, len);

sets the string to spaces. Or use 0 to zero out the whole thing. Depends on how you're using the string.

Yukiko
Interested
Posts: 14
Joined: Sat Nov 18, 2017 9:33 am

Re: Tile Map Code

Post by Yukiko » Thu Jan 11, 2018 3:53 am

How would I move tiles from one index/address to another? I'm building a 360 degree view, made up of 180 one tile wide images. I use a loop with the following code to populate the screen.

Code: Select all

VDP_drawImageEx(PLAN_B, &background_sewing_2_2_0***, TILE_ATTR_FULL(PAL3, FALSE, FALSE, FALSE, TILE_USERINDEX+10+bg_row+(CHATBOXy*bg_row)), BGx-1+bg_row, BGy+pal_offset+ntsc_offset, FALSE, TRUE);
It works. However, there are some artifacts when turning. I'm hoping the artifacts will be reduced if I move the tiles in memory, rather than reloading them via the above code.

Code: Select all

memcpy (to, from, len) 	

to	Pointer to the destination array where the content is to be copied, type-casted to a pointer of type void*.
from	Pointer to the source of data to be copied, type-casted to a pointer of type void*.
len	Number of bytes to copy.
This function looks like what I need. I don't know how to use it though. :( Would there be a better one? In Gens the tile addresses I use range from 0x002E to 0x0271.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Tile Map Code

Post by Stef » Thu Jan 11, 2018 1:32 pm

VDP_drawImageEx is slow as it upload the tiles each time, you should probably use VDP_loadTileSet(..) first then use VDP_setMapEx(..).
Normally you can find several examples about how to use these methods efficiently on the forum as we already discusses many time about it ;)
Some examples :
viewtopic.php?f=19&t=1908
viewtopic.php?f=19&t=2145
viewtopic.php?f=19&t=2028

Yukiko
Interested
Posts: 14
Joined: Sat Nov 18, 2017 9:33 am

Re: Tile Map Code

Post by Yukiko » Fri Jan 12, 2018 4:25 am

I was hoping to shuffle tiles that were already in memory around, however VDP_loadTileSet works really well. The artifacts are gone, and the speed has improved.

Code: Select all

VDP_loadTileSet(background_sewing_2_2_0***.tileset, TILE_USERINDEX+10+(bg_row*1)+(CHATBOXy*bg_row), FALSE);
Using this function some of the graphics became garbled. I thought it was a memory issue. One graphic would load perfectly, while another would skip half the tiles.

I experimented with a solid orange graphic. It would not load. If I added black lines down it, it would. It seems to me that VDP_loadTileSet is skipping low detailed tiles it doesn't think it needs.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Tile Map Code

Post by Stef » Fri Jan 12, 2018 8:38 am

Are your data compressed ? if yes then you need to unpack your tileset first otherwise it will do it at each VDP_loadTileSet(..) call.
That can bring a *big* speed improvement. Also reloading tiles each time is slow by definition as you're loading them in VRAM.
Can't you load all tiles then just modify your tilemap instead ?

Yukiko
Interested
Posts: 14
Joined: Sat Nov 18, 2017 9:33 am

Re: Tile Map Code

Post by Yukiko » Fri Jan 12, 2018 10:06 am

Are your data compressed ? if yes then you need to unpack your tileset first otherwise it will do it at each VDP_loadTileSet(..) call.
That can bring a *big* speed improvement.
It isn't compressed. Speed isn't my problem though. It runs quite fast. I tried using -1 compression instead of NONE, and wow, it was much slower. The problem is that VDP_loadTileSet produces garbled graphics.

GarbledGrahic.png
GarbledGrahic.png (9.27 KiB) Viewed 10654 times

On the left is what happens if I use VDP_loadTileSet (loaded after VDP_drawImageEx). VDP_drawImageEx is used on the right. It looks like the single colour tiles are being dropped.
Can't you load all tiles then just modify your tilemap instead ?
A single image would be 3600 tiles. I can't scroll the plane because I have other tiles on one side. I've thought about modifying the tilemap, but given I'm using 180 chunks, I think I'd just end up exponentially increasing the complexity.

I'm going for something like in Zork Grand Inquisitor, where you can turn 360 degrees in a pre-rendered environment. https://www.youtube.com/watch?v=cfGVtUEqg00

Sik
Very interested
Posts: 939
Joined: Thu Apr 10, 2008 3:03 pm
Contact:

Re: Tile Map Code

Post by Sik » Fri Jan 12, 2018 11:56 am

Oh, prerendered backgrounds... wait, that's completely going to trash your ROM space unless you switch to Sega CD instead x_x

Ever looked at Silent Debuggers? (PC Engine game) It gets away with just scrolling sideways to make it look like the player is turning around. Would that be a better approach?
Sik is pronounced as "seek", not as "sick".

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Tile Map Code

Post by Stef » Fri Jan 12, 2018 12:25 pm

Yukiko wrote:
Fri Jan 12, 2018 10:06 am
It isn't compressed. Speed isn't my problem though. It runs quite fast. I tried using -1 compression instead of NONE, and wow, it was much slower. The problem is that VDP_loadTileSet produces garbled graphics.

...

On the left is what happens if I use VDP_loadTileSet (loaded after VDP_drawImageEx). VDP_drawImageEx is used on the right. It looks like the single colour tiles are being dropped.
Can't you load all tiles then just modify your tilemap instead ?
A single image would be 3600 tiles. I can't scroll the plane because I have other tiles on one side. I've thought about modifying the tilemap, but given I'm using 180 chunks, I think I'd just end up exponentially increasing the complexity.

I'm going for something like in Zork Grand Inquisitor, where you can turn 360 degrees in a pre-rendered environment. https://www.youtube.com/watch?v=cfGVtUEqg00
Ok i understand now...
You said a single image is about 3600 tiles, but you probably don't count duplicated / flipped tiles. When you define an image, rescomp (resource compiler) automatically optimize the tileset so duplicated / flipped tiles are not stored, it's why you also need the tilemap information here and that explain why you VDP_loadTileSet(..) method can't work.
When you use VDP_drawImageEx(..) SGDK will first upload the complete TileSet of the image then load the tilemap, If the tileset is too large to entire in VRAM you will get in troubles so in your case you have to split the whole 360 view in small blocks (image column as you are doing) and statically define VRAM position for the TileSet of each block. I believe that is exactly what you do...
And indeed you have to use VDP_drawImageEx(..) here, you cannot just move tiles in memory because duplicate tiles aren't stored. Also moving tiles in VRAM (even using VRAM copy) would be slow... but instead of refreshing the whole screen, why you don't use scrolling capability when needed ? so you roll your display over the 512 px width tilemap (and refresh only right/left most column) ? exactly as classics games are doing.

Yukiko
Interested
Posts: 14
Joined: Sat Nov 18, 2017 9:33 am

Re: Tile Map Code

Post by Yukiko » Fri Jan 12, 2018 1:36 pm

Sik wrote:
Fri Jan 12, 2018 11:56 am
Oh, prerendered backgrounds... wait, that's completely going to trash your ROM space unless you switch to Sega CD instead x_x
I think I could fit 20 locations in 3MiB. That leaves 1MiB for the rest of the game. :P They don't all even need to be 360 degrees, some could be 270.

My next cartridge I'm going to design will allow for two socketed M27C322s. If I'm lucky I'll be able to fit in an Arduino Pro Mini with an MP3 decoder attached to the audio lines. I was thinking the game on one ROM, with the intro and ending animations on the other.

Shame the Sega CD is a dying platform. Maybe one day a flash emulator will come out for it.

Sik wrote:
Fri Jan 12, 2018 11:56 am
Ever looked at Silent Debuggers? (PC Engine game) It gets away with just scrolling sideways to make it look like the player is turning around. Would that be a better approach?
Stef wrote:
Fri Jan 12, 2018 12:25 pm
Also moving tiles in VRAM (even using VRAM copy) would be slow... but instead of refreshing the whole screen, why you don't use scrolling capability when needed ? so you roll your display over the 512 px width tilemap (and refresh only right/left most column) ? exactly as classics games are doing.
rom008.jpg
rom008.jpg (65.86 KiB) Viewed 10640 times

What the protagonist sees is in the view port to the left. The box to the right will show the protagonist, and what is behind the protagonist. Both views are on the same plane. If I try sliding I'd wipe out the rear view. The HUD is on another plane. I don't want to turn the HUD into a sprite as I'm already bumping the sprite limit.

Stef wrote:
Fri Jan 12, 2018 12:25 pm
And indeed you have to use VDP_drawImageEx(..) here, you cannot just move tiles in memory because duplicate tiles aren't stored.
Ah ha! The problem has been discovered. So, the solution is to open Paint and put dots all over the duplicate tiles.

Post Reply