feat(engine): C++ native script system (ScriptBehavior + registry)
ScriptComponent stores only a script name string (POD, serializable); instances are reconciled by ScriptRuntime each frame (PhysicsWorld pattern): orphan/rename cleanup with OnDestroy, per-name instantiation from a file-level self-registering registry (ENGINE_REGISTER_SCRIPT), OnCreate on bind, OnUpdate after physics step on scaled dt. Engine framerate loop only sees the base interface, so future Lua/C# behaviors plug in as foreign ScriptBehavior implementations with zero engine changes. Ships a Spinner sample script and Inspector script picker.
This commit is contained in:
+5
-2
@@ -1,9 +1,12 @@
|
||||
# App 层源文件:main + editor/(docking 编辑器,纯 App 代码)
|
||||
# App 层源文件:main + editor/(docking 编辑器)+ scripts/(C++ 原生脚本)
|
||||
file(GLOB EDITOR_SOURCES CONFIGURE_DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/editor/*.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/editor/*.h)
|
||||
|
||||
add_executable(HelloVulkan main.cpp ${EDITOR_SOURCES})
|
||||
file(GLOB SCRIPT_SOURCES CONFIGURE_DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/scripts/*.cpp)
|
||||
|
||||
add_executable(HelloVulkan main.cpp ${EDITOR_SOURCES} ${SCRIPT_SOURCES})
|
||||
|
||||
target_include_directories(HelloVulkan PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/editor)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Engine/ECS/Components.h"
|
||||
#include "Engine/Renderer/Model.h"
|
||||
#include "Engine/Physics/PhysicsWorld.h" // Apply Impulse 调试(GetPhysics 门面)
|
||||
#include "Engine/Scripting/Script.h" // 脚本下拉(ScriptRegistry::Names)
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
@@ -126,6 +127,11 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
||||
{
|
||||
reg.emplace<Engine::ECS::CharacterControllerComponent>(selected);
|
||||
}
|
||||
if (!reg.all_of<Engine::ECS::ScriptComponent>(selected) &&
|
||||
ImGui::MenuItem("Script"))
|
||||
{
|
||||
reg.emplace<Engine::ECS::ScriptComponent>(selected);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::Separator();
|
||||
@@ -428,6 +434,39 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Script(按名解析注册表脚本;实例生命周期在 ScriptRuntime)----
|
||||
if (auto* sc = reg.try_get<Engine::ECS::ScriptComponent>(selected))
|
||||
{
|
||||
if (ImGui::CollapsingHeader("Script", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
// 下拉列出全部已注册脚本(注册序);"<none>" = 清空(对账自动销毁实例)
|
||||
const auto& names = Engine::Scripting::ScriptRegistry::Names();
|
||||
std::vector<const char*> items;
|
||||
items.push_back("<none>");
|
||||
for (const auto& n : names)
|
||||
{
|
||||
items.push_back(n.c_str());
|
||||
}
|
||||
int cur = 0;
|
||||
for (size_t i = 0; i < names.size(); ++i)
|
||||
{
|
||||
if (names[i] == sc->scriptName)
|
||||
{
|
||||
cur = static_cast<int>(i) + 1;
|
||||
}
|
||||
}
|
||||
if (ImGui::Combo("script", &cur, items.data(),
|
||||
static_cast<int>(items.size())))
|
||||
{
|
||||
sc->scriptName = (cur <= 0) ? "" : names[static_cast<size_t>(cur) - 1];
|
||||
}
|
||||
if (sc->scriptName.empty())
|
||||
{
|
||||
ImGui::TextDisabled("no script assigned");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Animation ----
|
||||
if (auto* anim = reg.try_get<Engine::ECS::AnimationComponent>(selected))
|
||||
{
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Spinner.cpp:C++ 原生脚本示范——Y 轴匀速自转。
|
||||
//
|
||||
// 写脚本三步:
|
||||
// 1. 新建 .cpp 放本目录(CMake 自动进构建)
|
||||
// 2. 继承 ScriptBehavior,覆写 Name/OnCreate/OnUpdate/OnDestroy
|
||||
// 3. 文件级 ENGINE_REGISTER_SCRIPT(类名) 自注册——Inspector 下拉
|
||||
// 与场景序列化按该名解析
|
||||
//
|
||||
|
||||
#include <Engine/Scripting/Script.h>
|
||||
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
class Spinner final : public Engine::Scripting::ScriptBehavior {
|
||||
public:
|
||||
const char* Name() const override { return "Spinner"; }
|
||||
|
||||
void OnCreate() override {
|
||||
// 实体与组件已就位;需要别的组件时用 Get<T>()(不存在会炸)
|
||||
}
|
||||
|
||||
void OnUpdate(float dt) override {
|
||||
// 四元数复合绕 Y(避免每帧欧拉↔四元数往返累积失真)
|
||||
const glm::quat step = glm::angleAxis(
|
||||
glm::radians(m_Speed * dt), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
Transform().SetRotationQuat(step * Transform().GetRotationQuat());
|
||||
}
|
||||
|
||||
private:
|
||||
float m_Speed = 90.0f; // 度/秒
|
||||
};
|
||||
|
||||
ENGINE_REGISTER_SCRIPT(Spinner)
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user