Drawing bitmaps into arrays for displaying multiple bitmaps CORRECTLY

Status
Not open for further replies.

Duhjoker

Well-known member
Quick catch up...... I'm building a DIY multiple MCU color gaming console using teensy3.6 and an ILI9341 tft. The master library offeres a wide range of mcu to use with spate libraries Teensy3 and ESP32. The gaming library is pretty easy to use but building it has been a challenge but thanks to guys and your patience I'm almost done.

The last issue I'm having, we have talked about in other threads but no real solution has been found. So thought it might be better to start a thread based purely on the problem and maybe get more eyes. At least I hope. In order to make good games we need a backdrop or tilemap made from bitmaps to give our player sprites a world to move around in and interact with. Normally..... we would do this using a drawtile map function something like this......

Code:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
first we store the bitmaps in hex or binary form one color at a time

const byte rock_black[] PROGMEM = {16,16,
B00010101,B10110100,B00001101,B10001110,B00001101,B10001101,B00001101,B10000101,B00001001,B10000101,B00001001,B10000001,B00001001,B10000110,B00001011,B00000010,B00001001,B10000001,B00011001,B10000001,B00111011,B10000011,B00100011,B11000010,B01000001,B10000010,B01000001,B10000010,B00000001,B11000111,B10000001,B10100110};

const byte rock_beige[] PROGMEM = {16,16,
B00001000,B00001000,B00000010,B00000001,B00000010,B00000000,B10000010,B00000000,B10000100,B00000000,B00000100,B00000000,B00000100,B00011001,B00000100,B00100001,B00000100,B00100000,B00000100,B00010000,B10000100,B00100000,B10011100,B00000001,B00100000,B01000001,B00100000,B01000001,B01000000,B00000000,B01010010,B0011001};

const byte rock_brown[] PROGMEM = {16,16,
B11100010,B01000011,B11110000,B01110000,B11110000,B01110010,B01110000,B01111010,B01110010,B01111010,B11110010,B01111110,B11110010,B01100000,B11110010,B01011100,B11110010,B01011110,B11100010,B01011110,B01000000,B01011100,B01000000,B00111100,B10011110,B00111100,B10011110,B00111100,B10111110,B00111000,B00101100,B01000000};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

const byte sand_brown[] PROGMEM = {16,16,
B00010001,B00010001,B00000000,B00000000,B10001000,B10001000,B00000000,B00000000,B00010001,B00010001,B00000000,B00000000,B10001000,B10001000,B00000000,B00000000,B00010001,B00010001,B00000000,B00000000,B10001000,B10001000,B00000000,B00000000,B00010001,B00010001,B00000000,B00000000,B10001000,B10001000,B00000000,B00000000};

const byte sand_beige[] PROGMEM = {16,16,
B11101110,B11101110,B11111111,B11111111,B01110111,B01110111,B11111111,B11111111,B11101110,B11101110,B11111111,B11111111,B01110111,B01110111,B11111111,B11111111,B11101110,B11101110,B11111111,B11111111,B01110111,B01110111,B11111111,B11111111,B11101110,B11101110,B11111111,B11111111,B01110111,B01110111,B11111111,B11111111};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

const byte blank_tile1[] PROGMEM = {16,16,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000};

const byte blank_tile2[] PROGMEM = {16,16,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000};

const byte blank_tile3[] PROGMEM = {16,16,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000};


////////now we put them sprite sheets which gives each tile a number in ram 0 to what ever


const byte *spritesheet1[] = {blank_tile1,rock_black,};

const byte *spritesheet2[] = {blank_tile2, rock_brown, sand_brown,};

const byte *spritesheet3[] = {blank_tile3, rock_beige, sand_beige,};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


/////////next we take the integers from each sprite sheet and put them in an array where each integer is taken from the sprite sheet 


///black
const byte tilemap1[] PROGMEM = {20,15,
16,16,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1,1,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,1,1,1,1,0,0,0,0,0,0,1,1,1,1,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,1,1,1,1,0,0,0,0,0,0,1,1,1,1,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,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,0,
1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,
1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,
0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,0,
0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,0,
1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,};

///brown
const byte tilemap2[] PROGMEM = {20,15,
16,16,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,
1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
1,1,1,1,1,1,1,1,2,2,2,2,2,2,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,};

///beige
const byte tilemap3[] PROGMEM = {20,15,
16,16,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,
1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,
1,1,1,1,1,1,1,1,2,2,2,2,2,2,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,};



//////////and then we draw the tiles to the screen using these functions




//   tft.drawTilemap(x, y, tilemap1, spritesheet1, BLACK);
//    tft.drawTilemap(x, y, tilemap2, spritesheet2, BROWN);
//     tft.drawTilemap(x, y, tilemap3, spritesheet3, LIGHTBROWN);

To do all that we use these functions....

Code:
void        drawBitmap1(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);

	////////TILEMAP and COLLISION

	boolean     getBitmapPixel(const uint8_t* bitmap, uint16_t x, uint16_t y);

	void        drawTilemap(int x, int y, const uint8_t *tilemap, const uint8_t **spritesheet, uint16_t color);
	void        drawTilemap(int x, int y, const uint8_t *tilemap, const uint8_t **spritesheet, uint16_t dx, uint16_t dy, uint16_t dw, uint16_t dh, uint16_t color);

	void        drawBitmapTm(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *bitmap, uint16_t dx, uint16_t dy, uint16_t dw, uint16_t dh, uint16_t color);

	typedef struct {       //line 171 "Public Variables   - ADD by Summoner123
		int x;                    //X coordinate                 - ADD by Summoner123
		int y;                    //Y coordinate                 - ADD by Summoner123
		const byte *spritecol;    //Sprite of object             - ADD by Summoner123
	}object;
	object solid[60];         // Matriz were saved a Sprite, X and Y cordinates of all tiles on the screen - ADD by Summoner123

	byte numcolision = 0;     //count of solid objects indacat how many tiles drawed on the screen - ADD by Summoner123

	bool flagcollision = true;

	//////////////////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////////////////
	////---------------------------------collision--------------------------------------------////
	//////////////////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////////////////

	boolean collidePointRect(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t w, int16_t h);
	boolean collideRectRect(int16_t x1, int16_t y1, int16_t w1, int16_t h1, int16_t x2, int16_t y2, int16_t w2, int16_t h2);
	boolean collideBitmapBitmap(int16_t x1, int16_t y1, const uint8_t* b1, int16_t x2, int16_t y2, const uint8_t* b2);

and this....

Code:
/ Draw a 1-bit image (bitmap) at the specified (x,y) position from the
// provided bitmap buffer (must be PROGMEM memory) using the specified
// foreground color (unset bits are transparent).
void Grafx::drawBitmap1(int16_t x, int16_t y,
	const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) {

	int16_t i, j, byteWidth = (w + 7) / 8;
	uint16_t byte;

	for (j = 0; j<h; j++) {
		for (i = 0; i<w; i++) {
			if (i & 7) byte <<= 1;
			else      byte = pgm_read_byte(bitmap + j * byteWidth + i / 8);
			if (byte & 0x80) drawPixel(x + i, y + j, color);
		}
	}
}

//////////////////////////TILEMAP////////////////////////////////////////

boolean Grafx::getBitmapPixel(const uint8_t* bitmap, uint16_t x, uint16_t y) {
	return pgm_read_byte(bitmap + 2 + y * ((pgm_read_byte(bitmap) + 7) / 8) + (x >> 3)) & (B10000000 >> (x % 8));
}

void Grafx::drawTilemap(int x, int y, const uint8_t *tilemap, const uint8_t **spritesheet, uint16_t color) {
	drawTilemap(x, y, tilemap, spritesheet, 0, 0, Grafx_TFTWIDTH, Grafx_TFTHEIGHT, color);
}
void Grafx::drawTilemap(int x, int y, const uint8_t *tilemap, const uint8_t **spritesheet, uint16_t dx, uint16_t dy, uint16_t dw, uint16_t dh, uint16_t color) {
	uint16_t tilemap_width = pgm_read_byte(tilemap);
	uint16_t tilemap_height = pgm_read_byte(tilemap + 1);
	uint16_t tile_width = pgm_read_byte(tilemap + 2);
	uint16_t tile_height = pgm_read_byte(tilemap + 3);
	tilemap += 4; // now the first tiyleis at tilemap
	uint16_t ddw = dw + dx;
	uint16_t ddh = dh + dy;
	uint16_t maxDdx = (dw - x + tile_width - 1) / tile_width;
	uint16_t maxDdy = (dh - y + tile_height - 1) / tile_height;
	if (tilemap_width < maxDdx) {
		maxDdx = tilemap_width;
	}
	if (tilemap_height < maxDdy) {
		maxDdy = tilemap_height;
	}
	int16_t startDdx = (-x) / tile_width;
	int16_t startDdy = (-y) / tile_height;
	if (startDdx < 0) {
		startDdx = 0;
	}
	if (startDdy < 0) {
		startDdy = 0;
	}
	if (flagcollision)numcolision = 0;                                 //Line 735 - clear numcolision - ADD by Summoner123

	for (uint16_t ddy = startDdy; ddy < maxDdy; ddy++) {
		for (uint16_t ddx = startDdx; ddx < maxDdx; ddx++) {
			int16_t drawX = ddx*tile_width + x + dx;
			int16_t drawY = ddy*tile_height + y + dy;
			uint16_t tile = pgm_read_byte(tilemap + ddy*tilemap_width + ddx);
			if (drawX >= dx && drawY >= dy && drawX <= (ddw - tile_width) && drawY <= (ddh - tile_height)) {
				drawBitmap1(drawX, drawY, spritesheet[tile], tile_width, tile_height, color);

				if (flagcollision) {
					solid[numcolision].x = drawX;                     //Save X coordinate      - ADD by Summoner123
					solid[numcolision].y = drawY;                     //Save Y coordinate      - ADD by Summoner123
					solid[numcolision].spritecol = spritesheet[tile]; //Save Sprite of tile    - ADD by Summoner123
					numcolision++;                                    //Increment numcolision  - ADD by Summoner123
				}
			}
			else { // we need to draw a partial bitmap
				drawBitmapTm(drawX, drawY, tile_width, tile_height, spritesheet[tile], dx, dy, dw, dh, color);
			}
		}
	}
}

