完成玩家与其他对象交互-2
This commit is contained in:
+14
-2
@@ -224,7 +224,13 @@
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
}
|
||||
},
|
||||
"properties":[
|
||||
{
|
||||
"name":"hazard",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":26,
|
||||
@@ -252,7 +258,13 @@
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
}
|
||||
},
|
||||
"properties":[
|
||||
{
|
||||
"name":"hazard",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":27,
|
||||
|
||||
@@ -31,6 +31,7 @@ enum class TileType {
|
||||
SLOPE_2_1, ///< @brief 斜坡瓦片,高度:左1/2右1
|
||||
SLOPE_1_2, ///< @brief 斜坡瓦片,高度:左1 右1/2
|
||||
SLOPE_2_0, ///< @brief 斜坡瓦片,高度:左1/2右0
|
||||
HAZARD, ///< @brief 危险瓦片(例如火焰、尖刺等)
|
||||
// 未来补充其它类型
|
||||
};
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include "../component/collider_component.h"
|
||||
#include "../component/tilelayer_component.h"
|
||||
#include "../object/game_object.h"
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "glm/common.hpp"
|
||||
#include <glm/common.hpp>
|
||||
|
||||
namespace engine::physics {
|
||||
|
||||
@@ -37,8 +37,9 @@ void PhysicsEngine::unregisterCollisionLayer(engine::component::TileLayerCompone
|
||||
}
|
||||
|
||||
void PhysicsEngine::update(float delta_time) {
|
||||
// 每帧开始时先清空碰撞对列表
|
||||
// 每帧开始时先清空碰撞对列表和瓦片触发事件列表
|
||||
collision_pairs_.clear();
|
||||
tile_trigger_events_.clear();
|
||||
|
||||
// 遍历所有注册的物理组件
|
||||
for (auto* pc : components_) {
|
||||
@@ -66,6 +67,9 @@ void PhysicsEngine::update(float delta_time) {
|
||||
}
|
||||
// 处理对象间碰撞
|
||||
checkObjectCollisions();
|
||||
|
||||
// 检测瓦片触发事件 (检测前已经处理完位移)
|
||||
checkTileTriggers();
|
||||
}
|
||||
|
||||
void PhysicsEngine::checkObjectCollisions()
|
||||
@@ -118,7 +122,7 @@ void PhysicsEngine::resolveTileCollisions(engine::component::PhysicsComponent* p
|
||||
if (world_aabb.size.x <= 0.0f || world_aabb.size.y <= 0.0f) return;
|
||||
// -- 检查结束, 正式开始处理 --
|
||||
|
||||
auto tolerance = 1.0f; // 检查右边缘和下边缘时,需要减1像素,否则会检查到下一行/列的瓦片
|
||||
constexpr float tolerance = 1.0f; // 检查右边缘和下边缘时,需要减1像素,否则会检查到下一行/列的瓦片
|
||||
auto ds = pc->velocity_ * delta_time; // 计算物体在delta_time内的位移
|
||||
auto new_obj_pos = obj_pos + ds; // 计算物体在delta_time后的新位置
|
||||
|
||||
@@ -323,6 +327,52 @@ float PhysicsEngine::getTileHeightAtWidth(float width, engine::component::TileTy
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::checkTileTriggers()
|
||||
{
|
||||
for (auto* pc : components_) {
|
||||
if (!pc || !pc->isEnabled()) continue; // 检查组件是否有效和启用
|
||||
auto* obj = pc->getOwner();
|
||||
if (!obj) continue;
|
||||
auto* cc = obj->getComponent<engine::component::ColliderComponent>();
|
||||
if (!cc || !cc->isActive() || cc->isTrigger()) continue; // 如果游戏对象本就是触发器,则不需要检查瓦片触发事件
|
||||
|
||||
// 获取物体的世界AABB
|
||||
auto world_aabb = cc->getWorldAABB();
|
||||
|
||||
// 使用 set 来跟踪循环遍历中已经触发过的瓦片类型,防止重复添加(例如,玩家同时踩到两个尖刺,只需要受到一次伤害)
|
||||
std::set<engine::component::TileType> triggers_set;
|
||||
|
||||
// 遍历所有注册的碰撞瓦片层分别进行检测
|
||||
for (auto* layer : collision_tile_layers_) {
|
||||
if (!layer) continue;
|
||||
auto tile_size = layer->getTileSize();
|
||||
constexpr float tolerance = 1.0f; // 检查右边缘和下边缘时,需要减1像素,否则会检查到下一行/列的瓦片
|
||||
// 获取瓦片坐标范围
|
||||
auto start_x = static_cast<int>(floor(world_aabb.position.x / tile_size.x));
|
||||
auto end_x = static_cast<int>(ceil((world_aabb.position.x + world_aabb.size.x - tolerance) / tile_size.x));
|
||||
auto start_y = static_cast<int>(floor(world_aabb.position.y / tile_size.y));
|
||||
auto end_y = static_cast<int>(ceil((world_aabb.position.y + world_aabb.size.y - tolerance) / tile_size.y));
|
||||
|
||||
// 遍历瓦片坐标范围进行检测
|
||||
for (int x = start_x; x < end_x; ++x) {
|
||||
for (int y = start_y; y < end_y; ++y) {
|
||||
auto tile_type = layer->getTileTypeAt({x, y});
|
||||
// 未来可以添加更多触发器类型的瓦片,目前只有 HAZARD 类型
|
||||
if (tile_type == engine::component::TileType::HAZARD) {
|
||||
triggers_set.insert(tile_type); // 记录触发事件,set 保证每个瓦片类型只记录一次
|
||||
}
|
||||
}
|
||||
}
|
||||
// 遍历触发事件集合,添加到 tile_trigger_events_ 中
|
||||
for (const auto& type : triggers_set) {
|
||||
tile_trigger_events_.emplace_back(obj, type);
|
||||
spdlog::trace("tile_trigger_events_中 添加了 GameObject {} 和瓦片触发类型: {}",
|
||||
obj->getName(), static_cast<int>(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::applyWorldBounds(engine::component::PhysicsComponent *pc)
|
||||
{
|
||||
if (!pc || !world_bounds_) return;
|
||||
|
||||
@@ -30,6 +30,8 @@ private:
|
||||
|
||||
/// @brief 存储本帧发生的 GameObject 碰撞对 (每次 update 开始时清空)
|
||||
std::vector<std::pair<engine::object::GameObject*, engine::object::GameObject*>> collision_pairs_;
|
||||
/// @brief 存储本帧发生的瓦片触发事件 (GameObject*, 触发的瓦片类型, 每次 update 开始时清空)
|
||||
std::vector<std::pair<engine::object::GameObject*, engine::component::TileType>> tile_trigger_events_;
|
||||
|
||||
public:
|
||||
PhysicsEngine() = default;
|
||||
@@ -60,6 +62,10 @@ public:
|
||||
const std::vector<std::pair<engine::object::GameObject*, engine::object::GameObject*>>& getCollisionPairs() const {
|
||||
return collision_pairs_;
|
||||
};
|
||||
/// @brief 获取本帧检测到的所有瓦片触发事件。(此列表在每次 update 开始时清空)
|
||||
const std::vector<std::pair<engine::object::GameObject*, engine::component::TileType>>& getTileTriggerEvents() const {
|
||||
return tile_trigger_events_;
|
||||
};
|
||||
|
||||
private:
|
||||
void checkObjectCollisions(); ///< @brief 检测并处理对象之间的碰撞,并记录需要游戏逻辑处理的碰撞对。
|
||||
@@ -77,6 +83,11 @@ private:
|
||||
* @return 瓦片上对应高度(从瓦片下侧起算)。
|
||||
*/
|
||||
float getTileHeightAtWidth(float width, engine::component::TileType type, glm::vec2 tile_size);
|
||||
|
||||
/**
|
||||
* @brief 检测所有游戏对象与瓦片层的触发器类型瓦片碰撞,并记录触发事件。(位移处理完毕后再调用)
|
||||
*/
|
||||
void checkTileTriggers();
|
||||
};
|
||||
|
||||
} // namespace engine::physics
|
||||
@@ -219,6 +219,10 @@ void LevelLoader::loadObjectLayer(const nlohmann::json& layer_json, Scene& scene
|
||||
if (tag) {
|
||||
game_object->setTag(tag.value());
|
||||
}
|
||||
// 如果是危险瓦片,且没有手动设置标签,则自动设置标签为 "hazard"
|
||||
else if (tile_info.type == engine::component::TileType::HAZARD) {
|
||||
game_object->setTag("hazard");
|
||||
}
|
||||
|
||||
// 获取重力信息并设置
|
||||
auto gravity = getTileProperty<bool>(tile_json, "gravity");
|
||||
@@ -360,6 +364,10 @@ engine::component::TileType LevelLoader::getTileType(const nlohmann::json &tile_
|
||||
auto is_unisolid = property.value("value", false);
|
||||
return is_unisolid ? engine::component::TileType::UNISOLID : engine::component::TileType::NORMAL;
|
||||
}
|
||||
else if (property.contains("name") && property["name"] == "hazard") {
|
||||
auto is_hazard = property.value("value", false);
|
||||
return is_hazard ? engine::component::TileType::HAZARD : engine::component::TileType::NORMAL;
|
||||
}
|
||||
// TODO: 可以在这里添加更多的自定义属性处理逻辑
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ void GameScene::init() {
|
||||
void GameScene::update(float delta_time) {
|
||||
Scene::update(delta_time);
|
||||
handleObjectCollisions();
|
||||
handleTileTriggers();
|
||||
}
|
||||
|
||||
void GameScene::render() {
|
||||
@@ -190,6 +191,31 @@ void GameScene::handleObjectCollisions()
|
||||
} else if (obj2->getName() == "player" && obj1->getTag() == "item") {
|
||||
PlayerVSItemCollision(obj2, obj1);
|
||||
}
|
||||
// 处理玩家与"hazard"对象碰撞
|
||||
else if (obj1->getName() == "player" && obj2->getTag() == "hazard") {
|
||||
obj1->getComponent<game::component::PlayerComponent>()->takeDamage(1);
|
||||
spdlog::debug("玩家 {} 受到了 HAZARD 对象伤害", obj1->getName());
|
||||
} else if (obj2->getName() == "player" && obj1->getTag() == "hazard") {
|
||||
obj2->getComponent<game::component::PlayerComponent>()->takeDamage(1);
|
||||
spdlog::debug("玩家 {} 受到了 HAZARD 对象伤害", obj2->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameScene::handleTileTriggers()
|
||||
{
|
||||
const auto& tile_trigger_events = context_.getPhysicsEngine().getTileTriggerEvents();
|
||||
for (const auto& event : tile_trigger_events) {
|
||||
auto* obj = event.first; // 瓦片触发事件的对象
|
||||
auto tile_type = event.second; // 瓦片类型
|
||||
if (tile_type == engine::component::TileType::HAZARD) {
|
||||
// 玩家碰到到危险瓦片,受伤
|
||||
if (obj->getName() == "player") {
|
||||
obj->getComponent<game::component::PlayerComponent>()->takeDamage(1);
|
||||
spdlog::debug("玩家 {} 受到了 HAZARD 瓦片伤害", obj->getName());
|
||||
}
|
||||
// TODO: 其他对象类型的处理,目前让敌人无视瓦片伤害
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ private:
|
||||
[[nodiscard]] bool initEnemyAndItem(); ///< @brief 初始化敌人和道具
|
||||
|
||||
void handleObjectCollisions(); ///< @brief 处理游戏对象间的碰撞逻辑(从PhysicsEngine获取信息)
|
||||
void handleTileTriggers(); ///< @brief 处理瓦片触发事件(从PhysicsEngine获取信息)
|
||||
void PlayerVSEnemyCollision(engine::object::GameObject* player, engine::object::GameObject* enemy); ///< @brief 玩家与敌人碰撞处理
|
||||
void PlayerVSItemCollision(engine::object::GameObject* player, engine::object::GameObject* item); ///< @brief 玩家与道具碰撞处理
|
||||
|
||||
|
||||
Reference in New Issue
Block a user