Camera scrolling function update to higher resolution

 hi guys 

Im building a game that uses a much higher resolution and Im having some trouble getting the camera controls pinned down correctly. They originally were written for a low res screen and for a Zelda port I was making and its been so long I forgot how to set it up right, Here is the original code.......
  int player_x = 70;
  int player_y = 40;
  int player_direction = 0;

int x=0,y=0;

int camerax = -20 ;
int cameray = -10 ;
int xmax = -68;

//in the void loop
////////////////////////////////////////////////////////////////////////////////////
//////camera controls
if(player_x > 59 && camerax <  0 && camerax  > -52){player_x = 59;camerax--;}   
//value 59 equals player position on right when camera will start to follow 
//value 0 and -52 equals camera boundary positions
else  if(player_x < 15 && camerax <  0 && camerax  > -52){player_x = 15;camerax++;}
//value 15 equals player position on left when camera will start to follow 
//value 0 and -52 equals camera boundary positions
else  if(player_x > 59 && camerax <= 0 && camerax >= -52)camerax--;
else  if(player_x < 15 && camerax <= 0 && camerax >= -52)camerax++;   
  
  
      if(player_y > 28 && cameray < 0 && cameray >  -40){player_y = 28;cameray--;}
//value 28 equals player position on right when camera will start to follow 
//value 0 and -40 equals camera boundary positions
else  if(player_y < 15 && cameray < 0 && cameray >  -40){player_y = 15;cameray++;}
//value 15 equals player position on left when camera will start to follow 
//value 0 and -40 equals camera boundary positions
else  if(player_y > 28 && cameray <= 0 && cameray >= -40)cameray--;
else  if(player_y < 15 && cameray <= 0 && cameray >= -40)cameray++;  

      if(camerax > 0)camerax= 0;
else  if(camerax < -52) camerax = -52;
//value 0 and -52 equals camera boundary positions 
      if(cameray > 0)cameray= 0;   
else  if(cameray < -40) cameray = -40;
//value 0 and -40 equals camera boundary positions

im having trouble with the camera boundaries. Its confusing because well look at it. I can get it so far but it only goes so far then stops. The more I play the worse it gets. Im hoping that some one might be able to help me set this up for screen that has a resolution of 320x240. the tile map uses 16x16 tiles and is 150x150 so 2400 pixels wide and high.

here is what I have now…

#include <GrafxT3.h>
#include <SPIN.h>
#include "SPI.h"
#include <Bounce.h>
#include "bitmaps.h"
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
#define TFT_DC  9
#define TFT_CS 10
#define TFT_RST 7
#define TFT_SCK 13
#define TFT_MISO 12
#define TFT_MOSI 11
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
uint8_t use_fb = 0;
uint8_t use_clip_rect = 0;
uint8_t use_set_origin = 0;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
GrafxT3 tft = GrafxT3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCK, TFT_MISO, &SPIN);
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
 int player_x = 160;
 int player_y = 120;
 int player_direction = 2;
 int x=-0,y=0;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
 int camerax = -240 ;
 int cameray = -160 ;
 int xmax = -2352;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////Pixel Color Includes////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
uint16_t palette[16];  // Should probably be 256, but I don't use many colors...
uint16_t pixel_data[2500];

//Extra integers for color palette
int a = 0xa; int b = 0xb; int c = 0xc; 
int d = 0xd; int e = 0xe; int f = 0xf;

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////Button assignments//////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////dpad + select buttons
const int buttonUp = 33; //up button
Bounce ButtonUp = Bounce(buttonUp, 10);  // 10 ms debounce
const int buttonDown = 38; //down_button
Bounce ButtonDown = Bounce(buttonDown, 10);  // 10 ms debounce
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 buttonS = 21; //select button
Bounce ButtonS = Bounce(buttonS, 10);  // 10 ms debounce