void Grafx::drawBitmapTm(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *bitmap, uint16_t dx, uint16_t dy, uint16_t dw, uint16_t dh, uint16_t color) {
	int16_t i, j, byteWidth = (w + 7) / 8;
	dw += dx;
	dh += dy;
	int16_t largest = 0;
	int16_t largesty = 0;
	for (j = 0; j < h; j++) {
		for (i = 0; i < w; i++) {
			if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (B10000000 >> (i % 8))) {
				int16_t drawX = x + i;
				int16_t drawY = y + j;

				if (drawX >= dx && drawX < dw && drawY >= dy && drawY < dh) {
					drawPixel(drawX, drawY, color);
				}
			}
		}
	}
}

boolean Grafx::collidePointRect(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t w, int16_t h) {
	if ((x1 >= x2) && (x1<x2 + w))
		if ((y1 >= y2) && (y1<y2 + h))
			return true;
	return false;
}

boolean Grafx::collideRectRect(int16_t x1, int16_t y1, int16_t w1, int16_t h1, int16_t x2, int16_t y2, int16_t w2, int16_t h2) {
	return !(x2 >= x1 + w1 ||
		x2 + w2 <= x1 ||
		y2 >= y1 + h1 ||
		y2 + h2 <= y1);
}

boolean Grafx::collideBitmapBitmap(int16_t x1, int16_t y1, const uint8_t* b1, int16_t x2, int16_t y2, const uint8_t* b2) {
	int16_t w1 = pgm_read_byte(b1);
	int16_t h1 = pgm_read_byte(b1 + 1);
	int16_t w2 = pgm_read_byte(b2);
	int16_t h2 = pgm_read_byte(b2 + 1);

	if (collideRectRect(x1, y1, w1, h1, x2, y2, w2, h2) == false) {
		return false;
	}

	int16_t xmin = (x1 >= x2) ? 0 : x2 - x1;
	int16_t ymin = (y1 >= y2) ? 0 : y2 - y1;
	int16_t xmax = (x1 + w1 >= x2 + w2) ? x2 + w2 - x1 : w1;
	int16_t ymax = (y1 + h1 >= y2 + h2) ? y2 + h2 - y1 : h1;
	for (uint8_t y = ymin; y < ymax; y++) {
		for (uint8_t x = xmin; x < xmax; x++) {
			if (getBitmapPixel(b1, x, y) && getBitmapPixel(b2, x1 + x - x2, y1 + y - y2)) {
				return true;
			}
		}
	}
	return false;
}

This code can be found in any of my repositories here... https://github.com/Duhjoker

There is also the way found here...... https://github.com/Rodot/Gamebuino/tree/master/examples/2.Intermediate/TileMapRAM

Code:
#include <SPI.h>

#include <Gamebuino.h>

Gamebuino gb;



//store the different sprites in PROGMEM to save RAM

const byte grass[] PROGMEM = {16, 16, 0x10,0x0,0x28,0x2,0x10,0x0,0x0,0x0,0x0,0x0,0x10,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x8,0x4,0x0,0x0,0x0,};

const byte rocks[] PROGMEM = {16, 16, 0x20,0x0,0x50,0x0,0x21,0xe1,0x6,0x18,0x8,0xc,0x8,0x4,0x3c,0x6,0x42,0x7,0x40,0x37,0x40,0x1f,0x23,0x9e,0x1c,0x7e,0x8,0x3c,0x8,0x78,0x7,0xe0,0x0,0x0,};

const byte road_straight[] PROGMEM = {16, 16, 0x1f,0xf8,0x1f,0xf8,0x5e,0x78,0x1e,0x78,0x1e,0x78,0x1e,0x78,0x1e,0x79,0x1e,0x78,0x1f,0xf8,0x1f,0xf8,0x1e,0x78,0x1e,0x78,0x9e,0x78,0x1e,0x78,0x1e,0x78,0x1e,0x78,};

const byte road_turn[] PROGMEM = {16, 16, 0x1f,0xf8,0x1f,0xf8,0x1f,0xfc,0x1f,0xff,0x1f,0xff,0xf,0xff,0xf,0xff,0x7,0xff,0x87,0xff,0x3,0xff,0x1,0xff,0x0,0x7f,0x2,0x1f,0x0,0x0,0x0,0x0,0x40,0x0,};



// array containing the different sprites

#define NUM_SPRITES 4

const byte* sprites[NUM_SPRITES] = {

  grass, rocks, road_straight, road_turn};



/*

The world is stored in bi-dimensional array of bytes.

This array is stored in RAM so it can be too large.

For large maps see the PROGMEM tile map examples.

Each tile of the world is stored in a byte. A byte is made of 8 bits.

The bits B000000XX define the sprite's ID (so there is 4 different sprites max)

The bits B0000XX00 define the sprite rotation (4 values of rotation)

You can notice that 4 bits are unused for each byte (these one: BXXXX0000).

This means that we could save even more memory by storing 2 tiles per byte,

but that would make accessing the the tiles coordinates more complicated.

It will be the subject of another example.

We could also store more than 4 different sprites.

*/

#define WORLD_W 16

#define WORLD_H 8

byte world[WORLD_W][WORLD_H];



byte getSpriteID(byte x, byte y){

  return world[x][y] & B00000011;

}

byte getRotation(byte x, byte y){

  return (world[x][y] >> 2)  & B00000011;

}

void setTile(byte x, byte y, byte spriteID, byte rotation){

  world[x][y] = (rotation << 2) + spriteID;

}



// cursor

int cursor_x, cursor_y;

int camera_x, camera_y;



void setup()

{

  gb.begin();

  initGame();

}



void loop(){

  if(gb.update()){

    //pause the game if C is pressed

    if(gb.buttons.pressed(BTN_C)){

      initGame();

    }

    

    updateCursor();

    

    drawWorld();

    drawCursor();



  }

}



void initGame(){

  gb.titleScreen(F("DYNAMIC TILE MAP DEMO"));

  gb.pickRandomSeed(); //pick a different random seed each time for games to be different

  initWorld();

  gb.popup(F("\25:change \26:rotate"),60);

}



void initWorld(){

  for(byte y = 0; y < WORLD_H; y++){

    for(byte x = 0; x < WORLD_W; x++){

      setTile(x, y, 0, random(0,4)); //fill with grass with random rotation

    }

  }

}



void drawWorld(){

  for(byte y = 0; y < WORLD_H; y++){

    for(byte x = 0; x < WORLD_W; x++){

      byte spriteID = getSpriteID(x,y);

      byte rotation = getRotation(x,y);

      //coordinates on the screen depending on the camera position

      int x_screen = x*16 - camera_x;

      int y_screen = y*16 - camera_y;

      if(x_screen < -16 || x_screen > LCDWIDTH || y_screen < -16 || y_screen > LCDHEIGHT){

        continue; // don't draw sprites which are out of the screen

      }

      gb.display.drawBitmap(x_screen, y_screen, sprites[spriteID], rotation, 0);

    }

  }

}



void updateCursor(){

  byte spriteID = getSpriteID(cursor_x,cursor_y);

  byte rotation = getRotation(cursor_x,cursor_y);

  if(gb.buttons.repeat(BTN_A, 4)){

    spriteID = (spriteID+1) % NUM_SPRITES;

    gb.sound.playOK();

  }

  if(gb.buttons.repeat(BTN_B, 4)){

    rotation = (rotation+1) % 4;

    gb.sound.playOK();

  }

  setTile(cursor_x, cursor_y, spriteID, rotation);

  

  if(gb.buttons.repeat(BTN_RIGHT, 4)){

    cursor_x = wrap(cursor_x+1, WORLD_W);

    gb.sound.playTick();

  }

  if(gb.buttons.repeat(BTN_LEFT, 4)){

    cursor_x = wrap(cursor_x-1, WORLD_W);

    gb.sound.playTick();

  }

  if(gb.buttons.repeat(BTN_DOWN, 4)){

    cursor_y = wrap(cursor_y+1, WORLD_H);

    gb.sound.playTick();

  }

  if(gb.buttons.repeat(BTN_UP, 4)){

    cursor_y = wrap(cursor_y-1, WORLD_H);

    gb.sound.playTick();

  }

  

  //target position of the camera for the cursor to be centered

  int camera_x_target = cursor_x*16 - LCDWIDTH/2 + 8;

  int camera_y_target = cursor_y*16 - LCDHEIGHT/2 + 8;

  //apply the target coordinate to the current coordinates with some smoothing

  camera_x = (camera_x*3 + camera_x_target)/4;

  camera_y = (camera_y*3 + camera_y_target)/4;

  

}



