feat(editor): multi-script entities + asset browser panel
ScriptComponent now holds a list of {name, active} items (Unity
semantics: one entity, many scripts, each with its own enable bit;
component-level active stays as the master switch). ScriptRuntime
reconciles instances by name per entity -- rename/remove destroys
only the affected instances, untouched ones keep ticking. Scenes
persist Script.Items; old single-script key still loads.
AssetBrowserPanel (lightweight Unity Project window): fixed roots
(assets/scenes) as tabs, recursive directory tree, file list with
type filter (models/textures/scenes) and name substring filter,
double-click preloads models/HDRs into the asset caches (path-
deduped). Scene files point to the File menu for now; drag & drop
is a later iteration. Docked with Stats by default in fresh layouts.
This commit is contained in:
@@ -566,37 +566,58 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
||||
{
|
||||
ImGui::BeginDisabled(true);
|
||||
}
|
||||
// 下拉列出全部已注册脚本(注册序);"<none>" = 清空(对账自动销毁实例)
|
||||
// 多脚本列表(Unity 语义):每行 = 脚本下拉 + 独立启停 + 删除;
|
||||
// 实例生命周期由 ScriptRuntime 对账(删行/改名 → 下帧 OnDestroy)
|
||||
const auto& names = Engine::Scripting::ScriptRegistry::Names();
|
||||
std::vector<const char*> items;
|
||||
items.push_back("<none>");
|
||||
std::vector<const char*> options;
|
||||
options.push_back("<none>");
|
||||
for (const auto& n : names)
|
||||
{
|
||||
items.push_back(n.c_str());
|
||||
options.push_back(n.c_str());
|
||||
}
|
||||
int cur = 0;
|
||||
for (size_t i = 0; i < names.size(); ++i)
|
||||
int removeIdx = -1;
|
||||
for (int i = 0; i < static_cast<int>(sc->items.size()); ++i)
|
||||
{
|
||||
if (names[i] == sc->scriptName)
|
||||
auto& item = sc->items[static_cast<size_t>(i)];
|
||||
ImGui::PushID(i);
|
||||
int cur = 0;
|
||||
for (size_t k = 0; k < names.size(); ++k)
|
||||
{
|
||||
cur = static_cast<int>(i) + 1;
|
||||
if (names[k] == item.name)
|
||||
{
|
||||
cur = static_cast<int>(k) + 1;
|
||||
}
|
||||
}
|
||||
ImGui::SetNextItemWidth(
|
||||
ImGui::GetContentRegionAvail().x - 64.0f);
|
||||
if (ImGui::Combo("##script", &cur, options.data(),
|
||||
static_cast<int>(options.size())))
|
||||
{
|
||||
item.name = (cur <= 0) ? "" : names[static_cast<size_t>(cur) - 1];
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("##en", &item.active);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("-", ImVec2(24.0f, 0.0f)))
|
||||
{
|
||||
removeIdx = i;
|
||||
}
|
||||
ImGui::PopID();
|
||||
// 已填名但注册表查无此类:场景加载早于脚本注册/改名都会走到这
|
||||
if (!item.name.empty() && cur == 0)
|
||||
{
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.2f, 1.0f),
|
||||
"! not registered: %s",
|
||||
item.name.c_str());
|
||||
}
|
||||
}
|
||||
if (ImGui::Combo("script", &cur, items.data(),
|
||||
static_cast<int>(items.size())))
|
||||
if (removeIdx >= 0)
|
||||
{
|
||||
sc->scriptName = (cur <= 0) ? "" : names[static_cast<size_t>(cur) - 1];
|
||||
sc->items.erase(sc->items.begin() + removeIdx);
|
||||
}
|
||||
if (sc->scriptName.empty())
|
||||
if (ImGui::Button("+ Add Script"))
|
||||
{
|
||||
ImGui::TextDisabled("no script assigned");
|
||||
}
|
||||
// 已填名但注册表查无此类:场景加载早于脚本注册/改名都会走到这
|
||||
else if (cur == 0)
|
||||
{
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.2f, 1.0f),
|
||||
"! script not registered: %s",
|
||||
sc->scriptName.c_str());
|
||||
sc->items.push_back({});
|
||||
}
|
||||
if (!sc->active)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user