RPG advice and development

You can make your own font array. But if your library has its own font system you might not need to. I think this was before the Arduboy’s font system was standardised, or maybe the people making the game wanted a different (possibly smaller) font. Only the developers know.

In fairness, I think they were more concerned about making a fun game than they were about other people being able to understand their code. Not all open source projects are intended to be easily ‘hackable’. If the developers understood the code, that was all that mattered to them, it didn’t matter if other people could understand it.

Besides which, the more practised with coding you are, the easier it is to just pick code up, read it and understand what it’s doing. There’s a saying that “code is read more than it is written”. Usually that just means “keep your code readable”, but it also means people do actually read code more than they write it, whether it’s theirs or someone elses. Reading code is a skill, and all skills take practice.

If you’ve got an Arduboy that’s easy enough to do, you just plonk the array in a new sketch and write code to make it display the characters in order. Maybe even one of the sprite converters/editors would be able to draw it.


if(battleState == BATTLESTATE_ENEMYCHOICE && i == currentEnemyChoice) arduboy.drawBitmap(xPos + 5, 20, font[45], 5, 5, BLACK); ////////////// don't know what this does

That’s drawing a font character, so without knowing the font I can’t say for sure what it draws, but based on the code I’d guess font[45] is some kind of dot or triangle since the if-condition indicates this will only be used once. (i.e. it’s a selector, like you get on old-style game menus)

ThisThing

The line
else arduboy.drawBitmap(xPos, (battleState == BATTLESTATE_PLAYERDAMAGING && i == currentEnemyTurn)?22:26, enemyBitmaps[currentEnemies[i]], 16, 16, BLACK);

Is definitely drawing enemies on the screen. The enemy bitmaps don’t store the size, the size is actually written in the code, the 16, 16 part.

Nope, that’s not what the code says.
I’m not surprised to misread it though, the code is a bit messy.
It’s the continue; that only happens when that rather complex condition is met.
There’s an else before the draw call, which means that in every other case the enemies are being drawn.

Yes it could.

In fact the newer Arduboy library actually has a way to encode the image size in the bitmap itself.
Of course, you’d still have to calculate where you want to draw them (someone should suggest adding a ‘draw bitmap centred’ function), but it means you can call the draw function and it’ll draw the whole bitmap with the size stored with the bitmap.

Like I said before though, it depends on how your library works.
You might have to come up with your own system of storing the size with the bitmap, or calculating the size if you’ve got an 8 bit per pixel image (or 24 bit per pixel etc).

Definitely feel you on the reading vs writing code. The dune game that all this for is a demo for the library so I’ve been trying to keep my stuff in a way that makes it easier for new users to learn how to use the library.

Oh ok!!! I was watching the video for the game and when it draws the battle to the screen you get first the white background spot then the monsters then three options. Fight, defend, or run. The lil triangle would point out the three selections.

Ok normally when you write a bitmap, not using my writeRect functions, you would put the size info like so…