void drawCursor(){

  int x_screen = cursor_x*16 - camera_x;

  int y_screen = cursor_y*16 - camera_y;

  if(!(x_screen < -16 || x_screen > LCDWIDTH || y_screen < -16 || y_screen > LCDHEIGHT)){

    gb.display.drawRect(x_screen, y_screen, 16, 16);

  }

  

  gb.display.print(F("X"));

  gb.display.print(cursor_x);

  gb.display.print(F(" Y"));

  gb.display.print(cursor_y);

  

  byte spriteID = getSpriteID(cursor_x, cursor_y);

  gb.display.print(F(" I"));

  gb.display.print(spriteID);

  byte rotation = getRotation(cursor_x, cursor_y);

  gb.display.print(F(" R"));

  gb.display.print(rotation);

}

The problem is that no matter what I try I cannot fill up the screen doing so. It cuts off the last 5 vertical rows leaving them blank or default color. I and awesomw101 did a lot work trying to solve the problem which can be seen from here on..... Please look we did a lot of work

http://gamebuino.com/forum/viewtopic.php?f=8&t=3745&start=20

Using tile map or the other example or any variation of nets the same results. I've tried changing variables across the tile map functions all so far net the result of no image at all being displayed. The original example is for a nokia 5110 lcd which has a resolution of 84 by 48 pixels BUT...... I've printed and walked around in maps using the same code with maps of 16bit bitmaps and 300 by 300 bitmaps on the nokia. So its STRANGE that it cuts off the last 5 bars. its like there is some kind of offset that's not allowing me to place the tilemap there. But I can put bitmaps one by one in the last five bars no problem. its just when using an array. Both my 2.4inch and 2.8inch have this problem. I read in one of my libraries that certain manufacturers use offsets that take propietory drivers or some stuff but my screens come from pjrc along with my teensy and spans every library and mcu.


Ok like I said we have talked about this here and there with no real dedication. I'm hoping this will improve my chances of fixing the problem. If I have mistakenly doubled a thread feel free to cancel which ever one and tell me. I don't want to needlessly take up space.


EDIT::

If I mess with these.....

Code:
uint16_t tilemap_width = pgm_read_byte(tilemap);
	uint16_t tilemap_height = pgm_read_byte(tilemap + 1);    /////////// kills screen no matter what
	uint16_t tile_width = pgm_read_byte(tilemap + 2);        /////// shrinks the screen if 0 does nothung after 2 tried up to 8
	uint16_t tile_height = pgm_read_byte(tilemap + 3);   ///////// sends all pixels to top row 
	tilemap += 4; // now the first tiyleis at tilemap          ///////// just makes screen flash

I I play with these it cuts off the left side

