diff --git a/assets/maps/level1.tmj b/assets/maps/level1.tmj index d309774..84ba0e8 100644 --- a/assets/maps/level1.tmj +++ b/assets/maps/level1.tmj @@ -81,8 +81,8 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 129, 129, 337, 129, 129, 129, 129, 337, 129, 337, 333, 129, 129, 129, 129, 129, 129, 129, 333, 129, 129, 337, 129, 129, 129, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 362, 0, 0, 0, 0, 362, 0, 362, 358, 0, 0, 0, 0, 0, 0, 0, 358, 0, 0, 362, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 576, 1, 0, 0, 0, 0, 0, 0, 602, 602, 602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 366, 368, 368, 368, 368, 368, 368, 368, 370, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 261, 0, 0, 0, 0, 0, 0, 261, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 602, 602, 602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 366, 368, 368, 368, 368, 368, 368, 368, 370, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 261, 0, 0, 0, 0, 0, 0, 261, 0, 0, 0, 0, 0, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 0, 0, 0, 27, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 117, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, 33, 29, 29, 29, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 262, 262, 262, 262, 262, 262, 27, 29, 29, 29, 29, 29, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 77, 79, 181, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 142, 117, 0, 0, 0, 585, 0, 115, 33, 33, 65, 79, 181, 79, 79, 148, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 468, 0, 0, 0, 0, 0, 0, 0, 0, 121, 146, 79, 79, 79, 79, 79, 79, 181, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 79, 79, 181, 79, 79, 79, 79, 79, 79, 79, 79, 83, 84, 0, 0, 77, 79, 79, 79, 79, 79, 79, 79, 79, 181, 79, 79, 79, 79, 79, 79, 142, 33, 33, 258, 33, 33, 140, 79, 79, 79, 79, 79, 79, 79, 79, 148, 29, 29, 29, 29, 29, 29, 48, 49, 0, 0, 0, 0, 0, 0, 468, 0, 0, 121, 29, 29, 29, 146, 79, 79, 183, 79, 79, 183, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 183, 79, 79, 79, 79, @@ -126,8 +126,8 @@ "type":"", "visible":true, "width":32, - "x":32, - "y":160 + "x":124.227272727273, + "y":157.5 }, { "gid":607, @@ -319,18 +319,6 @@ "width":0, "x":87, "y":43 - }, - { - "gid":576, - "height":32, - "id":19, - "name":"", - "rotation":0, - "type":"", - "visible":true, - "width":32, - "x":64, - "y":160 }], "opacity":1, "type":"objectgroup", diff --git a/assets/maps/tileset.tsj b/assets/maps/tileset.tsj index 23baa52..6b4c5f5 100644 --- a/assets/maps/tileset.tsj +++ b/assets/maps/tileset.tsj @@ -54,6 +54,24 @@ "value":true }] }, + { + "id":34, + "properties":[ + { + "name":"unisolid", + "type":"bool", + "value":true + }] + }, + { + "id":35, + "properties":[ + { + "name":"unisolid", + "type":"bool", + "value":true + }] + }, { "id":36, "properties":[ @@ -63,6 +81,78 @@ "value":true }] }, + { + "id":38, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"0_2" + }] + }, + { + "id":39, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"2_1" + }] + }, + { + "id":41, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"1_2" + }] + }, + { + "id":42, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"2_0" + }] + }, + { + "id":44, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"0_2" + }] + }, + { + "id":45, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"2_1" + }] + }, + { + "id":47, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"1_2" + }] + }, + { + "id":48, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"2_0" + }] + }, { "id":63, "properties":[ @@ -81,6 +171,24 @@ "value":true }] }, + { + "id":66, + "properties":[ + { + "name":"solid", + "type":"bool", + "value":true + }] + }, + { + "id":67, + "properties":[ + { + "name":"solid", + "type":"bool", + "value":true + }] + }, { "id":69, "properties":[ @@ -144,6 +252,24 @@ "value":true }] }, + { + "id":83, + "properties":[ + { + "name":"unisolid", + "type":"bool", + "value":true + }] + }, + { + "id":85, + "properties":[ + { + "name":"unisolid", + "type":"bool", + "value":true + }] + }, { "id":86, "properties":[ @@ -153,6 +279,42 @@ "value":true }] }, + { + "id":114, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"0_1" + }] + }, + { + "id":116, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"1_0" + }] + }, + { + "id":120, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"0_1" + }] + }, + { + "id":122, + "properties":[ + { + "name":"slope", + "type":"string", + "value":"1_0" + }] + }, { "id":126, "properties":[ @@ -189,6 +351,24 @@ "value":true }] }, + { + "id":133, + "properties":[ + { + "name":"unisolid", + "type":"bool", + "value":true + }] + }, + { + "id":134, + "properties":[ + { + "name":"unisolid", + "type":"bool", + "value":true + }] + }, { "id":139, "properties":[ diff --git a/src/engine/component/tilelayer_component.h b/src/engine/component/tilelayer_component.h index f83ca96..cb0d6d1 100644 --- a/src/engine/component/tilelayer_component.h +++ b/src/engine/component/tilelayer_component.h @@ -24,6 +24,13 @@ enum class TileType { EMPTY, ///< @brief 空白瓦片 NORMAL, ///< @brief 普通瓦片 SOLID, ///< @brief 静止可碰撞瓦片 + UNISOLID, ///< @brief 单向静止可碰撞瓦片 + SLOPE_0_1, ///< @brief 斜坡瓦片,高度:左0 右1 + SLOPE_1_0, ///< @brief 斜坡瓦片,高度:左1 右0 + SLOPE_0_2, ///< @brief 斜坡瓦片,高度:左0 右1/2 + SLOPE_2_1, ///< @brief 斜坡瓦片,高度:左1/2右1 + SLOPE_1_2, ///< @brief 斜坡瓦片,高度:左1 右1/2 + SLOPE_2_0, ///< @brief 斜坡瓦片,高度:左1/2右0 // 未来补充其它类型 }; diff --git a/src/engine/physics/physics_engine.cpp b/src/engine/physics/physics_engine.cpp index ff47873..b868efd 100644 --- a/src/engine/physics/physics_engine.cpp +++ b/src/engine/physics/physics_engine.cpp @@ -58,6 +58,9 @@ void PhysicsEngine::update(float delta_time) { // 处理瓦片层碰撞(速度和位置的更新移入此函数) resolveTileCollisions(pc, delta_time); + + // 应用世界边界 + applyWorldBounds(pc); } // 处理对象间碰撞 checkObjectCollisions(); @@ -136,6 +139,16 @@ 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; + } else { + // 检测右下角斜坡瓦片 + auto width_right = new_obj_pos.x + obj_size.x - tile_x * tile_size.x; + auto height_right = getTileHeightAtWidth(width_right, tile_type_bottom, tile_size); + if (height_right > 0.0f) { + // 如果有碰撞(角点的世界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; + } + } } } else if (ds.x < 0.0f) { @@ -152,6 +165,15 @@ void PhysicsEngine::resolveTileCollisions(engine::component::PhysicsComponent* p // 撞墙了!速度归零,x方向移动到贴着墙的位置 new_obj_pos.x = (tile_x + 1) * layer->getTileSize().x; pc->velocity_.x = 0.0f; + } else { + // 检测左下角斜坡瓦片 + auto width_left = new_obj_pos.x - tile_x * tile_size.x; + auto height_left = getTileHeightAtWidth(width_left, tile_type_bottom, tile_size); + 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; + } + } } } // 轴分离碰撞检测:再检查Y方向是否有碰撞 (x方向使用初始值obj_pos.x) @@ -165,10 +187,24 @@ void PhysicsEngine::resolveTileCollisions(engine::component::PhysicsComponent* p auto tile_x_right = static_cast(floor((obj_pos.x + obj_size.x - tolerance) / tile_size.x)); auto tile_type_right = layer->getTileTypeAt({tile_x_right, tile_y}); // 右下角瓦片类型 - if (tile_type_left == engine::component::TileType::SOLID || tile_type_right == engine::component::TileType::SOLID) { + if (tile_type_left == engine::component::TileType::SOLID || tile_type_right == engine::component::TileType::SOLID || + tile_type_left == engine::component::TileType::UNISOLID || tile_type_right == engine::component::TileType::UNISOLID) { // 到达地面!速度归零,y方向移动到贴着地面的位置 new_obj_pos.y = tile_y * layer->getTileSize().y - obj_size.y; pc->velocity_.y = 0.0f; + } else { + // 检测斜坡瓦片(下方两个角点都要检测) + auto width_left = obj_pos.x - tile_x * tile_size.x; + auto width_right = obj_pos.x + obj_size.x - tile_x_right * tile_size.x; + auto height_left = getTileHeightAtWidth(width_left, tile_type_left, tile_size); + auto height_right = getTileHeightAtWidth(width_right, tile_type_right, tile_size); + auto height = glm::max(height_left, height_right); // 找到两个角点的最高点进行检测 + if (height > 0.0f) { // 说明至少有一个角点处于斜坡瓦片 + 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 速度归零 + } + } } } else if (ds.y < 0.0f) { @@ -239,4 +275,56 @@ void PhysicsEngine::resolveSolidObjectCollisions(engine::object::GameObject* mov } } +float PhysicsEngine::getTileHeightAtWidth(float width, engine::component::TileType type, glm::vec2 tile_size) +{ + auto rel_x = glm::clamp(width / tile_size.x, 0.0f, 1.0f); + switch (type) { + case engine::component::TileType::SLOPE_0_1: // 左0 右1 + return rel_x * tile_size.y; + case engine::component::TileType::SLOPE_0_2: // 左0 右1/2 + return rel_x * tile_size.y * 0.5f; + case engine::component::TileType::SLOPE_2_1: // 左1/2右1 + return rel_x * tile_size.y * 0.5f + tile_size.y * 0.5f; + case engine::component::TileType::SLOPE_1_0: // 左1 右0 + return (1.0f - rel_x) * tile_size.y; + case engine::component::TileType::SLOPE_2_0: // 左1/2右0 + return (1.0f - rel_x) * tile_size.y * 0.5f; + case engine::component::TileType::SLOPE_1_2: // 左1 右1/2 + return (1.0f - rel_x) * tile_size.y * 0.5f + tile_size.y * 0.5f; + default: + return 0.0f; // 默认返回0,表示没有斜坡 + } +} + +void PhysicsEngine::applyWorldBounds(engine::component::PhysicsComponent *pc) +{ + if (!pc || !world_bounds_) return; + + // 只限定左、上、右边界,不限定下边界,以碰撞盒作为判断依据 + auto* obj = pc->getOwner(); + auto* cc = obj->getComponent(); + auto* tc = obj->getComponent(); + auto world_aabb = cc->getWorldAABB(); + auto obj_pos = world_aabb.position; + auto obj_size = world_aabb.size; + + // 检查左边界 + if (obj_pos.x < world_bounds_->position.x) { + pc->velocity_.x = 0.0f; + obj_pos.x = world_bounds_->position.x; + } + // 检查上边界 + if (obj_pos.y < world_bounds_->position.y) { + pc->velocity_.y = 0.0f; + obj_pos.y = world_bounds_->position.y; + } + // 检查右边界 + 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; + } + // 更新物体位置(使用translate方法,新位置 - 旧位置) + tc->translate(obj_pos - world_aabb.position); +} + } // namespace engine::physics \ No newline at end of file diff --git a/src/engine/physics/physics_engine.h b/src/engine/physics/physics_engine.h index 01912ae..cd52179 100644 --- a/src/engine/physics/physics_engine.h +++ b/src/engine/physics/physics_engine.h @@ -1,11 +1,14 @@ #pragma once +#include "../utils/math.h" #include #include // for std::pair -#include "glm/vec2.hpp" +#include +#include namespace engine::component { class PhysicsComponent; class TileLayerComponent; + enum class TileType; } namespace engine::object { @@ -23,6 +26,7 @@ private: std::vector collision_tile_layers_; ///< @brief 注册的碰撞瓦片图层容器 glm::vec2 gravity_ = {0.0f, 980.0f}; ///< @brief 默认重力值 (像素/秒^2, 相当于100像素对应现实1m) float max_speed_ = 500.0f; ///< @brief 最大速度 (像素/秒) + std::optional world_bounds_; ///< @brief 世界边界,用于限制物体移动范围 /// @brief 存储本帧发生的 GameObject 碰撞对 (每次 update 开始时清空) std::vector> collision_pairs_; @@ -50,6 +54,8 @@ public: const glm::vec2& getGravity() const { return gravity_; } ///< @brief 获取当前的全局重力加速度 void setMaxSpeed(float max_speed) { max_speed_ = max_speed; } ///< @brief 设置最大速度 float getMaxSpeed() const { return max_speed_; } ///< @brief 获取当前的最大速度 + void setWorldBounds(const engine::utils::Rect& world_bounds) { world_bounds_ = world_bounds; } ///< @brief 设置世界边界 + const std::optional& getWorldBounds() const { return world_bounds_; } ///< @brief 获取世界边界 /// @brief 获取本帧检测到的所有 GameObject 碰撞对。(此列表在每次 update 开始时清空) const std::vector>& getCollisionPairs() const { return collision_pairs_; @@ -61,6 +67,16 @@ private: void resolveTileCollisions(engine::component::PhysicsComponent* pc, float delta_time); /// @brief 处理可移动物体与SOLID物体的碰撞。 void resolveSolidObjectCollisions(engine::object::GameObject* move_obj, engine::object::GameObject* solid_obj); + void applyWorldBounds(engine::component::PhysicsComponent* pc); ///< @brief 应用世界边界,限制物体移动范围 + + /** + * @brief 根据瓦片类型和指定宽度x坐标,计算瓦片上对应y坐标。 + * @param width 从瓦片左侧起算的宽度。 + * @param type 瓦片类型。 + * @param tile_size 瓦片尺寸。 + * @return 瓦片上对应高度(从瓦片下侧起算)。 + */ + float getTileHeightAtWidth(float width, engine::component::TileType type, glm::vec2 tile_size); }; } // namespace engine::physics \ No newline at end of file diff --git a/src/engine/render/camera.cpp b/src/engine/render/camera.cpp index a19f430..9cf8bf5 100644 --- a/src/engine/render/camera.cpp +++ b/src/engine/render/camera.cpp @@ -1,5 +1,6 @@ #include "camera.h" #include "../utils/math.h" +#include "../component/transform_component.h" #include namespace engine::render { @@ -14,9 +15,27 @@ void Camera::setPosition(const glm::vec2& position) { clampPosition(); } -void Camera::update(float /* delta_time */) +void Camera::update(float delta_time) { - // TODO 自动跟随目标 + if (target_ == nullptr) return; + glm::vec2 target_pos = target_->getPosition(); + glm::vec2 desired_position = target_pos - viewport_size_ / 2.0f; // 计算目标位置 (让目标位于视口中心) + + // 计算当前位置与目标位置的距离 + auto distance_ = glm::distance(position_, desired_position); + constexpr float SNAP_THRESHOLD = 1.0f; // 设置一个距离阈值 (constexpr: 编译时常量,避免每次调用都计算) + + if (distance_ < SNAP_THRESHOLD) { + // 如果距离小于阈值,直接吸附到目标位置 + position_ = desired_position; + } else { + // 否则,使用线性插值平滑移动 glm::mix(a,b,t): 在向量 a 和 b 之间进行插值,t 是插值因子,范围在0到1之间。 + // 公式: (b-a)*t + a; t = 0 时结果为 a,t = 1 时结果为 b + position_ = glm::mix(position_, desired_position, smooth_speed_ * delta_time); + position_ = glm::vec2(glm::round(position_.x), glm::round(position_.y)); // 四舍五入到整数,省略的话偶尔会出现画面割裂 + } + + clampPosition(); } void Camera::move(const glm::vec2 &offset) @@ -31,6 +50,16 @@ void Camera::setLimitBounds(const engine::utils::Rect& bounds) clampPosition(); // 设置边界后,立即应用限制 } +void Camera::setTarget(engine::component::TransformComponent *target) +{ + target_ = target; +} + +engine::component::TransformComponent *Camera::getTarget() const +{ + return target_; +} + const glm::vec2& Camera::getPosition() const { return position_; } diff --git a/src/engine/render/camera.h b/src/engine/render/camera.h index 489255e..308a49b 100644 --- a/src/engine/render/camera.h +++ b/src/engine/render/camera.h @@ -2,6 +2,10 @@ #include "../utils/math.h" #include +namespace engine::component { + class TransformComponent; +} + namespace engine::render { /** @@ -13,7 +17,9 @@ private: glm::vec2 viewport_size_; ///< @brief 视口大小(屏幕大小) glm::vec2 position_; ///< @brief 相机左上角的世界坐标 std::optional limit_bounds_; ///< @brief 限制相机的移动范围,空值表示不限制 - + float smooth_speed_ = 5.0f; ///< @brief 相机移动的平滑速度 + engine::component::TransformComponent* target_ = nullptr; ///< @brief 跟随目标变换组件,空值表示不跟随 + public: Camera(const glm::vec2& viewport_size, const glm::vec2& position = glm::vec2(0.0f, 0.0f), const std::optional limit_bounds = std::nullopt); @@ -27,10 +33,12 @@ public: void setPosition(const glm::vec2& position); ///< @brief 设置相机位置 void setLimitBounds(const engine::utils::Rect& bounds); ///< @brief 设置限制相机的移动范围 + void setTarget(engine::component::TransformComponent* target); ///< @brief 设置跟随目标变换组件 const glm::vec2& getPosition() const; ///< @brief 获取相机位置 std::optional getLimitBounds() const; ///< @brief 获取限制相机的移动范围 glm::vec2 getViewportSize() const; ///< @brief 获取视口大小 + engine::component::TransformComponent* getTarget() const; ///< @brief 获取跟随目标变换组件 // 禁用拷贝和移动语义 Camera(const Camera&) = delete; diff --git a/src/engine/scene/level_loader.cpp b/src/engine/scene/level_loader.cpp index 9672729..908c005 100644 --- a/src/engine/scene/level_loader.cpp +++ b/src/engine/scene/level_loader.cpp @@ -261,6 +261,29 @@ engine::component::TileType LevelLoader::getTileType(const nlohmann::json &tile_ auto is_solid = property.value("value", false); return is_solid ? engine::component::TileType::SOLID : engine::component::TileType::NORMAL; } + else if (property.contains("name") && property["name"] == "slope") { + auto slope_type = property.value("value", ""); + if (slope_type == "0_1") { + return engine::component::TileType::SLOPE_0_1; + } else if (slope_type == "1_0") { + return engine::component::TileType::SLOPE_1_0; + } else if (slope_type == "0_2") { + return engine::component::TileType::SLOPE_0_2; + } else if (slope_type == "2_0") { + return engine::component::TileType::SLOPE_2_0; + } else if (slope_type == "2_1") { + return engine::component::TileType::SLOPE_2_1; + } else if (slope_type == "1_2") { + return engine::component::TileType::SLOPE_1_2; + } else { + spdlog::error("未知的斜坡类型: {}", slope_type); + return engine::component::TileType::NORMAL; + } + } + else if (property.contains("name") && property["name"] == "unisolid") { + auto is_unisolid = property.value("value", false); + return is_unisolid ? engine::component::TileType::UNISOLID : engine::component::TileType::NORMAL; + } // TODO: 可以在这里添加更多的自定义属性处理逻辑 } } diff --git a/src/engine/scene/scene.cpp b/src/engine/scene/scene.cpp index 30d24c9..26ecf17 100644 --- a/src/engine/scene/scene.cpp +++ b/src/engine/scene/scene.cpp @@ -3,6 +3,7 @@ #include "../object/game_object.h" #include "../core/context.h" #include "../physics/physics_engine.h" +#include "../render/camera.h" #include // for std::remove_if #include @@ -25,6 +26,8 @@ void Scene::update(float delta_time) { // 先更新物理引擎 context_.getPhysicsEngine().update(delta_time); + // 更新相机 + context_.getCamera().update(delta_time); // 更新所有游戏对象,并删除需要移除的对象 for (auto it = game_objects_.begin(); it != game_objects_.end();) { diff --git a/src/game/scene/game_scene.cpp b/src/game/scene/game_scene.cpp index f9806d1..3d870a6 100644 --- a/src/game/scene/game_scene.cpp +++ b/src/game/scene/game_scene.cpp @@ -42,6 +42,18 @@ void GameScene::init() { spdlog::error("未找到玩家对象"); return; } + // 相机跟随玩家 + auto* player_transform = player_->getComponent(); + if (player_transform) { + context_.getCamera().setTarget(player_transform); + } + + // 设置相机边界 + 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 初始化完成。");