Bodmer / PNG_TEST_ONLY

Work in progress PNG decoder test sketches
8 stars 1 forks source link

Png output to a sprite. #1

Open ghost opened 3 years ago

ghost commented 3 years ago

I've been messing around with these code examples for a little bit, and they're an absolutely amazing resource. I've gotten them to output png images from an sd card, and that is definitely very helpful. Studying the code in your other repo for outputting jpeg's to sprites, it seems as if it takes and uses the decoded jpeg and then outputs it to the sprite. Would this be possible with a bit of modification to both the JpegDraw function from TFT_eFEX and the function proof of concepts here? I'm certainly willing to take a shot at modifying both to draw png's to a sprite, but I'm not sure if it's even possible based on this example.

Bodmer commented 3 years ago

The API for writing to a sprite is similar to writing to the tft, so you just need to create a sprite and use say spr. instead of tft.

I have added the png_to_sprite example. Note that both sprites and the png decoder consume a lot of RAM. So for large sprites you will need a board with a lot of RAM. With and ESP32 you can buy boards with PSRAM fitted, then the sprite class will use that.

ghost commented 3 years ago

Thank you for the advice and the example! This certainly saves me a few headaches. :)

ghost commented 3 years ago

Another quick question, I'm not exactly sure what I did wrong here. The sprite seems to be leaving itself behind when I move it. Here is the code I ran: `

define USE_LINE_BUFFER

include

include

include

include

TFT_eSPI tft = TFT_eSPI(); // Define display. TFT_eSprite character = TFT_eSprite(&tft); // Define character sprite.

include "pngle.h"

include "support_functions.h" // Functions for mapping png's to sprites.

include "input.h" // The functions used for input.

void setup() { Serial.begin(115200);

init_controls();

digitalWrite(15, HIGH); // TFT Screen Chip Select digitalWrite(5, HIGH); // SD Chip Select

tft.begin(); // Initiate display tft.setRotation(1); // Set to landscape mode. tft.fillScreen(0); // Set the screen to blank.

character.createSprite(32, 32);

if (!SD.begin()) { // If the SD card cannot be mounted. Serial.println("Failed to mount SD card."); return; } }

class Game { public: Game() { game_setup(); mainloop(); delay(2000); } private: int pos[2] = {0, 208};

int jumpHeight = 64; // without momentum
float jumpSpeed = 1; // without momentum
int initialHeight;
bool is_jump = false;
bool is_jumping = false;

int gnd = 208;

float walkSpeed = 1.5; // without momentum

float momentum = 0.0;
int momentumMove = 1; // 0 is left, 1 is right.
int moving = 0; // 0 = no movement, 1 = moving right, 2 = moving left
int last_moving = moving;

int cooldown = 0; // cooldown for dodge

void game_setup() {
  tft.fillScreen(0);
  setPngPosition(0, 0);
  load_file(SD, "/right_explorer.png");
}
void mainloop() {
  while (true) {
    movement();
    on_screen();
    update_display();
    delay(10);
  }
}
void movement() {
  int X = readX();
  int jump = readBlueButton();
  int dodge = readZ();

  if (dodge == 0 && cooldown == 0) {
    cooldown = 200;
    pos[1] = 0;
  }

  if (jump == 1 && is_jump == false && is_jumping == false) {
    is_jump = true;
    is_jumping = true;
    initialHeight = pos[1];
  }
  if (is_jump == true) {
    if (pos[1] > (initialHeight-(jumpHeight*(1+momentum)))) {
      pos[1] -= jumpSpeed;
    } else if (pos[1] <= (initialHeight-(jumpHeight*(1+momentum)))) {
      is_jump = false;
    }
  }
  if (is_jump == false) {
    if (pos[1] < gnd) {
      pos[1] += 2*jumpSpeed;
    } else {
      is_jumping = false;
    }
  }
  if (X < 1800) { // If joystick says move left
    pos[0] -= (walkSpeed-.2+momentum);
    moving = 2;
  } else if (X > 2000) { // If joystick says move right
    pos[0] += (walkSpeed+momentum);
    moving = 1;
  } else {
    moving = 0;
  }

  if (moving != 0) {
    if (momentum < 0.5) {
      momentum += 0.01;
    }
  } else {
    momentum = 0;
  }

  int last_moving = moving;
  if (cooldown != 0) {
    cooldown--;
  }
}

void on_screen() {
  if (pos[0] < 0) {
    pos[0] = 0;
  } else if (pos[0] > 288) {
    pos[0] = 288;
  }

  if (pos[1] < 0) {
    pos[1] = 0;
  }
}
void update_display() {
  character.pushSprite(pos[0], pos[1]);
}

};

void loop() { Game test; }` Here is the output from moving the character, jumping, and dodging, the highlighted character is the sprite. image_123927839

Bodmer commented 3 years ago

Yes, this is pixel smear because pixels at the edge are not overwritten. For a 1 pixel move you need to have a black 1 pixel border.

Delete line 100 in this example and you will see what I mean.

ghost commented 3 years ago

Is there a way to make this border transparent?

Bodmer commented 3 years ago

Yes, a colour can be designated as transparent, but that will lead to the same problem. The screen is like a piece of paper, if you draw something on it, then draw the same thing somehwere else then the first drawing it still there, so it has to be erased. Graphics cards handle this by having large RAM buffers to hold image "planes" that allow the background to be replaced automatically when a foreground image moves.

This makes creating games on these displays a more complex process. There are "games" software engines for the ESP32 but these typically work with small screens so full image buffers can be kept in RAM.

ghost commented 3 years ago

So if I was to let's say have a background image drawn to the screen, the artifacts would still be an issue I assume?

Bodmer commented 3 years ago

Yes, that would be like drawing on top of a picture, you have to redraw the background picture if you draw somewhere else on the picture. You will see that "old" computer games used to avoid overlaps of a sprite moving over other complex graphics, see here for an example where a small full screen buffer is used to redraw the whole screen every frame. Later gaphics chips handled things and made life much easier.

ghost commented 3 years ago

Fascinating, thank you for the information and the link to that example. I will certainly look into how they implemented that.

ghost commented 3 years ago

Sorry to be such a bother, but I have another question. I've been working on rewriting the files into a class so I can load and display more than one png to more than one sprites. My knowledge of C++ isn't the best, and I've ended up getting my esp stuck in a boot loop. The code I have written is in my fork of this repository. If you have any suggestions on what I have done wrong, they would be much appreciated.