Questions about optimizing code

Ah, thank you, that is all super helpful! And as for the compound assignment operator placement I was probably just mistaken, sorry, more specifically it had something to do with the placement in a chain of objects in different structs, I just thought what I wrote was a simplified version I had tried (because I was/am pretty tired), but I’m sure the actual parts in the code would be pretty essential for context.

I’d have plenty more reading/reorganization to do before I really need to post any more, but if that comes up as a potential issue or question when I get to making functions then I might ask about it, fair warning :stuck_out_tongue: thanks again!

1 Like

I’m not sure what you mean by that, but I’ll just assume it means “there was some particularly complex expression being used”.

Yeah, context is quite important.

One of the reasons I use this-> is because it makes it clear that the following identifier is a member variable or member function even when taken out of context.

E.g. if I showed someone:

int Point::getX() const
{
	return this->x;
}

They would be able to infer that x is a member variable of Point and it’s implicitly convertible to int (assuming the code compiled).


I will get back to answering your other questions at some point,
but I think some diagrams would be the best way to answer questions involving geometry.

Besides which I’d need to know a few things first:

  • Are your lasers going to be acting like projectiles or are they instantaneous?
  • Will your lasers be constrained to particular angles or will they be able to fire in any direction?
  • Will you be having more than one kind of background tile?
  • Will you be expecting the rocks to repeat as well, or just the background?
  • Do you want the map to loop inifinitely, or just to specified dimensions?

(Possibly some other stuff I might think of later.)

1 Like

I overhauled the structs in my other game and using a compound assignment operator didn’t really seem applicable anywhere I didn’t already have it (over just a regular operator) so I’m not so concerned with whatever issues I was having.

And I was just thinking of firing the laser in just four directions (up, down, left, right). As for rocks I wanted them to repeat, but since the map is what moves for player movement (and therefore the rocks are already dependent on map location) I am thinking if I repeated the map probably the rocks would also anyway, by association. Wasn’t thinking of any other map tiles (yet). Was just thinking of an infinite loop where the overall map is maybe 256x128 and once you approached the edge limit in any axis it just scrolled to the other end.

But I still have a lot of work to do yet (on this one and my other) before I’m ready to drag too much more out of ya :stuck_out_tongue:

So it’s a rectangular projectile?

They wouldn’t by default.
You could make them repeat, but there’s a problem.

If you want the rocks to be destructible then if you destroy one, that rock will be destroyed on every repetition of the map.

So if you destroyed all the rocks in one chunk of the map,
you’d have destroyed all the rocks on the map
because those other rocks are just ‘shadows’/‘reflections’ of the original rocks.

That is pretty much how it would work,
but you’d have to render the map multiple times.

The maths is a little tricky.

Okay, so I have (mostly) scrapped this initial game design in favor of reducing as much as I can, and because i would have had to have rewritten most of it anyway using functions, but I now have something thats still very similar, which I was hoping i might be able to merge with my other game (but, worst case scenario, could still use for something else).

All that being said, the most recent thing I did with my other game Ive also implemented here, which is the infinite background looping, and this is something that takes up the most resources and im hoping to slim down but am not sure how. I was thinking image compression might help, although between the two games i would already be at my RAM limitations, unless there was a way to do this that would also help cut down on RAM (and i dont know enough about compression to know).

If just looking into compression and seeing how i might implement it would be the way to go then I can do the legwork myself, im just not sure if thats the direction i should take. Here is my currrent code (sorry for the length, been having difficulty figuring out how to get header files working, but thats something i have been reading about in between posts):

#include <Arduboy2.h>
Arduboy2 arduboy;
uint8_t playerFrame = 0;
constexpr uint8_t worldWidth = 128;
constexpr uint8_t worldHeight = 64;
constexpr uint8_t tileWidth = 128;
constexpr uint8_t tileHeight = 64;
uint8_t const *playerSprite = nullptr;
uint8_t const *playerMask = nullptr;

struct PointXY {
  int x = 0;
  int y = 0;
};

PROGMEM const unsigned char planetBackgroundTile1[] = {
// Bitmap Image. No transparency
// Width: 64 Height: 64
128, 64, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
};

