完成变换与精灵组件

This commit is contained in:
Ziyu
2025-06-16 14:44:47 +08:00
parent 84741a45ac
commit 757b66afa3
13 changed files with 432 additions and 18 deletions
+9 -5
View File
@@ -4,6 +4,10 @@ namespace engine::object {
class GameObject;
}
namespace engine::core {
class Context;
}
namespace engine::component {
/**
@@ -32,12 +36,12 @@ public:
engine::object::GameObject* getOwner() const { return owner_; } ///< @brief 获取拥有此组件的 GameObject
protected:
// 关键循环函数,全部设为保护,只有 GameObject 需要(可以)调用 (未来将其中一个改为 = 0 以实现抽象类)
// 关键循环函数,全部设为保护,只有 GameObject 需要(可以)调用
virtual void init() {} ///< @brief 保留两段初始化的机制,GameObject 添加组件时自动调用,不需要外部调用
virtual void handleInput() {} ///< @brief 处理输入
virtual void update(float) {} ///< @brief 更新
virtual void render() {} ///< @brief 渲染
virtual void clean() {} ///< @brief 清理
virtual void handleInput(engine::core::Context&) {} ///< @brief 处理输入
virtual void update(float, engine::core::Context&) = 0; ///< @brief 更新,必须实现
virtual void render(engine::core::Context&) {} ///< @brief 渲染
virtual void clean() {} ///< @brief 清理
};
} // namespace engine::component
+119
View File
@@ -0,0 +1,119 @@
#include "sprite_component.h"
#include "transform_component.h"
#include "../object/game_object.h"
#include "../core/context.h"
#include "../render/renderer.h"
#include "../resource/resource_manager.h"
#include "../render/camera.h"
#include <stdexcept> // 用于 std::runtime_error
#include <spdlog/spdlog.h>
namespace engine::component {
SpriteComponent::SpriteComponent(
const std::string& texture_id,
engine::resource::ResourceManager& resource_manager,
engine::utils::Alignment alignment,
std::optional<SDL_FRect> source_rect_opt,
bool is_flipped
) : resource_manager_(&resource_manager), sprite_(texture_id, source_rect_opt, is_flipped),
alignment_(alignment)
{
if (!resource_manager_) {
spdlog::critical("创建 SpriteComponent 时 ResourceManager 为空!,此组件将无效。");
// 不要在游戏主循环中使用 try...catch / throw,会极大影响性能
}
// offset_ 和 sprite_size_ 将在 init 中计算
spdlog::trace("创建 SpriteComponent,纹理ID: {}", texture_id);
}
void SpriteComponent::init() {
if (!owner_) {
spdlog::error("SpriteComponent 在初始化前未设置所有者。");
return;
}
transform_ = owner_->getComponent<TransformComponent>();
if (!transform_) {
spdlog::warn(
"GameObject '{}' 上的 SpriteComponent 需要一个 TransformComponent,但未找到。",
owner_->getName()
);
// Sprite没有Transform无法计算偏移和渲染,直接返回
return;
}
// 获取大小及偏移
updateSpriteSize();
updateOffset();
}
void SpriteComponent::setAlignment(engine::utils::Alignment anchor) {
alignment_ = anchor;
updateOffset();
}
void SpriteComponent::updateOffset() {
// 如果尺寸无效,偏移为0
if (sprite_size_.x <= 0 || sprite_size_.y <= 0) {
offset_ = {0.0f, 0.0f};
return;
}
auto scale = transform_->getScale();
// 计算精灵左上角相对于 TransformComponent::position_ 的偏移
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{-sprite_size_.x / 2.0f, 0.0f} * scale; break;
case engine::utils::Alignment::TOP_RIGHT: offset_ = glm::vec2{-sprite_size_.x, 0.0f} * scale; break;
case engine::utils::Alignment::CENTER_LEFT: offset_ = glm::vec2{0.0f, -sprite_size_.y / 2.0f} * scale; break;
case engine::utils::Alignment::CENTER: offset_ = glm::vec2{-sprite_size_.x / 2.0f, -sprite_size_.y / 2.0f} * scale; break;
case engine::utils::Alignment::CENTER_RIGHT: offset_ = glm::vec2{-sprite_size_.x, -sprite_size_.y / 2.0f} * scale; break;
case engine::utils::Alignment::BOTTOM_LEFT: offset_ = glm::vec2{0.0f, -sprite_size_.y} * scale; break;
case engine::utils::Alignment::BOTTOM_CENTER: offset_ = glm::vec2{-sprite_size_.x / 2.0f, -sprite_size_.y} * scale; break;
case engine::utils::Alignment::BOTTOM_RIGHT: offset_ = glm::vec2{-sprite_size_.x, -sprite_size_.y} * scale; break;
case engine::utils::Alignment::NONE:
default: break;
}
}
void SpriteComponent::render(engine::core::Context& context) {
if (is_hidden_ || !transform_ || !resource_manager_) {
return;
}
// 获取变换信息(考虑偏移量)
const glm::vec2& pos = transform_->getPosition() + offset_;
const glm::vec2& scale = transform_->getScale();
float rotation_degrees = transform_->getRotation();
// 执行绘制
context.getRenderer().drawSprite(context.getCamera(), sprite_, pos, scale, rotation_degrees);
}
void SpriteComponent::setSpriteById(const std::string& texture_id, const std::optional<SDL_FRect>& source_rect_opt) {
sprite_.setTextureId(texture_id);
sprite_.setSourceRect(source_rect_opt);
updateSpriteSize();
updateOffset();
}
void SpriteComponent::setSourceRect(const std::optional<SDL_FRect>& source_rect_opt) {
sprite_.setSourceRect(source_rect_opt);
updateSpriteSize();
updateOffset();
}
void SpriteComponent::updateSpriteSize() {
if (!resource_manager_) {
spdlog::error("ResourceManager 为空!无法获取纹理尺寸。");
return;
}
if (sprite_.getSourceRect().has_value()) {
const auto& src_rect = sprite_.getSourceRect().value();
sprite_size_ = {src_rect.w, src_rect.h};
} else {
sprite_size_ = resource_manager_->getTextureSize(sprite_.getTextureId());
}
}
} // namespace engine::component
+90
View File
@@ -0,0 +1,90 @@
#pragma once
#include "../render/sprite.h"
#include "./component.h"
#include "../utils/alignment.h"
#include <string>
#include <optional>
#include <SDL3/SDL_rect.h>
#include <glm/vec2.hpp>
namespace engine::core {
class Context;
}
namespace engine::resource {
class ResourceManager;
}
namespace engine::component {
class TransformComponent;
/**
* @brief 管理 GameObject 的视觉表示,通过持有一个 Sprite 对象。
*
* 协调 Sprite 数据和渲染逻辑,并与 TransformComponent 交互。
*/
class SpriteComponent final : public engine::component::Component {
friend class engine::object::GameObject; // 友元不能继承,必须每个子类单独添加
private:
engine::resource::ResourceManager* resource_manager_ = nullptr; ///< @brief 保存资源管理器指针,用于获取纹理大小
TransformComponent* transform_ = nullptr; ///< @brief 缓存 TransformComponent 指针(非必须)
engine::render::Sprite sprite_; ///< @brief 精灵对象
engine::utils::Alignment alignment_ = engine::utils::Alignment::NONE; ///< @brief 对齐方式
glm::vec2 sprite_size_ = {0.0f, 0.0f}; ///< @brief 精灵尺寸
glm::vec2 offset_ = {0.0f, 0.0f}; ///< @brief 偏移量
bool is_hidden_ = false; ///< @brief 是否隐藏(不渲染)
public:
/**
* @brief 构造函数
* @param texture_id 纹理资源的标识符。
* @param resource_manager 资源管理器指针。
* @param alignment 初始对齐方式。
* @param source_rect_opt 可选的源矩形。
* @param is_flipped 初始翻转状态。
*/
SpriteComponent(
const std::string& texture_id,
engine::resource::ResourceManager& resource_manager,
engine::utils::Alignment alignment = engine::utils::Alignment::NONE,
std::optional<SDL_FRect> source_rect_opt = std::nullopt,
bool is_flipped = false
);
~SpriteComponent() override = default;
// 禁止拷贝和移动
SpriteComponent(const SpriteComponent&) = delete;
SpriteComponent& operator=(const SpriteComponent&) = delete;
SpriteComponent(SpriteComponent&&) = delete;
SpriteComponent& operator=(SpriteComponent&&) = delete;
void updateOffset(); ///< @brief 更新偏移量(根据当前的 alignment_ 和 sprite_size_ 计算 offset_)。
// Getters
const engine::render::Sprite& getSprite() const { return sprite_; } ///< @brief 获取精灵对象
const std::string& getTextureId() const { return sprite_.getTextureId(); } ///< @brief 获取纹理ID
bool isFlipped() const { return sprite_.isFlipped(); } ///< @brief 获取是否翻转
bool isHidden() const { return is_hidden_; } ///< @brief 获取是否隐藏
const glm::vec2& getSpriteSize() const { return sprite_size_; } ///< @brief 获取精灵尺寸
const glm::vec2& getOffset() const { return offset_; } ///< @brief 获取偏移量
engine::utils::Alignment getAlignment() const { return alignment_; } ///< @brief 获取对齐方式
// Setters
void setSpriteById(const std::string& texture_id, const std::optional<SDL_FRect>& source_rect_opt = std::nullopt); ///< @brief 设置精灵对象
void setFlipped(bool flipped) { sprite_.setFlipped(flipped); } ///< @brief 设置是否翻转
void setHidden(bool hidden) { is_hidden_ = hidden; } ///< @brief 设置是否隐藏
void setSourceRect(const std::optional<SDL_FRect>& source_rect_opt); ///< @brief 设置源矩形
void setAlignment(engine::utils::Alignment anchor); ///< @brief 设置对齐方式
private:
void updateSpriteSize(); ///< @brief 辅助函数,根据 sprite_ 的 source_rect_ 更新 sprite_size_
// Component 虚函数覆盖
void init() override; ///< @brief 初始化函数需要覆盖
void update(float, engine::core::Context&) override {} ///< @brief 更新函数留空
void render(engine::core::Context& context) override; ///< @brief 渲染函数需要覆盖
};
} // namespace engine::component
@@ -0,0 +1,18 @@
#include "transform_component.h"
#include "../object/game_object.h"
#include "sprite_component.h"
namespace engine::component {
void TransformComponent::setScale(const glm::vec2 &scale)
{
scale_ = scale;
if (owner_) {
auto sprite_comp = owner_->getComponent<SpriteComponent>();
if (sprite_comp) {
sprite_comp->updateOffset();
}
}
}
} // namespace engine::component
@@ -0,0 +1,46 @@
#pragma once
#include "./component.h"
#include <glm/vec2.hpp>
namespace engine::component {
/**
* @class TransformComponent
* @brief 管理 GameObject 的位置、旋转和缩放。
*/
class TransformComponent final : public Component {
friend class engine::object::GameObject; // 友元不能继承,必须每个子类单独添加
public:
glm::vec2 position_ = {0.0f, 0.0f}; ///< @brief 位置
glm::vec2 scale_ = {1.0f, 1.0f}; ///< @brief 缩放
float rotation_ = 0.0f; ///< @brief 角度制,单位:度
/**
* @brief 构造函数
* @param position 位置
* @param scale 缩放
* @param rotation 旋转
*/
TransformComponent(glm::vec2 position = {0.0f, 0.0f}, glm::vec2 scale = {1.0f, 1.0f}, float rotation = 0.0f)
: position_(position), scale_(scale), rotation_(rotation) {}
// 禁止拷贝和移动
TransformComponent(const TransformComponent&) = delete;
TransformComponent& operator=(const TransformComponent&) = delete;
TransformComponent(TransformComponent&&) = delete;
TransformComponent& operator=(TransformComponent&&) = delete;
// Getters and setters
const glm::vec2& getPosition() const { return position_; } ///< @brief 获取位置
float getRotation() const { return rotation_; } ///< @brief 获取旋转
const glm::vec2& getScale() const { return scale_; } ///< @brief 获取缩放
void setPosition(const glm::vec2& position) { position_ = position; } ///< @brief 设置位置
void setRotation(float rotation) { rotation_ = rotation; } ///< @brief 设置旋转
void setScale(const glm::vec2& scale); ///< @brief 设置缩放,应用缩放时应同步更新Sprite偏移量
void translate(const glm::vec2& offset) { position_ += offset; } ///< @brief 平移
private:
void update(float, engine::core::Context&) override {} ///< @brief 覆盖纯虚函数,这里不需要实现
};
} // namespace engine::component