//////action + start buttons
const int buttonX = 32; // X button up
Bounce ButtonX = Bounce(buttonX, 10);  // 10 ms debounce
const int buttonY = 26; // Y button left
Bounce ButtonY = Bounce(buttonY, 10);  // 10 ms debounce
const int buttonA = 21; // A button right
Bounce ButtonA = Bounce(buttonA, 10);  // 10 ms debounce
const int buttonB = 28; // B buttun down
Bounce ButtonB = Bounce(buttonB, 10);  // 10 ms debounce
const int buttonT = 4; // Start button
Bounce ButtonT = Bounce(buttonT, 10);  // 10 ms debounce

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////Set-up//////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

void setup() {
  while (!Serial && (millis() < 4000)) ;
  Serial.begin(115200);
  tft.begin();
  tft.setRotation(1);
  tft.fillScreen(BLACK);
  //tft.setFrameRate(60);
  tft.persistence = false;
   pinMode(buttonUp, INPUT_PULLUP);
   pinMode(buttonDown, INPUT_PULLUP);
   pinMode(buttonLeft, INPUT_PULLUP);
   pinMode(buttonRight, INPUT_PULLUP);
   pinMode(buttonS, INPUT_PULLUP);
   pinMode(buttonX, INPUT_PULLUP);
   pinMode(buttonY, INPUT_PULLUP);
   pinMode(buttonA, INPUT_PULLUP);
   pinMode(buttonB, INPUT_PULLUP);
   pinMode(buttonT, INPUT_PULLUP); 
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////Loop////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void loop(void) {
   //updates the GameRIot (the display, the sound, the buttons, everyyhing)
  //returns true when it's time to render a new frame (20 times/second)
//   if(tft.updateAll()){

///////////////////////////////////////////////////////////////////////////////
////////////////////////////////camera controls////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
        if(player_x > 160 && camerax < 0 && camerax > -160) {player_x = 160;camerax--;}
//value 59 equals player position on right when camera will start to follow 
//value 0 and -52 equals camers boundary positions
      else  if(player_x < 160 && camerax <  0 && camerax  > -160){player_x = 160;camerax++;}
//value 15 equals player position on left when camera will start to follow
//value 0 and -52 equals camera boundary positions 
      else  if(player_x > 160 && camerax <= 0 && camerax >= -160)camerax--;
      else  if(player_x < 160 && camerax <= 0 && camerax >= -160)camerax++;

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
       if(player_y > 160 && cameray < 0 && cameray >  -160){player_y = 160;cameray--;}
//value 28 equals player position on right when camera will start to folow
//value 0 and -40 equals camera boundary positions 
      else  if(player_y < 160 && cameray < 0 && cameray >  -160){player_y = 160;cameray++;}
//value 15 equals player position on left when camera will start to follow
//value 0 and -40 equals camera boundary positions
     else  if(player_y > 160 && cameray <= 0 && cameray >= -160)cameray--;
     else  if(player_y < 160 && cameray <= 0 && cameray >= -160)cameray++;

           if(camerax > 0)camerax= 0;
     else  if(camerax < -160) camerax = -160;
//value 0 and -52 equals camera boundary positions
           if(cameray > 0)cameray= 0;
     else  if(cameray < -160) cameray = -160;
//value 0 and -40 equals camera boundary positions
//////////////////////////////////////////////////////////////////////////////
///////////////////////////////Palette////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
 palette[0] = 0;
       palette[1] = BLACK;
             palette[2] = BLUE;
                   palette[3] = BROWN;
                         palette[4] = DARKGREEN;
                              palette[5] = GREY;
                                    palette[6] = PINK;
                                          palette[7] = RED;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////                                                
                                           palette[8] = LIGHTBROWN;
                                     palette[9] = GREEN;
                               palette[a]= DARKGREY;
                         palette[b] = LIGHTGREY;
                   palette[c] = YELLOW; 
             palette[d] = PURPLE; 
       palette[e] = WHITE;
 palette[f] = NAVY;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////Tilemap/////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

tft.drawTilemap(camerax, cameray, dune_demo, spritesheet, palette);
 
///////////////////////////////////////////////////////////////////////////////
///////////////////////////Buttons/////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////  
       if (ButtonUp.update());
               if (ButtonDown.update());
                       if (ButtonLeft.update());
                             if (ButtonRight.update());
                                       if (ButtonA.update());
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
                                       ButtonUp.rebounce(10);
                               ButtonDown.rebounce(10);
                       ButtonLeft.rebounce(10);
            ButtonRight.rebounce(10);
     ButtonA.rebounce(10);
///////////////////////////////////////////////////////////////////////////////
//////////////////////////Down/////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
 if (ButtonUp.fallingEdge()){
   tft.writeRectNBPP(player_x, player_y,16,16,4,paul_rearwalk1,palette);

                   player_direction = 1;
             player_y = player_y - 5;
             if(checkcolision())player_y--;} 
           if(player_y <= 16){
              player_y = 16;}
//////////////////////////////////////////////////////////////////////////////
/////////////////////////////////Up///////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
 if (ButtonDown.fallingEdge()){
   tft.writeRectNBPP(player_x, player_y,16,16,4,paul_frontwalk1,palette);

               player_direction = 2;
            player_y = player_y + 5;
            if(checkcolision())player_y++;}
            if(player_y >= 216){
              player_y = 216;}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////Left////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
 if (ButtonLeft.fallingEdge()){
   tft.writeRectNBPP(player_x, player_y,16,16,4,paul_leftwalk,palette);

                player_direction = 3;
             player_x = player_x - 5;
             if(checkcolision())player_x--;}  
            if(player_x >= 288){
              player_x = 288;}
//////////////////////////////////////////////////////////////////////////////
////////////////////////////Right////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
if (ButtonRight.fallingEdge()){
  tft.writeRectNBPP(player_x, player_y,16,16,4,paul_rightwalk,palette);
  
               player_direction = 4;
            player_x = player_x + 5;
           if(checkcolision())player_x++;}
            if(player_x <= 16){
              player_x = 16;}
///////////////////////////////////////////////////////////////////////////////     
//////////////////////////////PLAYER DIRECTION/////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
if (player_direction == 1){
  tft.writeRectNBPP(player_x, player_y,16,16,4,paul_rear,palette);
}
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
else if (player_direction == 2){
   tft.writeRectNBPP(player_x, player_y,16,16,4,paul_front,palette);
}
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
else if (player_direction == 3){
    tft.writeRectNBPP(player_x, player_y,16,16,4,paul_left,palette);
}
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
else if (player_direction == 4){
     tft.writeRectNBPP(player_x, player_y,16,16,4,paul_right,palette);
        }
     }