Code:
if (startDdx < 0) {
		startDdx = 0;
	}
	if (startDdy < 0) {
		startDdy = 0;

I just noticed that when the tilemap is on the player navigates the blank area as it was part of the tilemap by moving slow in that and all areas compared to having the tilemap commented out where he moves fast.
 
Last edited:
Sounds like you need to do some debugging...

If it were me, I would first walk through some of the code and see if anything obvious. Plug in the values and see if it looks like you are overflowing something...

Else, start adding in some Debug prints. Example, if the code is in there to clip on the edges, add a debug message at each of the clips and see if you hit those.
Maybe add debug print to start of function and print out details, like x, y, w, h, any offsets in effect, any clip rectangles in effect. Are the values printed there make sense?
...
 
Still learning here but do you mean adding returns with serial print messages when you say debug print?

Got up today and decided to take one of the simple games from the original gamebuino library and port it to my library. No problem there the library is meant for this. All I really had to do was add color to the functions being called. Theres a settins at the top for brick space, size, count of rows and count of columns. You can see thaem in this short sketch......

Code:
#include <SPI.h>
#include <Grafx.h>
#include <Bounce.h>

const int buttonLeft = 35; //left button
Bounce ButtonLeft = Bounce(buttonLeft, 10);  // 10 ms debounce
const int buttonRight = 17; //right button
Bounce ButtonRight = Bounce(buttonRight, 10);  // 10 ms debounce
const int buttonA = 21; // A button right
Bounce ButtonA = Bounce(buttonA, 10);  // 10 ms debounce

const byte logo[] PROGMEM =
{
 64,28,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00001111, B11100000, B00000000, B00000000, B01100000, B00000000, B00000001, B00000000,
  B00001111, B11100000, B00000000, B00000000, B01100000, B00000000, B00000011, B00000000,
  B00001100, B00100011, B01001110, B00011100, B01100110, B01110001, B10111111, B11000000,
  B00001111, B11100011, B10111011, B00010110, B01101100, B11111101, B10111011, B11000000,
  B00001111, B11100011, B00111110, B00001110, B01111000, B11011101, B10111011, B00000000,
  B00001100, B00100011, B00111000, B00110110, B01111100, B11011101, B10111011, B00000000,
  B00001111, B11100011, B00011110, B00111110, B01101100, B01111001, B11111011, B11000000,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000000, B11111100, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000001, B11111110, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000001, B11111110, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000100, B11111110, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000001, B11111100, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00010000, B00000000, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00000000, B00000000, B00111111, B11111111, B11111111, B11110000, B00000000, B00000000,
  B00000000, B00000000, B11111111, B11111111, B11111111, B11111110, B00000000, B00000000,
  B00000000, B00000001, B11111111, B11111111, B11111111, B11111110, B00000000, B00000000,
  B00000000, B00000001, B11111111, B11111111, B11111111, B11111110, B00000000, B00000000,
};


boolean paused = false;
#define BrickW 16
#define BrickH 10
#define BrickSpaceX 5
#define BrickSpaceY 5
#define BrickColsMax 16
#define BrickRows (Grafx_TFTWIDTH/(BrickSpaceX+BrickW))-1
int BrickCols = 6;

static unsigned char PROGMEM explode0[] = {
  8, 8,
  B00010000,
  B01000001,
  B00000100,
  B10010000,
  B00000100,
  B01000001,
  B00001000,
  B10100100,
  };

static unsigned char PROGMEM explode1[] = {
  8, 8,
  B00000000,
  B01000001,
  B00000100,
  B00010000,
  B00000100,
  B01000000,
  B00001000,
  B00000000,
};

static unsigned char PROGMEM explode2[] = {
  8, 8,
  B00000000,
  B00000000,
  B00000000,
  B00010000,
  B00000100,
  B01000000,
  B00001000,
  B00000000,
};


typedef struct
{
  int x;
  int y;
  int vx;
  int vy;
  byte w;
  byte h;
  int score;
}  PlayerStruct;
PlayerStruct Player = (PlayerStruct) {
   0, 0, 0, 0, 0, 0, 0
};

typedef struct
{
  int x;
  int y;
  int vx;
  int vy;
  int v;
  byte w;
  byte h;
  byte size;
  boolean exist;
}  BallStruct;
BallStruct Ball = (BallStruct) {
  0, 0, 0, 0, 0, 0, 0, true
};

typedef struct
{
  byte x;
  byte y;
  byte w;
  byte h;
  boolean exist;
}  BrickStruct;

/*
Teensy3.x and Arduino's
You are using 4 wire SPI here, so:
 MOSI:  11//Teensy3.x/Arduino UNO (for MEGA/DUE refere to arduino site)
 MISO:  12//Teensy3.x/Arduino UNO (for MEGA/DUE refere to arduino site)
 SCK:   13//Teensy3.x/Arduino UNO (for MEGA/DUE refere to arduino site)
ESP8266-----------------------------------
Use:
#define __CS  16  //(D0)
#define __DC  5   //(D1)
#define __RST 4   //(D2)

 SCLK:D5
 MOSI:D7
 */
#define __CS1 	10
#define __DC 	9
/*
Teensy 3.x can use: 2,6,10,15,20,21,22,23
Arduino's 8 bit: any
DUE: check arduino site
If you do not use reset, tie it to +3V3
*/

uint8_t errorCode = 0;

Grafx tft = Grafx(__CS1, __DC);

void setup() {
	Serial.begin(38400);
	long unsigned debug_start = millis();
	while (!Serial && ((millis() - debug_start) <= 5000));
	Serial.println("serial ok, testing lib...");
  tft.begin();

  pinMode(buttonLeft, INPUT_PULLUP);
   pinMode(buttonRight, INPUT_PULLUP);
    pinMode(buttonA, INPUT_PULLUP);

  //the following it's mainly for Teensy
  //it will help you to understand if you have choosed the
  //wrong combination of pins!
  errorCode = tft.getErrorCode();
  if (errorCode != 0) {
	  Serial.print("Init error! ");
	  if (bitRead(errorCode, 0)) Serial.print("MOSI or SCLK pin mismach!\n");
	  if (bitRead(errorCode, 1)) Serial.print("CS or DC pin mismach!\n");
  }
  else {
	  Serial.println("Inited");
  }
}

BrickStruct Brick[BrickRows][BrickColsMax];
int BrickCount = 0;
int DestBrickCount = 0;
boolean GameReset = true;
boolean GameWin = false;
boolean GameFirstRun = true;
byte Level = 1;

void loop() {
  //Game Init/NextLevel

   if (ButtonLeft.update());
    if (ButtonRight.update());
     if (ButtonA.update());

   ButtonLeft.rebounce(10);
   ButtonRight.rebounce(10);
  
  if (GameReset) {
    if (GameWin) {
     tft.clearScreen();
      tft.setTextScale(1);
       Level++;
        if (BrickCols < BrickColsMax) {
        BrickCols++;
        tft.print("Next level!\n");
        tft.print("Level:\n");
        tft.print(Level);
        tft.print("\nScore:\n");
        tft.print(Player.score);
        }
       else
       {
        tft.print("You win the game!\n");
        tft.print("Level:\n");
        tft.print(Level);
        tft.print("\nScore:\n");
        tft.print(Player.score);
      }
    tft.update();
//      gb.sound.playOK();
     delay(5000);
     }
     else if (!GameFirstRun)
     {
      tft.clearScreen();
      tft.setTextScale(1);
      tft.print("Game Over!\n");
      tft.print("Level:\n");
      tft.print(Level);
      tft.print("\nScore:\n");
      tft.print(Player.score);
//* * * gb.sound.playCancel();
      tft.updateAll();
      BrickCols = 4;
      Player.score = 0;
      Level = 0;
      DestBrickCount = 0;
      delay( 5000);
      }

   //Brick Init
    BrickCount = 0;
    for (byte row = 0; row < BrickRows; row++)
    for (byte col = 0; col < BrickCols; col++) {
      Brick[row][col] = (BrickStruct) {
        (Grafx_TFTWIDTH / (BrickSpaceX + BrickW)) / 4 + row * (BrickW + BrickSpaceX), 5 + BrickSpaceY + col * (BrickH + BrickSpaceY), BrickW, BrickH, true
        };
        BrickCount++;
        }

    GameReset = false;
    GameWin = false;
    GameFirstRun = false;

    //Player Init
    Player.w = 10;
    Player.h = 2;
    Player.x = (Grafx_TFTWIDTH - Player.w) / 2;
    Player.vx = 2;
    Player.y = Grafx_TFTHEIGHT - Player.h;
    Player.vy = 0;



   //Ball Init
   Ball.size = 5;
   Ball.x = (Grafx_TFTWIDTH - Player.w) / 2;
   Ball.y = Grafx_TFTHEIGHT - Player.h - Ball.size;
   Ball.v = 1;
   Ball.vx = -Ball.v;
   Ball.vy = -Ball.v;
  //Random move the ball left or right* * 
   if (random(0, 2) == 1) {
    Ball.vx = -Ball.vx;
    }
  }

   //Game Run
  if (tft.updateAll()) {

    //pause the game if C is pressed
    if (ButtonA.fallingEdge()) {
      paused = !paused; //toggle paused state
      }
      if (!paused) {

      //move the player
      if (ButtonLeft.fallingEdge()) {
      Player.x = max(0, Player.x - Player.vx );
      }
      if (ButtonRight.fallingEdge()) {
      Player.x = min(Grafx_TFTWIDTH - Player.w, Player.x + Player.vx );
      }
      //move the ball
      Ball.x = Ball.x + Ball.vx;
      Ball.y = Ball.y + Ball.vy;
      //collision with the top
      if (Ball.x < 0) {
      Ball.x = 0;
      Ball.vx = -Ball.vx;
//* * * * gb.sound.playTick();
      }
       //collision with the left
      if (Ball.y < 0) {
          Ball.y = 0;
      Ball.vy = -Ball.vy;
//* * * * gb.sound.playTick();
       }
       //collision with the right
       if ((Ball.x + Ball.size) > Grafx_TFTWIDTH) {
       Ball.x = Grafx_TFTWIDTH - Ball.size;
       Ball.vx = -Ball.vx;
//* * * * gb.sound.playTick();
       }
       //collision with the bottom
       if ((Ball.y + Ball.size) > Grafx_TFTHEIGHT) {
       Ball.y = Grafx_TFTHEIGHT - Ball.size;
       Ball.vy = -Ball.vy;
       GameReset = true;
//* * * * gb.sound.playTick();* * * * 
      }
      //collision with the player
      if (tft.collideRectRect(Ball.x, Ball.y, Ball.size, Ball.size, Player.x, Player.y, Player.w, Player.h)) {
        int BallOnPlayer = ((Ball.x + ( Ball.size / 2 )) - Player.x) + 1;
      if (BallOnPlayer > (Player.w / 2))
      Ball.vx = Ball.v;
       else
      Ball.vx = -Ball.v;
      Ball.vy = -Ball.vy;
//* * * * gb.sound.playTick();
      }

     }

     //draw the ball
    tft.fillRect(Ball.x, Ball.y, Ball.size, Ball.size, WHITE);
    //draw the player
    tft.fillRect(Player.x, Player.y, Player.w, Player.h, WHITE);
    //Draw Bricks
    for (byte row = 0; row < BrickRows; row++)
      for (byte col = 0; col < BrickCols; col++) {
        //draw the bricks if they do exist
      if (Brick[row][col].exist)
       tft.fillRect(Brick[row][col].x, Brick[row][col].y, Brick[row][col].w, Brick[row][col].h, GREEN);
     }
    //collision with the bricks
    for (byte row = 0; row < BrickRows; row++)
      for (byte col = 0; col < BrickCols; col++) {
        //collision with the bricks
        if (tft.collideRectRect(Ball.x, Ball.y, Ball.size, Ball.size, Brick[row][col].x, Brick[row][col].y, Brick[row][col].w, Brick[row][col].h) && Brick[row][col].exist) {
         //Incr Score
         Player.score++;
         //Incr Destroyed Bricks
         DestBrickCount++;
         //Hide the Brick
           Brick[row][col].exist = false;
           Ball.vy = -Ball.vy;
         //Explosion Animation
           byte AnimationDelay=40;
//* * * * * gb.sound.playCancel();
          tft.drawBitmapGB(Ball.x - Ball.size, Ball.y - Ball.size, explode2, RED);
          tft.update();
          delay(AnimationDelay);
          tft.drawBitmapGB(Ball.x - Ball.size, Ball.y - Ball.size, explode1, GREEN);
          tft.update();
          delay(AnimationDelay);
          tft.drawBitmapGB(Ball.x - Ball.size, Ball.y - Ball.size, explode0, BLUE);
          tft.update();
          delay(AnimationDelay);
          tft.drawBitmapGB(Ball.x - Ball.size, Ball.y - Ball.size, explode1, YELLOW);
          tft.update();
          delay(AnimationDelay);
          tft.drawBitmapGB(Ball.x - Ball.size, Ball.y - Ball.size, explode2, ORANGE);
          tft.update();
          delay(AnimationDelay);
//          gb.sound.playTick();
          }
       }
     //No more Bricks exist
     if (DestBrickCount >= BrickCount) {
     GameWin = true;
     GameReset = true;
     DestBrickCount = 0;
     }
  }
  }

as you can see I gave it 16 columns but it only prints out the 2/3 from the left or still leaves theright side blank. The ball also trons and the player paddle never shows up. But check out the way it does the columns......

Code:
 //Draw Bricks
    for (byte row = 0; row < BrickRows; row++)
      for (byte col = 0; col < BrickCols; col++) {
        //draw the bricks if they do exist
      if (Brick[row][col].exist)
       tft.fillRect(Brick[row][col].x, Brick[row][col].y, Brick[row][col].w, Brick[row][col].h, GREEN);
     }
even this function fails to display the right side

what I want is to add a border and make the rows with different colors and brick layouts. Which shouldn't be hard after fixing the right side of the screen thing.

It also goes to black screen inbetween the ball moving like a tron cycle in an angled rectangle. Heres a pic




The sketch above can easily be pasted to your own teensy set up. Maybe some one could compile it and see what you get. Please?
 
Last edited:
Does not compile... Needs Grafx.h...

Which then assumes that I need to install your library... And then understand your library...
Now if your example was directly using my ili9341_t3n library, and showing the issues, I would dig in as I spent a lot of time trying to verify all of the clipping code.

And again I would start simple: That is have simple app that loops doing your.
Code:
//Draw Bricks
    for (byte row = 0; row < BrickRows; row++)
      for (byte col = 0; col < BrickCols; col++) {
        //draw the bricks if they do exist
      if (Brick[row][col].exist)
       tft.fillRect(Brick[row][col].x, Brick[row][col].y, Brick[row][col].w, Brick[row][col].h, GREEN);
     }
With the test case I would do something like:
Serial.printf("Rows: %d, Cols: %d\n", BrickRows, BrickCols);

Does it print out that right number of rows and cols as you expect?

Then I would edit your library and for example in fillrect
Code:
void GrafxT3::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
        Serial.printf("DR: (%d %d %d %d, %d) OS:(%d %d) CX(%d %d) CY(%d %d)\n", x, y, w, h, color, _originx, _originy, 
                displayclipx1, displayclipx2, displayclipy1, displayclipy2);
	x+=_originx;
	y+=_originy;

	// Rectangular clipping (drawChar w/big text requires this)
	if((x >= _displayclipx2) || (y >= _displayclipy2)) return;
	if (((x+w) <= _displayclipx1) || ((y+h) <= _displayclipy1)) return;
	if(x < _displayclipx1) {	w -= (_displayclipx1-x); x = _displayclipx1; 	}
	if(y < _displayclipy1) {	h -= (_displayclipy1 - y); y = _displayclipy1; 	}
	if((x + w - 1) >= _displayclipx2)  w = _displayclipx2  - x;
	if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
...

I would then check to see if it looked correct... Note again typed on fly so... But things I would look at include:
The clip rectangle... For example would not surprise me if maybe the CX (displayclipx2) was 240 and should be 320... Did not get set properly in rotation... Or maybe there is some place that just checks for hard coded constants like: GrafxT3_TFTWIDTH
Which is defined as 240 and does not take screen orientation into account...

Good luck

Again things that catch my eye include:
#define BrickRows (Grafx_TFTWIDTH/(BrickSpaceX+BrickW))-11

As again your Grafx_TFTWidth is defined as 240

Edit 2: Again not sure where your idea of 16 comes from?
The current value for BrickRows (240/21) = 11

Even if you were using the right value of 320: 320/21 = 15
 
Last edited:
And don't forget to look at things like:
Code:
    //Brick Init
    BrickCount = 0;
    for (byte row = 0; row < BrickRows; row++)
      for (byte col = 0; col < BrickCols; col++) {
        Brick[row][col] = (BrickStruct) {
          ((Grafx_TFTWIDTH / (BrickSpaceX + BrickW)) / 4 + row * (BrickW + BrickSpaceX), 5 + BrickSpaceY + col * (BrickH + BrickSpaceY), BrickW, BrickH, true
        };
        BrickCount++;
      }
For example what is [0][0] set to?
Is it where you expect it to be?

My gut tells me you need to make a pass through and replace almost everything that uses constants like Grafx_TFTWIDTH and Grafx_TFHEIGHT or the like with method calls to get the current width and height... Probably tft.width() and tft.height()
 
Sorry about that. The sketch can be used with t3n but I had to add a special bitmap function to display the bricks...........

Code:
void ILI9341_t3n::drawBitmapGB(int16_t x, int16_t y, const uint8_t *bitmap, uint16_t color) {
   int16_t w = pgm_read_byte(bitmap);
   int16_t h = pgm_read_byte(bitmap + 1);
   bitmap = bitmap + 2; //add an offset to the pointer to start after the width and height
   drawBitmap1(x,y,bitmap,w,h,color);
}

For drawBitmap1() just choose the appropriate bitmap function to use the included arguments or variables. All the others functions were just updated versions found in gfx that used color so drawFastVLine() could be any color.

haven't really had a chance to look at the post yet but the fix above should work. If not ill re-write it for t3n. It just takes a minute.
 
At this point does not matter. You simply need to do some simple debugging... There are strong probably hints above.

1) You don't have 16 brick rows! Why the math: #define BrickRows (Grafx_TFTWIDTH/(BrickSpaceX+BrickW))-1
Where Grafx_TFTWDITH is 240
Where BRICKW is 16 and BrickspaceX is 5... I believe this comes out to 10... Which surprise is the number of bricks you are showing ;)

