Add frame tick system for continuous animation

- Added INPUT_FRAME_TICK event type to input_event.h
- Added wants_frame_updates() virtual method to Game base class
- Implemented frame tick logic in main loop (basic1.cpp and emulator/main.cpp)
- Added Lua bindings: game.set_frame_updates(bool) and INPUT.FRAME_TICK
- Updated LuaGame to support frame updates via registry flag
- Updated ball.lua to use continuous frame updates for smooth animation
- Both hardware and emulator now support continuous animation for physics/games
This commit is contained in:
Adolfo Reyna
2026-02-07 13:20:10 -05:00
parent 8d176925f8
commit 2a472fc29f
10 changed files with 86 additions and 4 deletions
+8
View File
@@ -520,6 +520,14 @@ int main()
} }
needs_refresh = true; needs_refresh = true;
} }
} else if (launcher.is_game_selected()) {
// No input, but check if game wants continuous updates
current_game = launcher.get_selected_game();
if (current_game->wants_frame_updates()) {
// Send frame tick event for animation/physics updates
InputEvent frame_tick = {INPUT_FRAME_TICK, 0, 0, 0, 0, 0, true};
needs_refresh = current_game->update(frame_tick);
}
} }
// 4. Redraw and queue async refresh on Core 1 // 4. Redraw and queue async refresh on Core 1
+1
View File
@@ -15,6 +15,7 @@ public:
virtual bool update(const InputEvent& event) = 0; virtual bool update(const InputEvent& event) = 0;
virtual void draw() = 0; virtual void draw() = 0;
virtual bool wants_to_exit() const { return false; } virtual bool wants_to_exit() const { return false; }
virtual bool wants_frame_updates() const { return false; }
// Public members for Lua bindings access // Public members for Lua bindings access
uint16_t width; uint16_t width;
+10
View File
@@ -171,7 +171,17 @@ bool LuaGame::wants_to_exit() const {
return exit; return exit;
} }
bool LuaGame::wants_frame_updates() const {
if (!L) return false;
// Check registry for frame updates flag
lua_pushstring(L, "__wants_frame_updates");
lua_gettable(L, LUA_REGISTRYINDEX);
bool wants_updates = lua_toboolean(L, -1);
lua_pop(L, 1);
return wants_updates;
}
bool LuaGame::call_lua_function(const char* func_name, int nargs, int nresults) { bool LuaGame::call_lua_function(const char* func_name, int nargs, int nresults) {
int result = lua_pcall(L, nargs, nresults, 0); int result = lua_pcall(L, nargs, nresults, 0);
if (result != LUA_OK) { if (result != LUA_OK) {
+11
View File
@@ -107,6 +107,17 @@ int main() {
needs_redraw = true; needs_redraw = true;
} }
} }
} else if (launcher.is_game_selected()) {
// No user input, but check if game wants frame tick updates
current_game = launcher.get_selected_game();
if (current_game->wants_frame_updates()) {
InputEvent frame_event = {INPUT_FRAME_TICK, 0, 0, 0, 0, 0, true};
needs_redraw = current_game->update(frame_event) || needs_redraw;
if (current_game->wants_to_exit()) {
launcher.reset();
needs_redraw = true;
}
}
} }
// Always redraw every frame for emulator // Always redraw every frame for emulator
+19
View File
@@ -186,6 +186,21 @@ static int lua_game_exit(lua_State* L) {
return 0; return 0;
} }
// game.set_frame_updates(enabled) - enable/disable continuous frame tick events
static int lua_game_set_frame_updates(lua_State* L) {
LuaGame* game = get_game(L);
if (!game) return 0;
bool enabled = lua_toboolean(L, 1);
// Set frame updates flag (will be checked in wants_frame_updates())
lua_pushstring(L, "__wants_frame_updates");
lua_pushboolean(L, enabled);
lua_settable(L, LUA_REGISTRYINDEX);
return 0;
}
// ============================================================================ // ============================================================================
// INPUT TYPE CONSTANTS // INPUT TYPE CONSTANTS
// ============================================================================ // ============================================================================
@@ -283,6 +298,10 @@ void lua_bindings_register(lua_State* L, LuaGame* game) {
lua_pushcfunction(L, lua_game_exit); lua_pushcfunction(L, lua_game_exit);
lua_settable(L, -3); lua_settable(L, -3);
lua_pushstring(L, "set_frame_updates");
lua_pushcfunction(L, lua_game_set_frame_updates);
lua_settable(L, -3);
// Create empty vars table for persistent state // Create empty vars table for persistent state
lua_pushstring(L, "vars"); lua_pushstring(L, "vars");
lua_newtable(L); lua_newtable(L);
+5 -2
View File
@@ -14,6 +14,9 @@ function init()
game.vars.radius = 10 game.vars.radius = 10
game.vars.frame_count = 0 game.vars.frame_count = 0
-- Enable continuous frame updates for smooth animation
game.set_frame_updates(true)
print("Bouncing Ball initialized") print("Bouncing Ball initialized")
end end
@@ -28,8 +31,8 @@ function update(event)
return true return true
end end
-- Update physics if running -- Update physics if running (on any frame tick)
if game.vars.state == STATE_RUNNING then if event.type == INPUT.FRAME_TICK and game.vars.state == STATE_RUNNING then
-- Move ball -- Move ball
game.vars.ball_x = game.vars.ball_x + game.vars.vel_x game.vars.ball_x = game.vars.ball_x + game.vars.vel_x
game.vars.ball_y = game.vars.ball_y + game.vars.vel_y game.vars.ball_y = game.vars.ball_y + game.vars.vel_y
+12
View File
@@ -193,6 +193,18 @@ bool LuaGame::wants_to_exit() const {
return exit; return exit;
} }
bool LuaGame::wants_frame_updates() const {
if (!L) return false;
// Check if Lua script wants continuous frame updates
lua_pushstring(L, "__wants_frame_updates");
lua_gettable(L, LUA_REGISTRYINDEX);
bool wants_updates = lua_toboolean(L, -1);
lua_pop(L, 1);
return wants_updates;
}
bool LuaGame::call_lua_function(const char* func_name, int nargs, int nresults) { bool LuaGame::call_lua_function(const char* func_name, int nargs, int nresults) {
int result = lua_pcall(L, nargs, nresults, 0); int result = lua_pcall(L, nargs, nresults, 0);
if (result != LUA_OK) { if (result != LUA_OK) {
+6
View File
@@ -64,6 +64,12 @@ public:
*/ */
bool wants_to_exit() const override; bool wants_to_exit() const override;
/**
* @brief Check if game wants continuous frame updates
* @return true if Lua script set __wants_frame_updates flag
*/
bool wants_frame_updates() const override;
/** /**
* @brief Get Lua state for bindings access * @brief Get Lua state for bindings access
*/ */
+11
View File
@@ -94,6 +94,17 @@ public:
*/ */
virtual bool wants_to_exit() const { return false; } virtual bool wants_to_exit() const { return false; }
/**
* @brief Check if game wants continuous frame updates
*
* Games that need animation or continuous updates (like physics simulations)
* can override this to return true. They will receive INPUT_FRAME_TICK events
* every frame, even without user input.
*
* @return true if game needs frame updates, false for event-driven only
*/
virtual bool wants_frame_updates() const { return false; }
/** /**
* @brief Get the type of game for safe downcasting without RTTI * @brief Get the type of game for safe downcasting without RTTI
*/ */
+2 -1
View File
@@ -17,7 +17,8 @@ enum InputType {
INPUT_TOUCH_UP, INPUT_TOUCH_UP,
INPUT_BUTTON_0, INPUT_BUTTON_0,
INPUT_BUTTON_1, INPUT_BUTTON_1,
INPUT_GESTURE INPUT_GESTURE,
INPUT_FRAME_TICK // Sent every frame for animation/continuous updates
}; };
// Unified input event structure // Unified input event structure