Tile mapper questions

Understanding the language, error messages, etc.

Tile mapper questions

Postby deeph » Mon Jul 13, 2015 6:21 am

Hi,

I am currently working on a tile mapper which seems to work properly, but I can't figure out why is the emu (simbuino) crashing when the Y camera reaches the lower bound of the map.

Plus I'd like to speed it up a little bit because I've seen faster ones in other games (that don't even need to speed up the frame rate). Any advice ?

Here's the code :

Code: Select all
#include <SPI.h>
#include <Gamebuino.h>

const byte PROGMEM map_test[]={
  1,1,1,1,1,1,1,1,1,1,1,1,0,2,2,2,2,2,2,2,0,0,0,3,
  1,1,1,1,1,1,1,1,1,1,1,0,0,0,2,2,2,2,2,0,0,0,0,3,
  2,1,1,1,1,1,1,1,1,1,1,0,0,0,0,2,2,0,0,0,0,0,0,3,
  2,2,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
  2,2,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
  2,2,2,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,3,
  2,2,2,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,3,3,
  0,2,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,3,3,
  3,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,3,3,3,
  3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,
  3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
  3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
};

const byte PROGMEM tile_0[]={ 8,8,0x80,0x84,0x10,0x41,0x00,0x20,0x02,0x00 };
const byte PROGMEM tile_1[]={ 8,8,0x10,0x30,0x28,0x60,0x54,0xC0,0xFE,0x10 };
const byte PROGMEM tile_2[]={ 8,8,0x00,0x00,0x08,0x14,0x26,0x52,0xD9,0x88 };
const byte PROGMEM tile_3[]={ 8,8,0xFA,0xDF,0xAD,0xFA,0xEF,0xD7,0xFF,0xFD };

const byte* tiles[]={ tile_0, tile_1, tile_2, tile_3 };

Gamebuino gb;

void setup(){
  gb.begin();
  gb.setFrameRate(60);
  const byte map_width = 24;
  const byte map_height = 24;
  const byte tile_width = 8;
  const byte tile_height = 8;
  byte camera_x = 0;
  byte camera_y = 0;
  byte x_v = 1;
  byte y_v = 1;
  while(1){
    if(gb.update()){
      for(byte y = 0; y <= 6; y++){
        for(byte x = 0; x <= 11; x++){
          gb.display.drawBitmap(x*tile_width-camera_x%8, y*tile_height-camera_y%8, tiles[pgm_read_byte(map_test+((camera_y/8+y)*map_width+camera_x/8+x))]);
        }
      }
      camera_y+=-gb.buttons.repeat(BTN_UP,1)*(camera_y > 0)+gb.buttons.repeat(BTN_DOWN,1)*(camera_y < map_height*tile_height-48);
      camera_x+=-gb.buttons.repeat(BTN_LEFT,1)*(camera_x > 0)+gb.buttons.repeat(BTN_RIGHT,1)*(camera_x < map_width*tile_width-84);
    }
  }
}

void loop(){
  while(1){
  }
}


And a screenshot :

Image

Thanks :)
deeph
 
Posts: 52
Joined: Mon Jul 13, 2015 6:09 am
Location: France

Re: Tile mapper questions

Postby Myndale » Mon Jul 13, 2015 11:54 am

There are a few bugs in your code that are resulting in an attempt to access an invalid Flash address. Simbuino shouldn't be crashing under any circumstances though so thanks for bringing it to my attention, I'll add a fix in the next release.

With respect to the underlying cause, map_width and map_height are both being set to 24 but the map array itself is 24x12, so the first step is to set map_height to 12. Even with this fix the drawing logic will still try to read and draw tiles outside the proper map area if you move to the bottom right, a quick check in your draw loop will fix that:

Code: Select all
          int mapx = camera_x/8+x;
          int mapy = camera_y/8+y;
          if ((mapx>=0) && (mapx<map_width) && (mapy>=0) && (mapy<map_height))
            gb.display.drawBitmap(x*tile_width-camera_x%8, y*tile_height-camera_y%8, tiles[pgm_read_byte(map_test+mapy*map_width+mapx)]);
Myndale
 
Posts: 507
Joined: Sat Mar 01, 2014 1:25 am

Re: Tile mapper questions

Postby deeph » Mon Jul 13, 2015 12:56 pm

Myndale wrote:With respect to the underlying cause, map_width and map_height are both being set to 24 but the map array itself is 24x12, so the first step is to set map_height to 12.


Yes I find it out shortly after I made this post... :oops:

Thanks ! :)

Now I'm trying to handle the player sprite :

Code: Select all
#include <SPI.h>
#include <Gamebuino.h>

