A WIP Tank Game

Advice on general approaches or feasibility and discussions about game design

A WIP Tank Game

Postby DFX2KX » Fri May 23, 2014 9:20 pm

Well, I was just seeing if I could make something of my own creation boot in Deneth's GB emulator. It's been a lot of work, but now that I have something actually working, I decided to make a game out of it.

ripper121 made a nice image converter, that after I got passed it's quirks, has worked quite well!

Enough of me babbling on, I owe Rodot, Deneth, and Ripper some screenshots, for more or less making this possible.

IT's SOMETHING! XD.jpg
IT's SOMETHING! XD.jpg (12.76 KiB) Viewed 6389 times


Well, if anyone's wondering. Gamebuino doesn't like 15x15 sprites. It took me an hour to figure out why it looked this way.

Well, then... seems I converted this wrong....jpg
Well, then... seems I converted this wrong....jpg (10.57 KiB) Viewed 6389 times


This was caused by the converter not working. Try using another color then white, for the 'white', that's what worked for me finally.

Hey, we can turn!.jpg
Hey, we can turn!.jpg (11.23 KiB) Viewed 6389 times


It still doesn't like turning left. Working on that at the moment.

If anyone has a good idea on how to make tiles that move, let me know. I'm trying to get it so that the tank stays in the center (Which I've gotten working) and the world is what moves (Just to look unique).

Now to go take a break and jump for joy. This is the first Audrino, first C program, first game That was actually compiled, and the first non-PC game I've ever started working on (and had ANY success). All at once :D Once I clean up the code, (and make the level moving part), I'll put the code up for the curious.
DFX2KX
 
Posts: 250
Joined: Mon Apr 14, 2014 3:48 am

Re: A WIP Tank Game

Postby ripper121 » Sat May 24, 2014 1:30 am

You have to define the 3 colors to convert it right, so when you paint in black (R:255 G:254 B:254) you have to set in the converter also the same black color.
User avatar
ripper121
 
Posts: 224
Joined: Fri Apr 04, 2014 2:02 pm
Location: Germany

Re: A WIP Tank Game

Postby ripper121 » Sat May 24, 2014 1:54 am

The best is, you use Paint with Black(0,0,0), White(255,255,255), Grey(190,190,190)
and a size of 84x48 Pixel for Fullscree, 64x28 Pixel for Logo, 8x8 Sprite.
User avatar
ripper121
 
Posts: 224
Joined: Fri Apr 04, 2014 2:02 pm
Location: Germany

Re: A WIP Tank Game

Postby DFX2KX » Sat May 24, 2014 2:40 am

ripper121 wrote:The best is, you use Paint with Black(0,0,0), White(255,255,255), Grey(190,190,190)
and a size of 84x48 Pixel for Fullscree, 64x28 Pixel for Logo, 8x8 Sprite.


yeah, think that might have been part of the problem with it, was not having the right white/black/gray set with it. Now that I'm back from dinner, I'm going to redo this code for some sense, and see about getting tiles to work. I'm happy that I at least have it doing something!

Edit: I have gray working! (as much as that can be simulated on the emulator)

https://www.youtube.com/watch?v=XTlq5ulf0IQ&feature=youtu.be
DFX2KX
 
Posts: 250
Joined: Mon Apr 14, 2014 3:48 am

Re: A WIP Tank Game

Postby rodot » Sat May 24, 2014 10:05 am

Looks good!

The bitmaps width have to be a mutliple of 8, it'll be specified in the reference. Also my bitmap converter automatically enlarge the bitmap width for it to be a multiple of 8. I think you should add that in yours ripper121 ;)

You may want to take a look at crabator's source code for a moving world of tiles. Not sure if it's really readable though... what I did is that the world itself is not moving, it's the "camera" which follows the character. Also it's toroidal world (it warps around like in asteroids) so you feel like it's super large, but it's only 16x12 tiles wide.
User avatar
rodot
Site Admin
 
Posts: 1290
Joined: Mon Nov 19, 2012 11:54 pm
Location: France

Re: A WIP Tank Game

Postby DFX2KX » Sat May 24, 2014 6:28 pm

rodot wrote:Looks good!

The bitmaps width have to be a mutliple of 8, it'll be specified in the reference. Also my bitmap converter automatically enlarge the bitmap width for it to be a multiple of 8. I think you should add that in yours ripper121 ;)

You may want to take a look at crabator's source code for a moving world of tiles. Not sure if it's really readable though... what I did is that the world itself is not moving, it's the "camera" which follows the character. Also it's toroidal world (it warps around like in asteroids) so you feel like it's super large, but it's only 16x12 tiles wide.


Ah! okay! Yeah, wasn't sure what was going on in Crabator. had the play() function in there, and some functions for moving the X and Y's but not that many comments. Obviously the tutorials are still developing as we go. I wasn't sure how to approach moving the 'camera' part of things. but I'll try a few things and see what I get. I did figure out it might be possible to make some tiles procedurally change sprites, that has implications beyond this tank game.
DFX2KX
 
Posts: 250
Joined: Mon Apr 14, 2014 3:48 am

Re: A WIP Tank Game

Postby rodot » Sun May 25, 2014 12:00 pm

Yeah, crabator is not well commented and probably not the most elegant code you can find :/
But this game's purpose was to showcase Gamebuino, not to be a tutorial. For now I prefer to work on very simple examples to show each feature separately in the reference.
User avatar
rodot
Site Admin
 
Posts: 1290
Joined: Mon Nov 19, 2012 11:54 pm
Location: France

Re: A WIP Tank Game

Postby DFX2KX » Sun May 25, 2014 11:13 pm

rodot wrote:Yeah, crabator is not well commented and probably not the most elegant code you can find :/
But this game's purpose was to showcase Gamebuino, not to be a tutorial. For now I prefer to work on very simple examples to show each feature separately in the reference.


agreed. I've gotten some success with tiling things (and even moving them around on screen), but getting it to act reliably, or fetching the coords of one tile in particular are proving very difficult.

Tired two-dimensional matrices, tried the 1d matrix trick (like what you did with Bitmaps, which was clever), and still can't get it work quite right. That being said, I've learned more about C in the previous 4 days then I've learned in as many years. I think that's kinda the idea, no? XD
DFX2KX
 
Posts: 250
Joined: Mon Apr 14, 2014 3:48 am

Re: A WIP Tank Game

Postby rodot » Mon May 26, 2014 4:54 pm

DFX2KX wrote:I've learned more about C in the previous 4 days then I've learned in as many years


I feel that every time I make a project on my own instead of going to school ^_^
If you have any particular question don't hesitate to ask.
User avatar
rodot
Site Admin
 
Posts: 1290
Joined: Mon Nov 19, 2012 11:54 pm
Location: France

Re: A WIP Tank Game

Postby DFX2KX » Mon May 26, 2014 9:51 pm

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 = &current_c;
  int * const ref_r = &current_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
successful mapping accomplished.jpg
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'
not drawing what we can't see test.jpg
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
Where we're at.jpg
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
DFX2KX
 
Posts: 250
Joined: Mon Apr 14, 2014 3:48 am

Next

Return to Project Guidance & Game development

Who is online

Users browsing this forum: No registered users and 1 guest