From c4afa83817bd72c50bd211f5dc018915a5ca3aa9 Mon Sep 17 00:00:00 2001 From: Ziyu Date: Wed, 11 Jun 2025 09:47:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=9C=9F=E7=8B=BC=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E4=B8=8E=E6=97=A0=E6=95=8C=E9=97=AA=E7=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Tiled地图解析思路.md | 31 ++++++++++++++++++++++++- assets/maps/level1.tmj | 4 ++-- src/game/component/player_component.cpp | 30 ++++++++++++++++++++++++ src/game/component/player_component.h | 9 +++++++ src/game/component/state/idle_state.cpp | 4 ++-- src/game/component/state/walk_state.cpp | 4 ++-- 6 files changed, 75 insertions(+), 7 deletions(-) diff --git a/Tiled地图解析思路.md b/Tiled地图解析思路.md index 0bedeec..2868c32 100644 --- a/Tiled地图解析思路.md +++ b/Tiled地图解析思路.md @@ -28,4 +28,33 @@ - **对象层物体特别注意:** 信息分散在两个区块中: - 地图tmj中: 绘制区域、名字 (及针对当前地图添加的自定义属性) - - gid对应的图块集tsj中: 源图片信息 (画在任何地图上都会有的属性) \ No newline at end of file + - gid对应的图块集tsj中: 源图片信息 (画在任何地图上都会有的属性) + + +# 地图自定义属性约定 +- 限瓦片层 + - unisolid (bool) : 单向平台 + - ladder (bool) : 梯子 + - slope (string) : 斜坡图块,格式"x_y"。x表示左边高度,y表示右边高度。范围0~1。(如果是2则表示1/2高度) + +- 限对象层 + - tag (string) : 对象类型,例如player,enemy,item + - gravity (bool) : 是否使用重力(有此字段则默认添加 PhysicsComponent) + - health (int) : 添加生命组件,生命值为 value + - animation (json_string) : 添加动画组件,json格式为: + ```json + { + "name1" : {"duration" : int, "row" : int, "frames": array(int)}, + "name2" : ... + } + ``` + 含义为: + ```json + { "动画名" : {"帧持续时间" : ms, "行" : (从0开始数), "帧序列(列)": [0,1,2...]} } + ``` + 要求每个动画序列帧必须在同一行。 + - 图块碰撞编辑器:识别为碰撞盒,限一个图形,只支持矩形和圆形 + +- 两层皆可 + - solid (bool) : 静止不可进入的图块/物体 + - hazard (bool) : 对玩家造成伤害的瓦片 \ No newline at end of file diff --git a/assets/maps/level1.tmj b/assets/maps/level1.tmj index 36824ec..7278ed5 100644 --- a/assets/maps/level1.tmj +++ b/assets/maps/level1.tmj @@ -126,8 +126,8 @@ "type":"", "visible":true, "width":32, - "x":482.643939393939, - "y":381.098484848485 + "x":636.507575757576, + "y":157.962121212121 }, { "gid":607, diff --git a/src/game/component/player_component.cpp b/src/game/component/player_component.cpp index 9bdf19e..a7da2bc 100644 --- a/src/game/component/player_component.cpp +++ b/src/game/component/player_component.cpp @@ -81,6 +81,11 @@ void PlayerComponent::setState(std::unique_ptr new_state) { } +bool PlayerComponent::is_on_ground() const +{ + return coyote_timer_ <= coyote_time_ || physics_component_->hasCollidedBelow(); +} + void PlayerComponent::handleInput(engine::core::Context& context) { if (!current_state_) return; @@ -93,6 +98,31 @@ void PlayerComponent::handleInput(engine::core::Context& context) { void PlayerComponent::update(float delta_time, engine::core::Context& context) { if (!current_state_) return; + // 一旦离地,开始计时 Coyote Timer + if (!physics_component_->hasCollidedBelow()) { + coyote_timer_ += delta_time; + } else { // 如果碰撞到地面,重置 Coyote Timer + coyote_timer_ = 0.0f; + } + + // 如果处于无敌状态,则进行闪烁 + if (health_component_->isInvincible()) { + flash_timer_ += delta_time; // 闪烁计时器增加 + if (flash_timer_ >= 2 * flash_interval_) { + flash_timer_ -= 2 * flash_interval_; // 闪烁计时器在 0~2倍闪烁间隔 中循环 + } + // 一半时间可见,一半时间不可见。 + if (flash_timer_ < flash_interval_) { + sprite_component_->setHidden(true); + } else { + sprite_component_->setHidden(false); + } + } + // 非无敌状态时确保精灵可见 + else if (sprite_component_->isHidden()) { + sprite_component_->setHidden(false); + } + auto next_state = current_state_->update(delta_time, context); if (next_state) { setState(std::move(next_state)); diff --git a/src/game/component/player_component.h b/src/game/component/player_component.h index 796345f..e260231 100644 --- a/src/game/component/player_component.h +++ b/src/game/component/player_component.h @@ -46,6 +46,14 @@ private: // --- 属性相关参数 --- float stunned_duration_ = 0.4f; ///< @brief 玩家被击中后的硬直时间(单位:秒) + // 土狼时间(Coyote Time): 允许玩家在离地后短暂时间内仍然可以跳跃 + static constexpr float coyote_time_ = 0.1f; ///< @brief Coyote Time (单位:秒) + float coyote_timer_ = 0.0f; ///< @brief Coyote Time 计时器 + + // 无敌闪烁时间 + static constexpr float flash_interval_ = 0.1f; ///< @brief 闪烁间隔时间(单位:秒) + float flash_timer_ = 0.0f; ///< @brief 闪烁计时器,用于无敌状态下的闪烁效果 + public: PlayerComponent() = default; ~PlayerComponent() override = default; @@ -81,6 +89,7 @@ public: float getStunnedDuration() const { return stunned_duration_; } ///< @brief 获取硬直时间 void setState(std::unique_ptr new_state); ///< @brief 切换玩家状态 + bool is_on_ground() const; ///< @brief 检查玩家是否在地面上(考虑了Coyote Time) private: // 核心循环函数 diff --git a/src/game/component/state/idle_state.cpp b/src/game/component/state/idle_state.cpp index 51d2fc5..28d2578 100644 --- a/src/game/component/state/idle_state.cpp +++ b/src/game/component/state/idle_state.cpp @@ -56,8 +56,8 @@ std::unique_ptr IdleState::update(float, engine::core::Context&) auto friction_factor = player_component_->getFrictionFactor(); physics_component->velocity_.x *= friction_factor; - // 如果下方没有碰撞,则切换到 FallState - if (!physics_component->hasCollidedBelow()) { + // 如果离地,则切换到 FallState + if (!player_component_->is_on_ground()) { return std::make_unique(player_component_); } return nullptr; diff --git a/src/game/component/state/walk_state.cpp b/src/game/component/state/walk_state.cpp index 6d36203..ee3ab97 100644 --- a/src/game/component/state/walk_state.cpp +++ b/src/game/component/state/walk_state.cpp @@ -67,8 +67,8 @@ std::unique_ptr WalkState::update(float, engine::core::Context&) 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()) { + // 如果离地,则切换到 FallState + if (!player_component_->is_on_ground()) { return std::make_unique(player_component_); }