2) in 1) you should not be using Grafx_TFTWidth, this assumes default rotation (portrait). You should be using the current display width.
probably tft.width(). Even if you got the correct width of 320... 320/21 - 1 = 14

3) Everywhere in your code that referes to Grafx_TFTWidth is likely now what you want, likewise for everywhere you use Grafx_TFTHeight is also wrong... Need to use tft.height();

But again all of this is simple debugging stuff. that is if you think it should print out N blocks and it only outputs M blocks, print out the variable that controls the loop. Is it what you expected it to be? If not go find where it was defined.

If the bricks are not displaying in the place you expect. Go to where they are defined and print out the values... Example in your init code you have:
Code:
    //Brick Init
    BrickCount = 0;
    for (byte row = 0; row < BrickRows; row++)
      for (byte col = 0; col < BrickCols; col++) {
        Brick[row][col] = (BrickStruct) {
          (ILI9341_TFTWIDTH / (BrickSpaceX + BrickW)) / 4 + row * (BrickW + BrickSpaceX), 5 + BrickSpaceY + col * (BrickH + BrickSpaceY), BrickW, BrickH, true
        };
        BrickCount++;
      }
Simply add print statements in here to print out the each bricks X, Y position. Maybe in inner loop print out each of the bricks after you set it, maybe on the end of the inner loop do a println() to break it up into lines. This again would show how many rows and colls...

You will also figure out that the positions don't look right, again probably because of 320 vs 240, but maybe other stuff as well...

Again no use in anyone else takes much of look until you cleanup the obvious stuff here.
 
Ok well I have it but it has an error concerning the update in the t3n files and it needed the collision functions as well as the extra bitmap function. I'll post it on GitHub later.

I haven't started any real testing yet but just by changing the width and height to the appropriate integers as mentioned above in my settings file and gained an extra brick and a half on the screen so I gained 48 vertical pixels right off the bat.



also I have the brick and columns settings for the game here at the top.......

Code:
boolean paused = false;
#define BrickW 16      /////brick width in pixels
#define BrickH 10      /////brick height
#define BrickSpaceX 5    //////distance between bricks X
#define BrickSpaceY 5    ////// distance between bricks Y
#define BrickColsMax 16      ////number of collums vertical
#define BrickRows (Grafx_TFTWIDTH/(BrickSpaceX+BrickW))-1
int BrickCols = 6;    ////number of rows horizontal

in the pic you really cant see it but the first couple rows on the left are squeezed together

ok back to reading the earlier posts
 
ok when I do the serial print with these settings at the top.......
Code:
boolean paused = false;
#define BrickW 16
#define BrickH 10
#define BrickSpaceX 5
#define BrickSpaceY 5
#define BrickColsMax 15
#define BrickRows (Grafx_TFTWIDTH/(BrickSpaceX+BrickW))-0 //// deletes how ever many rows? brick cols max at 15 with set to - draws one less horizontal row
int BrickCols = 5;

prints out 15 rows 4 columns on serial monitor but not on screen. the screen gets 11 and a 1/2 rows 5 columns

Ok when I change the fillrect to what you wrote I get a blank screen with a trail of pixels here and there for the balls movement
 
Last edited:
I got unzipped a fresh copy of the original Gamebuino library with the working tilemap function. Where it came from. So heres original bitmap functions for the header......

Code:
        void drawBitmap(int8_t x, int8_t y, const uint8_t *bitmap);   /////  basic library library monotone bitmap
	void drawBitmap(int8_t x, int8_t y, int8_t w, int8_t h , const uint8_t *bitmap);   /////////for grey scale and frame rate
	void drawBitmap(int8_t x, int8_t y, const uint8_t *bitmap, uint8_t rotation, uint8_t flip);    ////////////rotation
	void drawBitmap(int8_t x, int8_t y, int8_t w, int8_t h, const uint8_t *bitmap, uint8_t dx, uint8_t dy, uint8_t dw, uint8_t dh);///////tilemap
	boolean getBitmapPixel(const uint8_t* bitmap, uint8_t x, uint8_t y);
	
	void drawTilemap(int x, int y, const uint8_t *tilemap, const uint8_t **spritesheet);
	void drawTilemap(int x, int y, const uint8_t *tilemap, const uint8_t **spritesheet,uint8_t dx,uint8_t dy,uint8_t dw,uint8_t dh);

nothing unusual except the missing color functions but lets look at their cpp counterparts. You will notice in the second bitmap function that it first it enables grey scale which uses the buffer and the frame rate to create grey from monotone bitmaps when they are added together with in a function. but it also has some of the coordinates system for drawing the tilemap.

Code:
///////////not this
void Display::drawBitmap(int8_t x, int8_t y, const uint8_t *bitmap) {
   int8_t w = pgm_read_byte(bitmap);
   int8_t h = pgm_read_byte(bitmap + 1);
   bitmap = bitmap + 2; //add an offset to the pointer to start after the width and height
   drawBitmap(x,y,w,h,bitmap);
}

////////this function
void Display::drawBitmap(int8_t x, int8_t y, int8_t w, int8_t h , const uint8_t *bitmap) {
#if (ENABLE_BITMAPS > 0)
/*   original code
    int8_t i, j, byteWidth = (w + 7) / 8;
    for (j = 0; j < h; j++) {
        for (i = 0; i < w; i++) {
            if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (B10000000 >> (i % 8))) {
                drawPixel(x + i, y + j);
            }
        }
    }
  */
  uint8_t * buffer = getBuffer();
  const uint8_t col = color;
  const uint8_t bw = (w+7) / 8;
  
  // clip
  if (x >= LCDWIDTH)
    return;
  if (x + w <= 0)
    return;
  if (y >= LCDHEIGHT)
    return;
  if (y + h <= 0)
    return;
  if (y < 0)
    h += y, bitmap -= bw * y, y = 0;
  if (y + h > LCDHEIGHT)
    h = LCDHEIGHT - y;  
  uint8_t x1 = max(0, x);
  uint8_t x2 = min(LCDWIDTH, x + w);
  
#ifdef ENABLE_GRAYSCALE
   uint8_t g = y ^ frameCount;
#endif  

  // draw
  uint8_t first_bitmap_mask = 0x80 >> ((x1 - x) & 7);
  const uint8_t * bitmap_line = bitmap + (x1 - x) / 8;
  uint8_t screen_mask = 0x01 << (y % 8);
  uint8_t * screen_row = buffer + (y / 8) * LCDWIDTH + x1;  
  for (uint8_t dy=0; dy<h; dy++, bitmap_line+=bw)     ////////////this
  {
    const uint8_t * bitmap_ptr = bitmap_line;    
    uint8_t bitmap_mask = first_bitmap_mask;    
    uint8_t pixels = pgm_read_byte(bitmap_ptr);
    uint8_t * dst = screen_row;
    
    if (col == BLACK)
      for (uint8_t sx=x1; sx<x2; sx++, dst++)
      {
        if (pixels & bitmap_mask)
          *dst |= screen_mask;
        bitmap_mask >>= 1;
        if (!bitmap_mask)
        {
          bitmap_mask = 0x80;
          pixels = pgm_read_byte(++bitmap_ptr);
        }
      }
    else if (col == WHITE)
    {
      uint8_t inv_screen_mask = ~screen_mask;
      for (uint8_t sx=x1; sx<x2; sx++, dst++)
      {
        if (pixels & bitmap_mask)
          *dst &= inv_screen_mask;
        bitmap_mask >>= 1;
        if (!bitmap_mask)
        {
          bitmap_mask = 0x80;
          pixels = pgm_read_byte(++bitmap_ptr);
        }
      }
    }
#ifdef ENABLE_GRAYSCALE
    else if (col == GRAY)
    {
      uint8_t inv_screen_mask = ~screen_mask;
      for (uint8_t sx=x1; sx<x2; sx++, dst++)
      {
        if (pixels & bitmap_mask)
        {
         if ((sx^g) & 1)
            *dst |= screen_mask;
          else
           *dst &= inv_screen_mask;
        }
        bitmap_mask >>= 1;
        if (!bitmap_mask)
        {
          bitmap_mask = 0x80;
          pixels = pgm_read_byte(++bitmap_ptr);
        }
      }
       g ^= 1;
    }
#endif
   else // invert
      for (uint8_t sx=x1; sx<x2; sx++, dst++)
      {
        if (pixels & bitmap_mask)
          *dst ^= screen_mask;
        bitmap_mask >>= 1;
        if (!bitmap_mask)
        {
          bitmap_mask = 0x80;
          pixels = pgm_read_byte(++bitmap_ptr);
        }
      }
    
    screen_mask <<= 1;
    if (!screen_mask)
    {
      screen_mask = 1;
      screen_row += LCDWIDTH;
    }
  }
#else
   drawRect(x, y, w, h);
#endif
}

