Wat je nodig hebt
-
Je 1.44" TFT-scherm, aangesloten zoals in project 01.
-
Je 0.96" OLED-schermpje, aangesloten zoals in de OLED-start — vier draadjes via I2C.
-
De HC-SR04 afstandssensor (project 02, op D6/D7).
- Verder niets nieuws. We hergebruiken alles wat al aangesloten is.
Het idee
Een groen blokje zweeft aan de linkerkant van het scherm. De hoogte ervan bepaal jij met je hand boven de sensor: hand dichtbij = blokje omhoog, hand ver weg = blokje omlaag. Van rechts komen rode blokjes aangeschoven. Laat ze niet je blokje raken; elk blokje dat je ontwijkt is een punt.
Een gevuld staafje zweeft aan de linkerkant van het scherm. De hoogte ervan bepaal jij met je hand boven de sensor: hand dichtbij = staafje omhoog, hand ver weg = staafje omlaag. Van rechts komen kleine open vierkantjes aangeschoven. Laat ze niet je staafje raken; elk vierkantje dat je ontwijkt is een punt.
Je hand als schuifknop
De kern is één regel die je al kent uit project 02: we meten de afstand en zetten die met
map() om naar een hoogte op het scherm. Dat is de hele besturing.
long cm = leesAfstand(); // hoe ver is je hand? (project 02)
cm = constrain(cm, 5, 40); // houd het tussen 5 en 40 cm
spelerY = map(cm, 5, 40, 14, 110); // dichtbij -> boven, ver -> onder
long cm = leesAfstand(); // hoe ver is je hand? (project 02)
cm = constrain(cm, 5, 40); // houd het tussen 5 en 40 cm
spelerY = map(cm, 5, 40, 10, 52); // dichtbij -> boven, ver -> onder
Waarom 10 en 52? Het scherm is maar 64 pixels hoog.
Boven laten we 10 pixels vrij voor de score, en het staafje is zelf 12 pixels hoog —
dus de laagste stand is 64 − 12 = 52. Zo kan je speler nooit half buiten beeld zakken.
Het complete spel
Hier is alles bij elkaar. Plak het als geheel in de Arduino IDE en upload. Houd je hand boven de sensor en ontwijk de rode blokjes. Ben je af? Houd je hand vlak bij de sensor om opnieuw te starten.
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8
#define TRIG 6
#define ECHO 7
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
// --- speler (links) ---
const int SPELER_X = 6, SPELER_B = 10, SPELER_H = 18;
int spelerY = 60, vorigeY = 60;
// --- hindernis (van rechts) ---
int hindX, hindY, vorigeHindX = 120;
int snelheid = 3;
int score = 0;
bool gameOver = false;
long leesAfstand() {
digitalWrite(TRIG, LOW); delayMicroseconds(2);
digitalWrite(TRIG, HIGH); delayMicroseconds(10);
digitalWrite(TRIG, LOW);
long duur = pulseIn(ECHO, HIGH, 30000);
if (duur == 0) return 40; // niets gezien -> "ver"
return duur * 0.034 / 2;
}
void nieuweHindernis() {
hindX = 122;
hindY = random(14, 128 - 8);
}
void toonScore() {
tft.fillRect(0, 0, 128, 12, ST77XX_BLACK);
tft.setCursor(2, 2);
tft.setTextSize(1);
tft.setTextColor(ST77XX_WHITE);
tft.print("Score: ");
tft.print(score);
}
void resetSpel() {
score = 0; snelheid = 3; gameOver = false;
spelerY = vorigeY = 60;
tft.fillScreen(ST77XX_BLACK);
nieuweHindernis();
vorigeHindX = hindX;
toonScore();
}
void setup() {
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT);
tft.initR(INITR_144GREENTAB);
randomSeed(analogRead(A5));
resetSpel();
}
void loop() {
if (gameOver) {
if (leesAfstand() < 10) { delay(300); resetSpel(); } // hand dichtbij = opnieuw
return;
}
// --- 1. hand bepaalt de hoogte ---
long cm = constrain(leesAfstand(), 5, 40);
spelerY = map(cm, 5, 40, 14, 128 - SPELER_H);
// --- 2. hindernis verplaatsen ---
tft.fillRect(vorigeHindX, hindY, 8, 8, ST77XX_BLACK); // wis oude
hindX -= snelheid;
tft.fillRect(hindX, hindY, 8, 8, ST77XX_RED); // teken nieuwe
vorigeHindX = hindX;
// --- 3. speler (laatst getekend = altijd bovenop) ---
tft.fillRect(SPELER_X, vorigeY, SPELER_B, SPELER_H, ST77XX_BLACK);
tft.fillRect(SPELER_X, spelerY, SPELER_B, SPELER_H, ST77XX_GREEN);
vorigeY = spelerY;
// --- 4. botsing of ontweken? ---
bool xRaakt = (hindX < SPELER_X + SPELER_B) && (hindX + 8 > SPELER_X);
bool yRaakt = (hindY < spelerY + SPELER_H) && (hindY + 8 > spelerY);
if (xRaakt && yRaakt) {
gameOver = true;
tft.fillScreen(ST77XX_BLACK);
tft.setTextSize(2); tft.setTextColor(ST77XX_RED);
tft.setCursor(10, 40); tft.println("GAME");
tft.setCursor(10, 60); tft.println("OVER");
tft.setTextSize(1); tft.setTextColor(ST77XX_WHITE);
tft.setCursor(10, 92); tft.print("Score: "); tft.print(score);
tft.setCursor(10, 106); tft.print("Hand dichtbij = opnieuw");
return;
}
if (hindX + 8 < SPELER_X) { // helemaal voorbij zonder raken
score++;
toonScore();
if (snelheid < 7) snelheid++; // wordt sneller
nieuweHindernis();
vorigeHindX = hindX;
}
delay(30);
}
Hier is alles bij elkaar. Plak het als geheel in de Arduino IDE en upload. Houd je hand boven de sensor en ontwijk de vierkantjes. Ben je af? Houd je hand vlak bij de sensor om opnieuw te starten.
oled.clearDisplay(),
tekent het hele beeld opnieuw en zet het met oled.display() in één klap
op het scherm. Geen "vorige positie" onthouden dus — dat scheelt een hoop code, en flikkeren doet het niet.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define TRIG 6
#define ECHO 7
Adafruit_SSD1306 oled(128, 64, &Wire, -1);
// het scherm is 128 x 64: bovenaan 10 pixels voor de score,
// daaronder het speelveld van y = 10 tot y = 63
const int VELD_TOP = 10;
// --- speler (links): een hoog, GEVULD staafje ---
const int SPELER_X = 4, SPELER_B = 6, SPELER_H = 12;
int spelerY = 30;
// --- hindernis (van rechts): een klein, OMLIJND vierkantje ---
const int HIND = 7;
int hindX, hindY;
int snelheid = 3;
int score = 0;
bool gameOver = false;
long leesAfstand() {
digitalWrite(TRIG, LOW); delayMicroseconds(2);
digitalWrite(TRIG, HIGH); delayMicroseconds(10);
digitalWrite(TRIG, LOW);
long duur = pulseIn(ECHO, HIGH, 30000);
if (duur == 0) return 40; // niets gezien -> "ver"
return duur * 0.034 / 2;
}
void nieuweHindernis() {
hindX = 121; // 121 + 7 = 128: net binnen de rechterrand
hindY = random(VELD_TOP, 64 - HIND + 1); // 10 t/m 57, dus altijd in beeld
}
void resetSpel() {
score = 0; snelheid = 3; gameOver = false;
spelerY = 30;
nieuweHindernis();
}
void setup() {
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT);
oled.begin(SSD1306_SWITCHCAPVCC, 0x3C); // adres 0x3C (soms 0x3D)
oled.setTextColor(SSD1306_WHITE);
randomSeed(analogRead(A0));
resetSpel();
}
void loop() {
if (gameOver) {
if (leesAfstand() < 10) { delay(300); resetSpel(); } // hand dichtbij = opnieuw
delay(50);
return;
}
// --- 1. hand bepaalt de hoogte ---
long cm = constrain(leesAfstand(), 5, 40);
spelerY = map(cm, 5, 40, VELD_TOP, 64 - SPELER_H); // 10 ... 52
// --- 2. hindernis opschuiven ---
hindX -= snelheid;
// --- 3. botsing? twee rechthoeken raken elkaar als ze
// en horizontaal en verticaal overlappen ---
bool xRaakt = (hindX < SPELER_X + SPELER_B) && (hindX + HIND > SPELER_X);
bool yRaakt = (hindY < spelerY + SPELER_H) && (hindY + HIND > spelerY);
if (xRaakt && yRaakt) {
gameOver = true;
oled.clearDisplay();
oled.setTextSize(2);
oled.setCursor(8, 8); oled.print("GAME OVER");
oled.setTextSize(1);
oled.setCursor(8, 34); oled.print("Score: "); oled.print(score);
oled.setCursor(8, 46); oled.print("Hand dichtbij");
oled.setCursor(8, 55); oled.print("= opnieuw");
oled.display();
return;
}
// --- 4. helemaal voorbij zonder raken? punt! ---
if (hindX + HIND < SPELER_X) {
score++;
if (snelheid < 7) snelheid++; // wordt sneller
nieuweHindernis();
}
// --- 5. het hele beeld opnieuw tekenen ---
oled.clearDisplay();
oled.setTextSize(1);
oled.setCursor(0, 0);
oled.print("Score: ");
oled.print(score);
oled.drawRect(hindX, hindY, HIND, HIND, SSD1306_WHITE); // omlijnd = gevaar
oled.fillRect(SPELER_X, spelerY, SPELER_B, SPELER_H, SSD1306_WHITE); // gevuld = jij
oled.display(); // <-- nu pas zichtbaar
delay(30);
}
Uitleg per onderdeel
Elk stukje komt uit een eerder project — nu klikken ze in elkaar:
- leesAfstand()
- Exact de functie uit project 02. We hoefden hem niet opnieuw te bedenken.
- map + constrain
- Zet de gemeten centimeters om naar een schermhoogte — dezelfde truc als de balk in project 02.
- tekenen
- De animatie-aanpak van jouw scherm: op de TFT wis je de oude plek zwart en teken je de nieuwe (project 01); op de OLED maak je het kladblok leeg en teken je elke ronde het hele beeld opnieuw (de OLED-start).
- botsing (xRaakt/yRaakt)
- Twee rechthoeken overlappen als ze én horizontaal én verticaal overlappen. Dat check je met vier vergelijkingen.
map(...)-regel de twee laatste
getallen om: map(cm, 5, 40, 110, 14).
map(...)-regel de twee laatste
getallen om: map(cm, 5, 40, 52, VELD_TOP).
Waar nu heen?
Je hebt de hele reeks doorlopen: scherm, sensoren, een weerstation en een spel dat alles combineert. Ideeën om verder te gaan:
- Voeg levens toe (bijv. 3 harten bovenin) in plaats van meteen game over.
- Laat de snelheid en het aantal hindernissen oplopen voor een echte uitdaging.
- Bouw een startmenu en onthoud je highscore.
- Verzin een heel nieuw spel met dezelfde bouwstenen — een reactietest, een "houd de bal in de lucht", of iets waarbij je met de IR-sensor zwaait.
map, en tekenen & wissen. Daar bouw je alles mee.
Mooi gebouwd 🎉
Van een los rood printje naar een hands-free spel. Alle projecten blijven staan om terug te lezen — en er is altijd ruimte voor een volgende in de werkplaats.