//}
/////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////collision/////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
   bool checkcolision() { // Transformed it into a function 
    uint16_t i;  
     for(i=0; i < tft.numcolision + 1; i++)
   {
    if(tft.collideRectRect(player_x, player_y,16,16,tft.solid[i].x,tft.solid[i].y,16,16))
{
     if(tft.solid[i].spritecol == blank_tile)return false; //Do nothing because it's floor - this line not needed
else if(tft.solid[i].spritecol == cave)return true;
else if(tft.solid[i].spritecol == grass)return false;
else if(tft.solid[i].spritecol == grassbl)return false;
else if(tft.solid[i].spritecol == grassbr)return false;
else if(tft.solid[i].spritecol == grasstl)return false;
else if(tft.solid[i].spritecol == grasstr)return false;
else if(tft.solid[i].spritecol == rock){tft.popup(F(" ""Rock"" "),1); return true;} //Return True if character have touched the wall
else if(tft.solid[i].spritecol == rockbl)return true;
else if(tft.solid[i].spritecol == rockbr)return true;
else if(tft.solid[i].spritecol == rocktl)return true;
else if(tft.solid[i].spritecol == rocktr)return true;
else if(tft.solid[i].spritecol == sand)return false;
else if(tft.solid[i].spritecol == seitch)return true;
else if(tft.solid[i].spritecol == stairsl)return true;
else if(tft.solid[i].spritecol == stairsr)return true;
else if(tft.solid[i].spritecol == tree)return false;
   }
}
return false; // Return false if don't touch anything
}
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
 