void Display::drawBitmap(int8_t x, int8_t y, int8_t w,int8_t h, const uint8_t *bitmap, uint8_t dx, uint8_t dy, uint8_t dw, uint8_t dh) {
    int8_t i, j, byteWidth = (w + 7) / 8;
    dw += dx;
    dh += dy;
    int8_t largest = 0;
    int8_t largesty = 0;
    for (j = 0; j < h; j++) {
        for (i = 0; i < w; i++) {
            if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (B10000000 >> (i % 8))) {
                int8_t drawX = x + i;
                int8_t drawY = y + j;
                
                if(drawX >= dx && drawX < dw && drawY >= dy && drawY < dh){
                    drawPixel(drawX, drawY);
                }
            }
        }
    }
}

boolean Display::getBitmapPixel(const uint8_t* bitmap, uint8_t x, uint8_t y){
  return pgm_read_byte(bitmap+2 + y * ((pgm_read_byte(bitmap)+7)/8) + (x >> 3)) & (B10000000 >> (x % 8));
}

void Display::drawBitmap(int8_t x, int8_t y, const uint8_t *bitmap,
        uint8_t rotation, uint8_t flip) {
    if((rotation == NOROT) && (flip == NOFLIP)){
        drawBitmap(x,y,bitmap); //use the faster algorithm
        return;
    }
    uint8_t w = pgm_read_byte(bitmap);
    uint8_t h = pgm_read_byte(bitmap + 1);
    bitmap = bitmap + 2; //add an offset to the pointer to start after the width and height
#if (ENABLE_BITMAPS > 0)
    int8_t i, j, //coordinates in the raw bitmap
            k, l, //coordinates in the rotated/flipped bitmap
            byteNum, bitNum, byteWidth = (w + 7) >> 3;

    rotation %= 4;

    for (i = 0; i < w; i++) {
        byteNum = i / 8;
        bitNum = i % 8;
        for (j = 0; j < h; j++) {
            if (pgm_read_byte(bitmap + j * byteWidth + byteNum) & (B10000000 >> bitNum)) {
                switch (rotation) {
                    case NOROT: //no rotation
                        k = i;
                        l = j;
                        break;
                    case ROTCCW: //90° counter-clockwise
                        k = j;
                        l = w - i - 1;
                        break;
                    case ROT180: //180°
                        k = w - i - 1;
                        l = h - j - 1;
                        break;
                    case ROTCW: //90° clockwise
                        k = h - j - 1;
                        l = i;
                        break;
                }
                if (flip) {
                    flip %= 4;
                    if (flip & B00000001) { //horizontal flip
                        k = w - k - 1;
                    }
                    if (flip & B00000010) { //vertical flip
                        l = h - l;
                    }
                }
                k += x; //place the bitmap on the screen
                l += y;
                drawPixel(k, l);
            }
        }
    }
#else
    drawRect(x, y, w, h);
#endif
}

ok so now lets look at the original tilemap function in .cpp

Code:
void Display::drawTilemap(int x, int y, const uint8_t *tilemap, const uint8_t **spritesheet,uint8_t dx,uint8_t dy,uint8_t dw,uint8_t dh){
    uint8_t tilemap_width = pgm_read_byte(tilemap);
    uint8_t tilemap_height = pgm_read_byte(tilemap + 1);
    uint8_t tile_width = pgm_read_byte(tilemap + 2);
    uint8_t tile_height = pgm_read_byte(tilemap + 3);
    tilemap += 4; // now the first tiyleis at tilemap
    uint8_t ddw = dw + dx;
    uint8_t ddh = dh + dy;
    uint8_t maxDdx = (dw - x + tile_width - 1) / tile_width;
    uint8_t maxDdy = (dh - y + tile_height - 1) / tile_height;
    if(tilemap_width < maxDdx){
        maxDdx = tilemap_width;
    }
    if(tilemap_height < maxDdy){
        maxDdy = tilemap_height;
    }
    int8_t startDdx = (-x) / tile_width;
    int8_t startDdy = (-y) / tile_height;
    if(startDdx < 0){
        startDdx = 0;
    }
    if(startDdy < 0){
        startDdy = 0;
    }
	if(flagcollision)numcolision = 0;                                 //Line 735 - clear numcolision - ADD by Summoner123

    for(uint8_t ddy = startDdy;ddy < maxDdy;ddy++){
        for(uint8_t ddx = startDdx;ddx < maxDdx;ddx++){
            int8_t drawX = ddx*tile_width + x + dx;
            int8_t drawY = ddy*tile_height + y + dy;
            uint8_t tile = pgm_read_byte(tilemap + ddy*tilemap_width + ddx);
            if(drawX >= dx && drawY >= dy && drawX <= (ddw-tile_width) && drawY <= (ddh-tile_height)){
                drawBitmap(drawX,drawY,tile_width,tile_height,spritesheet[tile]);

				if(flagcollision){
			solid[numcolision].x = drawX;                     //Save X coordinate      - ADD by Summoner123
            solid[numcolision].y = drawY;                     //Save Y coordinate      - ADD by Summoner123
            solid[numcolision].spritecol = spritesheet[tile]; //Save Sprite of tile    - ADD by Summoner123
            numcolision++;                                    //Increment numcolision  - ADD by Summoner123
            				}
				}else{ // we need to draw a partial bitmap
                drawBitmap(drawX,drawY,tile_width,tile_height,spritesheet[tile],dx,dy,dw,dh);
            }
        }
    }
}

ok so there are four bitmap functions. The Drawtilemap() uses two different the bitmap functions to work. The first uses the second bitmap function to work. the second finsishes it off.

at least that's whati understand from the notes in the function. I want tokeep the frame rate stuff as it will be useful for strobing a second image to make it look like the character is walking.

also I get large integer warnings about larger integers with these lines in this function......

Code:
void Display::drawBitmap(int8_t x, int8_t y, int8_t w,int8_t h, const uint8_t *bitmap, uint8_t dx, uint8_t dy, uint8_t dw, uint8_t dh) {
    int8_t i, j, byteWidth = (w + 7) / 8;
    dw += dx;
    dh += dy;
    int8_t largest = 0;    //////////this line large inter warning
    int8_t largesty = 0;  //////////same as above
    for (j = 0; j < h; j++) {
        for (i = 0; i < w; i++) {
            if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (B10000000 >> (i % 8))) {
                int8_t drawX = x + i;
                int8_t drawY = y + j;
                
                if(drawX >= dx && drawX < dw && drawY >= dy && drawY < dh){
                    drawPixel(drawX, drawY);
                }
            }
        }
    }
}
 
Last edited:
Kurt gave some good tips after looking into your code. Keep going from there - figure out what and where problems might be and do some prints Serial - or even to the screen. When I was doing TOUCH work on the display I wrote a DEBUG routine that could be called to print given text values on a give line on a remote edge of the screen ( this allows you to overwrite the text in the same spot for each value and have it right where you are looking already without having to focus on SerMon the scrolls wildly and back to the screen ). Get creative. USB goes both ways you can have it run to a point and print copious status and stop one line or block or screen at a time - then press a button, touch the screen, send a USB text char over to have it continue. You need to understand your code and what it is doing better than anyone else would care to. Once you develop some debug practices that work once to solve a problem you can re-use or expand on them as needed.

If you see something ODD happening - add a debug IF to watch for a value out of range and stop - SPEW the appropriate variables - have the code stop waiting for you to see the screen and understand what is wrong or what is missing to understand the problem. For debug purposes change colors - print diagnostic dots - or the blanking area between desired blocks- anything that comes to mind to show where the code math is leading.

