From a31890fc20adb2c7cc4eb9cd92baec682371ef58 Mon Sep 17 00:00:00 2001 From: Ziyu Date: Sat, 7 Jun 2025 10:49:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=8A=A8=E7=94=BB=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E4=B8=8E=E5=8A=A8=E7=94=BB=E8=BD=BD=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 + Tiled地图解析思路.md | 2 + assets/maps/actor.tsj | 30 +++++++ assets/maps/level1.tmj | 2 +- src/engine/component/animation_component.cpp | 95 ++++++++++++++++++++ src/engine/component/animation_component.h | 61 +++++++++++++ src/engine/render/animation.cpp | 51 +++++++++++ src/engine/render/animation.h | 73 +++++++++++++++ src/engine/scene/level_loader.cpp | 68 ++++++++++++++ src/engine/scene/level_loader.h | 9 ++ src/game/component/player_component.cpp | 4 +- src/game/component/player_component.h | 3 + src/game/component/state/fall_state.cpp | 2 +- src/game/component/state/idle_state.cpp | 2 +- src/game/component/state/jump_state.cpp | 1 + src/game/component/state/player_state.cpp | 25 ++++++ src/game/component/state/player_state.h | 2 + src/game/component/state/walk_state.cpp | 2 +- src/game/scene/game_scene.cpp | 46 ++++++++++ src/game/scene/game_scene.h | 1 + 20 files changed, 477 insertions(+), 5 deletions(-) create mode 100644 src/engine/component/animation_component.cpp create mode 100644 src/engine/component/animation_component.h create mode 100644 src/engine/render/animation.cpp create mode 100644 src/engine/render/animation.h create mode 100644 src/game/component/state/player_state.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 056f5f6..4ca82b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ add_executable(${TARGET} src/engine/resource/font_manager.cpp src/engine/render/renderer.cpp src/engine/render/camera.cpp + src/engine/render/animation.cpp src/engine/input/input_manager.cpp src/engine/object/game_object.cpp src/engine/component/sprite_component.cpp @@ -49,6 +50,7 @@ add_executable(${TARGET} src/engine/component/tilelayer_component.cpp src/engine/component/physics_component.cpp src/engine/component/collider_component.cpp + src/engine/component/animation_component.cpp src/engine/physics/physics_engine.cpp src/engine/physics/collision.cpp src/engine/scene/scene.cpp @@ -56,6 +58,7 @@ add_executable(${TARGET} src/engine/scene/level_loader.cpp src/game/scene/game_scene.cpp src/game/component/player_component.cpp + src/game/component/state/player_state.cpp src/game/component/state/idle_state.cpp src/game/component/state/walk_state.cpp src/game/component/state/jump_state.cpp diff --git a/Tiled地图解析思路.md b/Tiled地图解析思路.md index 25dfaae..0bedeec 100644 --- a/Tiled地图解析思路.md +++ b/Tiled地图解析思路.md @@ -20,6 +20,8 @@ - 源图片信息:如果非"solid"类型,则 - 碰撞盒大小、偏移: 获取 "objectgroup" --> 获取 "objects" --> "x","y","width","height" - 获取其他指定自定义属性(模版函数),进行必要设置/组件添加。(例如tag、gravity) + - 源图片信息:如果有"animation"字段(自定义),则载入动画信息并创建动画组件 + - 必要讯息(自定义):动画名称、每一帧的源矩形、帧持续时间。 - 如果没有gid,则代表自己绘制的形状(可能是碰撞盒、触发器等,未来按需处理) - 自定义属性的获取方法:图块集中 获取 "tiles" --> 获取 "properties" diff --git a/assets/maps/actor.tsj b/assets/maps/actor.tsj index 1b23ab1..0b69935 100644 --- a/assets/maps/actor.tsj +++ b/assets/maps/actor.tsj @@ -41,6 +41,11 @@ "y":0 }, "properties":[ + { + "name":"animation", + "type":"string", + "value":"{\n \"fly\": {\"frames\": [0,1,2,3]}\n}" + }, { "name":"tag", "type":"string", @@ -79,6 +84,11 @@ "y":0 }, "properties":[ + { + "name":"animation", + "type":"string", + "value":"{\n \"idle\": {\"duration\": 200, \"row\":0, \"frames\":[0,1,2,3,2,1]},\n \"walk\": {\"row\":1, \"frames\":[0,1,2,3,4,5]},\n \"climb\":{\"row\":2, \"frames\":[0,1,2,3]},\n \"hurt\": {\"row\":4, \"frames\":[0,1]},\n \"jump\": {\"row\":5, \"frames\":[0]},\n \"fall\": {\"row\":5, \"frames\":[1]}\n}" + }, { "name":"gravity", "type":"bool", @@ -122,6 +132,11 @@ "y":0 }, "properties":[ + { + "name":"animation", + "type":"string", + "value":"{\n \"idle\": {\"duration\": 300, \"frames\":[0,1,2,3]},\n \"jump\": {\"row\": 1, \"frames\":[1]},\n \"fall\": {\"row\": 1, \"frames\":[2]}\n}" + }, { "name":"gravity", "type":"bool", @@ -165,6 +180,11 @@ "y":0 }, "properties":[ + { + "name":"animation", + "type":"string", + "value":"{\n \"walk\": {\"frames\":[0,1,2,3,4,5]}\n}" + }, { "name":"gravity", "type":"bool", @@ -208,6 +228,11 @@ "y":0 }, "properties":[ + { + "name":"animation", + "type":"string", + "value":"{\n \"idle\": {\"duration\": 200, \"frames\":[0,1,2,3,4,3,2,1]}\n}" + }, { "name":"tag", "type":"string", @@ -246,6 +271,11 @@ "y":0 }, "properties":[ + { + "name":"animation", + "type":"string", + "value":"{\n \"idle\": {\"duration\": 200, \"frames\":[0,1,2,3,4]}\n}" + }, { "name":"tag", "type":"string", diff --git a/assets/maps/level1.tmj b/assets/maps/level1.tmj index 84ba0e8..c29deac 100644 --- a/assets/maps/level1.tmj +++ b/assets/maps/level1.tmj @@ -157,7 +157,7 @@ "gid":609, "height":32, "id":4, - "name":"", + "name":"frog", "rotation":0, "type":"", "visible":true, diff --git a/src/engine/component/animation_component.cpp b/src/engine/component/animation_component.cpp new file mode 100644 index 0000000..4c007c5 --- /dev/null +++ b/src/engine/component/animation_component.cpp @@ -0,0 +1,95 @@ +#include "animation_component.h" +#include "sprite_component.h" +#include "../object/game_object.h" +#include "../render/animation.h" +#include + +namespace engine::component { + +AnimationComponent::~AnimationComponent() = default; + +void AnimationComponent::init() { + if (!owner_) { + spdlog::error("AnimationComponent 没有所有者 GameObject!"); + return; + } + sprite_component_ = owner_->getComponent(); + if (!sprite_component_) { + spdlog::error("GameObject '{}' 的 AnimationComponent 需要 SpriteComponent,但未找到。", owner_->getName()); + return; + } +} + +void AnimationComponent::update(float delta_time, engine::core::Context&) { + // 如果没有正在播放的动画,或者没有当前动画,或者没有精灵组件,或者当前动画没有帧,则直接返回 + if (!is_playing_ || !current_animation_ || !sprite_component_ || current_animation_->isEmpty()) { + spdlog::trace("AnimationComponent 更新时没有正在播放的动画或精灵组件为空。"); + return; + } + + // 推进计时器 + animation_timer_ += delta_time; + + // 根据时间获取当前帧 + const auto& current_frame = current_animation_->getFrame(animation_timer_); + + // 更新精灵组件的源矩形 (使用 SpriteComponent 的新方法) + sprite_component_->setSourceRect(current_frame.source_rect); + + // 检查非循环动画是否已结束 + if (!current_animation_->isLooping() && animation_timer_ >= current_animation_->getTotalDuration()) { + is_playing_ = false; + animation_timer_ = current_animation_->getTotalDuration(); // 将时间限制在结束点 + if (is_one_shot_removal_) { // 如果 is_one_shot_removal_ 为 true,则删除整个 GameObject + owner_->setNeedRemove(true); + } + } +} + +void AnimationComponent::addAnimation(std::unique_ptr animation) { + if (!animation) return; + std::string name = animation->getName(); // 获取名称 + animations_[name] = std::move(animation); + spdlog::debug("已将动画 '{}' 添加到 GameObject '{}'", name, owner_ ? owner_->getName() : "未知"); +} + +void AnimationComponent::playAnimation(const std::string& name) { + auto it = animations_.find(name); + if (it == animations_.end() || !it->second) { + spdlog::warn("未找到 GameObject '{}' 的动画 '{}'", name, owner_ ? owner_->getName() : "未知"); + return; + } + + // 如果已经在播放相同的动画,不重新开始(注释这一段则重新开始播放) + if (current_animation_ == it->second.get() && is_playing_) { + return; + } + + current_animation_ = it->second.get(); + animation_timer_ = 0.0f; + is_playing_ = true; + + // 立即将精灵更新到第一帧 + if (sprite_component_ && !current_animation_->isEmpty()) { + const auto& first_frame = current_animation_->getFrame(0.0f); + sprite_component_->setSourceRect(first_frame.source_rect); + spdlog::debug("GameObject '{}' 播放动画 '{}'", owner_ ? owner_->getName() : "未知", name); + } +} + +std::string AnimationComponent::getCurrentAnimationName() const { + if (current_animation_) { + return current_animation_->getName(); + } + return ""; +} + + bool AnimationComponent::isAnimationFinished() const { + // 如果没有当前动画(说明从未调用过playAnimation),或者当前动画是循环的,则返回 false + if (!current_animation_ || current_animation_->isLooping()) { + return false; + } + return animation_timer_ >= current_animation_->getTotalDuration(); + } + +} // namespace engine::component \ No newline at end of file diff --git a/src/engine/component/animation_component.h b/src/engine/component/animation_component.h new file mode 100644 index 0000000..69d14c8 --- /dev/null +++ b/src/engine/component/animation_component.h @@ -0,0 +1,61 @@ +#pragma once +#include "./component.h" +#include +#include +#include + +namespace engine::render { + class Animation; +} +namespace engine::component { + class SpriteComponent; +} + +namespace engine::component { + +/** + * @brief GameObject的动画组件。 + * + * 持有一组Animation对象并控制其播放, + * 根据当前帧更新关联的SpriteComponent。 + */ +class AnimationComponent : public Component { + friend class engine::object::GameObject; +private: + /// @brief 动画名称到Animation对象的映射。 + std::unordered_map> animations_; + SpriteComponent* sprite_component_ = nullptr; ///< @brief 指向必需的SpriteComponent的指针 + engine::render::Animation* current_animation_ = nullptr; ///< @brief 指向当前播放动画的原始指针 + + float animation_timer_ = 0.0f; ///< @brief 动画播放中的计时器 + bool is_playing_ = false; ///< @brief 当前是否有动画正在播放 + bool is_one_shot_removal_ = false; ///< @brief 是否在动画结束后删除整个GameObject + +public: + AnimationComponent() = default; + ~AnimationComponent() override; + + // 删除复制/移动操作 + AnimationComponent(const AnimationComponent&) = delete; + AnimationComponent& operator=(const AnimationComponent&) = delete; + AnimationComponent(AnimationComponent&&) = delete; + AnimationComponent& operator=(AnimationComponent&&) = delete; + + void addAnimation(std::unique_ptr animation); ///< @brief 向 animations_ map容器中添加一个动画。 + void playAnimation(const std::string& name); ///< @brief 播放指定名称的动画。 + void stopAnimation() { is_playing_ = false; } ///< @brief 停止当前动画播放。 + + // --- Getters and Setters --- + std::string getCurrentAnimationName() const; + bool isPlaying() const { return is_playing_; } + bool isAnimationFinished() const; + bool isOneShotRemoval() const { return is_one_shot_removal_; } + void setOneShotRemoval(bool is_one_shot_removal) { is_one_shot_removal_ = is_one_shot_removal; } + +protected: + // 核心循环方法 + void init() override; + void update(float, engine::core::Context&) override; +}; + +} // namespace engine::component \ No newline at end of file diff --git a/src/engine/render/animation.cpp b/src/engine/render/animation.cpp new file mode 100644 index 0000000..b2638e4 --- /dev/null +++ b/src/engine/render/animation.cpp @@ -0,0 +1,51 @@ +#include "animation.h" +#include +#include + +namespace engine::render { + +Animation::Animation(const std::string& name, bool loop) + : name_(name), loop_(loop) {} + +void Animation::addFrame(const SDL_FRect& source_rect, float duration) { + if (duration <= 0.0f) { + spdlog::warn("尝试向动画 '{}' 添加无效持续时间的帧", name_); + return; + } + frames_.push_back({source_rect, duration}); + total_duration_ += duration; +} + +const AnimationFrame& Animation::getFrame(float time) const { + if (frames_.empty()) { + spdlog::error("动画 '{}' 没有帧,无法获取帧", name_); + return frames_.back(); // 返回最后一帧(空的) + } + + float current_time = time; + + if (loop_ && total_duration_ > 0.0f) { + // 对循环动画使用模运算获取有效时间 + current_time = glm::mod(time, total_duration_); + } else { + // 对于非循环动画,如果时间超过总时长,则停留在最后一帧 + if (current_time >= total_duration_) { + return frames_.back(); + } + } + + // 遍历帧以找到正确的帧 + float accumulated_time = 0.0f; + for (const auto& frame : frames_) { + accumulated_time += frame.duration; + if (current_time < accumulated_time) { + return frame; + } + } + + // 理论上在不应到达这里,但为了安全起见,返回最后一帧 + spdlog::warn("动画 '{}' 在获取帧信息时出现错误。", name_); + return frames_.back(); +} + +} // namespace engine::render \ No newline at end of file diff --git a/src/engine/render/animation.h b/src/engine/render/animation.h new file mode 100644 index 0000000..fd6600c --- /dev/null +++ b/src/engine/render/animation.h @@ -0,0 +1,73 @@ +#pragma once +#include +#include +#include + +namespace engine::render { + +/** + * @brief 代表动画中的单个帧。 + * + * 包含纹理图集上的源矩形和该帧的显示持续时间。 + */ +struct AnimationFrame { + SDL_FRect source_rect; ///< @brief 纹理图集上此帧的区域 + float duration; ///< @brief 此帧显示的持续时间(秒) +}; + +/** + * @brief 管理一系列动画帧。 + * + * 存储动画的帧、总时长、名称和循环行为。 + */ +class Animation final { +private: + std::string name_; ///< @brief 动画的名称 (例如, "walk", "idle")。 + std::vector frames_; ///< @brief 动画帧列表 + float total_duration_ = 0.0f; ///< @brief 动画的总持续时间(秒) + bool loop_ = true; ///< @brief 默认动画是循环的 + +public: + /** + * @brief 构造函数 + * @param name 动画的名称。 + * @param loop 动画是否应该循环播放。 + */ + Animation(const std::string& name = "default", bool loop = true); + ~Animation() = default; + + // 禁止拷贝和移动,因为 Animation 通常由管理器持有,不应随意拷贝 + Animation(const Animation&) = delete; + Animation& operator=(const Animation&) = delete; + Animation(Animation&&) = delete; + Animation& operator=(Animation&&) = delete; + + /** + * @brief 向动画添加一帧。 + * + * @param source_rect 纹理图集上此帧的区域。 + * @param duration 此帧应显示的持续时间(秒)。 + */ + void addFrame(const SDL_FRect& source_rect, float duration); + + /** + * @brief 获取在给定时间点应该显示的动画帧。 + * @param time 当前时间(秒)。如果动画循环,则可以超过总持续时间。 + * @return 对应时间点的动画帧。 + */ + const AnimationFrame& getFrame(float time) const; + + // --- Setters and Getters --- + const std::string& getName() const { return name_; } ///< @brief 获取动画名称。 + const std::vector& getFrames() const { return frames_; } ///< @brief 获取动画帧列表。 + size_t getFrameCount() const { return frames_.size(); } ///< @brief 获取帧数量。 + float getTotalDuration() const { return total_duration_; } ///< @brief 获取动画的总持续时间(秒)。 + bool isLooping() const { return loop_; } ///< @brief 检查动画是否循环播放。 + bool isEmpty() const { return frames_.empty(); } ///< @brief 检查动画是否没有帧。 + + void setName(const std::string& name) { name_ = name; } ///< @brief 设置动画名称。 + void setLooping(bool loop) { loop_ = loop; } ///< @brief 设置动画是否循环播放。 + +}; + +} // namespace engine::render \ No newline at end of file diff --git a/src/engine/scene/level_loader.cpp b/src/engine/scene/level_loader.cpp index 908c005..5d405fa 100644 --- a/src/engine/scene/level_loader.cpp +++ b/src/engine/scene/level_loader.cpp @@ -5,11 +5,13 @@ #include "../component/sprite_component.h" #include "../component/collider_component.h" #include "../component/physics_component.h" +#include "../component/animation_component.h" #include "../object/game_object.h" #include "../scene/scene.h" #include "../core/context.h" #include "../resource/resource_manager.h" #include "../render/sprite.h" +#include "../render/animation.h" #include "../utils/math.h" #include #include @@ -229,6 +231,23 @@ void LevelLoader::loadObjectLayer(const nlohmann::json& layer_json, Scene& scene } } + // 获取动画信息并设置 + auto anim_string = getTileProperty(tile_json, "animation"); + if (anim_string) { + // 解析string为JSON对象 + nlohmann::json anim_json; + try { + anim_json = nlohmann::json::parse(anim_string.value()); + } catch (const nlohmann::json::parse_error& e) { + spdlog::error("解析动画 JSON 字符串失败: {}", e.what()); + continue; // 跳过此对象 + } + // 添加AnimationComponent + auto* ac = game_object->addComponent(); + // 添加动画到 AnimationComponent + addAnimation(anim_json, ac, src_size); + } + // 添加到场景中 scene.addGameObject(std::move(game_object)); spdlog::info("加载对象: '{}' 完成", object_name); @@ -236,6 +255,55 @@ void LevelLoader::loadObjectLayer(const nlohmann::json& layer_json, Scene& scene } } +void LevelLoader::addAnimation(const nlohmann::json& anim_json, engine::component::AnimationComponent *ac, const glm::vec2& sprite_size) +{ + // 检查 anim_json 必须是一个对象,并且 ac 不能为 nullptr + if (!anim_json.is_object() || !ac) { + spdlog::error("无效的动画 JSON 或 AnimationComponent 指针。"); + return; + } + // 遍历动画 JSON 对象中的每个键值对(动画名称 : 动画信息) + for (const auto& anim : anim_json.items()) { + const std::string& anim_name = anim.key(); + const auto& anim_info = anim.value(); + if (!anim_info.is_object()) { + spdlog::warn("动画 '{}' 的信息无效或为空。", anim_name); + continue; + } + // 获取可能存在的动画帧信息 + auto duration_ms = anim_info.value("duration", 100); // 默认持续时间为100毫秒 + auto duration = static_cast(duration_ms) / 1000.0f; // 转换为秒 + auto row = anim_info.value("row", 0); // 默认行数为0 + // 帧信息(数组)是必须存在的 + if (!anim_info.contains("frames") || !anim_info["frames"].is_array()) { + spdlog::warn("动画 '{}' 缺少 'frames' 数组。", anim_name); + continue; + } + // 创建一个Animation对象 (默认为循环播放) + auto animation = std::make_unique(anim_name); + + // 遍历数组并进行添加帧信息到animation对象 + for (const auto& frame : anim_info["frames"]) { + if (!frame.is_number_integer()) { + spdlog::warn("动画 {} 中 frames 数组格式错误!", anim_name); + continue;; + } + auto column = frame.get(); + // 计算源矩形 + SDL_FRect src_rect = { + column * sprite_size.x, + row * sprite_size.y, + sprite_size.x, + sprite_size.y + }; + // 添加动画帧到 Animation + animation->addFrame(src_rect, duration); + } + // 将 Animation 对象添加到 AnimationComponent 中 + ac->addAnimation(std::move(animation)); + } +} + std::optional LevelLoader::getColliderRect(const nlohmann::json &tile_json) { if (!tile_json.contains("objectgroup")) return std::nullopt; diff --git a/src/engine/scene/level_loader.h b/src/engine/scene/level_loader.h index 21dd3a4..7d6b894 100644 --- a/src/engine/scene/level_loader.h +++ b/src/engine/scene/level_loader.h @@ -7,6 +7,7 @@ #include "../utils/math.h" namespace engine::component { +class AnimationComponent; struct TileInfo; enum class TileType; } @@ -39,6 +40,14 @@ private: void loadTileLayer(const nlohmann::json& layer_json, Scene& scene); ///< @brief 加载瓦片图层 void loadObjectLayer(const nlohmann::json& layer_json, Scene& scene); ///< @brief 加载对象图层 + /** + * @brief 添加动画到指定的 AnimationComponent。 + * @param anim_json 动画json数据(自定义) + * @param ac AnimationComponent 指针(动画添加到此组件) + * @param sprite_size 每一帧动画的尺寸 + */ + void addAnimation(const nlohmann::json& anim_json, engine::component::AnimationComponent* ac, const glm::vec2& sprite_size); + /** * @brief 获取瓦片属性 * @tparam T 属性类型 diff --git a/src/game/component/player_component.cpp b/src/game/component/player_component.cpp index 19866f2..76b80e0 100644 --- a/src/game/component/player_component.cpp +++ b/src/game/component/player_component.cpp @@ -3,6 +3,7 @@ #include "../../engine/component/transform_component.h" #include "../../engine/component/physics_component.h" #include "../../engine/component/sprite_component.h" +#include "../../engine/component/animation_component.h" #include "../../engine/object/game_object.h" #include "../../engine/input/input_manager.h" #include @@ -21,9 +22,10 @@ void PlayerComponent::init() { transform_component_ = owner_->getComponent(); physics_component_ = owner_->getComponent(); sprite_component_ = owner_->getComponent(); + animation_component_ = owner_->getComponent(); // 检查必要组件是否存在 - if (!transform_component_ || !physics_component_ || !sprite_component_) { + if (!transform_component_ || !physics_component_ || !sprite_component_ || !animation_component_) { spdlog::error("Player 对象缺少必要组件!"); } diff --git a/src/game/component/player_component.h b/src/game/component/player_component.h index 5151e8f..19b411e 100644 --- a/src/game/component/player_component.h +++ b/src/game/component/player_component.h @@ -10,6 +10,7 @@ namespace engine::component { class TransformComponent; class PhysicsComponent; class SpriteComponent; + class AnimationComponent; } namespace game::component::state { @@ -28,6 +29,7 @@ private: engine::component::TransformComponent* transform_component_ = nullptr; // 指向 TransformComponent 的非拥有指针 engine::component::SpriteComponent* sprite_component_ = nullptr; engine::component::PhysicsComponent* physics_component_ = nullptr; + engine::component::AnimationComponent* animation_component_ = nullptr; std::unique_ptr current_state_; bool is_dead_ = false; @@ -52,6 +54,7 @@ public: engine::component::TransformComponent* getTransformComponent() const { return transform_component_; } engine::component::SpriteComponent* getSpriteComponent() const { return sprite_component_; } engine::component::PhysicsComponent* getPhysicsComponent() const { return physics_component_; } + engine::component::AnimationComponent* getAnimationComponent() const { return animation_component_; } void setIsDead(bool is_dead) { is_dead_ = is_dead; } ///< @brief 设置玩家是否死亡 bool isDead() const { return is_dead_; } ///< @brief 获取玩家是否死亡 diff --git a/src/game/component/state/fall_state.cpp b/src/game/component/state/fall_state.cpp index 18f61b3..d8d17ae 100644 --- a/src/game/component/state/fall_state.cpp +++ b/src/game/component/state/fall_state.cpp @@ -11,7 +11,7 @@ namespace game::component::state { void FallState::enter() { - + playAnimation("fall"); // 播放下落动画 } void FallState::exit() { diff --git a/src/game/component/state/idle_state.cpp b/src/game/component/state/idle_state.cpp index 4e9792b..3355d24 100644 --- a/src/game/component/state/idle_state.cpp +++ b/src/game/component/state/idle_state.cpp @@ -11,7 +11,7 @@ namespace game::component::state { void IdleState::enter() { - + playAnimation("idle"); // 播放待机动画 } void IdleState::exit() { diff --git a/src/game/component/state/jump_state.cpp b/src/game/component/state/jump_state.cpp index 65ecbdc..2da2eaf 100644 --- a/src/game/component/state/jump_state.cpp +++ b/src/game/component/state/jump_state.cpp @@ -13,6 +13,7 @@ namespace game::component::state { void JumpState::enter() { + playAnimation("jump"); // 播放跳跃动画 auto physics_component = player_component_->getPhysicsComponent(); physics_component->velocity_.y = -player_component_->getJumpForce(); // 向上跳跃 spdlog::debug("PlayerComponent 进入 JumpState,设置初始垂直速度为: {}", physics_component->velocity_.y); diff --git a/src/game/component/state/player_state.cpp b/src/game/component/state/player_state.cpp new file mode 100644 index 0000000..e388165 --- /dev/null +++ b/src/game/component/state/player_state.cpp @@ -0,0 +1,25 @@ +#include "player_state.h" +#include "../player_component.h" +#include "../../../engine/component/animation_component.h" +#include "../../../engine/object/game_object.h" +#include + +namespace game::component::state { + +void PlayerState::playAnimation(const std::string& animation_name) { + if (!player_component_) { + spdlog::error("PlayerState 没有关联的 PlayerComponent,无法播放动画 '{}'", animation_name); + return; + } + + auto animation_component = player_component_->getAnimationComponent(); + if (!animation_component) { + spdlog::error("PlayerComponent '{}' 没有 AnimationComponent,无法播放动画 '{}'", + player_component_->getOwner()->getName(), animation_name); + return; + } + + animation_component->playAnimation(animation_name); +} + +} // namespace game::component::state \ No newline at end of file diff --git a/src/game/component/state/player_state.h b/src/game/component/state/player_state.h index f29ba7b..8b1d485 100644 --- a/src/game/component/state/player_state.h +++ b/src/game/component/state/player_state.h @@ -30,6 +30,8 @@ public: PlayerState(PlayerState&&) = delete; PlayerState& operator=(PlayerState&&) = delete; + void playAnimation(const std::string& animation_name); ///< @brief 播放指定名称的动画,使用 AnimationComponent 的方法 + protected: // 核心状态方法 virtual void enter() = 0; ///< @brief 进入 diff --git a/src/game/component/state/walk_state.cpp b/src/game/component/state/walk_state.cpp index d08e074..c48d778 100644 --- a/src/game/component/state/walk_state.cpp +++ b/src/game/component/state/walk_state.cpp @@ -14,7 +14,7 @@ namespace game::component::state { void WalkState::enter() { - + playAnimation("walk"); // 播放步行动画 } void WalkState::exit() { diff --git a/src/game/scene/game_scene.cpp b/src/game/scene/game_scene.cpp index 94a2ca3..bbc12a1 100644 --- a/src/game/scene/game_scene.cpp +++ b/src/game/scene/game_scene.cpp @@ -7,6 +7,7 @@ #include "../../engine/component/physics_component.h" #include "../../engine/component/collider_component.h" #include "../../engine/component/tilelayer_component.h" +#include "../../engine/component/animation_component.h" #include "../../engine/physics/physics_engine.h" #include "../../engine/scene/level_loader.h" #include "../../engine/input/input_manager.h" @@ -39,6 +40,11 @@ void GameScene::init() { context_.getInputManager().setShouldQuit(true); return; } + if (!initEnemyAndItem()) { + spdlog::error("敌人和道具初始化失败,无法继续。"); + context_.getInputManager().setShouldQuit(true); + return; + } Scene::init(); spdlog::trace("GameScene 初始化完成。"); @@ -121,4 +127,44 @@ bool GameScene::initPlayer() return true; } +bool GameScene::initEnemyAndItem() +{ + bool success = true; + for (auto& game_object : game_objects_){ + if (game_object->getName() == "eagle"){ + if (auto* ac = game_object->getComponent(); ac){ + ac->playAnimation("fly"); + } else { + spdlog::error("Eagle对象缺少 AnimationComponent,无法播放动画。"); + success = false; + } + } + if (game_object->getName() == "frog"){ + if (auto* ac = game_object->getComponent(); ac){ + ac->playAnimation("idle"); + } else { + spdlog::error("Frog对象缺少 AnimationComponent,无法播放动画。"); + success = false; + } + } + if (game_object->getName() == "opossum"){ + if (auto* ac = game_object->getComponent(); ac){ + ac->playAnimation("walk"); + } else { + spdlog::error("Opossum对象缺少 AnimationComponent,无法播放动画。"); + success = false; + } + } + if (game_object->getTag() == "item"){ + if (auto* ac = game_object->getComponent(); ac){ + ac->playAnimation("idle"); + } else { + spdlog::error("Item对象缺少 AnimationComponent,无法播放动画。"); + success = false; + } + } + } + return success; +} + } // 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 231ca2d..26f7b2e 100644 --- a/src/game/scene/game_scene.h +++ b/src/game/scene/game_scene.h @@ -28,6 +28,7 @@ public: private: [[nodiscard]] bool initLevel(); ///< @brief 初始化关卡 [[nodiscard]] bool initPlayer(); ///< @brief 初始化玩家 + [[nodiscard]] bool initEnemyAndItem(); ///< @brief 初始化敌人和道具 };