PROGMEM const unsigned char ardunautDown[] = {
// Bitmap Image. No transparency
// Width: 7 Height: 8
7, 8,
0x00, 0x18, 0xFE, 0x3B, 0xFE, 0x18, 0x00, 

0x00, 0x18, 0xFE, 0x3B, 0x7E, 0x38, 0x00, 

0x00, 0x18, 0xFE, 0x3B, 0xFE, 0x18, 0x00, 

0x00, 0x38, 0x7E, 0x3B, 0xFE, 0x18, 0x00, 
};

PROGMEM const unsigned char ardunautUp[] = {
// Bitmap Image. No transparency
// Width: 7 Height: 8
7, 8, 
0x00, 0x18, 0xFE, 0x3F, 0xFE, 0x18, 0x00, 

0x00, 0x18, 0xFE, 0x3F, 0x7E, 0x38, 0x00, 

0x00, 0x18, 0xFE, 0x3F, 0xFE, 0x18, 0x00, 

0x00, 0x38, 0x7E, 0x3F, 0xFE, 0x18, 0x00, 
};

PROGMEM const unsigned char ardunautLeft[] = {
// Bitmap Image. No transparency
// Width: 7 Height: 8
7, 8, 
0x00, 0x00, 0x3E, 0xE3, 0x3E, 0x1C, 0x00, 

0x00, 0x48, 0xB6, 0x73, 0xBE, 0x1C, 0x00, 

0x00, 0xC0, 0x3E, 0x2B, 0x76, 0x9C, 0x00, 

0x00, 0x48, 0xB6, 0x73, 0xBE, 0x1C, 0x00, 
};

PROGMEM const unsigned char ardunautRight[] = {
// Bitmap Image. No transparency
// Width: 7 Height: 8
7, 8, 
0x00, 0x1C, 0x3E, 0xE3, 0x3E, 0x00, 0x00, 

0x00, 0x9C, 0x7E, 0x33, 0xB6, 0x48, 0x00, 

0x00, 0x1C, 0xB6, 0x6B, 0x3E, 0xC8, 0x00, 

0x00, 0x9C, 0x7E, 0x33, 0xB6, 0x48, 0x00, 
};

PROGMEM const unsigned char ardunautDownMask[] = {
// Bitmap Image. No transparency
// Width: 7 Height: 8
7, 8, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
};

PROGMEM const unsigned char ardunautRightMask[] = {
// Bitmap Image. No transparency
// Width: 7 Height: 8
7, 8, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
};

PointXY planetBackground1;
PointXY planetBackground2;
PointXY planetBackground3;
PointXY planetBackground4;

void setup() {
  arduboy.boot();
  arduboy.setFrameRate(10);
  arduboy.clear();
  playerSprite = ardunautDown;
}