Might want to state near the top that this code isn’t targeting the Arduboy.

To be honest I think the first thing you should do is try to clean up the code so it’s more readable. I’ve cleaned up the camera stuff for you:

// Use named constants, not magic numbers
// This makes the code more self-documenting,
// so you don't need a comment every 5 lines.

// Camera offset
int cameraX = -20;
int cameraY = -10;

// Camera offset boundaries
const int cameraXMin = -52;
const int cameraXMax = 0;
const int cameraYMin = -40;
const int cameraYMax = 0;

// Player offset
int playerX = 70;
int playerY = 40;

// Player offset boundaries
const int playerXMin = 15;
const int playerXMax = 59;
const int playerYMin = 15;
const int playerYMax = 28;

// By clamping the camera before the checks
// you can avoid a lot of unnecessary comparisons
// as well as avoiding a frame where nothing happens.

// Clamp cameraX
if(cameraX < cameraXMin)
{
	cameraX = cameraXMin;
}
else if(cameraX > cameraXMax)
{
	 cameraX = cameraXMax;
}

// Clamp cameraY
if(cameraY < cameraYMin)
{
	cameraY = cameraYMin;
}
else if(cameraY > cameraYMax)
{
	 cameraY = cameraYMax;  
}

// Check if player is beyond X boundary
if(playerX < playerXMin)
{
	++cameraX;
	if(cameraX > cameraXMin && cameraX < cameraXMax)
	{
		playerX = playerXMin;
	}
}
else if(playerX > playerXMax)
{
	--cameraX;
	if(cameraX > cameraXMin && cameraX < cameraXMax)
	{
		playerX = playerXMax;
	}
}

// Check if player is beyond Y boundary
if(playerY < playerYMin)
{
	++cameraY;
	if(cameraY > cameraYMin && cameraY < cameraYMax)
	{
		playerY = playerYMin;
	}
}
else if(playerY > playerYMax)
{
	--cameraY;
	if(cameraY > cameraYMin && cameraY < cameraYMax)
	{
		playerY = playerYMax;
	}
}

