diff --git a/CMakeLists.txt b/CMakeLists.txt index d72c6ee..056f5f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,11 @@ add_executable(${TARGET} src/engine/scene/scene_manager.cpp src/engine/scene/level_loader.cpp src/game/scene/game_scene.cpp + src/game/component/player_component.cpp + src/game/component/state/idle_state.cpp + src/game/component/state/walk_state.cpp + src/game/component/state/jump_state.cpp + src/game/component/state/fall_state.cpp ) # 链接库 diff --git a/src/engine/component/physics_component.h b/src/engine/component/physics_component.h index 328384b..3411b89 100644 --- a/src/engine/component/physics_component.h +++ b/src/engine/component/physics_component.h @@ -28,6 +28,12 @@ private: bool use_gravity_ = true; ///< @brief 物体是否受重力影响 bool enabled_ = true; ///< @brief 组件是否激活 + // --- 碰撞状态标志 --- + bool collided_below_ = false; + bool collided_above_ = false; + bool collided_left_ = false; + bool collided_right_ = false; + public: /** * @brief 构造函数 @@ -61,6 +67,25 @@ public: const glm::vec2& getVelocity() const { return velocity_; } ///< @brief 获取当前速度 TransformComponent* getTransform() const { return transform_; } ///< @brief 获取TransformComponent指针 + // --- 碰撞状态访问与修改 (供 PhysicsEngine 使用) --- + /** @brief 重置所有碰撞标志 (在物理更新开始时调用) */ + void resetCollisionFlags() { + collided_below_ = false; + collided_above_ = false; + collided_left_ = false; + collided_right_ = false; + } + + void setCollidedBelow(bool collided) { collided_below_ = collided; } ///< @brief 设置下方碰撞标志 + void setCollidedAbove(bool collided) { collided_above_ = collided; } ///< @brief 设置上方碰撞标志 + void setCollidedLeft(bool collided) { collided_left_ = collided; } ///< @brief 设置左方碰撞标志 + void setCollidedRight(bool collided) { collided_right_ = collided; } ///< @brief 设置右方碰撞标志 + + bool hasCollidedBelow() const { return collided_below_; } ///< @brief 检查是否与下方发生碰撞 + bool hasCollidedAbove() const { return collided_above_; } ///< @brief 检查是否与上方发生碰撞 + bool hasCollidedLeft() const { return collided_left_; } ///< @brief 检查是否与左方发生碰撞 + bool hasCollidedRight() const { return collided_right_; } ///< @brief 检查是否与右方发生碰撞 + private: // 核心循环方法 void init() override; diff --git a/src/engine/physics/physics_engine.cpp b/src/engine/physics/physics_engine.cpp index b868efd..2a1668e 100644 --- a/src/engine/physics/physics_engine.cpp +++ b/src/engine/physics/physics_engine.cpp @@ -46,6 +46,8 @@ void PhysicsEngine::update(float delta_time) { continue; } + pc->resetCollisionFlags(); // 重置碰撞标志 + // 应用重力 (如果组件受重力影响):F = g * m if (pc->isUseGravity()) { pc->addForce(gravity_ * pc->getMass()); @@ -139,6 +141,7 @@ void PhysicsEngine::resolveTileCollisions(engine::component::PhysicsComponent* p // 撞墙了!速度归零,x方向移动到贴着墙的位置 new_obj_pos.x = tile_x * layer->getTileSize().x - obj_size.x; pc->velocity_.x = 0.0f; + pc->setCollidedRight(true); } else { // 检测右下角斜坡瓦片 auto width_right = new_obj_pos.x + obj_size.x - tile_x * tile_size.x; @@ -147,6 +150,7 @@ void PhysicsEngine::resolveTileCollisions(engine::component::PhysicsComponent* p // 如果有碰撞(角点的世界y坐标 > 斜坡地面的世界y坐标), 就让物体贴着斜坡表面 if (new_obj_pos.y > (tile_y_bottom + 1) * layer->getTileSize().y - obj_size.y - height_right) { new_obj_pos.y = (tile_y_bottom + 1) * layer->getTileSize().y - obj_size.y - height_right; + pc->setCollidedBelow(true); } } } @@ -165,6 +169,7 @@ void PhysicsEngine::resolveTileCollisions(engine::component::PhysicsComponent* p // 撞墙了!速度归零,x方向移动到贴着墙的位置 new_obj_pos.x = (tile_x + 1) * layer->getTileSize().x; pc->velocity_.x = 0.0f; + pc->setCollidedLeft(true); } else { // 检测左下角斜坡瓦片 auto width_left = new_obj_pos.x - tile_x * tile_size.x; @@ -172,6 +177,7 @@ void PhysicsEngine::resolveTileCollisions(engine::component::PhysicsComponent* p if (height_left > 0.0f) { if (new_obj_pos.y > (tile_y_bottom + 1) * layer->getTileSize().y - obj_size.y - height_left) { new_obj_pos.y = (tile_y_bottom + 1) * layer->getTileSize().y - obj_size.y - height_left; + pc->setCollidedBelow(true); } } } @@ -192,6 +198,7 @@ void PhysicsEngine::resolveTileCollisions(engine::component::PhysicsComponent* p // 到达地面!速度归零,y方向移动到贴着地面的位置 new_obj_pos.y = tile_y * layer->getTileSize().y - obj_size.y; pc->velocity_.y = 0.0f; + pc->setCollidedBelow(true); } else { // 检测斜坡瓦片(下方两个角点都要检测) auto width_left = obj_pos.x - tile_x * tile_size.x; @@ -203,6 +210,7 @@ void PhysicsEngine::resolveTileCollisions(engine::component::PhysicsComponent* p if (new_obj_pos.y > (tile_y + 1) * layer->getTileSize().y - obj_size.y - height) { new_obj_pos.y = (tile_y + 1) * layer->getTileSize().y - obj_size.y - height; pc->velocity_.y = 0.0f; // 只有向下运动时才需要让 y 速度归零 + pc->setCollidedBelow(true); } } } @@ -221,6 +229,7 @@ void PhysicsEngine::resolveTileCollisions(engine::component::PhysicsComponent* p // 撞到天花板!速度归零,y方向移动到贴着天花板的位置 new_obj_pos.y = (tile_y + 1) * layer->getTileSize().y; pc->velocity_.y = 0.0f; + pc->setCollidedAbove(true); } } } @@ -254,23 +263,35 @@ void PhysicsEngine::resolveSolidObjectCollisions(engine::object::GameObject* mov // 移动物体在左边,让它贴着右边SOLID物体(相当于向左移出重叠部分),y方向正常移动 move_tc->translate(glm::vec2(-overlap.x, 0.0f)); // 如果速度为正(向右移动),则归零 (if判断不可少,否则可能出现错误吸附) - if (move_pc->velocity_.x > 0.0f) move_pc->velocity_.x = 0.0f; + if (move_pc->velocity_.x > 0.0f) { + move_pc->velocity_.x = 0.0f; + move_pc->setCollidedRight(true); + } } else { // 移动物体在右边,让它贴着左边SOLID物体(相当于向右移出重叠部分),y方向正常移动 move_tc->translate(glm::vec2(overlap.x, 0.0f)); - if (move_pc->velocity_.x < 0.0f) move_pc->velocity_.x = 0.0f; + if (move_pc->velocity_.x < 0.0f) { + move_pc->velocity_.x = 0.0f; + move_pc->setCollidedLeft(true); + } } } else { // 重叠部分在y方向上更小,则认为碰撞发生在y方向上(推出y方向平移向量最小) if (move_center.y < solid_center.y) { // 移动物体在上面,让它贴着下面SOLID物体(相当于向上移出重叠部分),x方向正常移动 move_tc->translate(glm::vec2(0.0f, -overlap.y)); - if (move_pc->velocity_.y > 0.0f) move_pc->velocity_.y = 0.0f; + if (move_pc->velocity_.y > 0.0f) { + move_pc->velocity_.y = 0.0f; + move_pc->setCollidedBelow(true); + } } else { // 移动物体在下面,让它贴着上面SOLID物体(相当于向下移出重叠部分),x方向正常移动 move_tc->translate(glm::vec2(0.0f, overlap.y)); - if (move_pc->velocity_.y < 0.0f) move_pc->velocity_.y = 0.0f; + if (move_pc->velocity_.y < 0.0f) { + move_pc->velocity_.y = 0.0f; + move_pc->setCollidedAbove(true); + } } } } @@ -312,16 +333,19 @@ void PhysicsEngine::applyWorldBounds(engine::component::PhysicsComponent *pc) if (obj_pos.x < world_bounds_->position.x) { pc->velocity_.x = 0.0f; obj_pos.x = world_bounds_->position.x; + pc->setCollidedLeft(true); } // 检查上边界 if (obj_pos.y < world_bounds_->position.y) { pc->velocity_.y = 0.0f; obj_pos.y = world_bounds_->position.y; + pc->setCollidedAbove(true); } // 检查右边界 if (obj_pos.x + obj_size.x > world_bounds_->position.x + world_bounds_->size.x) { pc->velocity_.x = 0.0f; obj_pos.x = world_bounds_->position.x + world_bounds_->size.x - obj_size.x; + pc->setCollidedRight(true); } // 更新物体位置(使用translate方法,新位置 - 旧位置) tc->translate(obj_pos - world_aabb.position); diff --git a/src/engine/scene/level_loader.h b/src/engine/scene/level_loader.h index 1df77d1..21dd3a4 100644 --- a/src/engine/scene/level_loader.h +++ b/src/engine/scene/level_loader.h @@ -32,7 +32,7 @@ public: * @param scene 要加载数据的目标 Scene 对象。 * @return bool 是否加载成功。 */ - bool loadLevel(const std::string& map_path, Scene& scene); + [[nodiscard]] bool loadLevel(const std::string& map_path, Scene& scene); private: void loadImageLayer(const nlohmann::json& layer_json, Scene& scene); ///< @brief 加载图片图层 diff --git a/src/game/component/player_component.cpp b/src/game/component/player_component.cpp new file mode 100644 index 0000000..19866f2 --- /dev/null +++ b/src/game/component/player_component.cpp @@ -0,0 +1,73 @@ +#include "player_component.h" +#include "state/idle_state.h" +#include "../../engine/component/transform_component.h" +#include "../../engine/component/physics_component.h" +#include "../../engine/component/sprite_component.h" +#include "../../engine/object/game_object.h" +#include "../../engine/input/input_manager.h" +#include +#include +#include + +namespace game::component { + +void PlayerComponent::init() { + if (!owner_) { + spdlog::error("PlayerComponent 没有所属游戏对象!"); + return; + } + + // 获取必要的组件 + transform_component_ = owner_->getComponent(); + physics_component_ = owner_->getComponent(); + sprite_component_ = owner_->getComponent(); + + // 检查必要组件是否存在 + if (!transform_component_ || !physics_component_ || !sprite_component_) { + spdlog::error("Player 对象缺少必要组件!"); + } + + // 初始化状态机 + current_state_ = std::make_unique(this); + if (current_state_) { + setState(std::move(current_state_)); + } else { + spdlog::error("初始化玩家状态失败(make_unique 返回空指针)!"); + } + spdlog::debug("PlayerComponent 初始化完成。"); +} + +void PlayerComponent::setState(std::unique_ptr new_state) { + if (!new_state) { + spdlog::warn("尝试设置空的玩家状态!"); + return; + } + if (current_state_) { + current_state_->exit(); + } + + current_state_ = std::move(new_state); + spdlog::debug("玩家组件正在切换到状态: {}", typeid(*current_state_).name()); + current_state_->enter(); + +} + +void PlayerComponent::handleInput(engine::core::Context& context) { + if (!current_state_) return; + + auto next_state = current_state_->handleInput(context); + if (next_state) { + setState(std::move(next_state)); + } +} + +void PlayerComponent::update(float delta_time, engine::core::Context& context) { + if (!current_state_) return; + + auto next_state = current_state_->update(delta_time, context); + if (next_state) { + setState(std::move(next_state)); + } +} + +} // namespace game::component \ No newline at end of file diff --git a/src/game/component/player_component.h b/src/game/component/player_component.h new file mode 100644 index 0000000..5151e8f --- /dev/null +++ b/src/game/component/player_component.h @@ -0,0 +1,77 @@ +#pragma once +#include "../../engine/component/component.h" +#include "state/player_state.h" +#include + +namespace engine::input { + class InputManager; +} +namespace engine::component { + class TransformComponent; + class PhysicsComponent; + class SpriteComponent; +} + +namespace game::component::state { + class PlayerState; +} + +namespace game::component { + +/** + * @brief 处理玩家输入、状态和控制 GameObject 移动的组件。 + * 使用状态模式管理 Idle, Walk, Jump, Fall 等状态。 + */ +class PlayerComponent final : public engine::component::Component { + friend class engine::object::GameObject; +private: + engine::component::TransformComponent* transform_component_ = nullptr; // 指向 TransformComponent 的非拥有指针 + engine::component::SpriteComponent* sprite_component_ = nullptr; + engine::component::PhysicsComponent* physics_component_ = nullptr; + + std::unique_ptr current_state_; + bool is_dead_ = false; + + // --- 移动相关参数 + float move_force_ = 200.0f; ///< @brief 水平移动力 + float max_speed_ = 120.0f; ///< @brief 最大移动速度 (像素/秒) + float friction_factor_ = 0.85f; ///< @brief 摩擦系数 (Idle时缓冲效果,每帧乘以此系数) + float jump_force_ = 350.0f; ///< @brief 跳跃力 (按下"jump"键给的瞬间向上的力) + +public: + PlayerComponent() = default; + ~PlayerComponent() override = default; + + // 禁止拷贝和移动 + PlayerComponent(const PlayerComponent&) = delete; + PlayerComponent& operator=(const PlayerComponent&) = delete; + PlayerComponent(PlayerComponent&&) = delete; + PlayerComponent& operator=(PlayerComponent&&) = delete; + + // setters and getters + engine::component::TransformComponent* getTransformComponent() const { return transform_component_; } + engine::component::SpriteComponent* getSpriteComponent() const { return sprite_component_; } + engine::component::PhysicsComponent* getPhysicsComponent() const { return physics_component_; } + + void setIsDead(bool is_dead) { is_dead_ = is_dead; } ///< @brief 设置玩家是否死亡 + bool isDead() const { return is_dead_; } ///< @brief 获取玩家是否死亡 + void setMoveForce(float move_force) { move_force_ = move_force; } ///< @brief 设置水平移动力 + float getMoveForce() const { return move_force_; } ///< @brief 获取水平移动力 + void setMaxSpeed(float max_speed) { max_speed_ = max_speed; } ///< @brief 设置最大移动速度 + float getMaxSpeed() const { return max_speed_; } ///< @brief 获取最大移动速度 + void setFrictionFactor(float friction_factor) { friction_factor_ = friction_factor; } ///< @brief 设置摩擦系数 + float getFrictionFactor() const { return friction_factor_; } ///< @brief 获取摩擦系数 + void setJumpForce(float jump_force) { jump_force_ = jump_force; } ///< @brief 设置跳跃力 + float getJumpForce() const { return jump_force_; } + + void setState(std::unique_ptr new_state); ///< @brief 切换玩家状态 + +private: + // 核心循环函数 + void init() override; + void handleInput(engine::core::Context& context) override; + void update(float delta_time, engine::core::Context& context) override; + +}; + +} // namespace game::component \ No newline at end of file diff --git a/src/game/component/state/fall_state.cpp b/src/game/component/state/fall_state.cpp new file mode 100644 index 0000000..18f61b3 --- /dev/null +++ b/src/game/component/state/fall_state.cpp @@ -0,0 +1,58 @@ +#include "fall_state.h" +#include "idle_state.h" +#include "walk_state.h" +#include "../player_component.h" +#include "../../../engine/core/context.h" +#include "../../../engine/input/input_manager.h" +#include "../../../engine/component/physics_component.h" +#include "../../../engine/component/sprite_component.h" +#include + +namespace game::component::state { + +void FallState::enter() { + +} + +void FallState::exit() { + +} + +std::unique_ptr FallState::handleInput(engine::core::Context& context) +{ + auto input_manager = context.getInputManager(); + auto physics_component = player_component_->getPhysicsComponent(); + auto sprite_component = player_component_->getSpriteComponent(); + + // 下落状态下可以左右移动 + if (input_manager.isActionDown("move_left")) { + if (physics_component->velocity_.x > 0.0f) physics_component->velocity_.x = 0.0f; + physics_component->addForce({-player_component_->getMoveForce(), 0.0f}); + sprite_component->setFlipped(true); + } else if (input_manager.isActionDown("move_right")) { + if (physics_component->velocity_.x < 0.0f) physics_component->velocity_.x = 0.0f; + physics_component->addForce({player_component_->getMoveForce(), 0.0f}); + sprite_component->setFlipped(false); + } + return nullptr; +} + +std::unique_ptr FallState::update(float, engine::core::Context&) +{ + // 限制最大速度(水平方向) + auto physics_component = player_component_->getPhysicsComponent(); + auto max_speed = player_component_->getMaxSpeed(); + physics_component->velocity_.x = glm::clamp(physics_component->velocity_.x, -max_speed, max_speed); + + // 如果下方有碰撞,则根据水平速度来决定 切换到 IdleState 或 WalkState + if (physics_component->hasCollidedBelow()) { + if (glm::abs(physics_component->velocity_.x) < 1.0f) { + return std::make_unique(player_component_); + } else { + return std::make_unique(player_component_); + } + } + return nullptr; +} + +} // namespace game::component::state \ No newline at end of file diff --git a/src/game/component/state/fall_state.h b/src/game/component/state/fall_state.h new file mode 100644 index 0000000..dea12de --- /dev/null +++ b/src/game/component/state/fall_state.h @@ -0,0 +1,19 @@ +#pragma once +#include "player_state.h" + +namespace game::component::state { + +class FallState final : public PlayerState { + friend class game::component::PlayerComponent; +public: + FallState(PlayerComponent* player_component) : PlayerState(player_component) {} + ~FallState() override = default; + +private: + void enter() override; + void exit() override; + std::unique_ptr handleInput(engine::core::Context&) override; + std::unique_ptr update(float delta_time, engine::core::Context&) override; +}; + +} // namespace game::component::state diff --git a/src/game/component/state/idle_state.cpp b/src/game/component/state/idle_state.cpp new file mode 100644 index 0000000..4e9792b --- /dev/null +++ b/src/game/component/state/idle_state.cpp @@ -0,0 +1,50 @@ +#include "idle_state.h" +#include "fall_state.h" +#include "jump_state.h" +#include "walk_state.h" +#include "../player_component.h" +#include "../../../engine/core/context.h" +#include "../../../engine/input/input_manager.h" +#include "../../../engine/component/physics_component.h" +#include + +namespace game::component::state { + +void IdleState::enter() { + +} + +void IdleState::exit() { + +} + +std::unique_ptr IdleState::handleInput(engine::core::Context& context) +{ + auto input_manager = context.getInputManager(); + // 如果按下了左右移动键,则切换到 WalkState + if (input_manager.isActionDown("move_left") || input_manager.isActionDown("move_right")) { + return std::make_unique(player_component_); + } + + // 如果按下“jump”则切换到 JumpState + if (input_manager.isActionPressed("jump")) { + return std::make_unique(player_component_); + } + return nullptr; +} + +std::unique_ptr IdleState::update(float, engine::core::Context&) +{ + // 应用摩擦力(水平方向) + auto physics_component = player_component_->getPhysicsComponent(); + auto friction_factor = player_component_->getFrictionFactor(); + physics_component->velocity_.x *= friction_factor; + + // 如果下方没有碰撞,则切换到 FallState + if (!physics_component->hasCollidedBelow()) { + return std::make_unique(player_component_); + } + return nullptr; +} + +} // namespace game::component::state \ No newline at end of file diff --git a/src/game/component/state/idle_state.h b/src/game/component/state/idle_state.h new file mode 100644 index 0000000..eb181e6 --- /dev/null +++ b/src/game/component/state/idle_state.h @@ -0,0 +1,19 @@ +#pragma once +#include "player_state.h" + +namespace game::component::state { + +class IdleState final : public PlayerState { + friend class game::component::PlayerComponent; +public: + IdleState(PlayerComponent* player_component) : PlayerState(player_component) {} + ~IdleState() override = default; + +private: + void enter() override; + void exit() override; + std::unique_ptr handleInput(engine::core::Context&) override; + std::unique_ptr update(float delta_time, engine::core::Context&) override; +}; + +} // namespace game::component::state diff --git a/src/game/component/state/jump_state.cpp b/src/game/component/state/jump_state.cpp new file mode 100644 index 0000000..65ecbdc --- /dev/null +++ b/src/game/component/state/jump_state.cpp @@ -0,0 +1,59 @@ +#include "jump_state.h" +#include "idle_state.h" +#include "walk_state.h" +#include "fall_state.h" +#include "../player_component.h" +#include "../../../engine/core/context.h" +#include "../../../engine/input/input_manager.h" +#include "../../../engine/component/physics_component.h" +#include "../../../engine/component/sprite_component.h" +#include +#include + +namespace game::component::state { + +void JumpState::enter() { + auto physics_component = player_component_->getPhysicsComponent(); + physics_component->velocity_.y = -player_component_->getJumpForce(); // 向上跳跃 + spdlog::debug("PlayerComponent 进入 JumpState,设置初始垂直速度为: {}", physics_component->velocity_.y); +} + +void JumpState::exit() { + +} + +std::unique_ptr JumpState::handleInput(engine::core::Context& context) +{ + auto input_manager = context.getInputManager(); + auto physics_component = player_component_->getPhysicsComponent(); + auto sprite_component = player_component_->getSpriteComponent(); + + // 跳跃状态下可以左右移动 + if (input_manager.isActionDown("move_left")) { + if (physics_component->velocity_.x > 0.0f) physics_component->velocity_.x = 0.0f; + physics_component->addForce({-player_component_->getMoveForce(), 0.0f}); + sprite_component->setFlipped(true); + } else if (input_manager.isActionDown("move_right")) { + if (physics_component->velocity_.x < 0.0f) physics_component->velocity_.x = 0.0f; + physics_component->addForce({player_component_->getMoveForce(), 0.0f}); + sprite_component->setFlipped(false); + } + return nullptr; +} + +std::unique_ptr JumpState::update(float, engine::core::Context&) +{ + // 限制最大速度(水平方向) + auto physics_component = player_component_->getPhysicsComponent(); + auto max_speed = player_component_->getMaxSpeed(); + physics_component->velocity_.x = glm::clamp(physics_component->velocity_.x, -max_speed, max_speed); + + // 如果速度为正,切换到 FallState + if (physics_component->velocity_.y > 0.0f) { + return std::make_unique(player_component_); + } + + return nullptr; +} + +} // namespace game::component::state \ No newline at end of file diff --git a/src/game/component/state/jump_state.h b/src/game/component/state/jump_state.h new file mode 100644 index 0000000..d6cf1e0 --- /dev/null +++ b/src/game/component/state/jump_state.h @@ -0,0 +1,19 @@ +#pragma once +#include "player_state.h" + +namespace game::component::state { + +class JumpState final : public PlayerState { + friend class game::component::PlayerComponent; +public: + JumpState(PlayerComponent* player_component) : PlayerState(player_component) {} + ~JumpState() override = default; + +private: + void enter() override; + void exit() override; + std::unique_ptr handleInput(engine::core::Context&) override; + std::unique_ptr update(float delta_time, engine::core::Context&) override; +}; + +} // namespace game::component::state diff --git a/src/game/component/state/player_state.h b/src/game/component/state/player_state.h new file mode 100644 index 0000000..f29ba7b --- /dev/null +++ b/src/game/component/state/player_state.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include + +namespace engine::core { + class Context; +} + +namespace game::component { + class PlayerComponent; +} + +namespace game::component::state { + +/** + * @brief 玩家状态机的抽象基类。 + */ +class PlayerState { + friend class game::component::PlayerComponent; +protected: + PlayerComponent* player_component_ = nullptr; ///< @brief 指向拥有此状态的玩家组件 + +public: + PlayerState(PlayerComponent* player_component) : player_component_(player_component) {} + virtual ~PlayerState() = default; + + // 禁止拷贝和移动 + PlayerState(const PlayerState&) = delete; + PlayerState& operator=(const PlayerState&) = delete; + PlayerState(PlayerState&&) = delete; + PlayerState& operator=(PlayerState&&) = delete; + +protected: + // 核心状态方法 + virtual void enter() = 0; ///< @brief 进入 + virtual void exit() = 0; ///< @brief 离开 + virtual std::unique_ptr handleInput(engine::core::Context&) = 0; ///< @brief 处理输入 + virtual std::unique_ptr update(float, engine::core::Context&) = 0; ///< @brief 更新 + /* handleInput 和 update 返回值为下一个状态,如果不需要切换状态,则返回 nullptr */ + +}; + +} // namespace game::component::state \ No newline at end of file diff --git a/src/game/component/state/walk_state.cpp b/src/game/component/state/walk_state.cpp new file mode 100644 index 0000000..d08e074 --- /dev/null +++ b/src/game/component/state/walk_state.cpp @@ -0,0 +1,72 @@ +#include "walk_state.h" +#include "jump_state.h" +#include "fall_state.h" +#include "idle_state.h" +#include "../player_component.h" +#include "../../../engine/core/context.h" +#include "../../../engine/input/input_manager.h" +#include "../../../engine/object/game_object.h" +#include "../../../engine/component/physics_component.h" +#include "../../../engine/component/sprite_component.h" +#include + + +namespace game::component::state { + +void WalkState::enter() { + +} + +void WalkState::exit() { + +} + +std::unique_ptr WalkState::handleInput(engine::core::Context& context) +{ + auto input_manager = context.getInputManager(); + auto physics_component = player_component_->getPhysicsComponent(); + auto sprite_component = player_component_->getSpriteComponent(); + + // 如果按下“jump”则切换到 JumpState + if (input_manager.isActionPressed("jump")) { + return std::make_unique(player_component_); + } + + // 步行状态可以左右移动 + if (input_manager.isActionDown("move_left")) { + if (physics_component->velocity_.x > 0.0f) { + physics_component->velocity_.x = 0.0f; // 如果当前速度是向右的,则先减速到0 (增强操控手感) + } + // 添加向左的水平力 + physics_component->addForce({-player_component_->getMoveForce(), 0.0f}); + sprite_component->setFlipped(true); // 向左移动时翻转 + } else if (input_manager.isActionDown("move_right")) { + if (physics_component->velocity_.x < 0.0f) { + physics_component->velocity_.x = 0.0f; // 如果当前速度是向左的,则先减速到0 + } + // 添加向右的水平力 + physics_component->addForce({player_component_->getMoveForce(), 0.0f}); + sprite_component->setFlipped(false); // 向右移动时不翻转 + } else { + // 如果没有按下左右移动键,则切换到 IdleState + return std::make_unique(player_component_); + } + return nullptr; +} + +std::unique_ptr WalkState::update(float, engine::core::Context&) +{ + // 限制最大速度 + auto physics_component = player_component_->getPhysicsComponent(); + auto max_speed = player_component_->getMaxSpeed(); + physics_component->velocity_.x = glm::clamp(physics_component->velocity_.x, -max_speed, max_speed); + + // 如果下方没有碰撞,则切换到 FallState + if (!physics_component->hasCollidedBelow()) { + return std::make_unique(player_component_); + } + + return nullptr; +} + +} // namespace game::component::state \ No newline at end of file diff --git a/src/game/component/state/walk_state.h b/src/game/component/state/walk_state.h new file mode 100644 index 0000000..a33cb3d --- /dev/null +++ b/src/game/component/state/walk_state.h @@ -0,0 +1,19 @@ +#pragma once +#include "player_state.h" + +namespace game::component::state { + +class WalkState final : public PlayerState { + friend class game::component::PlayerComponent; +public: + WalkState(PlayerComponent* player_component) : PlayerState(player_component) {} + ~WalkState() override = default; + +private: + void enter() override; + void exit() override; + std::unique_ptr handleInput(engine::core::Context&) override; + std::unique_ptr update(float delta_time, engine::core::Context&) override; +}; + +} // namespace game::component::state diff --git a/src/game/scene/game_scene.cpp b/src/game/scene/game_scene.cpp index 3d870a6..94a2ca3 100644 --- a/src/game/scene/game_scene.cpp +++ b/src/game/scene/game_scene.cpp @@ -1,4 +1,5 @@ #include "game_scene.h" +#include "../component/player_component.h" #include "../../engine/core/context.h" #include "../../engine/object/game_object.h" #include "../../engine/component/transform_component.h" @@ -22,38 +23,22 @@ GameScene::GameScene(std::string name, engine::core::Context& context, engine::s } void GameScene::init() { - // 加载关卡(level_loader通常加载完成后即可销毁,因此不存为成员变量) - engine::scene::LevelLoader level_loader; - level_loader.loadLevel("assets/maps/level1.tmj", *this); - - // 注册"main"层到物理引擎 - auto* main_layer = findGameObjectByName("main"); - if (main_layer) { - auto* tile_layer = main_layer->getComponent(); - if (tile_layer) { - context_.getPhysicsEngine().registerCollisionLayer(tile_layer); - spdlog::info("注册\"main\"层到物理引擎"); - } - } - - // 获取玩家对象 - player_ = findGameObjectByName("player"); - if (!player_) { - spdlog::error("未找到玩家对象"); + if (is_initialized_) { + spdlog::warn("GameScene 已经初始化过了,重复调用 init()。"); return; } - // 相机跟随玩家 - auto* player_transform = player_->getComponent(); - if (player_transform) { - context_.getCamera().setTarget(player_transform); + spdlog::trace("GameScene 初始化开始..."); + + if (!initLevel()) { + spdlog::error("关卡初始化失败,无法继续。"); + context_.getInputManager().setShouldQuit(true); + return; + } + if (!initPlayer()) { + spdlog::error("玩家初始化失败,无法继续。"); + context_.getInputManager().setShouldQuit(true); + return; } - - // 设置相机边界 - auto world_size = main_layer->getComponent()->getWorldSize(); - context_.getCamera().setLimitBounds(engine::utils::Rect(glm::vec2(0.0f), world_size)); - - // 设置世界边界 - context_.getPhysicsEngine().setWorldBounds(engine::utils::Rect(glm::vec2(0.0f), world_size)); Scene::init(); spdlog::trace("GameScene 初始化完成。"); @@ -61,7 +46,6 @@ void GameScene::init() { void GameScene::update(float delta_time) { Scene::update(delta_time); - TestCollisionPairs(); } void GameScene::render() { @@ -70,51 +54,71 @@ void GameScene::render() { void GameScene::handleInput() { Scene::handleInput(); - TestPlayer(); } void GameScene::clean() { Scene::clean(); } -// --- 测试方法 --- +bool GameScene::initLevel() +{ + // 加载关卡(level_loader通常加载完成后即可销毁,因此不存为成员变量) + engine::scene::LevelLoader level_loader; + if (!level_loader.loadLevel("assets/maps/level1.tmj", *this)){ + spdlog::error("关卡加载失败"); + return false; + } -void GameScene::testCamera() { - auto& camera = context_.getCamera(); - auto& input_manager = context_.getInputManager(); - if (input_manager.isActionDown("move_up")) camera.move(glm::vec2(0, -1)); - if (input_manager.isActionDown("move_down")) camera.move(glm::vec2(0, 1)); - if (input_manager.isActionDown("move_left")) camera.move(glm::vec2(-1, 0)); - if (input_manager.isActionDown("move_right")) camera.move(glm::vec2(1, 0)); + // 注册"main"层到物理引擎 + auto* main_layer = findGameObjectByName("main"); + if (!main_layer) { + spdlog::error("未找到\"main\"层"); + return false; + } + auto* tile_layer = main_layer->getComponent(); + if (!tile_layer) { + spdlog::error("\"main\"层没有 TileLayerComponent 组件"); + return false; + } + context_.getPhysicsEngine().registerCollisionLayer(tile_layer); + spdlog::info("注册\"main\"层到物理引擎"); + + // 设置相机边界 + auto world_size = main_layer->getComponent()->getWorldSize(); + context_.getCamera().setLimitBounds(engine::utils::Rect(glm::vec2(0.0f), world_size)); + + // 设置世界边界 + context_.getPhysicsEngine().setWorldBounds(engine::utils::Rect(glm::vec2(0.0f), world_size)); + + spdlog::trace("关卡初始化完成。"); + return true; } -void GameScene::TestPlayer() +bool GameScene::initPlayer() { - if (!player_) return; - auto& input_manager = context_.getInputManager(); - auto* pc = player_->getComponent(); - if (!pc) return; - if (input_manager.isActionDown("move_left")) { - pc->velocity_.x = -100.0f; - } else { - pc->velocity_.x *= 0.9f; + // 获取玩家对象 + player_ = findGameObjectByName("player"); + if (!player_) { + spdlog::error("未找到玩家对象"); + return false; } - if (input_manager.isActionDown("move_right")) { - pc->velocity_.x = 100.0f; - } else { - pc->velocity_.x *= 0.9f; - } - if (input_manager.isActionPressed("jump")) { - pc->velocity_.y = -400.0f; - } -} -void GameScene::TestCollisionPairs() -{ - auto collision_pairs = context_.getPhysicsEngine().getCollisionPairs(); - for (auto& pair : collision_pairs) { - spdlog::info("碰撞对: {} 和 {}", pair.first->getName(), pair.second->getName()); + // 添加PlayerComponent到玩家对象 + auto* player_component = player_->addComponent(); + if (!player_component) { + spdlog::error("无法添加 PlayerComponent 到玩家对象"); + return false; } + + // 相机跟随玩家 + auto* player_transform = player_->getComponent(); + if (!player_transform) { + spdlog::error("玩家对象没有 TransformComponent 组件, 无法设置相机目标"); + return false; + } + context_.getCamera().setTarget(player_transform); + spdlog::trace("Player初始化完成。"); + return true; } } // namespace game::scene \ No newline at end of file diff --git a/src/game/scene/game_scene.h b/src/game/scene/game_scene.h index 411d51d..231ca2d 100644 --- a/src/game/scene/game_scene.h +++ b/src/game/scene/game_scene.h @@ -26,10 +26,9 @@ public: void clean() override; private: - // 测试函数 - void testCamera(); - void TestPlayer(); - void TestCollisionPairs(); + [[nodiscard]] bool initLevel(); ///< @brief 初始化关卡 + [[nodiscard]] bool initPlayer(); ///< @brief 初始化玩家 + }; } // namespace game::scene \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 738b873..ce76ce5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,7 +2,7 @@ #include int main(int /* argc */, char* /* argv */[]) { - // spdlog::set_level(spdlog::level::debug); + spdlog::set_level(spdlog::level::debug); engine::core::GameApp app; app.run();