From b4399d2f41a620eeb5def20194f138afa6b294c4 Mon Sep 17 00:00:00 2001 From: Ziyu Date: Sat, 31 May 2025 09:40:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E7=89=A9=E7=90=86=E5=BC=95?= =?UTF-8?q?=E6=93=8E=E4=B8=8E=E7=89=A9=E7=90=86=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 + src/engine/component/physics_component.cpp | 41 +++++++++++++ src/engine/component/physics_component.h | 71 ++++++++++++++++++++++ src/engine/core/context.cpp | 7 ++- src/engine/core/context.h | 11 +++- src/engine/core/game_app.cpp | 21 ++++++- src/engine/core/game_app.h | 6 ++ src/engine/physics/physics_engine.cpp | 51 ++++++++++++++++ src/engine/physics/physics_engine.h | 41 +++++++++++++ src/engine/scene/scene.cpp | 7 ++- src/game/scene/game_scene.cpp | 23 ++++++- src/game/scene/game_scene.h | 2 + 12 files changed, 274 insertions(+), 9 deletions(-) create mode 100644 src/engine/component/physics_component.cpp create mode 100644 src/engine/component/physics_component.h create mode 100644 src/engine/physics/physics_engine.cpp create mode 100644 src/engine/physics/physics_engine.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ff7027f..9646804 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,8 @@ add_executable(${TARGET} src/engine/component/transform_component.cpp src/engine/component/parallax_component.cpp src/engine/component/tilelayer_component.cpp + src/engine/component/physics_component.cpp + src/engine/physics/physics_engine.cpp src/engine/scene/scene.cpp src/engine/scene/scene_manager.cpp src/engine/scene/level_loader.cpp diff --git a/src/engine/component/physics_component.cpp b/src/engine/component/physics_component.cpp new file mode 100644 index 0000000..a1d15a5 --- /dev/null +++ b/src/engine/component/physics_component.cpp @@ -0,0 +1,41 @@ +#include "physics_component.h" +#include "transform_component.h" +#include "../object/game_object.h" +#include "../physics/physics_engine.h" +#include + +namespace engine::component { + +PhysicsComponent::PhysicsComponent(engine::physics::PhysicsEngine* physics_engine, bool use_gravity, float mass) + : physics_engine_(physics_engine), mass_(mass >= 0.0f ? mass : 1.0f), use_gravity_(use_gravity) { + if (!physics_engine_) { + spdlog::error("PhysicsComponent构造函数中,PhysicsEngine指针不能为nullptr!"); + } + spdlog::trace("物理组件创建完成,质量: {}, 使用重力: {}", mass_, use_gravity_); +} + +void PhysicsComponent::init() { + if (!owner_) { + spdlog::error("物理组件初始化前需要一个GameObject作为所有者!"); + return; + } + if (!physics_engine_) { + spdlog::error("物理组件初始化时,PhysicsEngine未正确初始化。"); + return; + } + transform_ = owner_->getComponent(); + if (!transform_) { + spdlog::warn("物理组件初始化时,同一GameObject上没有找到TransformComponent组件。"); + } + // 注册到PhysicsEngine + physics_engine_->registerComponent(this); + spdlog::trace("物理组件初始化完成。"); +} + +void PhysicsComponent::clean() +{ + physics_engine_->unregisterComponent(this); + spdlog::trace("物理组件清理完成。"); +} + +} // namespace engine::component \ No newline at end of file diff --git a/src/engine/component/physics_component.h b/src/engine/component/physics_component.h new file mode 100644 index 0000000..328384b --- /dev/null +++ b/src/engine/component/physics_component.h @@ -0,0 +1,71 @@ +#pragma once +#include "component.h" +#include "glm/vec2.hpp" + +namespace engine::physics { + class PhysicsEngine; +} + +namespace engine::component { +class TransformComponent; + +/** + * @brief 管理GameObject的物理属性 + * + * 存储速度、质量、力和重力设置。与PhysicsEngine交互。 + */ +class PhysicsComponent final: public Component { + friend class engine::object::GameObject; +public: + glm::vec2 velocity_ = {0.0f, 0.0f}; ///< @brief 物体的速度,设为公共成员变量,方便PhysicsEngine访问更新 + +private: + engine::physics::PhysicsEngine* physics_engine_ = nullptr; ///< @brief 指向PhysicsEngine的指针 + TransformComponent* transform_ = nullptr; ///< @brief TransformComponent的缓存指针 + + glm::vec2 force_ = {0.0f, 0.0f}; ///< @brief 当前帧受到的力 + float mass_ = 1.0f; ///< @brief 物体质量(默认1.0) + bool use_gravity_ = true; ///< @brief 物体是否受重力影响 + bool enabled_ = true; ///< @brief 组件是否激活 + +public: + /** + * @brief 构造函数 + * + * @param physics_engine 指向PhysicsEngine的指针,不能为nullptr + * @param use_gravity 物体是否受重力影响,默认true + * @param mass 物体质量,默认1.0 + */ + PhysicsComponent(engine::physics::PhysicsEngine* physics_engine, bool use_gravity = true, float mass = 1.0f); + ~PhysicsComponent() override = default; + + // 删除复制/移动操作 + PhysicsComponent(const PhysicsComponent&) = delete; + PhysicsComponent& operator=(const PhysicsComponent&) = delete; + PhysicsComponent(PhysicsComponent&&) = delete; + PhysicsComponent& operator=(PhysicsComponent&&) = delete; + + // PhysicsEngine使用的物理方法 + void addForce(const glm::vec2& force) { if (enabled_) force_ += force; } ///< @brief 添加力 + void clearForce() { force_ = {0.0f, 0.0f}; } ///< @brief 清除力 + const glm::vec2& getForce() const { return force_; } ///< @brief 获取当前力 + float getMass() const { return mass_; } ///< @brief 获取质量 + bool isEnabled() const { return enabled_; } ///< @brief 获取组件是否启用 + bool isUseGravity() const { return use_gravity_; } ///< @brief 获取组件是否受重力影响 + + // 设置器/获取器 + void setEnabled(bool enabled) { enabled_ = enabled; } ///< @brief 设置组件是否启用 + void setMass(float mass) { mass_ = (mass >= 0.0f) ? mass : 1.0f; } ///< @brief 设置质量,质量不能为负 + void setUseGravity(bool use_gravity) { use_gravity_ = use_gravity; } ///< @brief 设置组件是否受重力影响 + void setVelocity(const glm::vec2& velocity) { velocity_ = velocity; } ///< @brief 设置速度 + const glm::vec2& getVelocity() const { return velocity_; } ///< @brief 获取当前速度 + TransformComponent* getTransform() const { return transform_; } ///< @brief 获取TransformComponent指针 + +private: + // 核心循环方法 + void init() override; + void update(float, engine::core::Context&) override {} + void clean() override; +}; + +} // namespace engine::component \ No newline at end of file diff --git a/src/engine/core/context.cpp b/src/engine/core/context.cpp index 611d05f..efe409c 100644 --- a/src/engine/core/context.cpp +++ b/src/engine/core/context.cpp @@ -3,6 +3,7 @@ #include "../render/renderer.h" #include "../render/camera.h" #include "../resource/resource_manager.h" +#include "../physics/physics_engine.h" #include namespace engine::core { @@ -10,11 +11,13 @@ 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 参数 + engine::resource::ResourceManager& resource_manager, + engine::physics::PhysicsEngine& physics_engine) : input_manager_(input_manager), renderer_(renderer), camera_(camera), - resource_manager_(resource_manager) // 初始化 game_state_ 成员 + resource_manager_(resource_manager), + physics_engine_(physics_engine) { spdlog::trace("上下文已创建并初始化,包含输入管理器、渲染器、相机和资源管理器。"); } diff --git a/src/engine/core/context.h b/src/engine/core/context.h index 1f8bb77..709d9c2 100644 --- a/src/engine/core/context.h +++ b/src/engine/core/context.h @@ -13,6 +13,10 @@ namespace engine::resource { class ResourceManager; } +namespace engine::physics { + class PhysicsEngine; +} + namespace engine::core { /** @@ -27,6 +31,7 @@ private: engine::render::Renderer& renderer_; ///< @brief 渲染器 engine::render::Camera& camera_; ///< @brief 相机 engine::resource::ResourceManager& resource_manager_; ///< @brief 资源管理器 + engine::physics::PhysicsEngine& physics_engine_; ///< @brief 物理引擎 public: /** @@ -35,11 +40,13 @@ public: * @param renderer 对 Renderer 实例的引用。 * @param camera 对 Camera 实例的引用。 * @param resource_manager 对 ResourceManager 实例的引用。 + * @param physics_engine 对 PhysicsEngine 实例的引用。 */ Context(engine::input::InputManager& input_manager, engine::render::Renderer& renderer, engine::render::Camera& camera, - engine::resource::ResourceManager& resource_manager); + engine::resource::ResourceManager& resource_manager, + engine::physics::PhysicsEngine& physics_engine); // 禁止拷贝和移动,Context 对象通常是唯一的或按需创建/传递 Context(const Context&) = delete; @@ -52,7 +59,7 @@ public: 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 获取资源管理器 - + engine::physics::PhysicsEngine& getPhysicsEngine() const { return physics_engine_; } ///< @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 e8436c6..7b34b51 100644 --- a/src/engine/core/game_app.cpp +++ b/src/engine/core/game_app.cpp @@ -1,11 +1,12 @@ #include "game_app.h" #include "time.h" #include "context.h" +#include "config.h" #include "../resource/resource_manager.h" #include "../render/renderer.h" #include "../render/camera.h" #include "../input/input_manager.h" -#include "config.h" +#include "../physics/physics_engine.h" #include "../scene/scene_manager.h" #include "../../game/scene/game_scene.h" #include @@ -52,6 +53,7 @@ bool GameApp::init() { if (!initRenderer()) return false; if (!initCamera()) return false; if (!initInputManager()) return false; + if (!initPhysicsEngine()) return false; if (!initContext()) return false; if (!initSceneManager()) return false; @@ -93,6 +95,8 @@ void GameApp::render() { void GameApp::close() { spdlog::trace("关闭 GameApp ..."); + // 先关闭场景管理器,确保所有场景都被清理 + scene_manager_->close(); // 为了确保正确的销毁顺序,有些智能指针对象也需要手动管理 resource_manager_.reset(); @@ -208,10 +212,23 @@ bool GameApp::initInputManager() return true; } +bool GameApp::initPhysicsEngine() +{ + try { + physics_engine_ = std::make_unique(); + } + catch (const std::exception& e) { + spdlog::error("初始化物理引擎失败: {}", e.what()); + return false; + } + spdlog::trace("物理引擎初始化成功。"); + return true; +} + bool GameApp::initContext() { try { - context_ = std::make_unique(*input_manager_, *renderer_, *camera_, *resource_manager_); + context_ = std::make_unique(*input_manager_, *renderer_, *camera_, *resource_manager_, *physics_engine_); } catch (const std::exception& e) { spdlog::error("初始化上下文失败: {}", e.what()); return false; diff --git a/src/engine/core/game_app.h b/src/engine/core/game_app.h index 3f27ecb..17f12cb 100644 --- a/src/engine/core/game_app.h +++ b/src/engine/core/game_app.h @@ -18,6 +18,10 @@ namespace engine::input { class InputManager; } +namespace engine::physics { +class PhysicsEngine; +} + namespace engine::scene { class SceneManager; } @@ -45,6 +49,7 @@ private: std::unique_ptr input_manager_; std::unique_ptr context_; std::unique_ptr scene_manager_; + std::unique_ptr physics_engine_; public: GameApp(); @@ -76,6 +81,7 @@ private: [[nodiscard]] bool initRenderer(); [[nodiscard]] bool initCamera(); [[nodiscard]] bool initInputManager(); + [[nodiscard]] bool initPhysicsEngine(); [[nodiscard]] bool initContext(); [[nodiscard]] bool initSceneManager(); }; diff --git a/src/engine/physics/physics_engine.cpp b/src/engine/physics/physics_engine.cpp new file mode 100644 index 0000000..ba3a9f5 --- /dev/null +++ b/src/engine/physics/physics_engine.cpp @@ -0,0 +1,51 @@ +#include "physics_engine.h" +#include "../component/physics_component.h" +#include "../component/transform_component.h" +#include "../object/game_object.h" +#include +#include +#include "glm/common.hpp" + +namespace engine::physics { + +void PhysicsEngine::registerComponent(engine::component::PhysicsComponent* component) { + components_.push_back(component); + spdlog::trace("物理组件注册完成。"); +} + +void PhysicsEngine::unregisterComponent(engine::component::PhysicsComponent* component) { + // 使用 remove-erase 方法安全地移除指针 + auto it = std::remove(components_.begin(), components_.end(), component); + components_.erase(it, components_.end()); + spdlog::trace("物理组件注销完成。"); +} + +void PhysicsEngine::update(float delta_time) { + // 遍历所有注册的物理组件 + for (auto* pc : components_) { + if (!pc || !pc->isEnabled()) { // 检查组件是否有效和启用 + continue; + } + + // 应用重力 (如果组件受重力影响):F = g * m + if (pc->isUseGravity()) { + pc->addForce(gravity_ * pc->getMass()); + } + /* 还可以添加其它力影响,比如风力、摩擦力等,目前不考虑 */ + + // 更新速度: v += a * dt,其中 a = F / m + pc->velocity_ += (pc->getForce() / pc->getMass()) * delta_time; + pc->clearForce(); // 清除当前帧的力 + + // 更新位置:S += v * dt + auto* tc = pc->getTransform(); + if (tc) { + tc->translate(pc->velocity_ * delta_time); + } + + // 限制最大速度:v = min(v, max_speed) + pc->velocity_ = glm::clamp(pc->velocity_, -max_speed_, max_speed_); + } +} + +} // namespace engine::physics \ No newline at end of file diff --git a/src/engine/physics/physics_engine.h b/src/engine/physics/physics_engine.h new file mode 100644 index 0000000..c894aa7 --- /dev/null +++ b/src/engine/physics/physics_engine.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include "glm/vec2.hpp" + +namespace engine::component { + class PhysicsComponent; +} + +namespace engine::physics { + +/** + * @brief 负责管理和模拟物理行为及碰撞检测。 + */ +class PhysicsEngine { +private: + std::vector components_; ///< @brief 注册的物理组件容器,非拥有指针 + glm::vec2 gravity_ = {0.0f, 980.0f}; ///< @brief 默认重力值 (像素/秒^2, 相当于100像素对应现实1m) + float max_speed_ = 500.0f; ///< @brief 最大速度 (像素/秒) + +public: + PhysicsEngine() = default; + + // 禁止拷贝和移动 + PhysicsEngine(const PhysicsEngine&) = delete; + PhysicsEngine& operator=(const PhysicsEngine&) = delete; + PhysicsEngine(PhysicsEngine&&) = delete; + PhysicsEngine& operator=(PhysicsEngine&&) = delete; + + void registerComponent(engine::component::PhysicsComponent* component); ///< @brief 注册物理组件 + void unregisterComponent(engine::component::PhysicsComponent* component); ///< @brief 注销物理组件 + + void update(float delta_time); ///< @brief 核心循环:更新所有注册的物理组件的状态 + + // 设置器/获取器 + void setGravity(const glm::vec2& gravity) { gravity_ = gravity; } ///< @brief 设置全局重力加速度 + const glm::vec2& getGravity() const { return gravity_; } ///< @brief 获取当前的全局重力加速度 + void setMaxSpeed(float max_speed) { max_speed_ = max_speed; } ///< @brief 设置最大速度 + float getMaxSpeed() const { return max_speed_; } ///< @brief 获取当前的最大速度 +}; + +} // namespace engine::physics \ No newline at end of file diff --git a/src/engine/scene/scene.cpp b/src/engine/scene/scene.cpp index 9e1c219..30d24c9 100644 --- a/src/engine/scene/scene.cpp +++ b/src/engine/scene/scene.cpp @@ -1,6 +1,8 @@ #include "scene.h" -#include "../object/game_object.h" #include "scene_manager.h" +#include "../object/game_object.h" +#include "../core/context.h" +#include "../physics/physics_engine.h" #include // for std::remove_if #include @@ -20,6 +22,9 @@ void Scene::init() { void Scene::update(float delta_time) { if (!is_initialized_) return; + + // 先更新物理引擎 + context_.getPhysicsEngine().update(delta_time); // 更新所有游戏对象,并删除需要移除的对象 for (auto it = game_objects_.begin(); it != game_objects_.end();) { diff --git a/src/game/scene/game_scene.cpp b/src/game/scene/game_scene.cpp index 2b47aa1..ad79c0c 100644 --- a/src/game/scene/game_scene.cpp +++ b/src/game/scene/game_scene.cpp @@ -3,6 +3,8 @@ #include "../../engine/object/game_object.h" #include "../../engine/component/transform_component.h" #include "../../engine/component/sprite_component.h" +#include "../../engine/component/physics_component.h" +#include "../../engine/physics/physics_engine.h" #include "../../engine/scene/level_loader.h" #include "../../engine/input/input_manager.h" #include "../../engine/render/camera.h" @@ -39,7 +41,7 @@ void GameScene::render() { void GameScene::handleInput() { Scene::handleInput(); - testCamera(); + TestObject(); } void GameScene::clean() { @@ -51,11 +53,12 @@ void GameScene::clean() { void GameScene::createTestObject() { spdlog::trace("在 GameScene 中创建 test_object..."); auto test_object = std::make_unique("test_object"); + test_object_ = test_object.get(); // 添加组件 test_object->addComponent(glm::vec2(100.0f, 100.0f)); test_object->addComponent("assets/textures/Props/big-crate.png", context_.getResourceManager()); - + test_object->addComponent(&context_.getPhysicsEngine()); // 将创建好的 GameObject 添加到场景中 (一定要用std::move,否则传递的是左值) addGameObject(std::move(test_object)); spdlog::trace("test_object 创建并添加到 GameScene 中。"); @@ -70,4 +73,20 @@ void GameScene::testCamera() { if (input_manager.isActionDown("move_right")) camera.move(glm::vec2(1, 0)); } +void GameScene::TestObject() +{ + if (!test_object_) return; + auto& input_manager = context_.getInputManager(); + + if (input_manager.isActionDown("move_left")) { + test_object_->getComponent()->translate(glm::vec2(-1, 0)); + } + if (input_manager.isActionDown("move_right")) { + test_object_->getComponent()->translate(glm::vec2(1, 0)); + } + if (input_manager.isActionPressed("jump")) { + test_object_->getComponent()->setVelocity(glm::vec2(0, -400)); + } +} + } // namespace game::scene \ No newline at end of file diff --git a/src/game/scene/game_scene.h b/src/game/scene/game_scene.h index 602caf1..2ee35ca 100644 --- a/src/game/scene/game_scene.h +++ b/src/game/scene/game_scene.h @@ -13,6 +13,7 @@ namespace game::scene { * @brief 主要的游戏场景,包含玩家、敌人、关卡元素等。 */ class GameScene final: public engine::scene::Scene { + engine::object::GameObject* test_object_ = nullptr; ///< @brief 保存测试对象的指针,方便访问 public: GameScene(std::string name, engine::core::Context& context, engine::scene::SceneManager& scene_manager); @@ -28,6 +29,7 @@ private: // 测试函数 void createTestObject(); void testCamera(); + void TestObject(); }; } // namespace game::scene \ No newline at end of file