Your other code has several issues, but as for the actual camera part it seems you’ve substituted the numbers incorrectly because you’re checking if(player_y > 160 and if(player_y < 160, in which case the value of 160 is completely ignored by the code, hence why you’re getting stuck. The first of those numbers is supposed to be the maximum value of cameraY, the second is supposed to be the minimum.

Have a read through of the cleaned up camera code I’ve posted to make sure you know how the code works and try using some named constants so you’re less likely to make mistakes by trying to substitute arbitrary numbers. When the logic is working correctly it should just be a matter of tweaking numbers.


(Also a switch statement or two instead of those crazy if-else chains would be helpful, and using variables just to store the values of 0xA-0xF seems like a potentially detrimental level of laziness. I know you came here for a solution to a problem and not code review, but I seriously think cleaning up your code would help you avoid making mistakes.)

No I appreciate the input. Im still learning and a nice critique is always welcome. Guess I should have mentioned that the library isn’t arduboy specific but is based on arduboy. This is just a lil experimental side project ive been working on for fun.

since you mentioned it… What would be a better way to list my intergers for a-f

Why not use the literals 0x0A and so on? In my mind they are much clearer than a simple variable ‘A’. I assume you never need to reassign the value, right? Another approach is to define them …

#define COLOR_INDIGO 0x0A

… if you want to assign a more meaningful description to them.

1 Like

Ok i can do that.

Is there a way to speed up the scroll of the camera? Like maybe make it to two columns vertical or horizontal per movement instead of one?

With full scale computers it wouldn’t be an issue, but with embedded systems there’s a world of difference between a 128x64 2-colour screen and a 320x240 palette-indexed screen. Plus this is the ‘Arduboy’ forum, so most people are going to assume that any code questions are about the Arduboy unless stated otherwise. I didn’t realise the target was a different system until the second block of code (I tend to read the code before the text) when I saw the mentions of different colours.

@filmote is right about just using literals. Defining a variable a as a shorthand for 0xa (or 0xA or 0x0A) doesn’t really achieve anything other than creating an unnecessary variable. The 0x prefix is not clutter, it’s an important bit of information - it tells the programmer ‘this is a hexidecimal number’. Programmers are pretty much expected to know hexadecimal because it’s a useful skill supported by many programming languages, so if a person doesn’t understand what the 0x means that’s a crucial bit of knowledge they are lacking, not a fault with your code.

Depending on the situation it might be better to just use decimal, but I think whether decimal or hexadecimal is better in this case is probably a matter of taste.

I disagree about using defines to associate words to values. You should either use an enum class (assuming your compiler is C++11 compliant) or a regular enum. I know there are a lot of people here who use defines like that and I agree that they do work, but I much prefer enums because they are typed and you don’t have to manually number them if they’re sequential. (enum classes are even better because they’re very strongly typed and thus prevent certain bugs.)


To elaborate on what I said about switch statements, here’s two extracts from your code where I’ve converted if-else chains into switch statements:

Tile types:

// Out of interest all the cases of
// 'return false' could be removed if desired
// because of the 'default' case.
switch(tft.solid[i].spritecol)
{
	case blank_tile: return false; //Do nothing because it's floor - this line not needed
	case cave: return true;
	case grass: return false;
	case grassbl: return false;
	case grassbr: return false;
	case grasstl: return false;
	case grasstr: return false;
	// Return true if character has touched the wall
	case rock:
	{
		tft.popup(F(" \"Rock\" "), 1); // I fixed the quote escaping here
		return true;
	}
	case rockbl: return true;
	case rockbr: return true;
	case rocktl: return true;
	case rocktr: return true;
	case sand: return false;
	case seitch: return true;
	case stairsl: return true;
	case stairsr: return true;
	case tree: return false;
	default: return false;
}

Player Direction:

switch(player_direction)
{
	case 1:
	{
		tft.writeRectNBPP(player_x, player_y, 16, 16, 4, paul_rear, palette);
		break;
	}
	case 2:
	{
		tft.writeRectNBPP(player_x, player_y, 16, 16, 4, paul_front, palette);
		break;
	}
	case 3:
	{
		tft.writeRectNBPP(player_x, player_y, 16, 16, 4, paul_left, palette);
		break;
	}
	case 4:
	{
		tft.writeRectNBPP(player_x, player_y, 16, 16, 4, paul_right, palette);
		break;
	}
}

Also, let us (i.e. the forum) know if you make any progress on fixing the camera, or at least what the results of fixing the “if(player_y > 160 and if(player_y < 160” bug is.

I hope it’s ok to post here for gaming specific questions. I’ve had a huge amount of help at pjrc but we finished the library and it’s all down to gaming specific stuff now.

I really appreciate the help with the camera too. I had been running in circles for a couple weeks, working a lil at a time but not getting very far. It took me a sec to see understand the changes but once I did I was able to dial in the player and and camera boundaries pretty easily.

So much better. I like the way you cleaned up my code as well. All the original stuff was from an arduboy port of Zelda I was working on and had started my learning process.

I see you cleaned up the collision as well. Maybe it will actually work. Kind of curious as to why it doesn’t. Those parts were working fine using the arduous library. The only difference might be the new bitmap functions but it should still read the pixels and collide with them.

I’ll post what you need to see in a sec

ok so the tilemap and collision are kind of interconnected. Here are the header parts…

 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);
	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, const uint16_t * palette);
	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, const uint16_t * palette);

	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;
	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);

 void writeRect4BPPtm(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pixels, uint16_t dx, uint16_t dy, uint16_t dw, uint16_t dh, const uint16_t * palette );