const byte PROGMEM map_test[]={
  2,2,2,2,2,2,2,2,2,2,2,2,1,3,3,3,3,3,3,3,1,1,1,4,
  2,2,2,2,2,2,2,2,2,2,2,1,1,1,3,3,3,3,3,1,1,1,1,4,
  3,2,2,2,2,2,2,2,2,2,2,1,1,1,1,3,3,1,1,1,1,1,1,4,
  3,3,1,1,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,4,
  3,3,1,1,1,1,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,
  3,3,3,1,1,1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,1,1,4,
  3,3,3,1,1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,1,4,4,
  1,3,1,1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,1,1,4,4,
  4,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,4,4,4,
  4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,4,4,4,
  4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
  4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
};

const byte PROGMEM tile_0[]={
  8,8,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
const byte PROGMEM tile_1[]={
  8,8,0x80,0x84,0x10,0x41,0x00,0x20,0x02,0x00 };
const byte PROGMEM tile_2[]={
  8,8,0x10,0x30,0x28,0x60,0x54,0xC0,0xFE,0x10 };
const byte PROGMEM tile_3[]={
  8,8,0x00,0x00,0x08,0x14,0x26,0x52,0xD9,0x88 };
const byte PROGMEM tile_4[]={
  8,8,0xFA,0xDF,0xAD,0xFA,0xEF,0xD7,0xFF,0xFD };
const byte* tiles[]={
  tile_0, tile_1, tile_2, tile_3, tile_4 };

const byte PROGMEM player[]={
  8,8,0x18,0x24,0x24,0x18,0x3C,0x5A,0x24,0x24 };

#define MAP_WIDTH 24
#define MAP_HEIGHT 12
#define TILE_WIDTH 8
#define TILE_HEIGHT 8

Gamebuino gb;

void setup(){
  gb.begin();
  gb.display.persistence = true;
  byte x = 6;
  byte y = 6;
  byte i;
  int camera_x = x*TILE_WIDTH-LCDWIDTH/2+4;
  int camera_y = y*TILE_HEIGHT-LCDHEIGHT/2+4;
  draw_map(camera_x, camera_y, MAP_WIDTH, MAP_HEIGHT);
  gb.display.drawBitmap(x*TILE_WIDTH-camera_x, y*TILE_WIDTH-camera_y, player);
  gb.display.update();
  while(1){
    if(gb.update()){
      char x_temp=-gb.buttons.repeat(BTN_LEFT, 1)*(x > 0)+gb.buttons.repeat(BTN_RIGHT, 1)*(x < MAP_WIDTH-1);
      char y_temp=-gb.buttons.repeat(BTN_UP, 1)*(y > 0)+gb.buttons.repeat(BTN_DOWN, 1)*(y < MAP_HEIGHT-1);
      if(x_temp || y_temp){
        for(i = 1; i <=8; i++){
          gb.display.clear();
          camera_x = x*TILE_WIDTH-LCDWIDTH/2+4+i*x_temp;
          camera_y = y*TILE_HEIGHT-LCDHEIGHT/2+4+i*y_temp;
          draw_map(camera_x, camera_y, MAP_WIDTH, MAP_HEIGHT);
          gb.display.drawBitmap(x*TILE_WIDTH-camera_x+i*x_temp, y*TILE_WIDTH-camera_y+i*y_temp, player);
          gb.display.update();
        }
        x+=x_temp;
        y+=y_temp;
      }
    }
  }
}

void loop(){
  while(1){
  }
}

void draw_map(int camera_x, int camera_y, byte map_width, byte map_height){
  for(byte y = 0; y <= 6; y++){
    for(byte x = 0; x <= 11; x++){
      int tile_x = camera_x/TILE_WIDTH+x;
      int tile_y = camera_y/TILE_HEIGHT+y;
      byte tile = 0;
      if(tile_x >= 0 && tile_x < map_width && tile_y >= 0 && tile_y < map_height){
        tile = pgm_read_byte(map_test+(tile_y)*map_width+tile_x);
      }
      gb.display.drawBitmap(x*TILE_WIDTH-camera_x%TILE_WIDTH, y*TILE_HEIGHT-camera_y%TILE_HEIGHT, tiles[tile]);
    }
  }
}


It seems to work, there's just one thing I haven't fixed so far : the blank tiles outside the upper bound of the map aren't properly clipped :

Image

I think it is somewhat due to negative values of camera_x/y, but using abs() on them don't seems to work.
deeph
 
Posts: 52
Joined: Mon Jul 13, 2015 6:09 am
Location: France

Re: Tile mapper questions

Postby Marcus » Mon Jul 13, 2015 6:26 pm

That looks pretty neat, what game are you working on`? :-)
Marcus
 
Posts: 143
Joined: Fri Jan 09, 2015 6:51 pm

Re: Tile mapper questions

Postby deeph » Mon Jul 13, 2015 6:35 pm

Nothing in particular yet, I just want to have a functionnal tile mapper first. Maybe it'll then turn into a RPG ;)

I'm wondering what's best : stop scrolling the camera on the edges of the map or continue and draw blank tiles beyond (what I'm trying to do right now) ?

And if anybody has any advice about how to optimise it more, don't hesitate :)
deeph
 
Posts: 52
Joined: Mon Jul 13, 2015 6:09 am
Location: France

Re: Tile mapper questions

Postby awesome101 » Mon Jul 13, 2015 7:33 pm

Hey deeph, I have an optimization for how you store the numbers in the map array. If you are using less than 16 tiles, you can represent 2 tiles for every element in the array. For example:
Code: Select all
byte map[] PROGMEM = {B1000 0001...};


The B stands for binary.
The 4 bits on the left, in this case 1000, can represent grass, for example, and the other 4 bits on the right could represent sand. If you do it this way there are 16 different combinations that you can make, and that's why you need under 16 tiles. Now, you might be asking, how can you get the first 4 bits on the left, and on the right? You can do that using bitwise opperators. So if you shift the number to the right(>>) by 4, you will get the first half of the number. To get the other half you have to mask(&) it by the number >> 4. p.s. it's okay if you don't get some of this, because this isn't really for beginners.

example:
Code: Select all
byte map[] PROGMEM = {B1000 0001...};
byte first_half = (map[0] >> 4);
byte second_half = map[0] & (map[0] >> 4);


for more details you can go to:
https://en.wikipedia.org/wiki/Binary_number
https://en.wikipedia.org/wiki/Bitwise_operation

Hope this helped :D
awesome101
 
Posts: 159
Joined: Sat Jun 20, 2015 6:56 pm

Re: Tile mapper questions

Postby Marcus » Mon Jul 13, 2015 7:40 pm

Looking forward to your endeavors!

I'm a big fan of RPG and hopefully I can get a game together myself soon (waiting for my Gamebuino... should be here soon :-) Also I got Simbuino running now after upgrading to Windows 8.)

I still can't make up my mind on tile sizes and sprite sizes. The constrains of the small display are both challenging and fun to design for. I think I'm going with a unconventional mix (see this 5x5 and 11x8 for the sprites)- I started with 8x8 but the tiles where too large and the characters lacked of details.

Personally, I would stop scrolling instead of showing a blank screen, though it could be done like in some games such as my beloved "Secret of Mana" where there's usually just a continuous pattern of trees or similar (see this).
The advantage of stopping at the border is that it'll show the active area (enemies and so on), the small screen resolution is already cropping it hard as it is :-)
Marcus
 
Posts: 143
Joined: Fri Jan 09, 2015 6:51 pm

Re: Tile mapper questions

Postby deeph » Mon Jul 13, 2015 8:07 pm

awesome101 > That's interesting and I'll certainly use that method for small games with only a few different tiles, thanks ;)

Marcus > Those sprites are great :) But personnaly I prefere 8*8 sprites because that's what I'm used to with other devices (TI z80). The only thing is that the screen width isn't a 8 multiple, so you have either 4 cols of pixels on one side of the screen or 2 on both sides (what I'm doing right now, but I guess it'll be less convenient if I stop the camera on edges of the map).

I think you're right, showing a blank screen may not be the smarter thing to do with such a low res :D

So I'll let the pokemon-style camera and switch to a zelda-like one.

Though I'd like to know why those blank tiles weren't properly clipped.
deeph
 
Posts: 52
Joined: Mon Jul 13, 2015 6:09 am
Location: France

Re: Tile mapper questions

Postby Marcus » Mon Jul 13, 2015 8:20 pm

Thanks :-)

Yes, I can understand, it makes things. Easier. But for me it didn't quite work out.

8x8 looks more limited, especially as gray-scale is a bit wonky. With just monochrome it'll be very limited.
download/file.php?id=808 But some others on here made nice graphics in 6x6 or 8x8.
compared to ~11x8
download/file.php?id=632 - leaves a bit more freedom for design and poses.

I am not experienced enough to help you with the tile rendering, I hope you get it sorted out! :-)
Marcus
 
Posts: 143
Joined: Fri Jan 09, 2015 6:51 pm

Re: Tile mapper questions

Postby deeph » Tue Jul 14, 2015 8:48 am

Well, I've seen a lot of beautiful 8*8 monochrome sprites (http://revsoft.tifreakware.net/phpBB3/viewtopic.php?f=10&t=110 for example) :)

Otherwise it was easier than I thought :

Code: Select all
#include <SPI.h>
#include <Gamebuino.h>

const byte PROGMEM map_test[]={
  2,2,2,2,2,2,2,2,2,2,2,2,1,3,3,3,3,3,3,3,1,1,1,4,
  2,2,2,2,2,2,2,2,2,2,2,1,1,1,3,3,3,3,3,1,1,1,1,4,
  3,2,2,2,2,2,2,2,2,2,2,1,1,1,1,3,3,1,1,1,1,1,1,4,
  3,3,1,1,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,4,
  3,3,1,1,1,1,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,
  3,3,3,1,1,1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,1,1,4,
  3,3,3,1,1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,1,4,4,
  1,3,1,1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,1,1,4,4,
  4,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,4,4,4,
  4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,4,4,4,
  4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
  4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
};

const byte PROGMEM tile_0[]={
  8,8,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
const byte PROGMEM tile_1[]={
  8,8,0x80,0x84,0x10,0x41,0x00,0x20,0x02,0x00 };
const byte PROGMEM tile_2[]={
  8,8,0x10,0x30,0x28,0x60,0x54,0xC0,0xFE,0x10 };
const byte PROGMEM tile_3[]={
  8,8,0x00,0x00,0x08,0x14,0x26,0x52,0xD9,0x88 };
const byte PROGMEM tile_4[]={
  8,8,0xFA,0xDF,0xAD,0xFA,0xEF,0xD7,0xFF,0xFD };
const byte* tiles[]={
  tile_0, tile_1, tile_2, tile_3, tile_4 };

const byte PROGMEM player[]={
  8,8,0x18,0x24,0x24,0x18,0x3C,0x5A,0x24,0x24 };

#define MAP_WIDTH 24
#define MAP_HEIGHT 12
#define TILE_WIDTH 8
#define TILE_HEIGHT 8

Gamebuino gb;

void setup(){
  gb.begin();
  gb.display.persistence = true;
  byte player_x = 6;
  byte player_y = 6;
  byte i;
  int camera_x = player_x*TILE_WIDTH-LCDWIDTH/2+4;
  int camera_y = player_y*TILE_HEIGHT-LCDHEIGHT/2+4;
  draw_map(camera_x, camera_y, MAP_WIDTH, MAP_HEIGHT);
  gb.display.drawBitmap(player_x*TILE_WIDTH-camera_x, player_y*TILE_WIDTH-camera_y, player);
  gb.display.update();
  while(1){
    if(gb.update()){
      char x_temp = -gb.buttons.repeat(BTN_LEFT, 1)*(player_x > 0)+gb.buttons.repeat(BTN_RIGHT, 1)*(player_x < MAP_WIDTH-1);
      char y_temp = -gb.buttons.repeat(BTN_UP, 1)*(player_y > 0)+gb.buttons.repeat(BTN_DOWN, 1)*(player_y < MAP_HEIGHT-1);
      if(x_temp || y_temp){
        for(i = 1; i <=8; i++){
          gb.display.clear();
          camera_x = (player_x*TILE_WIDTH-LCDWIDTH/2+4+i*x_temp);
          camera_x = camera_x*(camera_x > 0)+(MAP_WIDTH*TILE_WIDTH-LCDWIDTH-camera_x)*(camera_x > MAP_WIDTH*TILE_WIDTH-LCDWIDTH);
          camera_y = player_y*TILE_HEIGHT-LCDHEIGHT/2+4+i*y_temp;
          camera_y = camera_y*(camera_y > 0)+(MAP_HEIGHT*TILE_HEIGHT-LCDHEIGHT-camera_y)*(camera_y > MAP_HEIGHT*TILE_HEIGHT-LCDHEIGHT);
          draw_map(camera_x, camera_y, MAP_WIDTH, MAP_HEIGHT);
          gb.display.drawBitmap(player_x*TILE_WIDTH-camera_x+i*x_temp, player_y*TILE_WIDTH-camera_y+i*y_temp, player);
          gb.display.update();
        }
        player_x += x_temp;
        player_y += y_temp;
      }
    }
  }
}

void loop(){
}

void draw_map(int camera_x, int camera_y, byte map_width, byte map_height){
  for(byte y = 0; y <= 6; y++){
    for(byte x = 0; x <= 11; x++){
      int tile_x = camera_x/TILE_WIDTH+x;
      int tile_y = camera_y/TILE_HEIGHT+y;
      if(tile_x >= 0 && tile_x < map_width && tile_y >= 0 && tile_y < map_height){
        gb.display.drawBitmap(x*TILE_WIDTH-camera_x%TILE_WIDTH, y*TILE_HEIGHT-camera_y%TILE_HEIGHT, tiles[pgm_read_byte(map_test+(tile_y)*map_width+tile_x)]);
      }
    }
  }
}


Image

The next thing I'll do is add animations/directions to the player sprite.
deeph
 
Posts: 52
Joined: Mon Jul 13, 2015 6:09 am
Location: France

Next

Return to Programming Questions

Who is online

Users browsing this forum: No registered users and 81 guests

cron