const byte bitmap[] = { 16,16, 

But with the write rectangle functions doing so will screw up your bitmaps so it has to be done in the call to draw the bitmap. Hmmmmm I’ll be back

Now that’s a good idea!

1 Like

Ok lets start easy. Most of my bitmaps are large enough I can do one on on for now. then once im able to do a battle I can work on adding more monsters. or leave it like it is. How would I properly remove the enemy amount and formation stuff from this function?

void drawBattle()
{
  arduboy.fillRect(0, 10, 128, 36, WHITE);  ////// draws a back grounds stripe for the monsters to sit on the length nof the screen

  for(int i = 0; i < currentEnemyAmount; i++)
  {
    byte xPos = ((128 - (currentEnemyAmount * 16) - ((currentEnemyAmount - 1) * 4)) / 2) + (i * 20); /////current ennemy amount and health
    if(currentEnemyHealth[i] != 0)
    {
      if(battleState == BATTLESTATE_ENEMYCHOICE && i == currentEnemyChoice) arduboy.drawBitmap(xPos + 5, 20, font[45], 5, 5, BLACK); ///// draws cursor for battle choices
      
      if(battleState == BATTLESTATE_ENEMIESDAMAGING && i == currentEnemyChoice && !battleDamageEffectToggle) continue;   //// toggles attack between monsters and player
      else arduboy.drawBitmap(xPos, (battleState == BATTLESTATE_PLAYERDAMAGING && i == currentEnemyTurn)?22:26, enemyBitmaps[currentEnemies[i]], 16, 16, BLACK); /////draws monsters
    }
  }

  arduboy.drawBitmap(battleChoicePositions[currentActionChoice], 50, font[43], 5, 5, WHITE); //positions of  battle choices
  drawText(battleChoices, 10, 50, WHITE, ALIGN_LEFT); //// draws battle choices text to screen
  
  drawText(labelHealth, 4, 2, WHITE, ALIGN_LEFT); 
  drawHealth(22, 2, WHITE);

  if(battleState == BATTLESTATE_ENEMYCHOICE)
  {
    drawText(enemyNames[currentEnemies[currentEnemyChoice]], 4, 14, BLACK, ALIGN_LEFT);
  }
}

Scratch all the above and let’s start fresh. I think what would be best on my case, since I’m still trying to learn is create a battle screen from start and fill the rest in.

There’s only gonna be so many monsters per worldmap module so I can make several scenes then have them triggered randomly. This will make it easier to adjust stats for monsters per room as well.

I’m gonna work on some thing and come back

1 Like

This is exactly what I would have suggested.

Taking code you don’t fully understand and removing bits is rarely a good idea, it’s better to build up something on your own (even if you’re using the other code as a reference) so you can see how it evolves and better understand it.


Also, at its heart, programming is about problem solving.
Programming languages are just a way of expressing solutions to problems.

If you’ve got a few minutes spare, I recommend having a read of this article, don’t take the ‘follow these steps’ bit literally, but what the article says about thinking a problem through before writing code and then translating the solution into code is invaluable.

Both me and a handful of other programmers I know often work problems out on paper (with words and diagrams) before writing any code.

Guess what I did today??? Come on guess! Give up? OK I’ll tell you. I bought an arduboy today!!! Ive been wanting one for a while and Idk why I haven’t yet. But ive been looking at some of the games people are producing and the complexity of the graphics are blowing the comps away. Wow!!!

Plus its a great way to learn the language which will help me help yall help me. lol. I cant wait!!! My Zelda graphics are gonna look way cool!!!

You know when I started all of this I couldn’t even understand how to draw a bitmap. Now look.

ok back to the business at hand…

First I understand how structure works enough to use it so we got that out of the way and I should be able to make a structure for each monsters stats and the player as well. I was able to make a mock up graphic which will be added below. So what I did was this…

void loop(void) {

//////////////////////////////////////////////////////////////////////////////
///////////////////////////////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] = BEIGE;
                                     palette[9] = GREEN;
                               palette[a]= DARKGREY;
                         palette[b] = LIGHTGREY;
                   palette[c] = YELLOW; 
             palette[d] = PURPLE; 
       palette[e] = WHITE;
 palette[f] = ORANGE;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
 
  tft.fillRect(0, 80, 320, 50, WHITE);  ////// draws a back grounds stripe for the monsters to sit on the length nof the screen
  tft.writeRectNBPP(143, 100, 34, 24, 4, cavespider, palette);
  tft.drawRoundRect(40, 180, 240, 40, 4, WHITE);
  tft.fillRoundRect(41, 181, 237, 37, 4, BLUE);
  tft.setTextcolor(white); tft.setTextSize(2);
  tft.printIn("cave spider");

ok so its pretty self explanatory graphic functions with a call to print the name of the monster. The library has the font built into it but can be changed by changing a line code in library and adding the font you want to use. So all we have to do is call the color and size of the text.

But I have two problems right off the bat. First the palette colors defines have to be in the same loop that calling the graphical function so all the different battle scenes that will be chosen by random battle scene will have to reside in the same loop or I have to list the palette for each function.

Second I don’t know how to place the text on the screen where I need it. I was thinking at some point I could do lil messages like cave spider attacks and attacks first and defends.

Any way how should I proceed? there are so many elements in an rpg its hard to figure out where to begin…Ok lets start by making a trigger that can bring up the code I listed earlier this post. Cody came up with this but I’m not sure how I would use it with my own collision functions…

Why not
Rect rectA{x,y,x,y};
Rect recB {ply-x,ply-y,16,16};
If(count == 0)
{
x = random(0,120);
y = random(0,64);
Count = 1;
}
If(arduboy.collide( rectA,rectB))
{

I can then assign a button to release back to the map but there needs to be a way for it to go back and be on the tile that the player was on when trigger was activated.

5 years ago I didn’t know the difference between a kilobyte and a gigabyte, or the difference between a CPU and a GPU. Now I’m experienced with at least 5 languages and have dabbled in at least 10.
With hard work, practice and a lot of reading it’s amazing how far you can go.

Can’t you just have several different pallette arrays stored in memory and find a way to swap between them when drawing?
For example (not sure if this will work):

Color palette0[] =
{
	0, BLACK, BLUE, BROWN, DARKGREEN, GREY, PINK, RED,
	BEIGE, GREEN, DARKGREY, LIGHTGREY, YELLOW, PURPLE, WHITE, ORANGE
};

Color palette1[] =
{
	BEIGE, GREEN, DARKGREY, LIGHTGREY, YELLOW, PURPLE, WHITE, ORANGE,
	0, BLACK, BLUE, BROWN, DARKGREEN, GREY, PINK, RED
};

Color * palettes[] =
{
	palette0, palette1
};

int paletteIndex = 0;

void loop(void)
{
	// Set up some code that makes button presses change the palette index
	// Make sure that paletteIndex can't be less than 0 or more than
	// the highest palette number (in this case 1)

	tft.fillRect(0, 80, 320, 50, WHITE);
	tft.writeRectNBPP(143, 100, 34, 24, 4, cavespider, palettes[paletteIndex]);
	tft.drawRoundRect(40, 180, 240, 40, 4, WHITE);
	tft.fillRoundRect(41, 181, 237, 37, 4, BLUE);
	tft.setTextcolor(white); tft.setTextSize(2);
	tft.printIn("cave spider");
}

There’s almost certainly a function that lets you dictate where text gets printed.
For example Arduboy2 has arduboy.setCursor(x, y);.
When you’ve got that, it’s just a matter of either hardcoding or calculating the text position.
For example if you want the text to go below the spider and you know where the spider is you just have to calculate the right place for it to go based on the spider’s coordinates.

Yeah, it’s not exactly a beginner’s project.

Cody’s code is just an example of using Arduboy2's collide function it takes two rectangles defined by their x, y, width and height and returns true if they intersect and false if they don’t.
Then you wrap collide in an ‘if’ block so the code in the block is executed when two things collide.
(A better word would be ‘intersect’ since it’s just testing if two rectangles intersect each other, it’s not working like an actual collision.)

ok check this out…

tft.writeRectNBPP(player_x, player_y,paulfrontw[0],paulfrontw[1],4,(paulfrontw+2),palette);

This is a way to call bitmaps without calling the size variables when calling the bitmap. Explained as player x and y placement, reads pixels wide of chosen bitmap, reads pixels high of chosen bitmap, 4 bits per pixel, bitmap name with offset of +2 and the palette.

then you just name the size of the bitmap like so…

const byte paulfrontw[] = {16,16,
0x00,0x00,etc

on the collision I have these three functions…

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

colliderectrect() would probably be best but I cant see how to use it properly with codys lil trigger there since it uses 8 variables. I do know that I need to call the height and width of the rectangle.

That’s what the cursor functions do? ok I think I can handle that.

Yep, that’ll work. That’s basically what the arduboy library does underneath.

If possible you might want to add a function to the tft class that does that for you so you don’t have to remember to keep indexing the width and height of the image (again, also what the Arduboy library does).

The Arduboy collide function compares two rectangles that each consist of an x, a y, a width and a height (2 * 4 = 8).
The collideRectRect function you referred to is simply using the raw x, y, width and height instead of having it packed in a rectangle.

Text wrapping might be a bit hard if you need it, but you can probably avoid it, or just reverse engineer the Arduboy library. Managing the cursor itself is pretty straightforward.

ok ive been looking at the trigger function and my colliderectrectfunction and I can see its colliding two rectangles. It needs the first set of coordinates for the invisible rectangle and the player as the second.

I came up with this by simply adding the width and height of the invisible rect. It should work like so…

Rect rectA {x,y,16,16};
Rect rectB {player_x, player_y,16,16};
If(count == 0)
{
x = random(0,320);
y = random(0,240);
Count = 1;
}
If(tft.collideRectRect( rectA ,rectB ???, ???, ???, etc))
{

but I don’t know how to fill the variables in the function call so it reads the structure above it. Maybe a hint?

Maybe something like this but I cant figure out how to declare the Recta, and Rectb, stuff…


Recta = (x=x1,y=y1);

Rectb = (player_x = x2, player_y =y2);
If(count == 0)
{
x = random(0,320);
y = random(0,240);
Count = 1;
}
If(tft.collideRectRect(Recta,16,16, Rectb,16,16))
{
  tft.fillRect(0, 60, 128, 36, WHITE);  ////// draws a back grounds stripe for the monsters to sit on the length nof the screen
  tft.writeRectNBPP(143, 120, 34, 24, 4, cavespider, palette);
  tft.drawRoundRect(0, 200, 320, 20, 3, BLUE);
  tft.setTextColor(WHITE);  tft.setTextSize(2);
 
  
     tft.updateScreen(); 
}
 }

The collideRectRect function takes 8 variables rather than two rect structures. You could create a wrapper function that accepts two rect structures and simply calls the collideRectRect with the rectA.x, rectA,y, rectA.w … and so forth.

1 Like

ok so player_x and player_y are already caaled in the main sketch for placement on map so I feel like I should be able to do this…

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

int player_x = 160;     //player position on screen x
int player_y = 176;     //player position on screen y

int w;
int z;

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); */
 
     tft.useFrameBuffer(use_fb);
     uint32_t start_time = millis(); 
 
}


 void loop(void) {

//////////////////////////////////////////////////////////////////////////////
///////////////////////////////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] = BEIGE;
                                     palette[9] = GREEN;
                               palette[a]= DARKGREY;
                         palette[b] = LIGHTGREY;
                   palette[c] = YELLOW; 
             palette[d] = PURPLE; 
       palette[e] = WHITE;
 palette[f] = ORANGE;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////



If(count == 0)
{
w = random(0,320);     ///////// x width
z = random(0,240);     ///////// y height
Count = 1;
}
If((tft.collideRectRect(w, z, 16,16, player_x, player_y,16,16))
{
  tft.fillRect(0, 60, 128, 36, WHITE);  ////// draws a back grounds stripe for the monsters to sit on the length nof the screen
  tft.writeRectNBPP(143, 120, 34, 24, 4, cavespider, palette);
  tft.drawRoundRect(0, 200, 320, 20, 3, BLUE);
  tft.setTextColor(WHITE);  tft.setTextSize(2);
 
  
     tft.updateScreen(); 
}
 }

ok two things… first playing with int x =0; and int y =0; can cause the screen to blank,. So I changed that to w and z. Its usually set to how I listed them. the second is I don’t know how to start the if statement off now to get it started,

if(count == 0)
{
x = random(0,320);
y = random(0,240);
count = 1;
}

Rect rectA {x,y,16,16};
Rect rectB {player_x, player_y,16,16};

if(tft.collideRectRect( rectA.x, rectA.y, rectA.width, rectA.height, rectB.x, rectB.y, rectB.width, rectB.height))
{

That doesn’t work at all…

Arduino: 1.8.2 (Windows 10), TD: 1.36, Board: "Teensy 3.6, Serial, 180 MHz, Faster, US English"

drawbattle: In function 'void setup()':
drawbattle:88: warning: unused variable 'start_time' 
      uint32_t start_time = millis(); 

               ^

drawbattle: In function 'void loop()':
drawbattle:104: warning: large integer implicitly truncated to unsigned type 
                                     palette[6] = PINK;

                                                ^

drawbattle:118: error: 'count' was not declared in this scope
  if(count == 0)

     ^

drawbattle:120: error: 'x' was not declared in this scope
 x = random(0,320);

 ^

drawbattle:121: error: 'y' was not declared in this scope
 y = random(0,240);

 ^

drawbattle:125: error: 'Rect' was not declared in this scope
 Rect rectA {x,y,16,16};

 ^

drawbattle:126: error: expected ';' before 'rectB'
 Rect rectB {player_x, player_y,16,16};

      ^

drawbattle:128: error: 'rectA' was not declared in this scope
 if(tft.collideRectRect( rectA.x, rectA.y, rectA.width, rectA.height, rectB.x, rectB.y, rectB.width, rectB.height))

                         ^

drawbattle:128: error: 'rectB' was not declared in this scope
 if(tft.collideRectRect( rectA.x, rectA.y, rectA.width, rectA.height, rectB.x, rectB.y, rectB.width, rectB.height))

                                                                      ^

Multiple libraries were found for "Bounce.h"
 Used: C:\Users\Duhjoker\Documents\Arduino\libraries\Bounce
 Not used: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Bounce
'count' was not declared in this scope

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

how do I activate count? so when my player walks he actvates the count

The output clearly states you haven’t declared the variable count or (RectA, RectB, x or y). Fix this and it might work?

This means you haven’t declared the variable count.

Likewise for x and y.

This means you haven’t actually declared Rect anywhere. It won’t work if you don’t actually have a Rect type, the code Cody wrote was Arduboy-specific (which is to be expected).

These errors are all knock-on effects from not having a Rect type, it upsets the parser.

Ok I was looking in the arduboy library and fount the structure bit for rect so I guess I need to add that?

Yep and maybe the Point structure if you plan to use it too.