Using the right approach or start again

Hi,

I'm asking for some general advice on my approach, programming and use of correct/best used functions.

I''m returnig for a 3rd session of Ardunio after a 5 year break, in the past my projects never really needed to run fast and were for my own interest, programs were long and messy. I started a project that works as wanted but the program was getting yet again long and messy (looking).

So I've abandond that, and decided to focus on learning and layout, my set up on the breadboard is mega pro mini with Adafruit 160x128 tft and four buttons. All working correctly, I thought I'd like to learn more about the graphic side of things so started a simple Space Invader scetch(just one invader at the mo)

My background is a child of the 60's that paid a months wages for a C64 and taught myself basic, which was very useful as a cnc machinist using logic statements in cnc programs.

My Space Invader program is simple in the use of draw pixels and lines, the Single Invader and base have black pixeks around the outside so they move without blinking, and that seems to work well, for now at least. On the C64 I used PEEK and POKE to read values (seems the same as read and write to the Ardunio EEPROM)

Is there a way to read back from the tft that is faster than logic statements (to detect the colour of the pixel above the bullet)

Should I be using the Progmem function instead of lines and pixels? Or I did read on Adafruit page the use of a canvas with the tft? Advice would be great and taken as Ican't wait to learn and move on to Pac Man

Phil.

int invad_1_pos_x = 10;
int invad_1_pos_y = 50;
int invader_direction = 1;
int base_1_pos_x = 80;
int base_1_pos_y = 116;
int base_bullet_pos_x;
int base_bullet_pos_y;
int base_bullet_fired;
int life = 3;
int score = 0;
int high_score_1;
int button_1; //RIGHT
int button_2; //BLUE ~ FIRE
int button_3; //LEFT
int button_4; //RED
int active_button = 0;
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
#include <Wire.h>
#define TFT_CS        42
#define TFT_RST        47 // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC         46 //card_CS white wire   45
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  tft.initR(INITR_BLACKTAB);
  tft.setRotation(1);
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextColor(ST77XX_GREEN, ST77XX_BLACK);
  tft.setCursor(2, 2);
  tft.setTextSize(1);
  tft.print("Score"); tft.setCursor(36, 2); tft.print(score);
  tft.setCursor(110, 2);
  tft.print("Lives"); tft.setCursor(144, 2); tft.print(life);
  draw_base();
}

