完成碰撞器组件与碰撞检测
This commit is contained in:
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user