Here is something that prints to TFT for debug - modify it to fit an area outside the block to print - this looks to be hardcoded to print left edge on TALL 320 px screen on the bottom 8 rows. I was using it when I was adding Interrupt detection to the TOUCH code to cut down on SPI polling. This is wasteful doing a rectangle clear - but it was the first one I found. Get this working then modify it to watch the screen orientation and change the math to move it to fit 320 wide x 240 high.
Code:
// Call it like this giving line offset, variable value and text label::   tftD( 4, isrWake, "TS_GM" );
// or tftD(0, millis(), "time TS_isrW");

// DEBUG ON TFT screen - Echoes to USB
// Eight lines fit - over that is USB only
// Could add a 4th PARAM: NO_USB, DELAY
// Could statis ARRAY to record last value and print - delay on change only
void tftD( int16_t line, int32_t val, const char *szinfo ) {
  static byte delta = 65;
  char szdelta[2] = "";
  delta++;
  if (delta > 122) delta = 65;
  else if (delta > 90) delta += 7;
  if ( line < 8 ) {
    tft.fillRect(0, BOXSIZE + 125 + (20 * line), 239, 18, ILI9341_YELLOW);
    tft.setCursor(0, 20 * line + BOXSIZE + 125);
    tft.setTextColor(ILI9341_NAVY);
    tft.setTextSize(2);
    tft.print(val);
    tft.print("=");
    tft.print(szinfo);
    tft.setCursor(239 - 12, 20 * line + BOXSIZE + 125);
    szdelta[0] = delta;
    tft.print(szdelta);
  }
  Serial.print(val);
  Serial.print("=(");
  Serial.print(line);
  Serial.print(")=");
  Serial.println(szinfo);
}
 
I have no idea what 'Gamebuino' is - bringing in code from the 8 bit AVR world?
int8_t largest = 0; //////////this line large inter warning

That is a signed 8 bit number { -128 to 127 } - the compiler is warning that the values involved may exceed that range. If these numbers are to cover the 320x240 screen area - it will not work in 8 bits signed or unsigned. But other vars in the same code are "uint8_t " which is 0-255 unsigned. If they are compared it will complain about sign - if they are worked together bits will be lost. To use the ILI9341 pixels all vars must be 16 bits.

On Teensy - a 32 bit ARM processor - often 32 bit values are as efficient or more so than 8 bit as it doesn't have to lop off 24 bits all the time. Using 16 bits can save memory and isn't as much overhead as reducing to 8 bits.

I found http://gamebuino.com/ - this is an 8 bit AVR and the pixels on the screen will fit in any 8 bit var::
CPU*: atmega328 @ 16Mhz, like an Arduino Uno
Display*: 84 x 48 black and white*for infinite pixel art possibilities.
 
Last edited:
OK so I got rid of any pointer looking for the tft's height and width leaving these in the settings file. That did give me more vertical colums by oane and a half but doing so made any thing that goes past those rows disappear and then re-appear when brought back. I decided to play around with the same original drawbitmap function from gamebuino. sorry I should have mentioned that gamebuino is the base hardware and software that does the gaming bits. Any way so I shortened the function from the gamebuino library and made it 16_t instead of 8_t. It needs help but I think its a step in the right direction. I just don't know how to fix it..........

Code:
void Grafx::drawBitmapTM1(int16_t x, int16_t y, int16_t w, int16_t h , const uint8_t *bitmap, uint16_t color) {
#if (ENABLE_BITMAPS > 0)
/*   original code
    int8_t i, j, byteWidth = (w + 7) / 8;
    for (j = 0; j < h; j++) {
        for (i = 0; i < w; i++) {
            if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (B10000000 >> (i % 8))) {
                drawPixel(x + i, y + j);
            }
        }
    }
  */
  uint8_t * buffer = getBuffer();
 // const uint8_t col = color;
  const uint16_t bw = (w+7) / 8;
  
  // clip
  if (x >= Grafx_TFTWIDTH)
    return;
  if (x + w <= 0)
    return;
  if (y >= Grafx_TFTHEIGHT)
    return;
  if (y + h <= 0)
    return;
  if (y < 0)
    h += y, bitmap -= bw * y, y = 0;
  if (y + h > Grafx_TFTHEIGHT)
    h = Grafx_TFTHEIGHT - y;  
  uint16_t x1 = max(0, x);
  uint16_t x2 = min(Grafx_TFTWIDTH, x + w);
  
//#ifdef ENABLE_GRAYSCALE
 //  uint8_t g = y ^ frameCount;
//#endif  

  // draw
  uint16_t first_bitmap_mask = 0x80 >> ((x1 - x) & 7);
  const uint16_t * bitmap_line = bitmap + (x1 - x) / 8;
  uint16_t screen_mask = 0x01 << (y % 8);
  uint16_t * screen_row = buffer + (y / 8) * Grafx_TFTWIDTH + x1;  
  for (uint16_t dy=0; dy<h; dy++, bitmap_line+=bw)
  {
    const uint16_t * bitmap_ptr = bitmap_line;    
    uint16_t bitmap_mask = first_bitmap_mask;    
    uint16_t pixels = pgm_read_byte(bitmap_ptr);
    uint16_t * dst = screen_row;
 
#else
   drawRect(x, y, w, h, color);
#endif
}

what this did was create a grid on the screen that flashed the three colors I'm using black, brown and light brown. But it does fit across the screen. Oddly the character walks of the screen when he gets so far right but comes back. Please give me a 15 minutes to edit this post with a picture.......
 
Last edited:
Apparently, that gamebuino library is absolutely not suitable, neither for the Teensy 3.x, nor for larger TFT displays.

5f2310c1-fe84-4e13-a87c-7bb1786618ea.jpg
 
Your right i have had to update alot of the stuff. Theres barely any thing left of the original gamebuino version in my libraries. But heres a pic of what i have now. Most of library does work but in a lil different way since it use more screen space. Being able to display tiles is pretty much the whole thing though. The rest of the library is updated graphivs functions and you can program it a thousand diffetent ways.



Ok if I set the TFT width and height back from the way it is above it cuts off the what's shown but the character can be seen all over the screen. I change it to the above I get full screen grid but the righ sight last vertical columns make him go off screen.

The libraries are on github under the handle I use here and every where else

This function is definitely right function to use but it has the enable bitmap function tagged to it which when disable turns every thing into rectangles. I can add the enable bitmaps define but I can't figure out how to re-write the function so it actually displays the bitmaps and not the squares (rectangles).

And what's up with not getting the character and the tilemap aligned.
 
Last edited:
At this point, again not sure how much I can help. I gave about as many hints as I can, and have not seen any of the results. Also don't want to start trying to debug something completely different.

For example: in the code that init the positions of the tiles. Did you print out the calculated positions... Did they look correct?
I can almost guarantee the answer is NO.

If it was working correctly I would suspect that the x's in a rows would print out something like:
3, 24, 45, ... , 297 Where each item is 21 units to the right of the previous one...

Again I will bet it won't print something like 297... Which then should lead you to look at your brickstruct:
Code:
typedef struct
{
  byte x;
  byte y;
  byte w;
  byte h;
  boolean exist;
}  BrickStruct;
How can a byte hold the value 297? ...

I would also suspect there could be issues like: What sets the orientation of the screen? Can it change? Why is this important?
Your bricks are a two dimensional array: BrickStruct Brick[BrickRows][BrickColsMax];
Where for example your #define for BrickRows as based on a contant which is/was 240, but if your code now deals with width() and height(), which can change depending on how/when you set the screen orientation or rotation...

so If BrickRows constant is 11, but your computation for actual number of brick rows based on width() is now 15, then your indexing into the array will give strange results... If things like this is variable depending on orientation, than use a single dimensional array and compute the index... Like how my TFT framebuffer works.

So again I believe there are enough hints and issues already found for you to hopefully figure it out.

Good luck
 
Lets not forget that I posted the thread to fix the tilemap. The breakout game was just an experimental test but it does give me a base to start with. I have been trying the stuff I understand and I'm still absorbing other stuff. Lets start with post #4.......

ok first I got rid of anything that defined the width and height except the one that's written in my settings file which feeds all the other library files. then I changed the 320 to width and the height to 240. When I made this simple change I gained a vertical row and a half.

So next I added the the debug code and changed the game settings to this
Code:
boolean paused = false;
#define BrickW 16
#define BrickH 10
#define BrickSpaceX 5
#define BrickSpaceY 5
#define BrickColsMax 15
#define BrickRows (Grafx_TFTWIDTH/(BrickSpaceX+BrickW))-0
int BrickCols = 5;

I get 15 rows 5 columns which is right but it prints out eleven and a half columns on screen

When I change the fillrect to your updated version I get a group of multicolored pixels representing the ball that moves around the screen like the ball does.

I don't really understand the math parts I was going on the settings at the top for how big and where and what ever. I can trying addinf the void width() and height but that's been set up properly now where when it calls Grafx_TFT width.

the rotation is set in the settings file using the function setrotation(). When I do change grafx_tftwidth and height as said ealier I get the extra vertical columns but any thing that goes past that point disappears.

Ok when I change Grafx_TFTWIDTH to tft.width() I get no change.
 
Last edited:
Again your picture in #8 is probably because the tiles that were supposed to print more to the right but x location rounded around as positions > 255 don't fit in a byte or char. Try changing that structure to have x and y be 16 bits...
 
I'm still sifting through all the stuff you guys gave me trying to absorb every thing. To do that I usually pick something to do that lets me walk away and work and still think about the new info. In doing so I decided to clean my libraries today of some useless functions. While I was at it I took the original bitmap and tile map functions and threw them into my library and updated them til they compiled but didn't get any where.

So out of boredom and for the sake of experimenting I took my

Int X=0, y=-0

Coordinates for placing the tile map and started playing with the X integer starting at 50 and noticed a lil bit of the right side of the map though a lil bit of the left side of the map went off the left side of the screen. So I changed it up to 80 and the right side of the map came into view.

So that and the tests I ran out of on the break out game tells me that the tilemap functions are working. But it still cuts off the right side of the screen.

If I leave the tft width at 220W and 340H with rotation 1, the right side is cut off but I can put stuff not tilemap in that area like the character player Sprite or I can post a bitmap in the area singularly. If I change rotation the cut off area is proportional to the rotation.

If I reverse the TFT width to 340W and height 220H and set rotation 1, I get the cut off but any thing moved to that area on the right at the cut off disappears or will not print.

I also have width() and height() instead of TFTWIDTH and TFTHEIGHT I get no change. Should I put the resolution in the brackets? Mine width and height are void inside the () so I leave them empty. I just can't see what's causing this offset when using a tilemap. Tetris works fine but it prints squares on squares using fill s ren and fill rectangle.

All right gonna look at break out now. And see if I can fix what you guys have been talking about.

Here you can see the right side of the map and where the player goes off screen. This is with width 340 and height 220

 
Last edited:
Good progress - keep at it! For instance that random spot where it stops working - what pixel # is that? Seeing that might tell you something.

With a ruler on the screen it looks to be at 210 for some reason - but the odd trail out is oddly at 240 pixels?

You have something compiling and somewhat displaying. Perhaps don't add or change anything until you know the extent of where you are working/stopping.

Put a GRID line across the screen with hashmarks on the H&W. See the numbers and understand until you can see where the problem is. The background dies at 210 ? But the trail runs to 240? Pick one or the other use the tips provided and dig in. Figure out when and where to print or stop the processing or how to have it tell you when it messes up.

As I noted above change the background color in some fashion - or print a spare pixel of a changing color - as it iterates. Perhaps it is looping back on itself and suddenly something will show up. Since nothing is showing on the right border print debug text out there on every element with a unique background color. If odd thing are happening it will flash or change colors unexpectedly during printing. Again as noted above develop these or similar things that work for you and apply them. It will either show nature of the problem or by its absence show the problem is not where you are looking. The USB SPEW scrolls annoyingly - set up a second teensy on a serial port - have that second Teensy just take serial input from the test unit and using the above display code print certain messages on certain lines as the code progresses. When something runs too fast to catch - slow that spot down - expand it to alternate lines. The limit is your imagination and understanding. When everything works these skills are never developed or used - but at some point something goes wrong and until you can 'see' it - it will elude you.
 
Again hard to give many comments here. Not sure what you are actually doing?

Example You say you are changing things, but how? If you are redefining the Grafx_TFTWidth from 240 to 320, what does that actually do to the underlying hardware which is Height=320 and width=240... Are the internal settings for tft.width() and tft.height() correct for the different orientations?

How is the Tilemaps defined? Again as pointed out a couple of times, at least in the program you posted earlier, that some of the data structures have their coordinates (x, y) defined using byte... Which won't work if the coordinate you need is >= 256... Try changing all of them to int or uint16_t... Now does it work better?

Again not sure what code base you are using: Do you still have something like your Brick init section?

Something like this?
Code:
    //Brick Init
    BrickCount = 0;
    for (byte row = 0; row < BrickRows; row++)
      for (byte col = 0; col < BrickCols; col++) {
        Brick[row][col] = (BrickStruct) {
          (Grafx_TFTWIDTH / (BrickSpaceX + BrickW)) / 4 + row * (BrickW + BrickSpaceX), 5 + BrickSpaceY + col * (BrickH + BrickSpaceY), BrickW, BrickH, true
        };
        BrickCount++;
      }
Why not right after this print out debug info? Maybe something like:
Code:
    Serial.printf("Screen(%d, %d), Bricks: %d\n", tft.width(), tft.height(), BrickCount);
    for (byte row = 0; row < BrickRows; row++)
        for (byte col = 0; col < BrickCols; col++) {
            Serial.printf("(%d, %d) ", Brick[row][col].x,  Brick[row][col].y);
        };
      Serial.println();
    }
    // Maybe wait until user input to continue as to look at data that was printed.
    while (Serial.read() == -1);
    while (Serial.read() != -1);