void loop() {
  invader_move(); draw_invader();
  button_check();
  game_logic();
  active_button = 0;
  if (invad_1_pos_y >= 108 && base_1_pos_x == invad_1_pos_x) {
    //tft.setCursor(30, 60);tft.setTextSize(2);tft.println("Game Over");
    while (1);
  }
}
//===============================================================
void game_logic() {
  if (active_button == 1 && base_1_pos_x < 144) {
    base_1_pos_x++; draw_base();
  }
  if (active_button == 3 && base_1_pos_x > 2) {
    base_1_pos_x--; draw_base();
  }
  if (active_button == 2 && base_bullet_fired == 0) {
    base_bullet_pos_x = base_1_pos_x + 6; base_bullet_pos_y = base_1_pos_y - 4; base_bullet_fired = 1;
  }
  if (base_bullet_fired == 1) {
    base_bullet();
  }
}
//===============================================================
void base_bullet() {
  if (base_bullet_pos_x + 1 >= invad_1_pos_x && base_bullet_pos_x <= invad_1_pos_x + 11 && base_bullet_pos_y >= invad_1_pos_y && base_bullet_pos_y <= invad_1_pos_y + 8) {
    score = score + 42;  tft.setCursor(36, 2); tft.print(score);
    base_bullet_fired = 0;
    tft.drawFastVLine(base_bullet_pos_x, base_bullet_pos_y - 2, 6, ST77XX_BLACK);
    tft.fillRect(invad_1_pos_x, invad_1_pos_y , 12, 10, ST77XX_BLACK);
    tft.drawFastVLine(base_bullet_pos_x, base_bullet_pos_y - 6, 6, ST77XX_BLACK);
    invad_1_pos_x = 10; invad_1_pos_y = 50;
  }

  if (base_bullet_pos_y > 12) {
    base_bullet_pos_y--;
    tft.drawFastVLine(base_bullet_pos_x, base_bullet_pos_y - 2, 4, ST77XX_GREEN);
    tft.drawPixel(base_bullet_pos_x, base_bullet_pos_y + 4, ST77XX_BLACK);
  }
  else {
    base_bullet_fired = 0;
    tft.drawFastVLine(base_bullet_pos_x, base_bullet_pos_y - 2, 6, ST77XX_BLACK);
  }
}
//===============================================================
void invader_move() {
  if (invader_direction == 1 && invad_1_pos_x <= 141) {
    invad_1_pos_x++; return;
  }
  if (invader_direction == 1 && invad_1_pos_x >= 142) {
    invader_direction = 2;
    invad_1_pos_x++; invad_1_pos_y++; return;
  }
  if (invader_direction == 2 && invad_1_pos_x >= 140) {
    invad_1_pos_y++; invader_direction = 0; return;
  }
  if (invader_direction == 2 && invad_1_pos_x <= 3) {
    invad_1_pos_y++; invader_direction = 1; return;
  }
  if (invader_direction == 0 && invad_1_pos_x >= 3) {
    invad_1_pos_x--; return;
  }
  if (invader_direction == 0 && invad_1_pos_x <= 2) {
    invader_direction = 2;
    invad_1_pos_x--;  invad_1_pos_y++; return;
  }
}
//===============================================================
void button_check() {
  if (digitalRead(2) == HIGH) {
    active_button = 1; return;
  }
  if (digitalRead(3) == HIGH) {
    active_button = 2; return;

  }
  if (digitalRead(4) == HIGH) {
    active_button = 3; return;

  }
  if (digitalRead(5) == HIGH) {
    active_button = 4; return;
  }
}
//===============================================================
void draw_base() {
  tft.drawLine(base_1_pos_x + 1, base_1_pos_y + 6, base_1_pos_x + 11, base_1_pos_y + 6, ST77XX_GREEN);
  tft.drawLine(base_1_pos_x + 1, base_1_pos_y + 5, base_1_pos_x + 11, base_1_pos_y + 5, ST77XX_GREEN);
  tft.drawLine(base_1_pos_x + 2, base_1_pos_y + 4, base_1_pos_x + 10, base_1_pos_y + 4, ST77XX_GREEN);
  tft.drawLine(base_1_pos_x + 5, base_1_pos_y + 3, base_1_pos_x + 7, base_1_pos_y + 3, ST77XX_GREEN);
  tft.drawLine(base_1_pos_x + 5, base_1_pos_y + 2, base_1_pos_x + 7, base_1_pos_y + 2, ST77XX_GREEN);
  tft.drawLine(base_1_pos_x + 6, base_1_pos_y + 0, base_1_pos_x + 6, base_1_pos_y + 1, ST77XX_GREEN);
  tft.drawPixel(base_1_pos_x + 0, base_1_pos_y + 6, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 0, base_1_pos_y + 5, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 1, base_1_pos_y + 4, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 4, base_1_pos_y + 3, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 4, base_1_pos_y + 2, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 5, base_1_pos_y + 1, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 5, base_1_pos_y + 0, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 7, base_1_pos_y + 0, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 7, base_1_pos_y + 1, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 8, base_1_pos_y + 2, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 8, base_1_pos_y + 3, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 11, base_1_pos_y + 4, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 12, base_1_pos_y + 5, ST77XX_BLACK);
  tft.drawPixel(base_1_pos_x + 12, base_1_pos_y + 6, ST77XX_BLACK);
}
//===============================================================
void draw_invader() {
  tft.drawPixel(invad_1_pos_x + 3, invad_1_pos_y + 1, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 9, invad_1_pos_y + 1, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 4, invad_1_pos_y + 2, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 8, invad_1_pos_y + 2, ST77XX_GREEN);
  tft.drawLine(invad_1_pos_x + 3, invad_1_pos_y + 3, invad_1_pos_x + 9, invad_1_pos_y + 3, ST77XX_GREEN);
  tft.drawLine(invad_1_pos_x + 2, invad_1_pos_y + 4, invad_1_pos_x + 10, invad_1_pos_y + 4, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 4, invad_1_pos_y + 4, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 8, invad_1_pos_y + 4, ST77XX_BLACK);
  tft.drawLine(invad_1_pos_x + 1, invad_1_pos_y + 5, invad_1_pos_x + 11, invad_1_pos_y + 5, ST77XX_GREEN);
  tft.drawLine(invad_1_pos_x + 3, invad_1_pos_y + 6, invad_1_pos_x + 9, invad_1_pos_y + 6, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 1, invad_1_pos_y + 6, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 1, invad_1_pos_y + 7, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 11, invad_1_pos_y + 6, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 11, invad_1_pos_y + 7, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 3, invad_1_pos_y + 7, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 9, invad_1_pos_y + 7, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 4, invad_1_pos_y + 8, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 5, invad_1_pos_y + 8, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 7, invad_1_pos_y + 8, ST77XX_GREEN);
  tft.drawPixel(invad_1_pos_x + 8, invad_1_pos_y + 8, ST77XX_GREEN);
  // outside
  tft.drawPixel(invad_1_pos_x + 0, invad_1_pos_y + 7, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 0, invad_1_pos_y + 6, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 0, invad_1_pos_y + 5, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 0, invad_1_pos_y + 4, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 1, invad_1_pos_y + 4, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 1, invad_1_pos_y + 3, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 2, invad_1_pos_y + 3, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 2, invad_1_pos_y + 2, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 3, invad_1_pos_y + 2, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 2, invad_1_pos_y + 1, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 2, invad_1_pos_y + 0, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 3, invad_1_pos_y + 0, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 4, invad_1_pos_y + 0, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 4, invad_1_pos_y + 1, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 5, invad_1_pos_y + 1, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 5, invad_1_pos_y + 2, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 6, invad_1_pos_y + 2, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 7, invad_1_pos_y + 2, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 7, invad_1_pos_y + 1, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 8, invad_1_pos_y + 1, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 8, invad_1_pos_y + 0, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 9, invad_1_pos_y + 0, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 10, invad_1_pos_y + 0, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 10, invad_1_pos_y + 1, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 10, invad_1_pos_y + 2, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 9, invad_1_pos_y + 2, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 10, invad_1_pos_y + 3, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 11, invad_1_pos_y + 3, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 11, invad_1_pos_y + 4, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 12, invad_1_pos_y + 4, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 12, invad_1_pos_y + 5, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 12, invad_1_pos_y + 6, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 12, invad_1_pos_y + 7, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 10, invad_1_pos_y + 6, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 10, invad_1_pos_y + 7, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 9, invad_1_pos_y + 8, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 8, invad_1_pos_y + 7, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 6, invad_1_pos_y + 8, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 4, invad_1_pos_y + 7, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 3, invad_1_pos_y + 8, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 2, invad_1_pos_y + 7, ST77XX_BLACK);
  tft.drawPixel(invad_1_pos_x + 2, invad_1_pos_y + 6, ST77XX_BLACK);
}

