Welp, the Tiling functions are more or less DONE.
I tried to save a bit of memory, by rotating and selecting particular tiles on the fly, based on the adjacent tiles. I don't think it worked out to saving any XD, but it does work, and has 'other applications' which I might try to streamline and make other games out of.
What I've got working so far:
* it compiles! (:P)
* It draws tiles as appropriate for what's nearby (just roads now, but I'll be adding grass and pond tiles, with functions to draw them, too, they'll be much simpler)
* it only draws what is within the viewport
* The viewport may, or may not, be sized differently from the whole screen (working on code to make it resize dyamically. The C menu will bump out from the side, so the camera will have to recenter on the newly sized view
* The viewport will stop moving on it's own if it reaches the edge of the matrix that represents the 'map'
If anyone's curious, here's the code. It takes up over a 1/3 of the apparent maximum size of a sketch. So not very efficient
- Code: Select all
//Armored Steel, by DFX2KX
#include <SPI.h>
#include <Gamebuino.h>
Gamebuino gb;
#include <EEPROM.h>
#include <avr/pgmspace.h>
//useful constants
const int NORTH = 0;
const int SOUTH = 1;
const int EAST = 2;
const int WEST = 3;
const int LEVEL_CSIZE = 10;
const int LEVEL_RSIZE = 5;
const int LEVEL1MAP = 0;
const int LEVEL2MAP = 1;
const int LEVEL3MAP = 2;
const int DONTFLIP = 1;
//some globals for the blasted tilset renderer
int global_current_c =0;
int global_current_r =0;
//bitmap assets
//road tile bitmaps
//road, straight (rotate for ew)
static unsigned char PROGMEM road_0_0[] =
{
16,16,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
B10000000,B00000001,
B10000000,B00000001,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
B10000000,B00000001,
B10000000,B00000001,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
};
//curved road (SE rotate WS WN NE)
static unsigned char PROGMEM road_0_1[] =
{
16,16,
B00000000,B01111111,
B00000011,B10000000,
B00000100,B00000000,
B00001000,B00000000,
B00010000,B00000000,
B00100000,B00000000,
B01000000,B00000000,
B01000000,B00001111,
B01000000,B00111111,
B10000000,B01110000,
B10000000,B11100000,
B10000000,B11000000,
B10000001,B10000000,
B10000001,B10000000,
B10000001,B10000000,
B10000001,B10000001,
};
//Road, three-way (ews rotate for NSW, WEN)
static unsigned char PROGMEM road_0_2[] =
{
16,16,
B11111111,B11111111,
B00000000,B00000001,
B00000000,B00000000,
B00000000,B00000001,
B00000000,B00000000,
B00000000,B00000001,
B00000000,B00000000,
B10000000,B00000001,
B10000000,B00000001,
B00000000,B00000000,
B10000000,B00000000,
B00000000,B00000000,
B10000000,B00000000,
B00000000,B00000000,
B10000000,B00000000,
B10000001,B10101011,
};
//Road four-way
static unsigned char PROGMEM road_0_3[] =
{
16,16,
B11010101,B10000001,
B00000000,B00000001,
B00000000,B00000000,
B00000000,B00000001,
B00000000,B00000000,
B00000000,B00000001,
B00000000,B00000000,
B10000000,B00000001,
B10000000,B00000001,
B00000000,B00000000,
B10000000,B00000000,
B00000000,B00000000,
B10000000,B00000000,
B00000000,B00000000,
B10000000,B00000000,
B10000001,B10101011,
};
//road, dead end (rotate for east, west, and south)
static unsigned char PROGMEM road_0_4[] =
{
16,16,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
B10000001,B10000001,
B10000000,B00000001,
B10000000,B00000001,
B10000000,B00000001,
B01000000,B00000010,
B00100000,B00000100,
B00110000,B00001100,
B00001100,B00110000,
B00000011,B11000000,
};
/*
const int PROGMEM tilemap_Level3[][LEVEL_CSIZE]={
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,1,0,0,1},
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,1,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
*/
//tilemap handling functions
int func_getTilemapTile(int mapselect, int collum, int row){
//the maps (yay) must exist inside of this functions
const int PROGMEM tilemap_Level1[][LEVEL_CSIZE]={
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,1,0,0,1},
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,1,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
const int PROGMEM tilemap_Level2[][LEVEL_CSIZE]={
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
};
const int PROGMEM tilemap_Level3[][LEVEL_CSIZE]={
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
};
//case statement deciding what map to look at
int output;
int * const r = &row;
int * const c = &collum;
//arrays do not like variables passed from functions to work as arguments for their pointers or []'s. a switch statment will be needed, with cases for every reasionable row or collum to be used. Arrays are awful
switch (mapselect){
case 0:
output = tilemap_Level1[*r][*c];
break;
case 1:
output = tilemap_Level2[*r][*c];
break;
case 2:
output = tilemap_Level3[*r][*c];
break;
};
if (output != 0) {
output = true;
};
return output;
};
void func_drawRoadTile(int x, int y, int tilesize, int north, int south, int east, int west){
//draws a tile of type tiletype, at x,y, with shape fitting matching tiles to north/south/east/west
//add all of the sides up, to get a number from 0-4, this ballparks what tile we'll need, and saves us mindlessly checking EVERYTHING.
switch (north + south + east + west){
case 0:
//no sides, an orphan tile, no tile is in the testfile, so we'll just draw a placeholder
gb.display.drawRect(x,y,tilesize,tilesize);
break;
case 1:
//single side is attached, so deadend tile
if(north == true){
//north facing tile
gb.display.drawBitmap(x, y, road_0_4, NOROT, 0);
}
else
{
//not north, let's try east
if(east == true){
//east facing tile
gb.display.drawBitmap(x, y, road_0_4, ROTCCW, 0);
}
else
{
//not not east, lets try south
if(south == true){
//south facing tile
gb.display.drawBitmap(x, y, road_0_4, ROT180, 0);
}
else
{
//if not south, let's try west
if(west == true){
//west facing tile
gb.display.drawBitmap(x, y, road_0_4, ROTCW, 0);
};
};
};
};
break;
case 2:
//two sides are matched to this tile, so either a straight, or a turn. let's use some if's to see which of these it is.
if((east + west == 2) || (north + south == 2)){
//a straight road! from hear it's easy to draw the right tile
if(north == true){
//north south road
gb.display.drawBitmap(x, y, road_0_0, NOROT, 0);
}
else
{
//east west straight road
gb.display.drawBitmap(x, y, road_0_0, ROTCW, 0);
};
}
else
{
//a curve, there are fout ways this can be drawn. The defualt of this sprite is south and east
if(south + east == 2){
//curve from south to east
gb.display.drawBitmap(x, y, road_0_1, NOROT, 0);
}
else
{
//let's try east north, or rotccw
if(north + east == 2){
//east to north curve
gb.display.drawBitmap(x, y, road_0_1, ROTCCW, 0);
}
else
{
//not north and east, let's try west and north, or rot180
if(north + west == 2){
//north to west curve
gb.display.drawBitmap(x, y, road_0_1, ROT180, 0);
}
else
{
//only other curve we've got is west/south, or rot cw
gb.display.drawBitmap(x, y, road_0_1, ROTCW, 0);
};
};
};
};
break;
case 3:
//t roads, can be checked for with one two way check, and a further check for the extra side
//does this t have an east/west connection?
if(east + west == 2){
//yes it does, check south gb.display.drawBitmap(x, y, road_0_2, NO, NOFLIP);
if(south == true){
//it does have a south connection, standard sprite shall do
gb.display.drawBitmap(x-1, y, road_0_2, NOROT, DONTFLIP);
}
else
{
//nope, rotate the sprite 180 degrees
gb.display.drawBitmap(x-1, y, road_0_2, ROT180, DONTFLIP);
};
}
else
{
//it doesn't have an east/west combo, so lets check which of those two it DOES have, the road has to be north/south/x
if(east == true){
//it has an east connection, meaning north/south/east sprite is rotccw
gb.display.drawBitmap(x-1, y, road_0_2, ROTCW, DONTFLIP);
}
else
{
//it's notth/south/west, so rotcw
gb.display.drawBitmap(x-1, y, road_0_2, ROTCCW, DONTFLIP);
};
};
break;
case 4:
//easy, it's an intersection!
gb.display.drawBitmap(x-1, y, road_0_3, NOROT, DONTFLIP);
break;
};
};
void func_draw_tailemap(int mapselect, int camera_x, int camera_y, int viewport_w, int viewport_h){
//draw the tiles, based on selected map, the position of the camera in world space, and the width and height of the viewport (the actual region of the screen to be drawn) the camera's point in viewport space is the center
int current_c = 0;
int current_r = 0;
int tilex;
int tiley;
int screenx;
int screeny;
int north;
int south;
int east;
int west;
int curtype;
int tilesize = 16;
boolean draw;
//okay, some constant pointers, so things don't break
int * const ref_c = ¤t_c;
int * const ref_r = ¤t_r;
int * const ref_t = &curtype;
// four quick variables representing the worldspace bounds
int view_world_left = camera_x - (viewport_w/2);
int view_world_right = camera_x + (viewport_w/2);
int view_world_top = camera_y - (viewport_h/2);
int view_world_bottom = camera_y + (viewport_h/2);
for(current_r; current_r <= (LEVEL_RSIZE-1); current_r += 1){
//repeat for each row in the standard level
global_current_r = current_r;
for(current_c; current_c <= (LEVEL_CSIZE-1); current_c += 1){
global_current_c = current_c;
//repeat ths loop for each collum in row
//get the tile's x and y, check if it's visible
tilex = current_c * tilesize;
tiley = current_r * tilesize;
//replaced with always true, so that we can test other things
if ((((tilex + tilesize -1 ) >= view_world_left) && ((tilex - 1) <= view_world_right) && ((tiley + tilesize + 1 ) >= view_world_top) && ((tiley + 1) <= view_world_bottom))){
//visible, so let's do some math
screenx = (tilex - view_world_left);
screeny = (tiley - view_world_top);
//we know where it is, let's see what it should look like (the map we're on now affaects what we draw)
curtype = func_getTilemapTile(LEVEL1MAP, *ref_c, *ref_r);
//not on an empty tile
if(*ref_t != 0){
//if there is no tile here skip and set north to false, otherwise, run the following checks
if((*ref_r - 1) > -1){
//check the tile to the north
north = func_getTilemapTile(mapselect, *ref_c, *ref_r-1);
if(north == *ref_t){
//north matches, set it to true
north = true;
}
else
{
//no match, set false
north = false;
};
}
else
{
//outside of map, north is false
north = false;
};
//if these's no tiles south, skip this check and set it false as well
if((*ref_r + 1) < LEVEL_RSIZE){
//check the tile to the south
south = func_getTilemapTile(mapselect, *ref_c, *ref_r+1);
if(south == *ref_t){
//north matches, set it to true
south = true;
}
else
{
//no match, set false
south = false;
}
}
else
{
//outside of map, south is false
south = false;
};
//check now to the west, set west to false if outside of the map
if((*ref_c - 1) > -1){
//check the tile to the west
west = func_getTilemapTile(mapselect, *ref_c-1, *ref_r);
if(west == *ref_t){
//north matches, set it to true
west = true;
}
else
{
//no match, set false
west = false;
};
}
else
{
//outside of map, west is false
west = false;
};
//if these's no tiles south, skip this check and set it false as well
if((*ref_c + 1) < LEVEL_CSIZE){
//check the tile to the south
east = func_getTilemapTile(mapselect, *ref_c+1, *ref_r);
if(east == *ref_t){
//north matches, set it to true
east = true;
}
else
{
//no match, set false
east = false;
};
}
else
{
//outside of map, north is false
east = false;
};
//okay, time to draw the appropriate tile! Finally!
//run different tile drawing functions for differant types of tile
switch (*ref_t){
//road tiles it will pick the right road bitmap to draw
case 1:
func_drawRoadTile(screenx,screeny, 16, north, south, east, west);
break;
case 0:
gb.display.drawRect(screenx,screeny,16,16);
};
};
};
};
//reset current_c with 0 after drawing of collum is complete
current_c = 0;
};
current_r = 0;
};
//a function that simply changes the value of the camera by whatever you want, unless it's hit the extent of the world, at which case it pins to the edge of the map
int func_moveCameraX(int startx,int moveby, int viewport_w){
//calculate motion. this is always ADDED to the starting postion. Negative numbers for moving right
int targetx = startx + moveby;
int view_world_left = targetx - (viewport_w/2);
int view_world_right = targetx + (viewport_w/2);
int world_size_x = LEVEL_CSIZE * 16;
//check if we're trying to look off the map any
if((view_world_left < 0) || (view_world_right > (world_size_x))){
return startx;
}
else
{
return targetx;
};
};
int func_moveCameraY(int starty,int moveby, int viewport_h){
//calculate motion. this is always ADDED to the starting postion. Negative numbers for moving up
int targety = starty + moveby;
int view_world_top = targety - (viewport_h/2);
int view_world_bottom = targety + (viewport_h/2);
int world_size_y = LEVEL_RSIZE * 16;
//check if we're trying to look off the map any
//gb.display.println(view_world_bottom);
if((view_world_top < 0) || (view_world_bottom >= (world_size_y+1))){
return starty;
}
else
{
return targety;
};
};
char inttochar(int c){
//because everyone needs this, makes a charstring out of an integer
char b[1];
String str;
str=String(c);
str.toCharArray(b,1);
return b[1];
};
void setup(){
// initialize the Gamebuino object
gb.begin(F("Armored Steel"));
gb.popup(F("map test go!"), 30);
};
void loop(){
if(gb.update()){
static int tilesize = 16;
static int viewport_w=LCDWIDTH;
static int viewport_h=LCDHEIGHT;
//center the camera
static int camera_x = (LEVEL_CSIZE*16)/2;
static int camera_y = (LEVEL_RSIZE*16)/2;
//test tile rendering, and viewports
if(gb.buttons.repeat(BTN_UP,2)){
camera_y = func_moveCameraY(camera_y,-1,viewport_h);
};
if(gb.buttons.repeat(BTN_DOWN,2)){
camera_y = func_moveCameraY(camera_y,1,viewport_h);
};
if(gb.buttons.repeat(BTN_LEFT,2)){
camera_x = func_moveCameraX(camera_x,-1,viewport_w);
};
if(gb.buttons.repeat(BTN_RIGHT,2)){
camera_x = func_moveCameraX(camera_x,1,viewport_w);
};
//we have the camera code, let's render the tiles
func_draw_tailemap(LEVEL1MAP, camera_x, camera_y, viewport_w, viewport_h);
};
};
skipping non-visible tiles, and procedurally drawing the rest isn't really useful for the little test maps I've been using, but the game is being built around 20X20 maps, provided memory holds out....
Pictures!
here's the map finally working
- Showing a test with the grid being overlayed, as well as whether the game thought there should be a tile there.
- successful mapping accomplished.jpg (36.91 KiB) Viewed 6269 times
and testing of 'culling'
- Set the values to be excessive, so I could watch the tiles disappear on cue.
- not drawing what we can't see test.jpg (12.54 KiB) Viewed 6269 times
Where I'm at for today's batch of coding
- Here is it coming to a stop in the bottom right, you can see the 180'd road sprite there because of the roads to the north and west
- Where we're at.jpg (22.73 KiB) Viewed 6269 times
PS: gb.display.println is a nice function, took me forever to notice it was there (thanks hello world!) I was worried I'd have to brush up on ASCII. Also, it boggles the mind that these screenshots are bigger then the *game itself* O_O