Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

PicoMonster

1. Projektidee

PicoMonster ist ein kleines Pokémon-inspiriertes Spiel, das auf einem Seeed XIAO RP2040 mit einem 240×240 Pixel großen ST7789-Display läuft.

Mein Ziel ist es, ein eigenes kleines Monster-Spiel für einen Mikrocontroller zu entwickeln. Das Projekt wird dabei vollständig selbst programmiert und Schritt für Schritt erweitert.

Das Projekt befindet sich momentan noch in Entwicklung.

2. Hardware

Für das Projekt verwende ich:

  • Seeed XIAO RP2040
  • ST7789 Display (240×240 Pixel)
  • drei Taster
  • Breadboard
  • Jumper-Kabel

3. Entwicklung

Nachdem die Hardware aufgebaut war, habe ich zunächst das Display zum Laufen gebracht. Danach habe ich eigene Funktionen für die Darstellung von Text, Rechtecken und Bildern erstellt.

Als Nächstes habe ich meine ersten Monster-Sprites eingebunden. Die Monster werden momentan mit einer Größe von 96×96 Pixel dargestellt.

Da meine Grafiken als PNG-Dateien vorliegen, musste ich sie zunächst in das für das Display passende RGB565-Format umwandeln. Dafür habe ich einen eigenen Converter entwickelt, der die RGB888-Farbdaten der PNG- Bilder in RGB565 umwandelt.

Die konvertierten Bilddaten können anschließend direkt als Sprite in meinem Programm verwendet und auf dem ST7789 dargestellt werden.

#include <filesystem>
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <fstream>
#include <iomanip>




uint16_t RGB888ToRGB565(unsigned char r, unsigned char g, unsigned char b ){
    uint16_t rgb565 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) ;
    return rgb565;

}


int main(){

auto assetpath = "../assets";

int width, height, channels;


for(const auto& asset : std::filesystem::directory_iterator(assetpath)) {
    if(!asset.is_regular_file()) {
        continue;
    }
    else{
        unsigned char* pixel = stbi_load(asset.path().string().c_str(), &width, &height, &channels, 4);
        int pixelcount = int(width * height); 
        std::cout << "File: " << asset.path().filename() << std::endl;



            std::string name = asset.path().stem().string();
            std::ofstream output(name + ".h");
            output << "#include <Arduino.h>" << std::endl;
            output << "const uint16_t " << name << "[" << pixelcount << "] PROGMEM = {" << std::endl;
            output << "    ";
            

        for (int i = 0; i< pixelcount*4;i++){

           
            uint16_t color = RGB888ToRGB565(pixel[i], pixel[i+1], pixel[i+2]);
            uint8_t alpha = pixel[i+3];


            for (int j = 0; j < pixelcount; j++) {
                uint16_t color = RGB888ToRGB565(pixel[i], pixel[i+1], pixel[i+2]);
                output << "0x"
                << std::uppercase
                << std::hex
                << std::setw(4)
                << std::setfill('0')
                << color;
                if (j < pixelcount - 1) {
                    output << ", ";
                }
                if ((j + 1) % 12 == 0) {
                    output << std::endl << "    ";
                }
                i += 4;
            }
            output << std::endl << "};" << std::endl;


            i += 3;
        }

        
        output << std::dec;

        

        output << "const uint8_t " << name << "_mask["  << ((pixelcount + 7) / 8) << "] PROGMEM = {" << std::endl;
        int maskSize = (pixelcount + 7) / 8;
        output << "    ";
        for (int byteIndex = 0; byteIndex < maskSize; ++byteIndex) {
            uint8_t byteVal = 0;
            for (int bit = 0; bit < 8; ++bit) {
                int pixIndex = byteIndex * 8 + bit;
                if (pixIndex >= pixelcount) break;
                uint8_t alpha = pixel[pixIndex * 4 + 3];
                if (alpha > 0) {
                    byteVal |= (1 << (7 - bit));
                }
            }
            output << "0x"
                   << std::uppercase
                   << std::hex
                   << std::setw(2)
                   << std::setfill('0')
                   << (int)byteVal;
            if (byteIndex < maskSize - 1) {
                output << ", ";
            }
            if ((byteIndex + 1) % 12 == 0) {
                output << std::endl << "    ";
            }
        }
        output << std::endl << "};" << std::endl;
        output << std::dec;

        stbi_image_free(pixel);
        
    }

}
}


Danach entstand das erste Menü mit den beiden Optionen NEW GAME und CONTINUE. Über die drei Taster kann zwischen den Optionen gewechselt und eine Auswahl bestätigt werden.

alt text

Anschließend wollte ich das Menü etwas lebendiger machen. Deshalb habe ich den Auswahlpfeil animiert. Der Pfeil bewegt sich dabei regelmäßig leicht nach links und rechts.

Für die zeitliche Steuerung der Animation verwende ich millis(). Dadurch kann sich der Pfeil bewegen, während gleichzeitig weiterhin die Taster abgefragt werden.

Danach habe ich die Monster-Auswahl eingebaut. Der Spieler kann zwischen verschiedenen Monstern wählen. Bei der Auswahl werden die Eigenschaften des Monsters, wie Name, Level, Leben, Erfahrung und Angriff, festgelegt.

alt text

Ein wichtiger nächster Schritt war das Speichern des Spiels. Ich wollte, dass der Spielstand auch nach dem Ausschalten des XIAO erhalten bleibt. Dafür verwende ich LittleFS.

Die Monsterdaten werden in einem Spielstand gespeichert. Bei einem neuen Spiel wird ein vorhandener alter Spielstand gelöscht und ein neuer angelegt. Bei CONTINUE wird überprüft, ob bereits ein Spielstand vorhanden ist. Falls einer existiert, wird er geladen.

Dadurch kann das Spiel nach einem Neustart wieder das zuvor ausgewählte Monster und dessen Eigenschaften anzeigen.

Daraufhin habe ich das eigentliche Hauptmenü des Spiels gebaut. Dort wird das ausgewählte Monster zusammen mit seinem Namen und seinem Level angezeigt. Außerdem kann der Spieler zwischen den verschiedenen Aktionen FIGHT, RUN, ITEM und QUIT auswählen.

alt text

4. Aktueller Stand

Das grundlegende Gerüst des Spiels funktioniert bereits.

Momentan gibt es:

  • ein funktionierendes 240×240 Display
  • Monster-Sprites
  • ein Hauptmenü
  • Steuerung über drei Taster
  • einen animierten Auswahlpfeil
  • eine Monster-Auswahl
  • ein Speicher- und Ladesystem
  • NEW GAME und CONTINUE
  • Home Screen mit Monster Das eigentliche Gameplay befindet sich noch in Entwicklung.

5. Ausblick

Als Nächstes möchte ich das eigentliche Spiel weiterentwickeln.

Geplant sind unter anderem:

  • weitere Monster
  • ein Levelsystem
  • Erfahrungspunkte
  • Kämpfe
  • weitere Animationen
  • zusätzliche Menüs
  • ein erweitertes Speichersystem

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors