# 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//////////////////////////////
/////////////////////////////////////////////////////////////////////////////
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 class`es 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_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 h1 = pgm_read_byte(b1 + 1);
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?