void loop() {
  if (!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  Sprites::drawExternalMask(64,32,playerSprite,playerMask,playerFrame,0);
  drawPlanetBackground();
  handleInput();
  arduboy.print(planetBackground2.y);
  arduboy.display();
}

void playerAnimate() {
  if (playerFrame < 3) {
    ++playerFrame;
  }
  else
    playerFrame = 0;
}

void drawPlanetBackground(){
  //Bottom right(player start)
  for (int x = 0; x < worldWidth; x += tileWidth ) {
    for (int y = 0; y < worldHeight; y += tileHeight) {
      Sprites::drawSelfMasked(x + planetBackground1.x, y - planetBackground1.y, planetBackgroundTile1, 0 );
    }
  }
  //Top right)
  for (int x = 0; x < worldWidth; x += tileWidth ) {
    for (int y = 0; y < worldHeight; y += tileHeight) {
      Sprites::drawSelfMasked(x + planetBackground2.x, y - planetBackground2.y - 64, planetBackgroundTile1, 0 );
    }
  }
  //Bottom left
  for (int x = 0; x < worldWidth; x += tileWidth ) {
    for (int y = 0; y < worldHeight; y += tileHeight) {
      Sprites::drawSelfMasked(x + planetBackground3.x - 128, y - planetBackground3.y, planetBackgroundTile1, 0 );
    }
  }
  //Top left
  for (int x = 0; x < worldWidth; x += tileWidth ) {
    for (int y = 0; y < worldHeight; y += tileHeight) {
      Sprites::drawSelfMasked(x + planetBackground4.x - 128, y - planetBackground4.y - 64, planetBackgroundTile1, 0 );
    }
  }
  //Bottom right limits and reset
  if (planetBackground1.y < -64){
    planetBackground1.y = 64;
  }
   if (planetBackground1.y > 64){
    planetBackground1.y = -64;
  }
  if (planetBackground1.x < -128){
    planetBackground1.x = 128;
  }
  if (planetBackground1.x > 128){
    planetBackground1.x = -128;
  }
  //Top right limits and reset
  if (planetBackground2.y < -128){
    planetBackground2.y = 0;
  }
  if (planetBackground2.y > 0){
    planetBackground2.y = -128;
  }
  if (planetBackground2.x < -128){
    planetBackground2.x = 128;
  }
  if (planetBackground2.x > 128){
    planetBackground2.x = -128;
  }
  //Bottom left limits and reset
  if (planetBackground3.x < 0){
    planetBackground3.x = 256;
  }
  if (planetBackground3.x > 256){
    planetBackground3.x = 0;
  }
  if (planetBackground3.y < -64){
    planetBackground3.y = 64;
  }
   if (planetBackground3.y > 64){
    planetBackground3.y = -64;
  }
  //Top left limits and reset
  if (planetBackground4.x < 0){
    planetBackground4.x = 256;
  }
  if (planetBackground4.x > 256){
    planetBackground4.x = 0;
  }
  if (planetBackground4.y < -128){
    planetBackground4.y = 0;
  }
  if (planetBackground4.y > 0){
    planetBackground4.y = -128;
  }
}

void handleInput(){
  if (arduboy.pressed(DOWN_BUTTON)){
  playerSprite = ardunautDown;
  playerMask = ardunautDownMask;
  playerAnimate();
  ++planetBackground1.y;
  ++planetBackground2.y;
  ++planetBackground3.y;
  ++planetBackground4.y;
}
if (arduboy.pressed(UP_BUTTON)){
  playerSprite = ardunautUp;
  playerMask = ardunautDownMask;
  playerAnimate();
  --planetBackground1.y;
  --planetBackground2.y;
  --planetBackground3.y;
  --planetBackground4.y;
}
if (arduboy.pressed(LEFT_BUTTON)){
  playerSprite = ardunautLeft;
  playerMask = ardunautRightMask;
  playerAnimate();
  ++planetBackground1.x;
  ++planetBackground2.x;
  ++planetBackground3.x;
  ++planetBackground4.x;
}
if (arduboy.pressed(RIGHT_BUTTON)){
  playerSprite = ardunautRight;
  playerMask = ardunautRightMask;
  playerAnimate();
  --planetBackground1.x;
  --planetBackground2.x;
  --planetBackground3.x;
  --planetBackground4.x;
}
if (!arduboy.pressed(UP_BUTTON) && !arduboy.pressed(DOWN_BUTTON) && !arduboy.pressed(LEFT_BUTTON) && !arduboy.pressed(RIGHT_BUTTON)) {
    playerFrame = 0;
  }
}

This is infinite (or as near infinite at least):

You can never have truly infinite because you’re limited by the ranges of the data types you use.

There are other ways to do it, but you have to be clever with your calculations.

If you just want the same background repeated over and over then there’s a relatively simple way to do it.

That said, I’d need to sit down and work out the maths.

Basically the idea is to just use the remainder (i.e. modulo) of the player’s position divided by the the background tile size to figure out where to draw the background tiles.

I think you should look at your architecture before you reach for image compression.

Your images only cut into progmem, they don’t affect RAM.

It depends on how you’re trying to use them.

Start by putting stuff in a header file and then doing #include "NameOfHeaderFile.h" at the top of your main file.

If you encounter errors, post the errors and ask how to fix them.

The only way to know how to fix errors (other than studying the language in depth enough to be able to make sense of the terminology) is to experience them and learn the common causes.


To me, these scream “all these variables have the same value”:

and that would be the case if it weren’t for:

So if you could figure out a way to do that wrapping during the image calculations then you could reduce yourself to just one background offset.


That said,
there should also be a way to just have the ship/camera move and to keep the background static.
(Like I did in the recent StarTerrain example.)

1 Like

Ah, thank you! Yeah, I had been looking at the star terrain example you posted just thinking it would be easier to implement here (because it doesn’t use parallax) so I figured this would be a better place to start, I just didn’t quite understand it yet, but that definitely helps to clarify, thanks!

Like I say, it could probably be modified to do parallax,
but I’d have to figure out the maths.

If there’s anything about it that you don’t understand, be sure to ask.

Not stopping to make sure you understand what’s going on is like going on a road trip
(or perhaps more aptly, launching a space rocket) without checking your fuel reserves.

1 Like

So Im not sure why it hadnt just occurred to me to do this sooner, but as the two separate programs are now they share some structs in common and i was curious to see how much it might slim down if i tried to combine the two, and after removing one of the parallax layers (because two still looks just as good) and putting the separate games together it only took up an additional 16 bytes of RAM (which i may be able to slim down further), and that still leaves me with 1216 (which is way more than i thought). So im thinking i can just use what I have.

Id still like to go back to the starTerrain example you posted in the other thread at some point (probably for that procedural generation project once ive worked my way up to it), but for now another reason why id like to keep it more along the lines how it is isnt just for the background draw repetition but for the coordinate repetition also, so it keeps the player restricted to a certain area while making it seem larger.

But incidentally, now I have to separate things into header files or itll get way out of hand real quick… but I started doing that and noticed my sprites werent drawing correctly (the rocket or the astronaut) and it was cutting about half a pixel off one side (in each program separately) and i needed to address that, wasnt sure why this might be happening (but i think it might just be something in the scaling how i exported it, going back to fix that now).

Sorry, seemed it was just the emulator, sprites display fine on the actual arduboy.

Ive updated the github with separate header files (that i could still use to break up more yet) and some additional features such as a space station and player movement that simulate orbit, and two more game states for on foot and entering the space station (the latter i havent done anything with yet, but you can exit with the b button), and i updated the github with a readme for the controls. Surprisingly all of this still leaves me with 1208 bytes RAM and about 40% ROM.

Im hoping i can squeeze in a few additional features like a laser, a shop in the space station where you can maybe buy upgrades for your ship, jetpack, and laser, some enemies, then i can have the basis for a core gameplay loop and have ‘planets’ you can go to that are just a slight variation in the gravity and difficulty levels of the enemies (damage, speed, etc)… I know this may be awfully hopeful trying to fit in so much too, so im not trying to get too ahead of myself or anything yet, im just surprised ive been able to fit in as much as i have so far and thats just what i was hoping for, so if i need to trim things down or work it into something else i could always go a different route, just what i have in mind right now.

Next I need to figure out a way to spawn enemies (randomly) but in a way their locations persist so they can move around

Okay, ive gotten enemy locations to work and to get them to chase the player, the only issue is they also rely on the map movement per player input and are tied to just one square (so they move when it resets), and i need to figure out how to get them to only chase within a certain range, at this point they just flock :stuck_out_tongue:

1 Like

This is a bit of a different situation though.

The reason StarTerrain can get away with using only a hash function is because the stars don’t need any kind of pattern to them.
They can look completely random and nobody will notice.

I don’t know what you mean by ‘coordinate repetition’.

Memory always seems to go up the quickest at the beginning for various reasons.
(Mainly due to functions being used for the first time.)
Then it slows down a bit afterwards.

I suddendly remembered I never got round to explaining how to do collisions.

The general idea is just that before you actually move something, you check where it’s going to end up and if that new position would make the object collide with something, you stop it colliding and move it to just before the collision would occur.

Depending on how you want the collision to be resolved, you’d possibly either remove both objects or you’d calculate a new velocity for one or more of the objects involved in the collision.

Have you looked at some of the larger Arduboy games?
You’d be surprised what you can fit in.

If you haven’t played it, give Dark & Under a go sometime.
I was one of the three people working on it and my primary duty was to find ways to save memory.
(I’d find some memory savings, and then @filmote would find ways to use up the memory I’d just clawed back. :P)

Usually when someone says ‘persist’ they mean after the device is turned off.

If you just mean ‘between frames’, then the answer is RAM.

Allow me to introduce you to my friend, Euclid of Alexandria.

EuclideanDistance

int DistanceSquared(int x0, int y0, int x1, int y1)
{
	const int xDistance = (x1 - x0);
	const int yDistance = (y1 - y0);
	return ((xDistance * xDistance) + (yDistance * yDistance));
}

float Distance(int x0, int y0, int x1, int y1)
{
	return sqrt(DistanceSquared(x0, y0, x1, y1));
}

Using just the squared distance can be a good optimisation if you’re only doing a comparison because sqrt is an expensive operation.

2 Likes

Hoooo boy, have i seen some of the games you guys have put out, buddy i daydreamed about em for a good year and a half before I could finally play em :stuck_out_tongue: I just don’t know half as much to be able to really utilize things as best I can yet.

And sorry, to clarify I essentially have my map set up using four different 128x64 backgrounds in the quadrants of a larger rectangle, and since the player input moves the map and not the player, the tiles of the map itself actually repeat (not just the background sprite but anything that’s tied to them also (unless I have it set to update the position like with the launchpad).

I have the player set to spawn in the top left quadrant now because anything would be drawn down from there so I can also use the map itself as my coordinate system… And i set up the enemies to spawn similarly to how I set it up for the rocks to spawn in my very first program, using a for loop to iterate through two arrays (one for x, one for y) and populate with random values within the range of my I’ve overall map size (until the specified number of enemies) only this time i added an additional for loop inside to check if the values were greater or less than player location (and set it to reset the value of the initializer so it just keeps updating). I figure this may not be the best way to do it, just figured I’d try and hey, it worked :stuck_out_tongue:

I just got the enemy sprites set up on their own coordinate system now though (independent of the map tiles but still moving with them), so now I move on to setting the chase parameters using what you just posted (and figuring out the collision detection after that), thanks a lot!

EDIT: Actually nevermind, still having some difficulty figuring out how i would work the euclidean distance into what I have… updated the code in my Github but will have to keep thinking on it (its a bit of a mess).

My instinct is that it should be cheaper the other way around,
but I can’t think what the maths involved would be like.

If I had time to sit down and think about it then I think I could probably think of a way around it,
but it would take me a while to process all those if (background1.y < -64)-style conditions and figure out how the backgrounds are actually moving.

I originally misunderstood what you meant by this.

Looking at the code, instead of doing:

uint16_t alienX[50];
uint16_t alienY[50];

You could just do:

Point alienPositions[50];

And then instead of:

  for (int x = 0; x < 20; x++) {
    alienX[x] = random(0, 252);
    alienY[x] = random(0, 124);
}

Do:

for (uiny8_y index = 0; index < 20; ++index)
	alienPositions[index] = Point(random(0, 252), random(0, 124));

I’d offer a solution, but I’m actually not sure how what you’ve got works at the moment.

You have an Alien struct, but only one alien?
Yet you store enough coordinates for 50 aliens?

I would have expected you’d just have an array of Aliens.

Sorry, how do you mean?

Sorry, yeah I actually meant to remove that struct after I ended up just using the arrays, didnt mean to make things more confusing than necessary.

And as for the rest of it, I have it set up so that when each quadrant reaches its respective limits it resets the backgrounds coordinates so the backgrounds continually alternate in each axis (and by association whatever may be tied to them). I got the alien positions to work correctly by just increment them per player movement alongside the map, rather than their positions being set by adding the map increment during the draw (if that makes any more sense). But I also wasnt sure how to move from two one dimensional arrays to using a two dimensional array so ill fix that, thanks!

So basically, at the start (before any backgrounds reach their limits and reset), the player is in the top left quadrant with the backgrounds drawing down and right, and the alien locations fit within that same 2x2 rectangle now (but also dont repeat with the backgrounds). The player draw command is reflective of the same coordinate system (at this point) because it also draws from the top left, but since the player stays in the center of the screen I made the secondary object ardunautMapPos in order to keep track of it where it is in relation to the the map and alien coordinates.

I get the idea of the euclidean distance because im pretty familiar with geometry and trig, and i would think that inputting the alienX and Y and the player X and Y would just give the distance (and you could say if the distance is shorter than whatever specified distance then close that distance to chase), im just not sure how to tell it to shorten that distance in turn.

The camera and player move, the world stays where it is.

Then the backgrounds don’t move until it’s time to draw them.

At which point they temporarily ‘move’ so they can be drawn,
but they don’t actually move, you’re just calculating their screen coordinates.

That’s what I did in the StarTerrain demo.

Oh hang on, I think I know.
You’re using the Egyptian log trick.

EgyptianLogs

As the block moves off the back log, the back log is moved to the front.

Except the logs are your background(s).

Nope, still don’t get it.

The example I gave isn’t a two dimensional array.
It’s a one dimensional array of Point instead of int.

That’s good. When I started programming I’d forgotten everything and had to relearn it all.

Yes, precisely.

Vectors!

Except I don’t have time to explain them because it’s late,
so here’s (rather oddly for me) a Youtube playlist explaining various aspects of vectors:

(If it’s not showing up, click here.)

You don’t necessarily have to listen to the whole of each video, you can skip through every few seconds and still make sense of it.

1 Like

Ah, yeah, thats why I was initially trying to do what you did in the star terrain demo for this part, figured it was probably more sensible, was still just a bit too far over my head yet though, sorry.

And yeah, thats essentially what im doing, I just saw some benefits in doing it that way over just drawing the backgrounds in a different location because the egyptian log trick still retains the map values for objects tied to it (and I wasnt sure if the star terrain demo was similar).

I just meant that I was initially having issues with the alien sprites because they were tied to the background coordinates so when they got egyptian logg’d the enemy coordinates did too, so now I have them on the own coordinate system (basically overlaying the background’s) which i increment the same way I move the map via player input.

And sorry, id just gotten myself more confusing thinking the points would have two values for the array… im still not too strong on arrays and i think ive been subconsciously avoiding them so ill just go ahead and get myself better read on that too, and points for that matter.

And i forgot about vectors, but i already have some idea of that from my old job field too, luckily. Just been getting rusty with my old skillset and kind of forget since its not right in front of me. But ill definitely look more into that and get a better idea of the math involved, thanks!

And no worries, appreciate the help as it is, sorry i seem to make more of a mess than i can clean up but hopefully that should help get things straightened out more here :stuck_out_tongue: thanks again!

Which part?

If you’re still worrying about what goes on in the hash function, don’t worry about it.
You don’t have to understand how it works to understand what it does.

It could just as easily be:

uint32_t hash(uint32_t value)
{
	// 127 and 8191 are prime numbers
	// (specifically 'mersenne primes')
	return ((value + 127) * 8191);
}

uint32_t hash(uint16_t x, uint16_t y)
{
	const uint32_t x2 = x;
	const uint32_t y2 = y;
	const uint32_t value = ((x2 << 16) | (y2 << 0));

	return hash(value);
}

uint32_t hash(int16_t x, int16_t y)
{
	return hash(static_cast<uint16_t>(x), static_cast<uint16_t>(y));
}

The important thing is the job that the hash function does:

  • It takes a pair of coordinates (x and y) and returns a seemingly random value (i.e. there should be no obvious pattern to the output)
  • It provides a valid output for all possible combination of input values (i.e. it’s a ‘surjective’ function)
  • It always provides the same output for the same pair of inputs and it has no side effects (i.e. it’s a ‘pure’ function)

Any function that meets those three criteria could be used for the hash,
the only difference would be in the quality of the result.

I’m not sure what you mean by this.

Or this.

I think you might be getting the meaning of ‘coordinate system’ mangled.

Usually for a 2D game there are only two coordinate systems that need to be taken into account: world coordinates and view/camera/screen coordinates.
(For a 3D game there’s usually two other systems to take into account.)

The typical way to handle a 2D world is to give all the terrain static coordinates (i.e. non-changing coordinates),
and all movable objects/entities dynamic coordinates (i.e. changing coordinates).

Then there’s a ‘camera’ object which represents the screen,
and that object moves around the map.

Then when it’s time to render, the world coordinates are translated to screen coordinates by subtracting the camera’s position.

CameraSmall
(Excuse the hand-drawn diagrams, I find hand drawing quicker and easier than digital drawing.)

For example, if the camera object is at (16, 16) (top left coordinate), and an enemy is at (64, 64),
then when they’re translated into camera/view/screen space,
the camera will be at (0, 0) (so the camera’s top left coincides with the screen’s top left),
and the enemy will be at (48, 48) ((64 - 16) == 48).

They’re literally just a block of memory with values laid end to end.
For example, int array[4] would be 8 bytes.
In memory it would look something like this:

[0x0400] array[0], low byte
[0x0401] array[0], high byte
[0x0402] array[1], low byte
[0x0403] array[1], high byte
[0x0404] array[2], low byte
[0x0405] array[2], high byte
[0x0406] array[3], low byte
[0x0407] array[3], high byte

The hex values represent RAM addresses, I made up the address values.
The low byte comes first because AVR is ‘little endian’ (like x86),
which means lower valued bytes are stored first.

A Point consists of 2 int16_t, which are two bytes.
In memory a Point looks something like this:

[0x0500] x, low byte
[0x0501] x, high byte
[0x0502] y, low byte
[0x0503] y, high byte

So an array of two points, Point points[2], would look like this in memory:

[0x0600] points[0].x, low byte
[0x0601] points[0].x, high byte
[0x0602] points[0].y, low byte
[0x0603] points[0].y, high byte
[0x0604] points[1].x, low byte
[0x0605] points[1].x, high byte
[0x0606] points[1].y, low byte
[0x0607] points[1].y, high byte

That’s literally all there is to it,
the data is just stored in memory contiguously,
and the compiler handles all the maths needed for moving the data around.

2 Likes

I’m still trying to determine that :stuck_out_tongue: just because there are a lot of parts that are new to me, so I can follow up to a point but like I said, then it just kind of starts to blend together, but I didn’t want you to have to break it down for me step by step so to spare you a lengthy explanation I’m trying to better inform myself on what’s going on at each step by breaking down and identifying each part I don’t know.

But that definitely helps a bit knowing that about hash functions- I remember you mentioning that before but sometimes I can’t see the forest for the trees when being faced with a lot of new information (an unfortunate byproduct of adhd, like I said, but something I’m working on), but I also forgot that somewhere along the lines so when I give it another look I’ll know that I shouldn’t need to follow along with that part as much. I just want to be able to follow along with the rest of it so I can change it to use for the other part without having to ask you to change it (and because I’m trying to do as much of the work myself so I’m not asking others to do it).

And as for my looser definition of coordinate system I suppose it isn’t in the usual sense then (as is in camera and word coordinates), I just can’t help but think of it in those terms because it’s still confined to some sort of coordinate system based off the screen x/y draw origin- all my map tile coordinates are based (and placed) in relation to it and so are my objects- only initially, since I move the map coordinates for player movement, other objects were tied to the map also (because, for example, during the draw command for my launchpad or enemies the x and y values were their x and y + background x and y so they would move with it).

Although effectively there are multiple actual coordinate systems at play (for the backgrounds, player position, and enemy locations), more so now that I changed the enemy coordinates to be their own coordinate system (so when player inputs left rather than just background x + and enemy x drawn with that, player inputs left and both background x + and enemy x+).

But given how much more complicated (and convoluted) my method gets I can see the benefits of the static world and camera method, so I’ll try to review again and implement it.

Also I better organized my game by further breaking it down into more header files (one for in flight, one for on foot, one for the menu, etc) to make it easier to look at/read. Not sure why I was having difficulty with that before

My advice would be to write it down as you go.

Draw some diagrams too.
(The stacks of paper on my desk are getting ridiculous.)

Every time you understand something, give yourself a biscuit.
(Perhaps not a biscuit, you’d get through at least 100 by the end, and that can’t be good for your health. But you get the idea - keep the reward loop running.)

Ah, ok, in that case technically the enemy coordinates are local to the background coordinates,
which does sort of mean that’s a separate coordinate system.

But that’s a cause for concern because that means there’s no such thing as ‘global’ coordinates,
because everything keeps moving.

Yeah.
I think really you need to separate the background rendering from the world itself.

At some point I’ll try to sit down and figure out a way to render your backgrounds infinitely without actually ‘moving’ the backgrounds as such.

I’ve put it off because I don’t like maths.
(Saying “I don’t like maths” is probably a terrible thing for a programmer to say given the amount of maths involved in programming.)

Do you need all four backgrounds or are you happy with having just one background tiled infinitely?
(The parallax is going to complicate things, that much I’m sure of.)

1 Like