and here are the .cpp stuff

void GrafxT3::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;
//	int32_t largest = 0;
//	int32_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 GrafxT3::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 GrafxT3::drawTilemap(int x, int y, const uint8_t *tilemap, const uint8_t **spritesheet, const uint16_t * palette){
	drawTilemap(x, y, tilemap, spritesheet, 0, 0, GrafxT3_TFTHEIGHT, GrafxT3_TFTWIDTH, palette);
}
void GrafxT3::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, const uint16_t * palette){
	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)){
				writeRectNBPP(drawX, drawY,tile_width, tile_height, 4, spritesheet[tile], palette );

				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
				writeRect4BPPtm(drawX, drawY, tile_width, tile_height, spritesheet[tile], dx, dy, dw, dh, palette);
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
//////////////-------------------------Collision----------------------//////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
boolean GrafxT3::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 GrafxT3::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 GrafxT3::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;
}

void GrafxT3::writeRect4BPPtm(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pixels, uint16_t dx, uint16_t dy, uint16_t dw, uint16_t dh, const uint16_t * palette )
{
	dw += dx;
	dh += dy;{
//	int32_t largest = 0;
//	int32_t largesty = 0;
		int16_t drawX = x;
				int16_t drawY = y;

				if (drawX >= dx && drawX < dw && drawY >= dy && drawY < dh){
	// Simply call through our helper
	writeRectNBPP(x, y, w, h,  4, pixels, palette );
        }
    }
}

I don’t want to get in trouble with admin for posting links to non canon arduboy libraries so if I missed something that you need please tell me and I will post it as soon as I see the post.

I’m pretty certain it’s ok (maybe even encouraged), it just pays to give as much detail as possible upfront so people can clearly see the post is about a non-Arduboy specific topic. (It’s a bit of a grey area as to what topic it should be under though.)

No problem, sometimes it just helps to have a fresh pair of eyes.
The easiness of the ‘dialling in’ is one of the main reasons I restructured the code the way I did.
Having well named constants instead of ‘magic numbers’ really helps with both clarity and making code easy to change.

Thank you. Programming isn’t always about high level maths or complicated algorithms, sometimes it’s just remembering to do a bit of spring cleaning every now and again and knowing techniques to keep code ‘clean’ (i.e. readable and reusable). Stopping to think “if I come back in a year and I’ve forgotten everything about this, how horrified am I going to be?” is a good skill to have. (It’s not always easy, I’ve got several folders full of code I’m too scared to go back to.)

Zelda has some simple concepts behind it but can be hard to get right.
It’s a good game to attempt though because it’s a very well known example of tile maps.
(Also it’s one of my favourite game series. My favourite 2D title is probably Minish Cap, followed closely by the ‘Oracle Of’ duet.)

I just made it look better, I didn’t change any functionality - if it was broken before, it’s still broken.
If you’ve switched to a new library there will probably be some library differences involved.

You won’t get in trouble for that. There’s no rule that says people have to use the Arduboy2 library or that there can’t be any alternative libraries, just that Arduboy2 is considered the ‘preferred’ library.
I found the whole code anyway (the header on your github page, the github of the original ILI9341 project and your pjrc post - my google-fu knows no bounds). (The history of that file is a bit of a mess, it looks like it’s been passed around quite a lot.)

I’ve had a quick skim and it looks functionally sound (I’m particularly impressed that it’s using the rectangle collisions to make bitmap collisions faster).

What aspect of the collisions aren’t working?
Just the if(tft.collideRectRect(player_x, player_y,16,16,tft.solid[i].x,tft.solid[i].y,16,16)) line?

