From 7e7ad40ef9ba58a374efb17aabba55bcf06b0459 Mon Sep 17 00:00:00 2001 From: Ziyu Date: Tue, 10 Jun 2025 11:34:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=94=80=E7=88=AC=E7=8A=B6?= =?UTF-8?q?=E6=80=81-=E7=B2=97=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + assets/maps/level1.tmj | 4 +- assets/maps/tileset.tsj | 9 +++ src/engine/component/animation_component.h | 1 + src/engine/component/physics_component.h | 4 ++ src/engine/component/tilelayer_component.h | 1 + src/engine/physics/physics_engine.cpp | 4 ++ src/engine/scene/level_loader.cpp | 4 ++ src/game/component/player_component.h | 3 + src/game/component/state/climb_state.cpp | 77 ++++++++++++++++++++++ src/game/component/state/climb_state.h | 19 ++++++ src/game/component/state/fall_state.cpp | 7 ++ src/game/component/state/idle_state.cpp | 8 +++ src/game/component/state/jump_state.cpp | 7 ++ src/game/component/state/walk_state.cpp | 6 ++ 15 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 src/game/component/state/climb_state.cpp create mode 100644 src/game/component/state/climb_state.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f0e4e44..56d177a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,7 @@ add_executable(${TARGET} src/game/component/state/walk_state.cpp src/game/component/state/jump_state.cpp src/game/component/state/fall_state.cpp + src/game/component/state/climb_state.cpp src/game/component/state/hurt_state.cpp src/game/component/state/dead_state.cpp src/game/component/ai/patrol_behavior.cpp diff --git a/assets/maps/level1.tmj b/assets/maps/level1.tmj index c4ec653..36824ec 100644 --- a/assets/maps/level1.tmj +++ b/assets/maps/level1.tmj @@ -126,8 +126,8 @@ "type":"", "visible":true, "width":32, - "x":114.060606060606, - "y":156.598484848485 + "x":482.643939393939, + "y":381.098484848485 }, { "gid":607, diff --git a/assets/maps/tileset.tsj b/assets/maps/tileset.tsj index 6b4c5f5..b40f53d 100644 --- a/assets/maps/tileset.tsj +++ b/assets/maps/tileset.tsj @@ -458,6 +458,15 @@ "value":true }] }, + { + "id":257, + "properties":[ + { + "name":"ladder", + "type":"bool", + "value":true + }] + }, { "id":261, "properties":[ diff --git a/src/engine/component/animation_component.h b/src/engine/component/animation_component.h index 69d14c8..623522b 100644 --- a/src/engine/component/animation_component.h +++ b/src/engine/component/animation_component.h @@ -44,6 +44,7 @@ public: void addAnimation(std::unique_ptr animation); ///< @brief 向 animations_ map容器中添加一个动画。 void playAnimation(const std::string& name); ///< @brief 播放指定名称的动画。 void stopAnimation() { is_playing_ = false; } ///< @brief 停止当前动画播放。 + void resumeAnimation() {is_playing_ = true; } ///< @brief 恢复当前动画播放。 // --- Getters and Setters --- std::string getCurrentAnimationName() const; diff --git a/src/engine/component/physics_component.h b/src/engine/component/physics_component.h index 3411b89..195f225 100644 --- a/src/engine/component/physics_component.h +++ b/src/engine/component/physics_component.h @@ -33,6 +33,7 @@ private: bool collided_above_ = false; bool collided_left_ = false; bool collided_right_ = false; + bool collided_ladder_ = false; public: /** @@ -74,17 +75,20 @@ public: collided_above_ = false; collided_left_ = false; collided_right_ = false; + collided_ladder_ = 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 设置右方碰撞标志 + void setCollidedLadder(bool collided) { collided_ladder_ = 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 检查是否与右方发生碰撞 + bool hasCollidedLadder() const { return collided_ladder_; } ///< @brief 检查是否与梯子发生碰撞 private: // 核心循环方法 diff --git a/src/engine/component/tilelayer_component.h b/src/engine/component/tilelayer_component.h index 1a494e9..56fe210 100644 --- a/src/engine/component/tilelayer_component.h +++ b/src/engine/component/tilelayer_component.h @@ -32,6 +32,7 @@ enum class TileType { SLOPE_1_2, ///< @brief 斜坡瓦片,高度:左1 右1/2 SLOPE_2_0, ///< @brief 斜坡瓦片,高度:左1/2右0 HAZARD, ///< @brief 危险瓦片(例如火焰、尖刺等) + LADDER, ///< @brief 梯子瓦片 // 未来补充其它类型 }; diff --git a/src/engine/physics/physics_engine.cpp b/src/engine/physics/physics_engine.cpp index fe950ed..106195c 100644 --- a/src/engine/physics/physics_engine.cpp +++ b/src/engine/physics/physics_engine.cpp @@ -361,6 +361,10 @@ void PhysicsEngine::checkTileTriggers() if (tile_type == engine::component::TileType::HAZARD) { triggers_set.insert(tile_type); // 记录触发事件,set 保证每个瓦片类型只记录一次 } + // 梯子类型不必记录到事件容器,物理引擎自己处理 + else if (tile_type == engine::component::TileType::LADDER) { + pc->setCollidedLadder(true); + } } } // 遍历触发事件集合,添加到 tile_trigger_events_ 中 diff --git a/src/engine/scene/level_loader.cpp b/src/engine/scene/level_loader.cpp index 5c69ab0..a109922 100644 --- a/src/engine/scene/level_loader.cpp +++ b/src/engine/scene/level_loader.cpp @@ -368,6 +368,10 @@ engine::component::TileType LevelLoader::getTileType(const nlohmann::json &tile_ auto is_hazard = property.value("value", false); return is_hazard ? engine::component::TileType::HAZARD : engine::component::TileType::NORMAL; } + else if (property.contains("name") && property["name"] == "ladder") { + auto is_ladder = property.value("value", false); + return is_ladder ? engine::component::TileType::LADDER : engine::component::TileType::NORMAL; + } // TODO: 可以在这里添加更多的自定义属性处理逻辑 } } diff --git a/src/game/component/player_component.h b/src/game/component/player_component.h index 24a5b59..796345f 100644 --- a/src/game/component/player_component.h +++ b/src/game/component/player_component.h @@ -39,6 +39,7 @@ private: // --- 移动相关参数 float move_force_ = 200.0f; ///< @brief 水平移动力 float max_speed_ = 120.0f; ///< @brief 最大移动速度 (像素/秒) + float climb_speed_ = 100.0f; ///< @brief 爬梯子速度 (像素/秒) float friction_factor_ = 0.85f; ///< @brief 摩擦系数 (Idle时缓冲效果,每帧乘以此系数) float jump_vel_ = 350.0f; ///< @brief 跳跃速度 (按下"jump"键给的瞬间向上的速度) @@ -70,6 +71,8 @@ public: 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 setClimbSpeed(float climb_speed) { climb_speed_ = climb_speed; } ///< @brief 设置爬梯子速度 + float getClimbSpeed() const { return climb_speed_; } ///< @brief 获取爬梯子速度 void setFrictionFactor(float friction_factor) { friction_factor_ = friction_factor; } ///< @brief 设置摩擦系数 float getFrictionFactor() const { return friction_factor_; } ///< @brief 获取摩擦系数 void setJumpVelocity(float jump_vel) { jump_vel_ = jump_vel; } ///< @brief 设置跳跃速度 diff --git a/src/game/component/state/climb_state.cpp b/src/game/component/state/climb_state.cpp new file mode 100644 index 0000000..98f2778 --- /dev/null +++ b/src/game/component/state/climb_state.cpp @@ -0,0 +1,77 @@ +#include "climb_state.h" +#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 "../../../engine/component/animation_component.h" +#include +#include + +namespace game::component::state { + +void ClimbState::enter() { + spdlog::debug("进入攀爬状态"); + playAnimation("climb"); + if (auto* physics = player_component_->getPhysicsComponent(); physics) { + physics->setUseGravity(false); // 禁用重力 + } +} + +void ClimbState::exit() { + spdlog::debug("退出攀爬状态"); + + if (auto* physics = player_component_->getPhysicsComponent(); physics) { + physics->setUseGravity(true); // 重新启用重力 + } +} + +std::unique_ptr ClimbState::handleInput(engine::core::Context& context) { + + auto input_manager = context.getInputManager(); + auto physics_component = player_component_->getPhysicsComponent(); + auto animation_component = player_component_->getAnimationComponent(); + + // --- 攀爬状态下,按键则移动,不按键则静止 --- + auto is_up = input_manager.isActionDown("move_up"); + auto is_down = input_manager.isActionDown("move_down"); + auto is_left = input_manager.isActionDown("move_left"); + auto is_right = input_manager.isActionDown("move_right"); + auto speed = player_component_->getClimbSpeed(); + + physics_component->velocity_.y = is_up ? -speed : // 三目运算符嵌套,自左向右执行 + is_down ? speed : 0.0f; + physics_component->velocity_.x = is_left ? -speed : + is_right ? speed : 0.0f; + + // --- 根据是否有按键决定动画播放情况 --- + (is_up || is_down || is_left || is_right) + ? animation_component->resumeAnimation() // 有按键则恢复动画播放 + : animation_component->stopAnimation(); // 无按键则停止动画播放 + + // 按跳跃键主动离开攀爬状态 + if (input_manager.isActionPressed("jump")) { + return std::make_unique(player_component_); + } + return nullptr; +} + +std::unique_ptr ClimbState::update(float, engine::core::Context&) { + auto physics_component = player_component_->getPhysicsComponent(); + // 如果着地,则切换到 IdleState + if (physics_component->hasCollidedBelow()) { + return std::make_unique(player_component_); + } + // 如果离开梯子区域,则切换到 FallState(能走到这里 说明非着地状态) + if (!physics_component->hasCollidedLadder()) { + 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/climb_state.h b/src/game/component/state/climb_state.h new file mode 100644 index 0000000..598a87d --- /dev/null +++ b/src/game/component/state/climb_state.h @@ -0,0 +1,19 @@ +#pragma once +#include "player_state.h" + +namespace game::component::state { + +class ClimbState final : public PlayerState { + friend class game::component::PlayerComponent; +public: + ClimbState(PlayerComponent* player_component) : PlayerState(player_component) {} + ~ClimbState() 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/fall_state.cpp b/src/game/component/state/fall_state.cpp index d8d17ae..f8e4bfc 100644 --- a/src/game/component/state/fall_state.cpp +++ b/src/game/component/state/fall_state.cpp @@ -1,6 +1,7 @@ #include "fall_state.h" #include "idle_state.h" #include "walk_state.h" +#include "climb_state.h" #include "../player_component.h" #include "../../../engine/core/context.h" #include "../../../engine/input/input_manager.h" @@ -24,6 +25,12 @@ std::unique_ptr FallState::handleInput(engine::core::Context& conte auto physics_component = player_component_->getPhysicsComponent(); auto sprite_component = player_component_->getSpriteComponent(); + // 如果按下上下键,且与梯子重合,则切换到 ClimbState + if (physics_component->hasCollidedLadder() && + (input_manager.isActionDown("move_up") || input_manager.isActionDown("move_down"))) { + 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; diff --git a/src/game/component/state/idle_state.cpp b/src/game/component/state/idle_state.cpp index 3355d24..3aefed9 100644 --- a/src/game/component/state/idle_state.cpp +++ b/src/game/component/state/idle_state.cpp @@ -2,6 +2,7 @@ #include "fall_state.h" #include "jump_state.h" #include "walk_state.h" +#include "climb_state.h" #include "../player_component.h" #include "../../../engine/core/context.h" #include "../../../engine/input/input_manager.h" @@ -21,6 +22,13 @@ void IdleState::exit() { std::unique_ptr IdleState::handleInput(engine::core::Context& context) { auto input_manager = context.getInputManager(); + auto physics_component = player_component_->getPhysicsComponent(); + + // 如果按"move_up"键,且与梯子重合,则切换到 ClimbState + if (physics_component->hasCollidedLadder() && input_manager.isActionDown("move_up")) { + return std::make_unique(player_component_); + } + // 如果按下了左右移动键,则切换到 WalkState if (input_manager.isActionDown("move_left") || input_manager.isActionDown("move_right")) { return std::make_unique(player_component_); diff --git a/src/game/component/state/jump_state.cpp b/src/game/component/state/jump_state.cpp index 6d3abce..c7a76e6 100644 --- a/src/game/component/state/jump_state.cpp +++ b/src/game/component/state/jump_state.cpp @@ -2,6 +2,7 @@ #include "idle_state.h" #include "walk_state.h" #include "fall_state.h" +#include "climb_state.h" #include "../player_component.h" #include "../../../engine/core/context.h" #include "../../../engine/input/input_manager.h" @@ -29,6 +30,12 @@ std::unique_ptr JumpState::handleInput(engine::core::Context& conte auto physics_component = player_component_->getPhysicsComponent(); auto sprite_component = player_component_->getSpriteComponent(); + // 如果按下上下键,且与梯子重合,则切换到 ClimbState + if (physics_component->hasCollidedLadder() && + (input_manager.isActionDown("move_up") || input_manager.isActionDown("move_down"))) { + 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; diff --git a/src/game/component/state/walk_state.cpp b/src/game/component/state/walk_state.cpp index c48d778..6d36203 100644 --- a/src/game/component/state/walk_state.cpp +++ b/src/game/component/state/walk_state.cpp @@ -2,6 +2,7 @@ #include "jump_state.h" #include "fall_state.h" #include "idle_state.h" +#include "climb_state.h" #include "../player_component.h" #include "../../../engine/core/context.h" #include "../../../engine/input/input_manager.h" @@ -27,6 +28,11 @@ std::unique_ptr WalkState::handleInput(engine::core::Context& conte auto physics_component = player_component_->getPhysicsComponent(); auto sprite_component = player_component_->getSpriteComponent(); + // 如果按"move_up"键,且与梯子重合,则切换到 ClimbState + if (physics_component->hasCollidedLadder() && input_manager.isActionDown("move_up")) { + return std::make_unique(player_component_); + } + // 如果按下“jump”则切换到 JumpState if (input_manager.isActionPressed("jump")) { return std::make_unique(player_component_);