From 757b66afa397bb9b955aff283f8c940688dacf5e Mon Sep 17 00:00:00 2001 From: Ziyu Date: Sat, 24 May 2025 17:29:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=8F=98=E6=8D=A2=E4=B8=8E?= =?UTF-8?q?=E7=B2=BE=E7=81=B5=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 + src/engine/component/component.h | 14 ++- src/engine/component/sprite_component.cpp | 119 +++++++++++++++++++ src/engine/component/sprite_component.h | 90 ++++++++++++++ src/engine/component/transform_component.cpp | 18 +++ src/engine/component/transform_component.h | 46 +++++++ src/engine/core/context.cpp | 22 ++++ src/engine/core/context.h | 58 +++++++++ src/engine/core/game_app.cpp | 27 ++++- src/engine/core/game_app.h | 4 + src/engine/object/game_object.cpp | 12 +- src/engine/object/game_object.h | 12 +- src/engine/utils/alignment.h | 25 ++++ 13 files changed, 432 insertions(+), 18 deletions(-) create mode 100644 src/engine/component/sprite_component.cpp create mode 100644 src/engine/component/sprite_component.h create mode 100644 src/engine/component/transform_component.cpp create mode 100644 src/engine/component/transform_component.h create mode 100644 src/engine/core/context.cpp create mode 100644 src/engine/core/context.h create mode 100644 src/engine/utils/alignment.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 49bdebc..d38bee6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(${TARGET} src/engine/core/game_app.cpp src/engine/core/time.cpp src/engine/core/config.cpp + src/engine/core/context.cpp src/engine/resource/resource_manager.cpp src/engine/resource/texture_manager.cpp src/engine/resource/audio_manager.cpp @@ -42,6 +43,8 @@ add_executable(${TARGET} src/engine/render/camera.cpp src/engine/input/input_manager.cpp src/engine/object/game_object.cpp + src/engine/component/sprite_component.cpp + src/engine/component/transform_component.cpp ) # 链接库 diff --git a/src/engine/component/component.h b/src/engine/component/component.h index c119db3..0293e67 100644 --- a/src/engine/component/component.h +++ b/src/engine/component/component.h @@ -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 \ No newline at end of file diff --git a/src/engine/component/sprite_component.cpp b/src/engine/component/sprite_component.cpp new file mode 100644 index 0000000..05a6be5 --- /dev/null +++ b/src/engine/component/sprite_component.cpp @@ -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 // 用于 std::runtime_error +#include + +namespace engine::component { + +SpriteComponent::SpriteComponent( + const std::string& texture_id, + engine::resource::ResourceManager& resource_manager, + engine::utils::Alignment alignment, + std::optional 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(); + 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& source_rect_opt) { + sprite_.setTextureId(texture_id); + sprite_.setSourceRect(source_rect_opt); + + updateSpriteSize(); + updateOffset(); +} + +void SpriteComponent::setSourceRect(const std::optional& 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 \ No newline at end of file diff --git a/src/engine/component/sprite_component.h b/src/engine/component/sprite_component.h new file mode 100644 index 0000000..1b31b01 --- /dev/null +++ b/src/engine/component/sprite_component.h @@ -0,0 +1,90 @@ +#pragma once +#include "../render/sprite.h" +#include "./component.h" +#include "../utils/alignment.h" +#include +#include +#include +#include + +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 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& 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& 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 \ No newline at end of file diff --git a/src/engine/component/transform_component.cpp b/src/engine/component/transform_component.cpp new file mode 100644 index 0000000..9cf6bc3 --- /dev/null +++ b/src/engine/component/transform_component.cpp @@ -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(); + if (sprite_comp) { + sprite_comp->updateOffset(); + } + } +} + +} // namespace engine::component \ No newline at end of file diff --git a/src/engine/component/transform_component.h b/src/engine/component/transform_component.h new file mode 100644 index 0000000..7b4f232 --- /dev/null +++ b/src/engine/component/transform_component.h @@ -0,0 +1,46 @@ +#pragma once +#include "./component.h" +#include + +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 \ No newline at end of file diff --git a/src/engine/core/context.cpp b/src/engine/core/context.cpp new file mode 100644 index 0000000..611d05f --- /dev/null +++ b/src/engine/core/context.cpp @@ -0,0 +1,22 @@ +#include "context.h" +#include "../input/input_manager.h" +#include "../render/renderer.h" +#include "../render/camera.h" +#include "../resource/resource_manager.h" +#include + +namespace engine::core { + +Context::Context(engine::input::InputManager& input_manager, + engine::render::Renderer& renderer, + engine::render::Camera& camera, + engine::resource::ResourceManager& resource_manager) // 添加 game_state 参数 + : input_manager_(input_manager), + renderer_(renderer), + camera_(camera), + resource_manager_(resource_manager) // 初始化 game_state_ 成员 +{ + spdlog::trace("上下文已创建并初始化,包含输入管理器、渲染器、相机和资源管理器。"); +} + +} // namespace engine::core \ No newline at end of file diff --git a/src/engine/core/context.h b/src/engine/core/context.h new file mode 100644 index 0000000..1f8bb77 --- /dev/null +++ b/src/engine/core/context.h @@ -0,0 +1,58 @@ +#pragma once +// 前置声明核心系统 +namespace engine::input { + class InputManager; +} + +namespace engine::render { + class Renderer; + class Camera; +} + +namespace engine::resource { + class ResourceManager; +} + +namespace engine::core { + +/** + * @brief 持有对核心引擎模块引用的上下文对象。 + * + * 用于简化依赖注入,传递Context即可获取引擎的各个模块。 + */ +class Context final { +private: + // 使用引用,确保每个模块都有效,使用时不需要检查指针是否为空。 + engine::input::InputManager& input_manager_; ///< @brief 输入管理器 + engine::render::Renderer& renderer_; ///< @brief 渲染器 + engine::render::Camera& camera_; ///< @brief 相机 + engine::resource::ResourceManager& resource_manager_; ///< @brief 资源管理器 + +public: + /** + * @brief 构造函数。 + * @param input_manager 对 InputManager 实例的引用。 + * @param renderer 对 Renderer 实例的引用。 + * @param camera 对 Camera 实例的引用。 + * @param resource_manager 对 ResourceManager 实例的引用。 + */ + Context(engine::input::InputManager& input_manager, + engine::render::Renderer& renderer, + engine::render::Camera& camera, + engine::resource::ResourceManager& resource_manager); + + // 禁止拷贝和移动,Context 对象通常是唯一的或按需创建/传递 + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + Context(Context&&) = delete; + Context& operator=(Context&&) = delete; + + // --- Getters --- + engine::input::InputManager& getInputManager() const { return input_manager_; } ///< @brief 获取输入管理器 + engine::render::Renderer& getRenderer() const { return renderer_; } ///< @brief 获取渲染器 + engine::render::Camera& getCamera() const { return camera_; } ///< @brief 获取相机 + engine::resource::ResourceManager& getResourceManager() const { return resource_manager_; } ///< @brief 获取资源管理器 + +}; + +} // namespace engine::core \ No newline at end of file diff --git a/src/engine/core/game_app.cpp b/src/engine/core/game_app.cpp index a7042c6..a2d9980 100644 --- a/src/engine/core/game_app.cpp +++ b/src/engine/core/game_app.cpp @@ -1,16 +1,21 @@ #include "game_app.h" #include "time.h" +#include "context.h" #include "../resource/resource_manager.h" #include "../render/renderer.h" #include "../render/camera.h" #include "../input/input_manager.h" #include "../object/game_object.h" +#include "../component/transform_component.h" +#include "../component/sprite_component.h" #include "config.h" #include #include namespace engine::core { +engine::object::GameObject game_object("test_game_object"); + GameApp::GameApp() = default; GameApp::~GameApp() { @@ -22,7 +27,7 @@ GameApp::~GameApp() { void GameApp::run() { if (!init()) { - spdlog::error("初始化失败,无法运行游戏。"); + spdlog::error("GameApp 初始化失败,无法运行游戏。"); return; } @@ -51,6 +56,8 @@ bool GameApp::init() { if (!initCamera()) return false; if (!initInputManager()) return false; + if (!initContext()) return false; + // 测试资源管理器 testResourceManager(); @@ -81,6 +88,7 @@ void GameApp::render() { // 2. 具体渲染代码 testRenderer(); + game_object.render(*context_); // 3. 更新屏幕显示 renderer_->present(); @@ -203,6 +211,17 @@ bool GameApp::initInputManager() return true; } +bool GameApp::initContext() +{ + try { + context_ = std::make_unique(*input_manager_, *renderer_, *camera_, *resource_manager_); + } catch (const std::exception& e) { + spdlog::error("初始化上下文失败: {}", e.what()); + return false; + } + return true; +} + // --- 测试用函数 --- void GameApp::testResourceManager() @@ -270,8 +289,10 @@ void GameApp::testInputManager() void GameApp::testGameObject() { - engine::object::GameObject game_object("test_game_object"); - game_object.addComponent(); + game_object.addComponent(glm::vec2(100, 100)); + game_object.addComponent("assets/textures/Props/big-crate.png", *resource_manager_, engine::utils::Alignment::CENTER); + game_object.getComponent()->setScale(glm::vec2(2.0f, 2.0f)); + game_object.getComponent()->setRotation(30.0f); } } // namespace engine::core \ No newline at end of file diff --git a/src/engine/core/game_app.h b/src/engine/core/game_app.h index 0f16782..11bfc21 100644 --- a/src/engine/core/game_app.h +++ b/src/engine/core/game_app.h @@ -21,6 +21,7 @@ class InputManager; namespace engine::core { // 命名空间的最佳实践:与文件路径一致 class Time; class Config; +class Context; /** * @brief 主游戏应用程序类,初始化SDL,管理游戏循环。 @@ -38,6 +39,8 @@ private: std::unique_ptr camera_; std::unique_ptr config_; std::unique_ptr input_manager_; + std::unique_ptr context_; + public: GameApp(); ~GameApp(); @@ -68,6 +71,7 @@ private: [[nodiscard]] bool initRenderer(); [[nodiscard]] bool initCamera(); [[nodiscard]] bool initInputManager(); + [[nodiscard]] bool initContext(); // 测试用函数 void testResourceManager(); diff --git a/src/engine/object/game_object.cpp b/src/engine/object/game_object.cpp index 96bf26e..0acf167 100644 --- a/src/engine/object/game_object.cpp +++ b/src/engine/object/game_object.cpp @@ -10,17 +10,17 @@ GameObject::GameObject(const std::string &name, const std::string &tag): name_(n spdlog::trace("GameObject created: {} {}", name_, tag_); } -void GameObject::update(float delta_time) { +void GameObject::update(float delta_time, engine::core::Context& context) { // 遍历所有组件并调用它们的 update 方法 for (auto& pair : components_) { - pair.second->update(delta_time); + pair.second->update(delta_time, context); } } -void GameObject::render() { +void GameObject::render(engine::core::Context& context) { // 遍历所有组件并调用它们的 render 方法 for (auto& pair : components_) { - pair.second->render(); + pair.second->render(context); } } @@ -33,10 +33,10 @@ void GameObject::clean() { components_.clear(); // 清空 map, unique_ptr 会自动释放内存 } -void GameObject::handleInput() { +void GameObject::handleInput(engine::core::Context& context) { // 遍历所有组件并调用它们的 handleInput 方法 for (auto& pair : components_) { - pair.second->handleInput(); + pair.second->handleInput(context); } } diff --git a/src/engine/object/game_object.h b/src/engine/object/game_object.h index 4e22dae..4fba002 100644 --- a/src/engine/object/game_object.h +++ b/src/engine/object/game_object.h @@ -6,6 +6,10 @@ #include // 用于完美转发 #include +namespace engine::core { + class Context; +} + namespace engine::object { /** @@ -117,10 +121,10 @@ public: } // 关键循环函数 - void update(float delta_time); ///< @brief 更新所有组件 - void render(); ///< @brief 渲染所有组件 - void clean(); ///< @brief 清理所有组件 - void handleInput(); ///< @brief 处理输入 + void update(float delta_time, engine::core::Context& context); ///< @brief 更新所有组件 + void render(engine::core::Context& context); ///< @brief 渲染所有组件 + void clean(); ///< @brief 清理所有组件 + void handleInput(engine::core::Context& context); ///< @brief 处理输入 }; diff --git a/src/engine/utils/alignment.h b/src/engine/utils/alignment.h new file mode 100644 index 0000000..d831e4c --- /dev/null +++ b/src/engine/utils/alignment.h @@ -0,0 +1,25 @@ +#ifndef ENGINE_UTILS_ALIGNMENT_H +#define ENGINE_UTILS_ALIGNMENT_H + +namespace engine::utils { + +/** + * @enum class Alignment + * @brief 定义对象或组件相对于其位置参考点的对齐方式。 + */ +enum class Alignment { + NONE, // 不指定对齐方式,偏移量通常为 (0,0) 或手动设置 + TOP_LEFT, // 左上角 + TOP_CENTER, // 顶部中心 + TOP_RIGHT, // 右上角 + CENTER_LEFT, // 中心左侧 + CENTER, // 正中心 (几何中心) + CENTER_RIGHT, // 中心右侧 + BOTTOM_LEFT, // 左下角 + BOTTOM_CENTER, // 底部中心 + BOTTOM_RIGHT // 右下角 +}; + +} // namespace engine::utils + +#endif // ENGINE_UTILS_ALIGNMENT_H \ No newline at end of file