I love Zelda. It was one of the first games I played way back nin the good ole days of 8 bit gaming. I followed the series after that. I think mmy favorite though is sprit tracks.

when I first started building the library about a year and a half ago I started small with a color 8bit parelell screen that was a lil bigger than the arduboys but I sooned learned that spi was much easier and is included in all the fastest libraries. Plus the wiring would have made it all hard to fit in case that fits in your pocket. So then I started with a library that was compatible with mcu,s from arduino to esp. But that particular library had many drawbacks that were just too much for me to fix on my own. looking around I found Kurts library that had a buffer built in and some other cool stuff so I started converting it and updating a lot of the functions to work the way they need to for gaming. this went through many changes as you can guess. But what we came up with is a nice lil library. The esp library is a direct offshoot from the t3 version and let me tell you. Its fast!!! Im gonna pull the master though in favor of the two specific versions. I also need to make a regular arduino version for people who want, that can build smaller games. The great thing about using the ili9341 drivers is that they cover a good variety of screens so your not stuck using one size.

Yep, lol I use the same handle every where so im not hard to find. Im into a lot of stuff. I build guitars and robots and quadcopters and now ive been playing with 3d printing. So nice having my own.

All right so on the collision… its just not working. The function that’s in my sketch should see the 16x16 bitmaps as named then use the list to confirm whether or not to keep going when it hits solid but it never stops on anything marked solid. I added the popup as a test but I think my pop up function needs debugging as well. I guess I could do a serial print and see that way if its even recognizing the 4bpp bitmaps.

I asked this before but is there any way to speed the camera scroll up where it shows two rows or columns instead of one every time it updates? I have the boundaries all locked in and I can control player speed with in the boundary but when it hits the camera boundary and starts moving it is painfully slow. I mean slow!!!

Odd choice, a lot of people don’t like Spirit Tracks.
(It’s not my favourite but I don’t dislike it either. It had some nice dungeon designs, a few good gadgets and some very nice music.)

Serial printing is better for debugging in my experience.

I looked over it for several minutes and can’t see a reason why the comparisons would be failing or why the rectangle collision would be failing, so I looked to see where checkcolision was being called and I noticed that in the movement bit where you’re checking for collisions you’ve got the lines:

player_y = player_y - 5;
if(checkcolision()) player_y--; 

This doesn’t seem right to me.
You move the player up 5, then if it’s colliding with something you move it up 1. Surely you should be moving the player down 5 instead?
It seems to me that it’s more likely that the collision function is working fine and that this is your problem.

Didn’t notice you’d asked this, must have missed it.
In short, yes:

// Camera offset
int cameraX = -20;
int cameraY = -10;

// Camera speed in pixels per update
int cameraXSpeed = 2;
int cameraYSpeed = 2;

// Camera offset boundaries
const int cameraXMin = -52;
const int cameraXMax = 0;
const int cameraYMin = -40;
const int cameraYMax = 0;

// Player offset
int playerX = 70;
int playerY = 40;

// Player offset boundaries
const int playerXMin = 15;
const int playerXMax = 59;
const int playerYMin = 15;
const int playerYMax = 28;

// By clamping the camera before the checks
// you can avoid a lot of unnecessary comparisons
// as well as avoiding a frame where nothing happens.

// Clamp cameraX
if(cameraX < cameraXMin)
{
	cameraX = cameraXMin;
}
else if(cameraX > cameraXMax)
{
	 cameraX = cameraXMax;
}

// Clamp cameraY
if(cameraY < cameraYMin)
{
	cameraY = cameraYMin;
}
else if(cameraY > cameraYMax)
{
	 cameraY = cameraYMax;  
}

// Check if player is beyond X boundary
if(playerX < playerXMin)
{
	cameraX += cameraXSpeed;
	if(cameraX > cameraXMin && cameraX < cameraXMax)
	{
		playerX = playerXMin;
	}
}
else if(playerX > playerXMax)
{
	cameraX -= cameraXSpeed;
	if(cameraX > cameraXMin && cameraX < cameraXMax)
	{
		playerX = playerXMax;
	}
}

