Open ghost opened 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.
Thank you for the advice and the example! This certainly saves me a few headaches. :)
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: `
TFT_eSPI tft = TFT_eSPI(); // Define display. TFT_eSprite character = TFT_eSprite(&tft); // Define character sprite.
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.
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.
Is there a way to make this border transparent?
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.
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?
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.
Fascinating, thank you for the information and the link to that example. I will certainly look into how they implemented that.
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.
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.