Then look at the data printed, to see if it makes sense... That is are all of the y's the same for each row? Should be.
Are all of the x's in each row incrementing all the way through or as I suspect now, they would suddenly reset after it should be > 256?

The above debug code should help to find a few different things, like: the byte versus uint16_t issue I mention....

It also may find that your usage of a two dimensional array may not be correct. That is if you defined the array Brick to have the array defined like: Bricks[10][16],
But your code then tries to access Row 10 or Row 11... Will be outside of the array... Also may not work correctly if sized for one orientation of screen but not another...
Again it may or not find these types of issues. If you exceed the allocation in one dimension (columns) it will overwrite the next rows entries... This will show up in the display by maybe some y values not being the same... If you exceed in the other dimension, you will write in memory that is not allocated to that array and harder to detect bad things will happen, like program crash, or random things change or....
 
Ok so I took your t3n library and worked it with the needed functions. Thought it would be better to use a library you guys are more familiar with. Then I can take what I learned and fix my main library. Almost set up but I was wondering about the pinout for the t3n. Is there a reason that TFT_RST is pinned to pin 7? I have everything set up where rst is on a 3.3v pin.
Code:
#define KURTS_FLEXI
#ifdef KURTS_FLEXI
#define TFT_DC 22
#define TFT_CS 15
#define TFT_RST -1
#define TFT_SCK 14
#define TFT_MISO 12
#define TFT_MOSI 7
#define DEBUG_PIN 13
#else
#define TFT_DC  9
#define TFT_CS 10
#define TFT_RST 7  //// can this be 3.3v
#define TFT_SCK 13
#define TFT_MISO 12
#define TFT_MOSI 11
#endif


Then I can start testing again.

In post #19 I was playing with my lil Dune demo that has the tilemap functions included. Trying to see if I could move the map into view, which I could kind of. Still left the dead spot. But as you can see the tilemap functions work, I just need to fix the dead spot now on both the breakout program and my Dune demo. You guys have given me the right data stuff to change I just need to play with it some more.

Also I was trying to get the character Sprite to quit tron'ing. I tried calling up date and update all between functions calling to display the bitmaps but that didn't work. The closest I got was using fill screen in between calls which worked and showed my animations of walking but the fill screen has two problems. One it makes the character lose brightness on the screen but also kills any tilemap function I have set up.
 
Last edited:
I was wondering if the post #19 was supposed to 'SNAKE' or as you say TRON'ing. With that working reliably/consistently wrong - stick with it and focus on one part at a time. Remove the snake/tron, fix the background, allow the snake to run to full desired dimensions. Focus on one draw loop or location and as shown by Kurt - debug that loop/spot.

If you've run the one ILI9341 BENCHMARK - it had a bug in I saw when I rotated the screen - it had a subtle off by one error - present in all rotations. It draws so fast you never see it flash by - until you rotate the screen, but I wondered why that one test had an abnormally high time? That out of bounds draw caused the controller to spaz in some way and go offline some measurable % of the test time. I posted a note about that with my fix - Kurt looked at it and confirmed it and it seems found similar other related issues that didn't show onscreen or in adding large % to the time test. I didn't follow up - I don't know if Kurt got his fixes back into the CORE LIB build. The point being whoever designed that cool benchmark had an issue in it for who knows how long. I had never seen the code before - but zeroed in on the one of the oddly named 5 or 20 tests it runs found the affected loop - then zeroed in on the related code and watched the extents and calculations in the loop and saw the final largest unfilled rectangle { IIRC } was badly bounded from the indexed math.

I was never much for video games - but staring at code to see things like that was a game I learned to enjoy {and still do - thus my 4000+ posts on this forum}. Like I did finding some small # of issues in FrankB's T3tris code. It is a developed skill seeing things knowing when they are wrong and getting an idea of where to look and focus your attention to resolve it - perhaps understanding no more about the larger code than that one spot at that one moment. In your case to support and extend this code this is something to work toward.
 
Yea im a hands on person when it comes to learning. Especially with arduino code as i can do trial and error by looking at examples or other peoples code. Im not really much of a gamer myself but i love all the old 8 to 32bit system games like zelda, dragon warrior and mario and stuff. But im an avid Dune fan and ive always wanted to build my own RPG. There aee a couple dune games out there but they are builder games like command and conquer or fresher like starcraft. But none of them give you feel of the book. So im hoping to build an RPG that puts you right in the middle of the universe. Im talking worm riding, spice collecting, and all that cool stuff that seperates it from every other scifi series.

I hope kurt will throw in his word soon on his pin for rst since im using his t3n library with spin which is a version of the Teensy library III9341_t3 found in the hardware libraries. So far all my libraries have been set up to use 3.3v for the ili9341 tft's and T3 hardwares.

https://github.com/KurtE/ILI9341_t3n
 
Last edited:
Reset either works held high to 3.3V - or it needs to be on a controlled pin AFAIK. I was using another display elsewhere and got odd behavior it seemed when I pinned it high so I put it back on a pin. If it works held high - then it won't need a dedicated pin. There may be a case where a reset is issued - use sublimetext and search to track that param down perhaps? Is it evey used in any case other than pushing it high - perhaps in setup/begin?
 
Status
Not open for further replies.
Back
Top