完成碰撞器组件与碰撞检测

This commit is contained in:
Ziyu
2025-06-16 14:44:47 +08:00
parent b4399d2f41
commit ddb02723f5
12 changed files with 477 additions and 1 deletions
+109
View File
@@ -0,0 +1,109 @@
#include "collider_component.h"
#include "transform_component.h"
#include "../object/game_object.h"
#include "../physics/collider.h"
#include <spdlog/spdlog.h>
namespace engine::component {
ColliderComponent::ColliderComponent(
std::unique_ptr<engine::physics::Collider> collider,
engine::utils::Alignment alignment,
bool is_trigger,
bool is_active)
: collider_(std::move(collider)),
alignment_(alignment),
is_trigger_(is_trigger),
is_active_(is_active)
{
if (!collider_) {
spdlog::error("创建 ColliderComponent 时传入了空的碰撞器!");
}
}
void ColliderComponent::init() {
if (!owner_) {
spdlog::error("ColliderComponent 没有所有者 GameObject!");
return;
}
transform_ = owner_->getComponent<TransformComponent>();
if (!transform_) {
spdlog::error("ColliderComponent 需要一个在同一个 GameObject 上的 TransformComponent!");
return;
}
// 在获取 transform_ 之后计算初始偏移量
updateOffset();
}
// 实现 setAlignment 方法
void ColliderComponent::setAlignment(engine::utils::Alignment anchor) {
alignment_ = anchor;
// 重新计算偏移量,确保 transform_ 和 collider_ 有效
if (transform_ && collider_) {
updateOffset();
}
}
void ColliderComponent::updateOffset() {
if (!collider_) return;
// 获取碰撞盒的最小包围盒尺寸
auto collider_size = collider_->getAABBSize();
// 如果尺寸无效,偏移为0
if (collider_size.x <= 0.0f || collider_size.y <= 0.0f) {
offset_ = {0.0f, 0.0f};
return;
}
auto scale = transform_->getScale();
// 根据 alignment_anchor_ 计算 AABB 左上角相对于 Transform 中心的偏移量
switch (alignment_) {
case engine::utils::Alignment::TOP_LEFT:
offset_ = glm::vec2{0.0f, 0.0f} * scale;
break;
case engine::utils::Alignment::TOP_CENTER:
offset_ = glm::vec2{-collider_size.x / 2.0f, 0.0f} * scale;
break;
case engine::utils::Alignment::TOP_RIGHT:
offset_ = glm::vec2{-collider_size.x, 0.0f} * scale;
break;
case engine::utils::Alignment::CENTER_LEFT:
offset_ = glm::vec2{0.0f, -collider_size.y / 2.0f} * scale;
break;
case engine::utils::Alignment::CENTER:
offset_ = glm::vec2{-collider_size.x / 2.0f, -collider_size.y / 2.0f} * scale;
break;
case engine::utils::Alignment::CENTER_RIGHT:
offset_ = glm::vec2{-collider_size.x, -collider_size.y / 2.0f} * scale;
break;
case engine::utils::Alignment::BOTTOM_LEFT:
offset_ = glm::vec2{0.0f, -collider_size.y} * scale;
break;
case engine::utils::Alignment::BOTTOM_CENTER:
offset_ = glm::vec2{-collider_size.x / 2.0f, -collider_size.y} * scale;
break;
case engine::utils::Alignment::BOTTOM_RIGHT:
offset_ = glm::vec2{-collider_size.x, -collider_size.y} * scale;
break;
default:
break; // 如果 alignment_ 是 NONE,则不做任何操作(手动设置 offset_)
}
}
engine::utils::Rect ColliderComponent::getWorldAABB() const {
if (!transform_ || !collider_) {
return {glm::vec2(0.0f, 0.0f), glm::vec2(0.0f, 0.0f)};
}
// 计算最小包围盒的左上角坐标(position)
const glm::vec2 top_left_pos = transform_->getPosition() + offset_;
// 计算最小包围盒的尺寸(size)
const glm::vec2 base_size = collider_->getAABBSize();
const glm::vec2 scale = transform_->getScale();
glm::vec2 scaled_size = base_size * scale;
// 返回最小包围盒的 Rect
return { top_left_pos, scaled_size };
}
} // namespace engine::component
+73
View File
@@ -0,0 +1,73 @@
#pragma once
#include "./component.h"
#include "../physics/collider.h"
#include "../utils/math.h"
#include "../utils/alignment.h"
#include <memory>
// 前置声明
namespace engine::component {
class TransformComponent;
}
namespace engine::component {
/**
* @brief 碰撞器组件。
*
* 持有 Collider 对象(如 AABBCollider)并提供获取世界坐标系下碰撞形状的方法。
*/
class ColliderComponent final : public Component {
friend class engine::object::GameObject;
private:
TransformComponent* transform_ = nullptr; ///< @brief 缓存的 TransformComponent 指针 (非拥有)
std::unique_ptr<engine::physics::Collider> collider_; ///< @brief 拥有的碰撞器对象。
glm::vec2 offset_ = {0.0f, 0.0f}; ///< @brief 碰撞器(最小包围盒的)左上角相对于变换原点的偏移量。
engine::utils::Alignment alignment_ = engine::utils::Alignment::NONE; ///< @brief 对齐方式。
bool is_trigger_ = false; ///< @brief 是否为触发器 (仅检测碰撞,不产生物理响应)
bool is_active_ = true; ///< @brief 是否激活
public:
/**
* @brief 构造函数。
* @param collider 指向 Collider 实例的 unique_ptr,所有权将被转移。
* @param alignment 初始的对齐锚点。
* @param is_trigger 此碰撞器是否为触发器。
* @param is_active 此碰撞器是否激活。
*/
explicit ColliderComponent(
std::unique_ptr<engine::physics::Collider> collider,
engine::utils::Alignment alignment = engine::utils::Alignment::NONE,
bool is_trigger = false,
bool is_active = true
);
/**
* @brief 根据当前的 alignment_anchor_ 和 collider_ 尺寸计算 offset_。
* @note 需要用到TransformComponent的scale,因此TransformComponent更新scale时,也要调用此方法。
*/
void updateOffset();
// --- Getters ---
const TransformComponent* getTransform() const { return transform_; } ///< @brief 获取缓存的TransformComponent
const engine::physics::Collider* getCollider() const { return collider_.get(); } ///< @brief 获取 Collider 对象。
const glm::vec2& getOffset() const { return offset_; } ///< @brief 获取当前计算出的偏移量。
engine::utils::Alignment getAlignment() const { return alignment_; } ///< @brief 获取设置的对齐锚点。
engine::utils::Rect getWorldAABB() const; ///< @brief 获取世界坐标系下的最小轴对齐包围盒(AABB)。
bool isTrigger() const { return is_trigger_; } ///< @brief 检查此碰撞器是否为触发器。
bool isActive() const { return is_active_; } ///< @brief 检查此碰撞器是否激活。
void setAlignment(engine::utils::Alignment anchor); ///< @brief 设置新的对齐方式并重新计算偏移量。
void setOffset(const glm::vec2& offset) { offset_ = offset; } ///< @brief 设置偏移量。
void setTrigger(bool is_trigger) { is_trigger_ = is_trigger; } ///< @brief 设置此碰撞器是否为触发器。
void setActive(bool is_active) { is_active_ = is_active; } ///< @brief 设置此碰撞器是否激活。
private:
// 核心循环方法
void init() override;
void update(float, engine::core::Context&) override {}
};
} // namespace engine::component
@@ -1,6 +1,7 @@
#include "transform_component.h"
#include "../object/game_object.h"
#include "sprite_component.h"
#include "collider_component.h"
namespace engine::component {
@@ -12,6 +13,10 @@ void TransformComponent::setScale(const glm::vec2 &scale)
if (sprite_comp) {
sprite_comp->updateOffset();
}
auto collider_comp = owner_->getComponent<ColliderComponent>();
if (collider_comp) {
collider_comp->updateOffset();
}
}
}