Does this program compile and work as intended?

yes, there is a bug when the bullet hits the invader the invader clears with fill box black, but the bullet stays on screen as the fast line black does not tie up with the position of the bullet. easy to fix but i'm not sure i'm going in the right direction yet.

there isn't a way to draw a bitmap image on the screen instead of draw_invader()?

Why all the return statements?

It depends on the integration/communication between µC and graphics controller. If the graphics memory is implemented in private memory on the display board, direct access to pixels takes more clock ticks than on the C64. Also serial communication (I2C, SPI...) instead of a parallel (8 bit) port slows down communication dramatically; that's a matter of the display module and available pins on the Arduino.

@gcjr I don't know if there is? kinda why I'm asking :smiley:

If the logic that constructs screen objects can't detect a collision (as it sounds like you have a problem with), you need to repair that logic. Some display controllers have a screen buffer that resides on the Arduino, therefore you can access it. But I don't think the ST controller works that way, the buffer is on the display module and it's difficult to read, if you can do it at all.

In principle, you should never have to read back data from a display, because anything that is there, you put there.

I thought?! right or wrong the program will run faster, if button 1 is pressed the return means it doesn't bother reading the other 3 buttons and gets on with movement? maybe I'm wrong there also :smiley:

then

void button_check() {
  if (digitalRead(2) == HIGH) {
    active_button = 1;
  }
  else if (digitalRead(3) == HIGH) {
    active_button = 2;
  }
 else if (digitalRead(4) == HIGH) {
    active_button = 3;
  }
 else if (digitalRead(5) == HIGH) {
    active_button = 4;
  }
}

That's not really true. The sprites, as available on home computers, save the background under the sprite and restore it when the sprite moves. As display controllers don't handle (multiple) sprites it's required to save and restore the screen contents under the sprite in code.

1 Like

If not then rewrite the code like

if (cond1) result = 1;
else if (cond2) result = 2;
 ...

I see. However I don't think this program uses sprites.

1 Like

I had to google µC , and now have a headache, you let some of the smoke out of my head :frowning:

look this over

const byte PinButs [] = { A1, A2, A3, A4 };
const int Nbuts = sizeof (PinButs);

int active_button;

// -----------------------------------------------------------------------------
int
button_check (void)
{
    for (int n = 0; n < Nbuts; n++)
        if (LOW == digitalRead (PinButs [n]))
            return n+1;

    return 0;
}

// -------------------------------------
void loop ()
{
    active_button = button_check ();

    if (active_button)
        Serial.println (active_button);
}

// -------------------------------------
void setup ()
{
    Serial.begin (9600);

    for (int n = 0; n < Nbuts; n++)
        pinMode (PinButs [n], INPUT_PULLUP);
}
2 Likes
1 Like

Sorry for that :frowning:

Old micro processors (µP) are somewhat different from micro controllers (µC). A µC is not a single-chip home computer. You'll have to learn more about habits that were fine on a C64 but not on a µC. According to your background I'd recommend you a Raspberry Pi (RasPi) instead of an Arduino for writing (Arcade...) games.

BTW my first home computer was a PET2001, the last one an Atari ST, and a large number of other fantastic models in between.

1 Like

The logic works as intended detects that the bullet and invader overlap in space, it then removes the invader and updates the score by 42(the true meaning of Life) the bug is that the bottom part of the bullet is not removed coz the fastline black does not tie up with the bullet position, a simple mistake that I know I can fix.

I'm not aiming to and have no intention of writing fast running games, I know my skills are not where I would like them, this is only a learning path for me that happens to be fun and interesting, I have a brand new unused genuine Due in the box, which I'm sure would be a much better option than the mega Mini, but I know the mega pinout and libraries for screens and sensors I have from 5 years ago.

Thanks, got library and examples installed, will be having a play after dinner :smiley: