feat: establish mech extraction prototype with bilingual UI

Track Unity source, scenes, editable assets, licenses, tools and QA evidence. Exclude generated caches, Windows builds and raw runtime logs.
This commit is contained in:
2026-11-12 12:28:40 +08:00
commit 92623a1bc8
256 changed files with 11842 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
[{"tool":"manage_build","arguments":{"action":"build","target":"windows64","output_path":"D:/vibeCoding/unityRouge/Builds/Windows/x64/Prototype/Ironspore.exe","scenes":"Assets/Scenes/Bootstrap.unity,Assets/Scenes/MechTestRange.unity,Assets/Scenes/WarzonePrototype.unity","development":"true","scripting_backend":"mono"}}]
+1
View File
@@ -0,0 +1 @@
[{"tool":"manage_build","arguments":{"action":"status"}}]
+1
View File
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"UnityEngine.Application.runInBackground = true; UnityEditor.EditorWindow.GetWindow(System.Type.GetType(\"UnityEditor.GameView,UnityEditor\")).Focus(); return MCPForUnity.Editor.Tools.ManageScene.HandleCommand(Newtonsoft.Json.Linq.JObject.Parse(\"{\\\"action\\\":\\\"screenshot\\\",\\\"fileName\\\":\\\"hangar.png\\\",\\\"outputFolder\\\":\\\"Docs/QA/Prototype/Screenshots\\\"}\"));"}}]
+4
View File
@@ -0,0 +1,4 @@
[
{"tool":"manage_packages","arguments":{"action":"get_package_info","package":"com.unity.inputsystem"}},
{"tool":"refresh_unity","arguments":{"mode":"force","scope":"all","compile":"request","wait_for_ready":false}}
]
+1
View File
@@ -0,0 +1 @@
[{"tool":"read_console","arguments":{"action":"get","types":["error","warning"],"count":15,"include_stacktrace":false}}]
+4
View File
@@ -0,0 +1,4 @@
[
{"tool":"manage_scene","arguments":{"action":"get_active"}},
{"tool":"manage_packages","arguments":{"action":"add_package","package":"[email protected]"}}
]
+4
View File
@@ -0,0 +1,4 @@
[
{"tool":"manage_editor","arguments":{"action":"stop"}},
{"tool":"execute_code","arguments":{"action":"execute","code":"var importer=UnityEditor.AssetImporter.GetAtPath(\"Assets/Game/Art/Models/Mechs/Mech_Scout_A.fbx\") as UnityEditor.ModelImporter; importer.globalScale=100; importer.SaveAndReimport(); UnityEditor.AssetDatabase.ImportAsset(\"Assets/Game/UI/GameTheme.tss\",UnityEditor.ImportAssetOptions.ForceUpdate); var theme=UnityEditor.AssetDatabase.LoadMainAssetAtPath(\"Assets/Game/UI/GameTheme.tss\"); return new { themeType=theme==null ? \"null\" : theme.GetType().FullName, input=UnityEditor.AssetDatabase.LoadMainAssetAtPath(\"Assets/Game/Input/GameInput.inputactions\").GetType().FullName };"}}
]
+1
View File
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"var result = Ironspore.Editor.PrototypeBuilder.Generate(); var assets = UnityEditor.AssetDatabase.LoadAllAssetsAtPath(\"ProjectSettings/ProjectSettings.asset\"); var so = new UnityEditor.SerializedObject(assets[0]); var prop = so.FindProperty(\"activeInputHandler\"); var before = prop.intValue; prop.intValue = 2; so.ApplyModifiedPropertiesWithoutUndo(); UnityEditor.AssetDatabase.SaveAssets(); return new { result = result, inputBackendBefore = before, inputBackendAfter = prop.intValue, model = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>(\"Assets/Game/Prefabs/Mechs/Mech_Scout_A.prefab\").GetComponentsInChildren<UnityEngine.Renderer>().Length };"}}]
+1
View File
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"var model=UnityEngine.GameObject.Find(\"HangarChassis_1\"); var renderers=model.GetComponentsInChildren<UnityEngine.Renderer>(); var b=new UnityEngine.Bounds(model.transform.position,UnityEngine.Vector3.zero); foreach(var r in renderers)b.Encapsulate(r.bounds); var g=Ironspore.GameSession.Active; return new { bounds=b.ToString(), rootScale=model.transform.localScale.ToString(), first=renderers[0].transform.lossyScale.ToString(), firstCenter=renderers[0].bounds.center.ToString(), theme=g.uiPanel!=null, cameras=UnityEngine.Object.FindObjectsByType<UnityEngine.Camera>().Length };"}}]
+1
View File
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"var d=Ironspore.GameSession.Active.GetComponent<UnityEngine.UIElements.UIDocument>(); var e=UnityEngine.UIElements.UQueryExtensions.Q<UnityEngine.UIElements.VisualElement>(d.rootVisualElement,null,\"crosshair\"); return new { bounds=e.worldBound.ToString(), parent=e.parent.worldBound.ToString(), visible=e.visible, childBounds=e[0].worldBound.ToString(), color=e[0].resolvedStyle.backgroundColor.ToString() };"}}]
@@ -0,0 +1 @@
[{"tool":"manage_build","arguments":{"action":"build","target":"windows64","output_path":"D:/vibeCoding/unityRouge/Builds/Windows/x64/Localization/Ironspore.exe","scenes":"Assets/Scenes/Bootstrap.unity,Assets/Scenes/MechTestRange.unity,Assets/Scenes/WarzonePrototype.unity","development":"true","scripting_backend":"mono"}}]
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"UnityEngine.Application.runInBackground = true; UnityEditor.EditorWindow.GetWindow(System.Type.GetType(\"UnityEditor.GameView,UnityEditor\")).Focus(); return MCPForUnity.Editor.Tools.ManageScene.HandleCommand(Newtonsoft.Json.Linq.JObject.Parse(\"{\\\"action\\\":\\\"screenshot\\\",\\\"fileName\\\":\\\"hangar-initial.png\\\",\\\"outputFolder\\\":\\\"Docs/QA/Localization/Screenshots\\\"}\"));"}}]
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"return Ironspore.Editor.LocalizationBuilder.Configure();"}}]
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"return new { methods = typeof(UnityEngine.TextCore.Text.FontAsset).GetMethods().Where(x => x.Name == \"CreateFontAsset\" || x.Name == \"TryAddCharacters\").Select(x => x.ToString()).ToArray(), properties = typeof(UnityEngine.UIElements.PanelTextSettings).GetProperties().Select(x => x.ToString()).ToArray(), weights = typeof(UnityEngine.TextCore.Text.FontAsset).GetProperties().Where(x => x.Name.Contains(\"Weight\") || x.Name.Contains(\"Atlas\") || x.Name.Contains(\"sourceFont\")).Select(x => x.ToString()).ToArray(), font = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Font>(\"Assets/ThirdParty/Adobe/NotoSansSC/NotoSansSC-VF.ttf\") != null };"}}]
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"var asm = typeof(UnityEditor.Editor).Assembly; var type = asm.GetType(\"UnityEditor.GameView\"); var view = UnityEditor.EditorWindow.GetWindow(type); var flags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static; var enumType = asm.GetType(\"UnityEditor.GameViewSizeGroupType\"); var values = System.Enum.GetValues(enumType).Cast<object>().Select(x => x.ToString() + \"=\" + (int)x).ToArray(); var groups = type.GetProperties(flags).Where(x => x.Name.ToLower().Contains(\"group\")).Select(x => x.Name + \"=\" + x.GetValue(view, null)).ToArray(); return string.Join(\"; \", values) + \" | \" + string.Join(\"; \", groups) + \" | render=\" + type.GetProperty(\"targetRenderSize\",flags).GetValue(view,null) + \" | selected=\" + type.GetProperty(\"selectedSizeIndex\",flags).GetValue(view,null) + \" | screen=\" + UnityEngine.Screen.width + \"x\" + UnityEngine.Screen.height;"}}]
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"var asm = typeof(UnityEditor.Editor).Assembly; var sizes = asm.GetType(\"UnityEditor.GameViewSizes\"); var singleton = typeof(UnityEditor.ScriptableSingleton<>).MakeGenericType(sizes); var instance = singleton.GetProperty(\"instance\").GetValue(null, null); var group = sizes.GetMethod(\"GetGroup\").Invoke(instance, new object[] { System.Enum.ToObject(asm.GetType(\"UnityEditor.GameViewSizeGroupType\"), 0) }); return new { sizeConstructors = asm.GetType(\"UnityEditor.GameViewSize\").GetConstructors(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Select(x => x.ToString()).ToArray(), groupMethods = group.GetType().GetMethods().Select(x => x.ToString()).Where(x => x.Contains(\"Size\")).ToArray(), sizeProperties = typeof(UnityEditor.EditorWindow).Assembly.GetType(\"UnityEditor.GameView\").GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).Where(x => x.Name.Contains(\"Size\")).Select(x => x.ToString()).ToArray() };"}}]
@@ -0,0 +1 @@
[{"tool":"manage_packages","arguments":{"action":"add_package","package":"[email protected]"}}]
@@ -0,0 +1 @@
[{"tool":"manage_packages","arguments":{"action":"status","job_id":"$package"}}]
@@ -0,0 +1,4 @@
[
{"tool":"manage_editor","arguments":{"action":"stop"}},
{"tool":"manage_packages","arguments":{"action":"search_packages","query":"com.unity.localization"}}
]
@@ -0,0 +1 @@
[{"tool":"refresh_unity","arguments":{"mode":"force","scope":"all","compile":"request","wait_for_ready":true}}]
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"return Ironspore.Editor.LocalizationSmoke.Begin();"}}]
+4
View File
@@ -0,0 +1,4 @@
[
{"tool":"read_console","arguments":{"action":"clear"}},
{"tool":"manage_editor","arguments":{"action":"play"}}
]
+1
View File
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"return Ironspore.Editor.RecoverySmoke.Begin();"}}]
+4
View File
@@ -0,0 +1,4 @@
[
{"tool":"manage_editor","arguments":{"action":"stop"}},
{"tool":"refresh_unity","arguments":{"mode":"force","scope":"all","compile":"request","wait_for_ready":false}}
]
+1
View File
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"UnityEditor.AssetDatabase.SaveAssets(); UnityEditor.EditorApplication.delayCall += () => UnityEditor.EditorApplication.OpenProject(\"D:/vibeCoding/unityRouge\"); return \"Restart scheduled to activate the Input System backend.\";"}}]
+1
View File
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"return Ironspore.Editor.RouteSmoke.Validate();"}}]
+1
View File
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"return Ironspore.Editor.PrototypeSmoke.Begin();"}}]
+1
View File
@@ -0,0 +1 @@
[{"tool":"execute_code","arguments":{"action":"execute","code":"Ironspore.GameSession.Active.StartTrial(); return \"Driving range started\";"}}]
+1
View File
@@ -0,0 +1 @@
[{"tool":"manage_editor","arguments":{"action":"stop"}}]
+1
View File
@@ -0,0 +1 @@
[{"tool":"get_test_job","arguments":{"job_id":"$test","include_failed_tests":true,"include_details":true}}]
+1
View File
@@ -0,0 +1 @@
[{"tool":"run_tests","arguments":{"mode":"EditMode"}}]
+4
View File
@@ -0,0 +1,4 @@
[
{"tool":"read_console","arguments":{"action":"get","types":["error","warning"],"count":10,"include_stacktrace":false}},
{"tool":"execute_code","arguments":{"action":"execute","code":"return MCPForUnity.Editor.Tools.ManageScene.HandleCommand(Newtonsoft.Json.Linq.JObject.Parse(\"{\\\"action\\\":\\\"screenshot\\\",\\\"fileName\\\":\\\"view.png\\\",\\\"outputFolder\\\":\\\"Docs/QA/Prototype/Screenshots\\\"}\"));"}}
]
+88
View File
@@ -0,0 +1,88 @@
"""Call the installed Unity MCP server through the standard MCP SDK.
Run with the Python environment belonging to the installed MCP for Unity server.
Scene mutations and builds stay inside the Unity MCP tools.
"""
import argparse
import asyncio
import json
import sys
from pathlib import Path
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
ROOT = Path(__file__).resolve().parents[2]
sys.stdout.reconfigure(encoding="utf-8")
def unpack(result):
data = getattr(result, "structuredContent", None)
if data is None:
texts = [x.text for x in result.content if x.type == "text"]
try:
data = json.loads("\n".join(texts))
except json.JSONDecodeError:
data = {"text": "\n".join(texts)}
payload = data.get("result", data) if isinstance(data, dict) else data
failed = isinstance(payload, dict) and payload.get("success") is False
return {"isError": bool(result.isError or failed), "result": data}
async def run(args):
executable = Path.home() / ".local/share/unity-mcp/.venv/Scripts/mcp-for-unity.exe"
logs = ROOT / "Logs/MCP"
logs.mkdir(parents=True, exist_ok=True)
params = StdioServerParameters(command=str(executable), args=["--transport", "stdio", "--default-instance", ROOT.name])
with (logs / "server.log").open("w", encoding="utf-8") as errlog:
async with stdio_client(params, errlog=errlog) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
project = await session.read_resource("mcpforunity://project/info")
project_text = "\n".join(x.text for x in project.contents if hasattr(x, "text"))
if str(ROOT).replace("\\", "/").lower() not in project_text.replace("\\\\", "/").replace("\\", "/").lower():
raise RuntimeError("MCP target is not the workspace: " + project_text)
if args.list:
available = await session.list_tools()
wanted = args.list.split(",")
out = [t.model_dump() for t in available.tools if t.name in wanted or args.list == "all"]
elif args.resource:
res = await session.read_resource(args.resource)
out = [x.text for x in res.contents if hasattr(x, "text")]
else:
commands = json.loads(Path(args.file).read_text(encoding="utf-8-sig"))
if isinstance(commands, dict):
commands = [commands]
out = []
jobs_path = logs / "jobs.json"
jobs = json.loads(jobs_path.read_text(encoding="utf-8")) if jobs_path.exists() else {}
for command in commands:
arguments = command.get("arguments", {})
job_reference = arguments.get("job_id", "")
if job_reference.startswith("$"):
arguments["job_id"] = jobs[job_reference[1:]]
result = unpack(await session.call_tool(command["tool"], arguments))
out.append(result)
print(json.dumps({"tool": command["tool"], **result}, ensure_ascii=False), flush=True)
payload = result["result"]
while isinstance(payload, dict) and isinstance(payload.get("result"), dict) and "data" not in payload:
payload = payload["result"]
job_id = payload.get("data", {}).get("job_id") if isinstance(payload, dict) and isinstance(payload.get("data"), dict) else None
if job_id:
category = "test" if command["tool"] in ("run_tests", "get_test_job") else "build" if command["tool"] == "manage_build" else "package"
jobs[category] = job_id
jobs_path.write_text(json.dumps(jobs), encoding="utf-8")
if result["isError"]:
break
(logs / "last-result.json").write_text(json.dumps(out, ensure_ascii=False, indent=2), encoding="utf-8")
if args.list or args.resource:
print(json.dumps(out, ensure_ascii=False, indent=2), flush=True)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--list")
parser.add_argument("--resource")
parser.add_argument("--file")
args = parser.parse_args()
asyncio.run(asyncio.wait_for(run(args), timeout=58))
+31
View File
@@ -0,0 +1,31 @@
IRONSPORE / 机甲搜打撤灰盒原型
Windows x64 · 键盘与鼠标
启动同目录的 Ironspore.exe。请保留整个文件夹,包括 Ironspore_Data、
UnityPlayer.dll、MonoBleedingEdge 和 ThirdPartyNotices。
主页右下角「语言 / Language」切换简体中文和 English,选择即时生效,
重启后保留。可用鼠标选择;列表内 Tab / Shift+Tab 切换选项,
Enter / Space 确认,Esc 或点击外部收起。
WASD 移动,鼠标瞄准;左键主武器,右键精瞄。
V 切换越肩 / 第一人称,Shift 推进,Space 跳跃 / 轻型升空。
Q 副武装,F 主动防护,E 交互,Tab 货仓,Esc 暂停。
货仓选择最多两组后,返回驾驶并原地按住 G 3 秒发送回收舱。
每次出击仅一个回收舱;整机撤离需到橙色平台呼叫运输机,等待抵达后按 E 登机。
驾驶试验场使用独立存档,可先试用底盘和配装。
Launch Ironspore.exe from this folder. Keep the complete folder together.
The bottom-right Language button switches Simplified Chinese and English instantly.
Your language choice is remembered across restarts. Tab / Shift+Tab navigates
the language list, Enter / Space confirms, and Esc or an outside click closes it.
WASD: move; mouse: aim; LMB/RMB: fire/precision aim; V: perspective;
Shift: boost; Space: jump/lift; Q: secondary; F: shield; E: interact;
Tab: cargo; hold G while standing still: send selected cargo; Esc: pause.
This is a gameplay prototype. Full combat balance, final art and performance
benchmarks are part of later development.
Font: Noto Sans SC. Copyright © 2014-2021 Adobe.
SIL Open Font License 1.1 and source metadata are in ThirdPartyNotices/NotoSansSC/.