新增 Prefab 系统:实体子树存取 + 编辑器 Save As Prefab/拖入实例化 + 脚本 Instantiate
This commit is contained in:
@@ -7,12 +7,16 @@
|
||||
//
|
||||
|
||||
#include "HierarchyPanel.h"
|
||||
#include "AssetBrowserPanel.h" // kPrefabDragPayload(Content Browser 源)
|
||||
#include "Engine/ECS/Components.h"
|
||||
#include "Engine/ECS/EntityHandle.h" // CreateEntity 返回完整 EntityHandle
|
||||
#include "Engine/Serialization/SceneSerializer.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -33,6 +37,34 @@ namespace {
|
||||
return "(unnamed)";
|
||||
}
|
||||
|
||||
// Prefab 拖入实例化(树节点/空白区落点共用)。根实体挂在场景根、
|
||||
// 保留授权位姿;返回根实体(失败 entt::null)
|
||||
entt::entity InstantiatePrefabDrop(const EditorContext& ctx,
|
||||
const char* path)
|
||||
{
|
||||
if (!ctx.scene || !ctx.assets || !path) {
|
||||
return entt::null;
|
||||
}
|
||||
return Engine::Serialization::SceneSerializer::InstantiatePrefab(
|
||||
*ctx.scene, *ctx.assets, path);
|
||||
}
|
||||
|
||||
// 实体名 → 合法文件名(路径分隔符/保留符净化;空名兜底)
|
||||
std::string PrefabFileName(std::string name)
|
||||
{
|
||||
for (char& c : name) {
|
||||
if (c == '/' || c == '\\' || c == ':' || c == '*' ||
|
||||
c == '?' || c == '"' || c == '<' || c == '>' ||
|
||||
c == '|') {
|
||||
c = '_';
|
||||
}
|
||||
}
|
||||
if (name.empty() || name == "(unnamed)") {
|
||||
name = "prefab";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
// 递归绘制单节点(含子树)。返回值无意义,选中写 selected
|
||||
void DrawEntityNode(const EditorContext& ctx, entt::entity e,
|
||||
entt::entity& selected)
|
||||
@@ -78,7 +110,9 @@ namespace {
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
// ---- 拖放目标:挂到本实体下(keepWorld,世界位姿不变)----
|
||||
// ---- 拖放目标:挂到本实体下(keepWorld,世界位姿不变);
|
||||
// Prefab 拖入 = 实例化子树后挂到本实体下(keepWorld 保持
|
||||
// 授权世界位姿)----
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* pl =
|
||||
ImGui::AcceptDragDropPayload(kHierPayload)) {
|
||||
@@ -91,6 +125,17 @@ namespace {
|
||||
ctx.scene->SetParent(dragged, e);
|
||||
}
|
||||
}
|
||||
if (const ImGuiPayload* pl =
|
||||
ImGui::AcceptDragDropPayload(kPrefabDragPayload)) {
|
||||
if (const entt::entity root =
|
||||
InstantiatePrefabDrop(ctx,
|
||||
static_cast<const char*>(
|
||||
pl->Data));
|
||||
root != entt::null) {
|
||||
ctx.scene->SetParent(root, e); // keepWorld 默认 true
|
||||
selected = root;
|
||||
}
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
@@ -102,6 +147,19 @@ namespace {
|
||||
ctx.scene->SetParent(child, e);
|
||||
selected = child;
|
||||
}
|
||||
if (ImGui::MenuItem("Save As Prefab")) {
|
||||
// 固定目录 + 实体名派生文件名(无文件对话框库,与场景
|
||||
// File 菜单同策略;重名覆盖,改名走文件系统)
|
||||
const std::string path =
|
||||
"assets/prefabs/" +
|
||||
PrefabFileName(DisplayName(reg, e)) + ".prefab";
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories("assets/prefabs", ec);
|
||||
if (Serialization::SceneSerializer::SerializePrefab(
|
||||
*ctx.scene, *ctx.assets, e, path)) {
|
||||
ENGINE_INFO("Hierarchy: prefab saved to '{}'", path);
|
||||
}
|
||||
}
|
||||
if (ctx.scene->GetParent(e) != entt::null &&
|
||||
ImGui::MenuItem("Detach to Root")) {
|
||||
ctx.scene->SetParent(e, entt::null);
|
||||
@@ -194,7 +252,8 @@ void HierarchyPanel::Draw(const EditorContext& ctx, entt::entity& selected)
|
||||
DrawEntityNode(ctx, e, selected);
|
||||
});
|
||||
|
||||
// ---- 空白区 = 挂根落点(UE Outliner 拖到空白 = 顶层)----
|
||||
// ---- 空白区 = 挂根落点(UE Outliner 拖到空白 = 顶层);
|
||||
// Prefab 拖入 = 实例化到场景根(授权位姿)----
|
||||
ImVec2 rem = ImGui::GetContentRegionAvail();
|
||||
if (rem.y > 4.0f) {
|
||||
ImGui::InvisibleButton("##hier_drop_root", rem);
|
||||
@@ -206,6 +265,14 @@ void HierarchyPanel::Draw(const EditorContext& ctx, entt::entity& selected)
|
||||
ctx.scene->SetParent(static_cast<entt::entity>(bits),
|
||||
entt::null);
|
||||
}
|
||||
if (const ImGuiPayload* pl =
|
||||
ImGui::AcceptDragDropPayload(kPrefabDragPayload)) {
|
||||
if (const entt::entity root = InstantiatePrefabDrop(
|
||||
ctx, static_cast<const char*>(pl->Data));
|
||||
root != entt::null) {
|
||||
selected = root;
|
||||
}
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user