// Check if player is beyond Y boundary
if(playerY < playerYMin)
{
	cameraY += cameraYSpeed;
	if(cameraY > cameraYMin && cameraY < cameraYMax)
	{
		playerY = playerYMin;
	}
}
else if(playerY > playerYMax)
{
	cameraY -= cameraYSpeed;
	if(cameraY > cameraYMin && cameraY < cameraYMax)
	{
		playerY = playerYMax;
	}
}

You could just use one ‘speed’, but I put two in just in case you want to make it go quicker on one axis than the other for some reason. Also it’s not const in case you want to change it in-game for some reason (e.g. you have a speed powerup or something).

Awesome!!! Thank you so much. I’ve been busy still with development and encoding my sprites by hand, but I just flashed my board and gave it shot and it’s far less annoyingly slow. Lol. Actually it’s perfect. I left at +2 and it scrolls just right.

No problem.

If the boards you’re using have the same sprite format as the Arduboy, there are many converters available.

If not you should probably have a go at writing a converter or find someone who can.
What format do the sprites have to be in?

That’s the movement sorted then.

Did my suggestion about the issue with the collisions fix the collisions?

Ok I’m trying to understand what you were saying about the collision. I have the player movement set to 5 because the tilemap slows down the player movement. +5 takes care of that.

I still don’t really understand the function enough though.

Player _y = player_y - 5;
If(checkcollsion()) player_y --;

Both parts of the above should move the player down. Maybe I need to set the player_y–; to ++? So when the player is moving down and it hits a solid object it causes it to move back up 1?

Assuming you are using screen coordinates, this line should move the player up 5 pixels:
player_y = player_y - 5;

And this line:
if(checkcolision()) player_y--;
Checks if the player is colliding with something, and if they are, moves them up 1 pixel. Presumably this means you’re actually moving them further into the thing they’re colliding with, when you should be undoing the previous up motion (which, to the player, will look like the character hasn’t actually moved because the sprite will be drawn afterwards).

If your coordinates aren’t screen coordinates (i.e. they are world coordinates), that might be moving the player down.

In pretty much all screens it’s conventional to make the top-left corner of the screen the point (0, 0).
(It’s partly a hangover from the day of cathode ray tube monitors; their electron guns would start drawing at the top left and partly because text is read from top left in most languages.)

Ahhhh ok. So I looked up the original 8bit B&W sketch and saw what you meant. I changed the ++ to – and vice versa then set the player speed back to 1. Then ran the sketch once more but no change. I also tried adding more + or - but that gives Lvalue errors.

My cable is getting kinda wonky so using the serial monitor is out. Instead I told it to fill the screen with purple when it hits a tile but the screen never flashes purple.

What I’m saying is that instead of this:

 if (ButtonUp.fallingEdge()){
   tft.writeRectNBPP(player_x, player_y,16,16,4,paul_rearwalk1,palette);
                   player_direction = 1;
         player_y = player_y - 5;
         if(checkcolision())player_y--;} 
       if(player_y <= 16){
          player_y = 16;}

The code should be this:

if(ButtonUp.fallingEdge())
{
	tft.writeRectNBPP(player_x, player_y,16,16,4,paul_rearwalk1,palette);
	player_direction = 1;
	player_y -= 5;
	if(checkcolision())
	{
		player_y += 5;
	}
}
if(player_y <= 16)
{
	player_y = 16;
}

So you’re undoing the movement when there’s a collision.
(And doing the same for the other 3 directions.)

If you mean you changed ++ to +++ there’s a good reason that didn’t work. ++ is an operator that means ‘add 1’, but there’s no such operator as +++, that gets interpreted as a ++ and a +, which usually results in the code being grammatically incorrect (i.e. it won’t compile).

Which bit of code did you change to do that?
Where abouts were you testing?