修复 LightGame Windows 输入问题并完善手动亮度控制

- 重构 PlayerController 输入:移除 IButtonInput 依赖,纯使用 IKeyboardState 与 IPointerInput;触摸屏改为上半屏跳跃、左右 1/3 分区分左右移动
- GameStateManager 增加 ESC 键暂停/恢复支持
- 将键盘亮度控制从 SdlPhotoSensor 平台层上提到 LightGameApp 游戏层(W/S 键),并引入 has_manual_override_ 标志防止 photo sensor 默认值覆盖手动调整
- SDLDisplay 初始化时调用 SDL_StopTextInput(),缓解 Windows 中文输入法在按字母键后拦截方向键/空格的问题
- IKeyboardState.h 补充 KEY_S、KEY_ESC、KEY_RETURN 常量
This commit is contained in:
SepComet 2026-06-11 10:41:53 +08:00
parent fda8b120d3
commit acf162d1b9
9 changed files with 98 additions and 79 deletions

View File

@ -2,6 +2,7 @@
#include "DrawContext.h"
#include "BitmapFont.h"
#include "ButtonInput.h"
#include "IKeyboardState.h"
#include <cstdio>
namespace LightGame
@ -10,14 +11,19 @@ namespace LightGame
: state_(GameState::Title),
hud_(),
title_blink_ms_(0),
title_show_prompt_(true)
title_show_prompt_(true),
esc_was_down_(false)
{
}
void GameStateManager::update(GameState current_state, const Platform::IButtonInput* button, uint32_t dt_ms)
void GameStateManager::update(GameState current_state, const Platform::IButtonInput* button, const Platform::IKeyboardState* keyboard, uint32_t dt_ms)
{
state_ = current_state;
const bool esc_down = keyboard && keyboard->is_key_down(Platform::KEY_ESC);
const bool esc_pressed = esc_down && !esc_was_down_;
esc_was_down_ = esc_down;
switch (state_)
{
case GameState::Title:
@ -30,14 +36,14 @@ namespace LightGame
break;
case GameState::Playing:
if (button && button->was_pressed())
if (esc_pressed || (button && button->was_pressed()))
{
state_ = GameState::Paused;
}
break;
case GameState::Paused:
if (button && button->was_pressed())
if (esc_pressed || (button && button->was_pressed()))
{
state_ = GameState::Playing;
}

View File

@ -15,6 +15,7 @@ namespace RenderData
namespace Platform
{
class IButtonInput;
class IKeyboardState;
}
namespace LightGame
@ -53,11 +54,12 @@ namespace LightGame
HudData hud_;
uint32_t title_blink_ms_;
bool title_show_prompt_;
bool esc_was_down_;
public:
GameStateManager();
void update(GameState current_state, const Platform::IButtonInput* button, uint32_t dt_ms);
void update(GameState current_state, const Platform::IButtonInput* button, const Platform::IKeyboardState* keyboard, uint32_t dt_ms);
void draw(Core::DrawContext& ctx, const RenderData::BitmapFont& font, int32_t screen_w, int32_t screen_h);
void set_state(GameState state) { state_ = state; }

View File

@ -9,6 +9,7 @@
#include "Level1Data.h"
#include "Level2Data.h"
#include "Level3Data.h"
#include "sprite_atlas.h"
namespace LightGame
{
@ -44,21 +45,58 @@ namespace LightGame
renderer_(),
current_level_index_(0),
player_id_(0),
manual_light_level_(2048),
light_step_(256),
has_manual_override_(false),
debug_mode_(false)
{
camera_.configure(screen_width, screen_height);
camera_.set_dead_zone(60, 40);
static const RenderData::Sprite player_idle[] = {
sprite_atlas::spr_player_idle_0,
sprite_atlas::spr_player_idle_1
};
static const RenderData::Sprite player_run[] = {
sprite_atlas::spr_player_run_0,
sprite_atlas::spr_player_run_1,
sprite_atlas::spr_player_run_2,
sprite_atlas::spr_player_run_3
};
player_controller_.set_sprites(
player_idle, 2,
player_run, 4,
&sprite_atlas::spr_player_jump,
&sprite_atlas::spr_player_fall);
}
void LightGameApp::update(uint32_t dt_ms)
{
if (photo_sensor_ && photo_sensor_->is_open())
if (keyboard_input_)
{
if (keyboard_input_->is_key_down(Platform::KEY_W))
{
const uint32_t next = static_cast<uint32_t>(manual_light_level_) + light_step_;
manual_light_level_ = static_cast<uint16_t>(next > 4095 ? 4095 : next);
has_manual_override_ = true;
}
if (keyboard_input_->is_key_down(Platform::KEY_S))
{
manual_light_level_ = manual_light_level_ >= light_step_
? manual_light_level_ - light_step_
: 0;
has_manual_override_ = true;
}
}
if (photo_sensor_ && photo_sensor_->is_open() && !has_manual_override_)
{
photo_sensor_->update();
manual_light_level_ = photo_sensor_->read_level();
}
const GameState prev_state = state_manager_.get_state();
state_manager_.update(prev_state, button_input_, dt_ms);
state_manager_.update(prev_state, button_input_, keyboard_input_, dt_ms);
switch (state_manager_.get_state())
{
@ -86,7 +124,7 @@ namespace LightGame
if (state_manager_.get_state() == GameState::Playing ||
state_manager_.get_state() == GameState::Paused)
{
const uint16_t light = photo_sensor_ ? photo_sensor_->read_level() : 2048;
const uint16_t light = manual_light_level_;
renderer_.draw(ctx, level_, camera_, light_system_, light);
if (debug_mode_)
@ -99,7 +137,7 @@ namespace LightGame
if (debug_mode_ && state_manager_.get_state() == GameState::Playing)
{
const uint16_t light = photo_sensor_ ? photo_sensor_->read_level() : 2048;
const uint16_t light = manual_light_level_;
char buf[32];
snprintf(buf, sizeof(buf), "L:%d", light);
ctx.draw_text(font, screen_width_ - 64, screen_height_ - 16,
@ -130,10 +168,15 @@ namespace LightGame
state_manager_.get_hud().current_level = level_index + 1;
}
bool LightGameApp::is_action_pressed()
{
return button_input_ && button_input_->was_pressed();
}
void LightGameApp::update_title(uint32_t dt_ms)
{
(void)dt_ms;
if (button_input_ && button_input_->was_pressed())
if (is_action_pressed())
{
load_level(0);
state_manager_.set_state(GameState::Playing);
@ -142,14 +185,14 @@ namespace LightGame
void LightGameApp::update_playing(uint32_t dt_ms)
{
const uint16_t light = photo_sensor_ ? photo_sensor_->read_level() : 2048;
const uint16_t light = manual_light_level_;
light_system_.update(level_.get_all_objects(), light);
state_manager_.get_hud().light_level = light;
GameObject* player = level_.get_object(player_id_);
if (player)
{
player_controller_.update(*player, level_, physics_, button_input_, keyboard_input_, pointer_input_, dt_ms);
player_controller_.update(*player, level_, physics_, keyboard_input_, pointer_input_, screen_width_, screen_height_, dt_ms);
camera_.follow(player->position);
if (player_controller_.is_dead())
@ -168,7 +211,7 @@ namespace LightGame
void LightGameApp::update_game_over(uint32_t dt_ms)
{
(void)dt_ms;
if (button_input_ && button_input_->was_pressed())
if (is_action_pressed())
{
state_manager_.get_hud().lives = 3;
state_manager_.get_hud().collectibles = 0;
@ -180,7 +223,7 @@ namespace LightGame
void LightGameApp::update_level_complete(uint32_t dt_ms)
{
(void)dt_ms;
if (button_input_ && button_input_->was_pressed())
if (is_action_pressed())
{
const int32_t next = current_level_index_ + 1;
if (next < kLevelCount)

View File

@ -52,6 +52,10 @@ namespace LightGame
int32_t current_level_index_;
uint32_t player_id_;
uint16_t manual_light_level_;
uint16_t light_step_;
bool has_manual_override_;
bool debug_mode_;
public:
@ -76,5 +80,6 @@ namespace LightGame
void update_game_over(uint32_t dt_ms);
void update_level_complete(uint32_t dt_ms);
void check_triggers();
bool is_action_pressed();
};
}

View File

@ -1,5 +1,4 @@
#include "PlayerController.h"
#include "ButtonInput.h"
#include "IKeyboardState.h"
#include "PointerInput.h"
#include <algorithm>
@ -44,9 +43,9 @@ namespace LightGame
}
void PlayerController::update(GameObject& obj, Level& level, Physics2D& physics,
const Platform::IButtonInput* button,
const Platform::IKeyboardState* keyboard,
const Platform::IPointerInput* pointer,
int32_t screen_width, int32_t screen_height,
uint32_t dt_ms)
{
if (state_ == PlayerState::Dead)
@ -59,7 +58,7 @@ namespace LightGame
return;
}
update_input(button, keyboard, pointer);
update_input(keyboard, pointer, screen_width, screen_height);
update_movement(obj, dt_ms);
physics.apply_gravity(obj, dt_ms);
@ -111,48 +110,45 @@ namespace LightGame
check_death(obj, level);
}
void PlayerController::update_input(const Platform::IButtonInput* button,
const Platform::IKeyboardState* keyboard,
const Platform::IPointerInput* pointer)
void PlayerController::update_input(const Platform::IKeyboardState* keyboard,
const Platform::IPointerInput* pointer,
int32_t screen_width, int32_t screen_height)
{
move_dir_ = 0;
jump_held_ = false;
if (button)
{
if (button->is_down())
{
jump_held_ = true;
}
}
if (pointer)
{
if (pointer->is_down())
{
const int32_t px = pointer->get_x();
if (px < 400)
const int32_t py = pointer->get_y();
// Top half of screen = jump (works across the full width)
if (py < screen_height / 2)
{
jump_held_ = true;
}
// Left 1/3 = move left, right 1/3 = move right
if (px < screen_width / 3)
{
move_dir_ = -1;
}
else if (px > 600)
else if (px > screen_width * 2 / 3)
{
move_dir_ = 1;
}
else
{
jump_held_ = true;
}
}
}
if (keyboard)
{
if (keyboard->is_key_down(Platform::KEY_LEFT) || keyboard->is_key_down(Platform::KEY_A))
if (keyboard->is_key_down(Platform::KEY_LEFT))
move_dir_ = -1;
if (keyboard->is_key_down(Platform::KEY_RIGHT) || keyboard->is_key_down(Platform::KEY_D))
if (keyboard->is_key_down(Platform::KEY_RIGHT))
move_dir_ = 1;
if (keyboard->is_key_down(Platform::KEY_SPACE) || keyboard->is_key_down(Platform::KEY_UP) || keyboard->is_key_down(Platform::KEY_W))
if (keyboard->is_key_down(Platform::KEY_UP))
jump_held_ = true;
}
}

View File

@ -6,7 +6,6 @@
namespace Platform
{
class IButtonInput;
class IKeyboardState;
class IPointerInput;
}
@ -72,16 +71,16 @@ namespace LightGame
const RenderData::Sprite* fall);
void update(GameObject& obj, Level& level, Physics2D& physics,
const Platform::IButtonInput* button,
const Platform::IKeyboardState* keyboard,
const Platform::IPointerInput* pointer,
int32_t screen_width, int32_t screen_height,
uint32_t dt_ms);
PlayerState get_state() const { return state_; }
bool is_dead() const { return state_ == PlayerState::Dead; }
private:
void update_input(const Platform::IButtonInput* button, const Platform::IKeyboardState* keyboard, const Platform::IPointerInput* pointer);
void update_input(const Platform::IKeyboardState* keyboard, const Platform::IPointerInput* pointer, int32_t screen_width, int32_t screen_height);
void update_movement(GameObject& obj, uint32_t dt_ms);
void update_animation(GameObject& obj, uint32_t dt_ms);
void check_death(GameObject& obj, Level& level);

View File

@ -7,11 +7,14 @@ namespace Platform
constexpr int KEY_A = 4;
constexpr int KEY_D = 7;
constexpr int KEY_W = 26;
constexpr int KEY_S = 22;
constexpr int KEY_SPACE = 44;
constexpr int KEY_LEFT = 80;
constexpr int KEY_RIGHT = 79;
constexpr int KEY_UP = 82;
constexpr int KEY_DOWN = 81;
constexpr int KEY_ESC = 41;
constexpr int KEY_RETURN = 40;
class IKeyboardState
{

View File

@ -57,6 +57,7 @@ namespace Platform
return false;
}
SDL_StopTextInput();
return true;
}

View File

@ -50,45 +50,9 @@ namespace Platform
void SdlPhotoSensor::update()
{
if (!opened_)
{
return;
}
SDL_PumpEvents();
const Uint8* keyboard_state = SDL_GetKeyboardState(nullptr);
if (!keyboard_state)
{
return;
}
if (keyboard_state[SDL_SCANCODE_UP])
{
uint32_t next = static_cast<uint32_t>(level_) + step_;
level_ = static_cast<uint16_t>(std::min(next, static_cast<uint32_t>(max_level_)));
}
if (keyboard_state[SDL_SCANCODE_DOWN])
{
if (level_ >= step_)
{
level_ -= step_;
}
else
{
level_ = min_level_;
}
}
if (keyboard_state[SDL_SCANCODE_HOME])
{
level_ = max_level_;
}
if (keyboard_state[SDL_SCANCODE_END])
{
level_ = min_level_;
}
// No-op: keyboard-based brightness control is handled in the game layer
// (LightGameApp) through IKeyboardState, not at the platform level.
(void)opened_;
}
void SdlPhotoSensor::shutdown()