feat: add Pocket Dungeon game prototype
Main / Build (BookPlayer) (push) Has been cancelled
Main / Build (Brainfuck) (push) Has been cancelled
Main / Build (Breakout) (push) Has been cancelled
Main / Build (Calculator) (push) Has been cancelled
Main / Build (Diceware) (push) Has been cancelled
Main / Build (EpubReader) (push) Has been cancelled
Main / Build (GPIO) (push) Has been cancelled
Main / Build (GraphicsDemo) (push) Has been cancelled
Main / Build (HelloWorld) (push) Has been cancelled
Main / Build (M5UnitTest) (push) Has been cancelled
Main / Build (Magic8Ball) (push) Has been cancelled
Main / Build (MediaKeys) (push) Has been cancelled
Main / Build (MystifyDemo) (push) Has been cancelled
Main / Build (SerialConsole) (push) Has been cancelled
Main / Build (Snake) (push) Has been cancelled
Main / Build (TamaTac) (push) Has been cancelled
Main / Build (TodoList) (push) Has been cancelled
Main / Build (TwoEleven) (push) Has been cancelled
Main / Bundle (push) Has been cancelled
Main / Build (BookPlayer) (push) Has been cancelled
Main / Build (Brainfuck) (push) Has been cancelled
Main / Build (Breakout) (push) Has been cancelled
Main / Build (Calculator) (push) Has been cancelled
Main / Build (Diceware) (push) Has been cancelled
Main / Build (EpubReader) (push) Has been cancelled
Main / Build (GPIO) (push) Has been cancelled
Main / Build (GraphicsDemo) (push) Has been cancelled
Main / Build (HelloWorld) (push) Has been cancelled
Main / Build (M5UnitTest) (push) Has been cancelled
Main / Build (Magic8Ball) (push) Has been cancelled
Main / Build (MediaKeys) (push) Has been cancelled
Main / Build (MystifyDemo) (push) Has been cancelled
Main / Build (SerialConsole) (push) Has been cancelled
Main / Build (Snake) (push) Has been cancelled
Main / Build (TamaTac) (push) Has been cancelled
Main / Build (TodoList) (push) Has been cancelled
Main / Build (TwoEleven) (push) Has been cancelled
Main / Bundle (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "GameKitScene.h"
|
||||
#include "GameKitLoop.h"
|
||||
#include "GameKitGrid.h"
|
||||
#include "GameKitInput.h"
|
||||
#include "GameKitDraw.h"
|
||||
#include "GameKitPrefs.h"
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace GameKit {
|
||||
|
||||
lv_obj_t* makePanel(lv_obj_t* parent, lv_color_t color, uint8_t radius = 8);
|
||||
lv_obj_t* makeCell(lv_obj_t* parent, uint16_t size, lv_color_t color, uint8_t radius = 4);
|
||||
lv_obj_t* makeLabel(lv_obj_t* parent, const char* text, lv_color_t color);
|
||||
void setCell(lv_obj_t* obj, int x, int y, uint16_t cellPx);
|
||||
|
||||
} // namespace GameKit
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace GameKit {
|
||||
|
||||
struct GridPos {
|
||||
int16_t x = 0;
|
||||
int16_t y = 0;
|
||||
};
|
||||
|
||||
inline bool operator==(GridPos a, GridPos b) { return a.x == b.x && a.y == b.y; }
|
||||
inline bool operator!=(GridPos a, GridPos b) { return !(a == b); }
|
||||
|
||||
struct GridSpec {
|
||||
uint8_t cols = 0;
|
||||
uint8_t rows = 0;
|
||||
uint16_t cellPx = 0;
|
||||
uint16_t originX = 0;
|
||||
uint16_t originY = 0;
|
||||
};
|
||||
|
||||
lv_point_t gridToPx(const GridSpec& grid, GridPos pos);
|
||||
bool gridContains(const GridSpec& grid, GridPos pos);
|
||||
|
||||
} // namespace GameKit
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "GameKitScene.h"
|
||||
#include <lvgl.h>
|
||||
|
||||
namespace GameKit {
|
||||
|
||||
InputKind keyToInput(uint32_t key);
|
||||
InputKind gestureToInput(lv_dir_t dir);
|
||||
InputKind pointToQuadrant(lv_obj_t* obj, lv_point_t point);
|
||||
void attachInput(lv_obj_t* target, Scene* scene);
|
||||
|
||||
} // namespace GameKit
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "GameKitScene.h"
|
||||
#include <lvgl.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace GameKit {
|
||||
|
||||
class Loop {
|
||||
lv_timer_t* timer = nullptr;
|
||||
Scene* scene = nullptr;
|
||||
uint32_t periodMs = 0;
|
||||
uint32_t lastTickMs = 0;
|
||||
|
||||
static void onTimer(lv_timer_t* timer);
|
||||
|
||||
public:
|
||||
~Loop();
|
||||
bool start(uint32_t tickMs, Scene* targetScene);
|
||||
void stop();
|
||||
bool isRunning() const { return timer != nullptr; }
|
||||
};
|
||||
|
||||
} // namespace GameKit
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <TactilityCpp/Preferences.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace GameKit {
|
||||
|
||||
class Prefs {
|
||||
Preferences prefs;
|
||||
|
||||
public:
|
||||
explicit Prefs(const char* ns) : prefs(ns) {}
|
||||
int32_t getInt(const char* key, int32_t fallback = 0) const { return prefs.getInt32(key, fallback); }
|
||||
void putInt(const char* key, int32_t value) const { prefs.putInt32(key, value); }
|
||||
};
|
||||
|
||||
} // namespace GameKit
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace GameKit {
|
||||
|
||||
struct Tick {
|
||||
uint32_t dtMs = 0;
|
||||
uint32_t nowMs = 0;
|
||||
};
|
||||
|
||||
enum class InputKind : uint8_t {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
Confirm,
|
||||
Cancel,
|
||||
Menu,
|
||||
Touch,
|
||||
Swipe,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
struct InputEvent {
|
||||
InputKind kind = InputKind::Unknown;
|
||||
lv_point_t point {0, 0};
|
||||
};
|
||||
|
||||
class Scene {
|
||||
public:
|
||||
virtual ~Scene() = default;
|
||||
virtual void onEnter(lv_obj_t* root) = 0;
|
||||
virtual void onExit() = 0;
|
||||
virtual void onInput(const InputEvent& input) = 0;
|
||||
virtual void onTick(const Tick& tick) = 0;
|
||||
virtual void render() = 0;
|
||||
};
|
||||
|
||||
} // namespace GameKit
|
||||
@@ -0,0 +1,13 @@
|
||||
# GameKit
|
||||
|
||||
Small LVGL-first helper library for Tactility games.
|
||||
|
||||
Current scope:
|
||||
- `Loop`: `lv_timer` lifecycle wrapper.
|
||||
- `Scene`: common enter/exit/input/tick/render interface.
|
||||
- `Input`: normalize keyboard arrows/WASD, gestures, and touch-quadrant clicks.
|
||||
- `Grid`: tiny tile-grid coordinate helpers.
|
||||
- `Draw`: lightweight panel/cell/label helpers.
|
||||
- `Prefs`: small wrapper around Tactility preferences.
|
||||
|
||||
This intentionally avoids a heavy sprite engine. First consumers should use LVGL rectangles/labels, then add canvas/sprite helpers only when a game proves it needs them.
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "GameKitDraw.h"
|
||||
|
||||
namespace GameKit {
|
||||
|
||||
lv_obj_t* makePanel(lv_obj_t* parent, lv_color_t color, uint8_t radius) {
|
||||
lv_obj_t* obj = lv_obj_create(parent);
|
||||
lv_obj_set_style_bg_color(obj, color, LV_PART_MAIN);
|
||||
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN);
|
||||
lv_obj_set_style_radius(obj, radius, LV_PART_MAIN);
|
||||
lv_obj_set_style_pad_all(obj, 0, LV_PART_MAIN);
|
||||
lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
|
||||
return obj;
|
||||
}
|
||||
|
||||
lv_obj_t* makeCell(lv_obj_t* parent, uint16_t size, lv_color_t color, uint8_t radius) {
|
||||
lv_obj_t* obj = makePanel(parent, color, radius);
|
||||
lv_obj_set_size(obj, size, size);
|
||||
return obj;
|
||||
}
|
||||
|
||||
lv_obj_t* makeLabel(lv_obj_t* parent, const char* text, lv_color_t color) {
|
||||
lv_obj_t* label = lv_label_create(parent);
|
||||
lv_label_set_text(label, text);
|
||||
lv_obj_set_style_text_color(label, color, LV_PART_MAIN);
|
||||
return label;
|
||||
}
|
||||
|
||||
void setCell(lv_obj_t* obj, int x, int y, uint16_t cellPx) {
|
||||
lv_obj_set_pos(obj, x * cellPx, y * cellPx);
|
||||
}
|
||||
|
||||
} // namespace GameKit
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "GameKitGrid.h"
|
||||
|
||||
namespace GameKit {
|
||||
|
||||
lv_point_t gridToPx(const GridSpec& grid, GridPos pos) {
|
||||
return lv_point_t {
|
||||
static_cast<lv_coord_t>(grid.originX + pos.x * grid.cellPx),
|
||||
static_cast<lv_coord_t>(grid.originY + pos.y * grid.cellPx)
|
||||
};
|
||||
}
|
||||
|
||||
bool gridContains(const GridSpec& grid, GridPos pos) {
|
||||
return pos.x >= 0 && pos.y >= 0 && pos.x < grid.cols && pos.y < grid.rows;
|
||||
}
|
||||
|
||||
} // namespace GameKit
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "GameKitInput.h"
|
||||
#include <tt_lvgl_keyboard.h>
|
||||
|
||||
namespace GameKit {
|
||||
|
||||
InputKind keyToInput(uint32_t key) {
|
||||
switch (key) {
|
||||
case LV_KEY_UP: return InputKind::Up;
|
||||
case LV_KEY_DOWN: return InputKind::Down;
|
||||
case LV_KEY_LEFT: return InputKind::Left;
|
||||
case LV_KEY_RIGHT: return InputKind::Right;
|
||||
case LV_KEY_ENTER: return InputKind::Confirm;
|
||||
case LV_KEY_ESC: return InputKind::Cancel;
|
||||
default: break;
|
||||
}
|
||||
if (key == 'w' || key == 'W') return InputKind::Up;
|
||||
if (key == 's' || key == 'S') return InputKind::Down;
|
||||
if (key == 'a' || key == 'A') return InputKind::Left;
|
||||
if (key == 'd' || key == 'D') return InputKind::Right;
|
||||
if (key == 'q' || key == 'Q') return InputKind::Cancel;
|
||||
return InputKind::Unknown;
|
||||
}
|
||||
|
||||
InputKind gestureToInput(lv_dir_t dir) {
|
||||
if (dir & LV_DIR_TOP) return InputKind::Up;
|
||||
if (dir & LV_DIR_BOTTOM) return InputKind::Down;
|
||||
if (dir & LV_DIR_LEFT) return InputKind::Left;
|
||||
if (dir & LV_DIR_RIGHT) return InputKind::Right;
|
||||
return InputKind::Unknown;
|
||||
}
|
||||
|
||||
InputKind pointToQuadrant(lv_obj_t* obj, lv_point_t point) {
|
||||
const lv_coord_t w = lv_obj_get_width(obj);
|
||||
const lv_coord_t h = lv_obj_get_height(obj);
|
||||
const lv_coord_t cx = w / 2;
|
||||
const lv_coord_t cy = h / 2;
|
||||
const lv_coord_t dx = point.x - cx;
|
||||
const lv_coord_t dy = point.y - cy;
|
||||
if (LV_ABS(dx) > LV_ABS(dy)) return dx < 0 ? InputKind::Left : InputKind::Right;
|
||||
return dy < 0 ? InputKind::Up : InputKind::Down;
|
||||
}
|
||||
|
||||
static void inputEvent(lv_event_t* e) {
|
||||
auto* scene = static_cast<Scene*>(lv_event_get_user_data(e));
|
||||
if (scene == nullptr) return;
|
||||
InputEvent input;
|
||||
const lv_event_code_t code = lv_event_get_code(e);
|
||||
lv_obj_t* target = lv_event_get_target_obj(e);
|
||||
if (code == LV_EVENT_KEY) {
|
||||
input.kind = keyToInput(lv_event_get_key(e));
|
||||
} else if (code == LV_EVENT_GESTURE) {
|
||||
input.kind = gestureToInput(lv_indev_get_gesture_dir(lv_indev_active()));
|
||||
} else if (code == LV_EVENT_CLICKED) {
|
||||
lv_indev_t* indev = lv_indev_active();
|
||||
if (indev) {
|
||||
lv_indev_get_point(indev, &input.point);
|
||||
lv_area_t coords;
|
||||
lv_obj_get_coords(target, &coords);
|
||||
input.point.x -= coords.x1;
|
||||
input.point.y -= coords.y1;
|
||||
input.kind = pointToQuadrant(target, input.point);
|
||||
} else {
|
||||
input.kind = InputKind::Confirm;
|
||||
}
|
||||
}
|
||||
if (input.kind != InputKind::Unknown) scene->onInput(input);
|
||||
}
|
||||
|
||||
void attachInput(lv_obj_t* target, Scene* scene) {
|
||||
lv_obj_add_flag(target, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_add_event_cb(target, inputEvent, LV_EVENT_KEY, scene);
|
||||
lv_obj_add_event_cb(target, inputEvent, LV_EVENT_GESTURE, scene);
|
||||
lv_obj_add_event_cb(target, inputEvent, LV_EVENT_CLICKED, scene);
|
||||
if (tt_lvgl_hardware_keyboard_is_available()) {
|
||||
lv_group_t* group = lv_group_get_default();
|
||||
if (group) {
|
||||
lv_group_add_obj(group, target);
|
||||
lv_group_focus_obj(target);
|
||||
lv_group_set_editing(group, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace GameKit
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "GameKitLoop.h"
|
||||
|
||||
namespace GameKit {
|
||||
|
||||
Loop::~Loop() { stop(); }
|
||||
|
||||
bool Loop::start(uint32_t tickMs, Scene* targetScene) {
|
||||
stop();
|
||||
if (targetScene == nullptr || tickMs == 0) return false;
|
||||
scene = targetScene;
|
||||
periodMs = tickMs;
|
||||
lastTickMs = lv_tick_get();
|
||||
timer = lv_timer_create(onTimer, tickMs, this);
|
||||
return timer != nullptr;
|
||||
}
|
||||
|
||||
void Loop::stop() {
|
||||
if (timer != nullptr) {
|
||||
lv_timer_delete(timer);
|
||||
timer = nullptr;
|
||||
}
|
||||
scene = nullptr;
|
||||
periodMs = 0;
|
||||
lastTickMs = 0;
|
||||
}
|
||||
|
||||
void Loop::onTimer(lv_timer_t* timer) {
|
||||
auto* loop = static_cast<Loop*>(lv_timer_get_user_data(timer));
|
||||
if (loop == nullptr || loop->scene == nullptr) return;
|
||||
const uint32_t now = lv_tick_get();
|
||||
uint32_t dt = loop->periodMs;
|
||||
if (loop->lastTickMs != 0) dt = now - loop->lastTickMs;
|
||||
loop->lastTickMs = now;
|
||||
loop->scene->onTick(Tick { dt, now });
|
||||
loop->scene->render();
|
||||
}
|
||||
|
||||
} // namespace GameKit
|
||||
Reference in New Issue
Block a user