# Unity Graph Parser **Repository Path**: TYL1226/unity-graph-parser ## Basic Information - **Project Name**: Unity Graph Parser - **Description**: No description available - **Primary Language**: Unknown - **License**: MulanPSL-1.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-10 - **Last Updated**: 2026-09-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # unity-graph-parser > **Version** 2.0.0 · **Language** Rust (edition 2021) · **License** Mulan PSL v1 (see [`LICENSE`](LICENSE)) > > **语言 / Language:** [简体中文](#中文简介) · English (this document) Turns a Unity project into a **queryable dependency graph** and gives both humans and AI agents a disciplined way to ask questions about it: *what uses this? what breaks if I change it? which assets are dead?* It scans `.meta` files to build a GUID → path map, parses 30+ Unity asset formats (`.prefab`, `.unity`, `.mat`, `.controller`, `.anim`, `.asset`, `.playable`, `.shader`, `.spriteatlas`, `.preset`, `.physicMaterial`, `.cs`, `.inputactions`, `.asmdef`, `.asmref`, `manifest.json`, …), and extracts every reference into a **bidirectional** graph with typed, semantically named edges. Pure Rust — no C compiler, no cmake, no system libraries. ``` parse ─► graph.json ─► structural queries (expand / depends / affects / closure) │ └─► chunks.jsonl ─► vector store ─► semantic recall ─► verify back ``` --- ## 中文简介 **unity-graph-parser** 把 Unity 工程解析成一张**可查询的依赖图**,让人和 AI Agent 都能 可靠地回答这类问题:*谁在用它?改了这个会影响什么?哪些资源已经没人引用了?* Unity 真实的依赖关系藏在序列化 YAML 和 GUID 引用里,用 grep 基本查不出来。本工具把这层 关系显式化、稳定化,并在其上叠加两层专为 Agent 设计的能力: - **本体层**——53 种边类型映射为带命名空间的谓词(`ugp:usesMaterial`、`ugp:runsShader`…), 带 domain/range 约束与逆关系,消除自然语言歧义; - **推理层**——结论都带**证明链**(`predicates` + `nodeId` 路径),可复核,而不只能相信。 ### 核心能力 | 能力 | 说明 | |---|---| | 解析 | 并行解析 30+ Unity 资产格式(`rayon` 加速);增量模式(SHA-256 变更检测)+ 二进制缓存 | | 图质量 | 每个节点内嵌**双向引用**(`references` / `referencedBy`);被引用但无专用解析器的资产自动补为合成节点,使 `issues` 只反映真实问题;非 UTF-8 文件(GBK/ANSI)容错读取 | | 确定性 | 排序后去重,导出去掉时间戳后**逐字节可复现**,由回归脚本守护 | | 查询 | 20+ 查询命令,支持 `--query-json`;`--expand` / `--subgraph` 给出带谓词标注的邻域切片 | | 本体导出 | `--relations` 输出 schema + 中英双语词典 + RDF N-Triples | | 关系校验 | range 检查**默认开启**(写入 `graph.relationViolations`,干净工程零变化);`--strict-relations=all` 追加源端 domain 深度检查 | | 推理 | `--closure` / `--affects` / `--depends`,每条命中自带证明链 | | 检索 | `--chunks` 产出确定性检索块;`unity-graph-lance` 提供 LanceDB 双向量器(离线哈希 1024 维 / 真语义 384 维多语言) | | Agent 集成 | 打包 `unity-graph-agent` 技能,适配 Claude Code、Codex、Trae、Pi agent、DeepSeek Harness | ### 快速开始 ```powershell # 1. 解析工程 → graph.json unity-graph path/to/UnityProject -o graph.json # 2. 查询(无需重新解析) unity-graph --load graph.json --stats-query unity-graph --load graph.json --find Hero --query-json unity-graph --load graph.json --expand "mat://Assets/Materials/Hero.mat" unity-graph --load graph.json --depends "script://Assets/Scripts/Player.cs" --depth 3 # 3. 本体导出与推理 unity-graph --load graph.json --relations rel/ # schema + 中英词典 + RDF N-Triples unity-graph --load graph.json --affects prefab://Assets/.../GameLevel.prefab unity-graph --load graph.json --closure mat://Assets/.../Fog.mat --dir in --depth 4 # 4. 语义检索(Python 负责嵌入,Rust 负责 LanceDB 存储与 ANN 检索) unity-graph rpg-game --chunks out-rpg/chunks.jsonl python scripts/lance_semantic_export.py out-rpg/chunks.jsonl out-rpg/sem-chunks.jsonl --force unity-graph-lance ingest-semantic out-rpg/sem-chunks.jsonl .lance-db python scripts/lance_semantic_query.py "角色怎么移动和操作" --db .lance-db ` --rust-bridge unity-graph-lance --out-qvec query.vec.json --top-k 5 ``` ### 实测数据(真实工程 rpg-game) | 指标 | 结果 | |---|---| | 图规模 | 8206 节点 / 12000 边 / 0 issues / 817 个 GUID | | 解析耗时 | 约 2.9 秒(release) | | 语义嵌入 | 8206 块 × 384 维多语言模型,约 107 秒 | | 语义召回 | 「角色怎么移动和操作」→ `PlayerMovement`;「dissolve VFX shader material」→ `DissolveVFX`(前二) | | 查询延迟 | 约 0.1 秒(含 `--type` / `--path` 过滤) | ### 目录结构 ``` src/ 图构建、提取器、查询引擎、本体(relations.rs)、闭包推理(closure.rs) lance-bridge/ unity-graph-lance:LanceDB 桥(哈希 + 语义两套向量器) scripts/ 回归守护、语义导入/查询、N-Triples 校验、技能同步 docs/ 设计文档、lint 报告、演示报告、回归基线 .agents/skills/ unity-graph-agent 技能(Codex / Pi / DSH 共读) .claude/ .trae/ .pi/ Claude Code、Trae、Pi 的适配产物 ``` ### 中文文档索引 - `docs/agent-integration.md` —— 多 Harness 技能集成与适配矩阵 - `docs/semantic-merge-rust-bridge.md` —— 语义向量并入 Rust 桥 - `docs/semantic-qa-demo-semantic.md` —— 语义问答演示(含证明链) - `docs/relations-semantics-design.md` —— 关系语义层设计 - `docs/relations-lint-report-rpg.md` —— 真实工程 lint 报告 - `docs/task-*.md` —— 确定性导出、domain 噪声、RDF 校验等任务报告 - `TASKS.md` —— 任务清单与验收状态 > 英文完整文档见下方,可从 [Why it exists](#why-it-exists) 继续阅读。 --- ## Why it exists Unity keeps its real dependency information inside serialized YAML and GUID references, so "what does this prefab actually depend on?" is not answerable with grep. This tool makes that graph explicit, stable, and cheap to query — then layers two things on top that matter for agents: - an **ontology** that turns 53 edge types into unambiguous predicates (`ugp:usesMaterial`, `ugp:runsShader`, …) with type constraints and inverses; - a **reasoning layer** whose answers carry proof paths (`predicates` + `nodeId` chain), so a conclusion can be re-verified instead of trusted. ## Features **Parsing and graph quality** - Parallel parsing with `rayon` (GUID map + per-file extraction). - **Bidirectional references** embedded in every node (`references` outgoing, `referencedBy` incoming, each with target name/path/type and via-field). - **Complete graph**: assets that are referenced but have no dedicated extractor (textures, FBX models, Shader Graphs, audio…) become lightweight *synthetic* nodes, so `issues` reports real problems instead of thousands of false "dangling target" entries. - **Robust text handling**: non-UTF-8 files (GBK/ANSI, common in CJK projects) are decoded lossily rather than silently dropped. - **Deterministic exports**: node/edge dedupe and AssetBundle synthesis are sort-then-dedupe, so repeated runs produce byte-identical output apart from the `generatedAt` timestamp (enforced by the regression guard). - AssetBundle tracking from `assetBundleName` / `assetBundleVariant`. - Incremental mode (SHA-256 change detection) and a binary cache (bincode). **Query and reasoning** - 20+ query commands; the read/query commands support `--query-json` for structured output (the export commands `--chunks` / `--relations` write files). - `--expand` / `--subgraph`: bounded, token-efficient neighbourhood reads with `predicate` annotations and highlight flags. - `--closure` / `--affects` / `--depends`: bounded graph closure over the ontology, returning `predicates` + `path` evidence per hit. - `--relations`: export the ontology schema, a bilingual (en/zh) lexicon, and RDF N-Triples instance assertions. **Retrieval and agent integration** - `--chunks`: deterministic per-node text chunks keyed by stable `nodeId`, ready to embed. - `unity-graph-lance`: LanceDB bridge with two vectorizers — an offline deterministic hash vectorizer, and real sentence-transformer vectors for cross-language / paraphrase recall. - A packaged **agent skill** (`unity-graph-agent`) adapted for Claude Code, OpenAI Codex, Trae, Pi agent, and DeepSeek Harness. --- ## Requirements | | | |---|---| | Rust | stable toolchain (edition 2021) | | OS | Windows, macOS, Linux (scripts are written for PowerShell) | | Optional | `protoc` on `PATH` (or `$env:PROTOC`) to build `unity-graph-lance` | | Optional | Python 3.10+ with `sentence-transformers` + `lancedb` for the semantic path | ## Build ```powershell cargo build --release # binary: target/release/unity-graph cargo test # unit tests # LanceDB bridge (needs protoc) $env:PROTOC = "path\to\protoc.exe" cargo build --release -p unity-graph-lance ``` If `cargo fetch` is unreliable on your network, set `$env:CARGO_HTTP_MULTIPLEXING = 'false'`. ## Quick start ```powershell # 1. Parse a project → graph.json unity-graph path/to/UnityProject -o graph.json # 2. Explore without re-parsing unity-graph --load graph.json --stats-query unity-graph --load graph.json --find Hero --query-json unity-graph --load graph.json --expand "mat://Assets/Materials/Hero.mat" unity-graph --load graph.json --depends "script://Assets/Scripts/Player.cs" --depth 3 # 3. Multiple export formats in one pass unity-graph path/to/UnityProject -o out/graph -f json,dot,mermaid,code-graph,neo4j ``` --- ## CLI reference ### Parse options | Option | Default | Description | |---|---|---| | `root` | `.` | Unity project root | | `-o, --output` | `graph.json` | Output path | | `-f, --format` | `json` | `json,dot,mermaid,code-graph,neo4j` (comma separated) | | `-t, --types` | `all` | Parse types | | `-i, --ignore` | `**/bak/**,**/Library/**,**/Temp/**` | Ignore globs | | `-c, --concurrency` | `10` | Worker threads | | `--incremental` | `false` | Re-parse only changed files | | `--force-full` | `false` | Ignore cached results | | `--cache ` | `.unity-graph-cache.db` | Cache file | | `--lean` | `false` | Skip embedding per-node reference lists (smaller exports) | | `--load ` | — | Load an exported graph and skip parsing | | `--verbose` / `--dry-run` / `--stats` | `false` | Diagnostics | ### Query commands | Command | Description | |---|---| | `--find ` | Search nodes by name (relevance ranked) | | `--type ` / `--path

` / `--guid ` | Filters for `--find` | | `--inspect ` | Full node inspection | | `--expand ` | One-hop bidirectional neighbourhood | | `--subgraph ` | Bounded bidirectional slice (`--depth`, 600-node cap) | | `--references ` / `--referenced-by ` | One-direction chains | | `--chain ` | Shortest dependency path (BFS, max depth 12) | | `--hierarchy ` | Transform hierarchy tree | | `--components ` | Components with serialized fields | | `--bundle ` | AssetBundle contents | | `--stats-query` | Aggregate statistics (includes `relation viols`, broken refs) | | `--orphans` | Nodes with no edges | | `--top-referenced [N]` / `--top-referencing [N]` | Reference hot spots | | `--diff ` | Diff the loaded graph against an earlier export | | `--depth ` | Chain/closure depth (default 3) | | `--query-json` | Emit structured JSON | ### Ontology, lint and reasoning | Command | Description | |---|---| | `--relations

` | Write ontology schema + en/zh lexicon + `relations.nt` | | `--strict-relations` | Echo the default range-lint count | | `--strict-relations=all` | Add source-side (domain) deep checks | | `--closure ` | Closure with evidence (`--pred` / `--group` / `--dir`) | | `--affects ` | Forward usage/containment closure | | `--depends ` | Reverse closure — who uses this | | `--pred ` / `--group ` / `--dir ` | Closure filters | | `--chunks ` | Write retrieval chunks (JSONL, keyed by `nodeId`) | --- ## Node ID conventions Node ids are stable across rebuilds and are the join key between the graph and any vector store. | Kind | Format | |---|---| | Prefab / Scene | `prefab://Assets/Prefabs/Hero.prefab`, `scene://…` | | GameObject / Component | `go://Assets/Prefabs/Hero.prefab#100000`, `comp://…#100000` | | Material / Shader / Clip | `mat://…`, `shader://…`, `shader_graph://…`, `anim://…` | | Script / Assembly | `script://Assets/Scripts/Player.cs`, `asmdef://…`, `asmref://…` | | AssetBundle / Package | `asset_bundle://characters`, `package://com.unity.…` | | Input / Timeline | `input_actions://…`, `playable://…`, `state://…` | A `#fileId` suffix distinguishes sub-objects of the same file. --- ## Relations semantics (ontology · lint · reasoning) Every edge type is bound to a namespaced predicate with domain/range, an inverse and transitivity flags. `src/relations.rs` is the single source of truth, and a unit test fails if an `EdgeType` lacks an ontology row — the table cannot silently drift. ```powershell # Export ontology + bilingual lexicon + RDF triples unity-graph --load graph.json --relations rel/ # rel/relations.schema.json machine-readable ontology # rel/relations.en.json English lexicon for agents (~40 KB) # rel/relations.zh.json Chinese lexicon # rel/relations.lexicon.md human-readable reference # rel/relations.nt N-Triples instance assertions # Range checks run BY DEFAULT and land in graph.relationViolations. # Clean projects serialize identically (empty field is omitted). unity-graph rpg-game --strict-relations=all # explicit source-side deep check # Reasoning — results are derived, never written back into the graph unity-graph --load graph.json --affects prefab://Assets/.../GameLevel.prefab unity-graph --load graph.json --depends script://Assets/.../Player.cs --depth 3 unity-graph --load graph.json --closure mat://Assets/.../Fog.mat --dir in --depth 4 unity-graph --load graph.json --closure --pred ugp:usesMaterial ``` Closure output carries evidence per hit: ``` d1 [ugp:usesScript] comp://Assets/…/GameLevel.prefab#6821645571629340619 (path: comp://Assets/…/GameLevel.prefab#6821645571629340619) ``` `relations.nt` was validated by loading it into Python `rdflib` — 12 000 triples parsed for the rpg-game project, SPARQL queries runnable, Turtle round-trip lossless (`docs/rdf-validation-report.md`). --- ## Hybrid retrieval ### 1. Chunks (the stable join surface) ```powershell unity-graph rpg-game -o graph.json --chunks chunks.jsonl ``` One line per node: `nodeId`, `type`, `name`, `filePath`, `text`. The text is deterministic and self-describing (type/name/file, curated properties, outbound and inbound neighbours with direction-aware phrasing), so embeddings stay comparable across rebuilds and every hit resolves back to a node id. ### 2. LanceDB bridge — two vectorizers ```powershell # ---- offline hash vectorizer (deterministic, 1024-dim) ---- unity-graph-lance ingest graph.json .lance-db unity-graph-lance query .lance-db "fog material shader graph" --limit 6 unity-graph-lance query .lance-db "culling component tool" --type csharp_script # ---- semantic vectorizer (real sentence-transformers, 384-dim) ---- # Python embeds; the Rust bridge owns LanceDB storage + the ANN index. unity-graph rpg-game --chunks out-rpg/chunks.jsonl python scripts/lance_semantic_export.py out-rpg/chunks.jsonl out-rpg/sem-chunks.jsonl --force unity-graph-lance ingest-semantic out-rpg/sem-chunks.jsonl .lance-db python scripts/lance_semantic_query.py "角色怎么移动和操作" --db .lance-db ` --rust-bridge unity-graph-lance --out-qvec query.vec.json --top-k 5 # ---- verify any hit against the authoritative graph ---- unity-graph --load graph.json --expand unity-graph --load graph.json --depends --depth 3 ``` The semantic table adds a `model` column and uses the embedder's native dimension (`paraphrase-multilingual-MiniLM-L12-v2` → 384). The model name is mirrored to `/ingest-meta.json` so query and ingest cannot silently diverge. **Measured on the rpg-game project** (8 206 nodes / 12 000 edges): | Step | Result | |---|---| | Parse (release) | ~2.9 s | | Hash ingest (8 206 chunks, 1024-dim) | ~3.3 s | | Query latency | ~0.1 s, with `--type` / `--path` filters | | Semantic embed (8 206 chunks, 384-dim) | ~107 s, then writes in well under a second | | Semantic recall | 「角色怎么移动和操作」→ `PlayerMovement`; 「dissolve VFX shader material」→ `DissolveVFX` (top 2); 「culling tool that hides far objects」→ `G_CullManager` | The hash vectorizer is lexical, so a pure-Chinese query returns `no results` there — the semantic path is what buys cross-language and paraphrase recall. See `docs/semantic-merge-rust-bridge.md` and `docs/semantic-qa-demo-semantic.md`. --- ## Agent integration The Unity asset workflow ships as a skill, **`unity-graph-agent`**, with one canonical source and generated per-harness adapters: | File | Harness | Status | |---|---|---| | `.agents/skills/unity-graph-agent/SKILL.md` | DeepSeek Harness, Codex, Pi | canonical (hand-written) | | `.agents/skills/unity-graph-agent/agents/openai.yaml` | Codex | hand-written metadata | | `.claude/skills/unity-graph-agent/SKILL.md` | Claude Code | generated | | `.trae/skills/unity-graph-agent.md` | Trae | generated | | `.trae/rules/unity-graph-agent.md` | Trae | always-on rule | | `.pi/settings.json` | Pi | enables `/skill:` commands | | `AGENTS.md` / `CLAUDE.md` | Codex / Claude Code | repo-level entry points | Keep them in sync: ```powershell pwsh scripts/sync_agent_skills.ps1 # regenerate adapters pwsh scripts/sync_agent_skills.ps1 -Check # CI: exit 1 on drift ``` The skill encodes four rules that matter more than any single command: 1. **Never read `graph.json` into context** — recall semantic candidates, then verify structurally. 2. **Never invent a `ugp:` predicate** — export the ontology and use declared predicates only. 3. **Every claim cites evidence** — `nodeId` + predicate + path, or an explicit "no evidence found". 4. **Reuse the cached graph** — re-parse only after sources change. Full harness matrix and portability notes: `docs/agent-integration.md`. --- ## Architecture ``` src/ ├── main.rs CLI entry point ├── lib.rs library root (version, time helpers) ├── model.rs core model: RichNode, Graph, NodeType, EdgeType, phrases ├── relations.rs ontology: predicate ⇄ EdgeType, domain/range, inverses ├── closure.rs bounded reasoning closure with proof paths ├── yaml.rs Unity YAML parser (lossy UTF-8 tolerant) ├── project.rs Unity project detection ├── guid_map.rs parallel GUID map builder ├── cache.rs incremental binary cache (bincode + SHA-256) ├── graph.rs graph builder: dedupe, validation, cycles, bundles, refs ├── query.rs query engine + display impls ├── cli.rs argument parsing and main flow ├── parsers/ asmdef / asmref / manifest / shader text parsers ├── extractors/ prefab, scene, material, controller, animation, asset, │ playable, sprite_atlas, preset, physic_material, csharp, │ input_actions └── export/ json, dot, mermaid, code_graph, neo4j, chunks, relations lance-bridge/ unity-graph-lance: LanceDB Rust SDK bridge scripts/ regression guard, semantic export/query, NT validation, skill sync docs/ design docs, lint reports, demo reports, regression baselines .agents/skills/ canonical agent skill (shared by Codex / Pi / DSH) ``` --- ## Testing and regression The repository ships a synthetic Unity project in `test-fixture/` and is validated end-to-end against the real project at `E:\Project\DeepseekHernessProject\rpg-game`. ```powershell cargo test # 7 unit tests pwsh scripts/regression.ps1 -Project all # fixture + rpg-game # deliberately refresh baselines after an intended output change pwsh scripts/regression.ps1 -Project all -Refresh ``` Current baselines (default outputs must not move): | Project | Nodes | Edges | Issues | Relation violations | |---|---|---|---|---| | `test-fixture` | 44 | 42 | 0 | 1 (intentional model defect, kept as a lint demo) | | `rpg-game` | 8 206 | 12 000 | 0 | 0 | The guard hashes every exported file after stripping the `generatedAt` timestamp, so an unintended change to the default output fails the run. For debugging a JSON difference, `scripts/json_diff.py` reports the first structurally differing path. --- ## Documentation | Document | Contents | |---|---| | `docs/relations-semantics-design.md` | Ontology design and negative-effect avoidance | | `docs/relations-semantics-impl-spec.md` | Per-task implementation spec | | `docs/relations-lint-report-rpg.md` | Real-project lint findings and ontology fixes | | `docs/agent-integration.md` | Multi-harness skill matrix and portability | | `docs/semantic-merge-rust-bridge.md` | Semantic vectors in the Rust bridge | | `docs/semantic-qa-demo-semantic.md` | Worked semantic Q&A with proof chains | | `docs/rdf-validation-report.md` | `relations.nt` loaded into rdflib + SPARQL | | `docs/task-*.md` | Determinism, domain-noise and RDF/QA task reports | | `CHANGELOG.md` | Release notes | | `TASKS.md` | Task list with acceptance status | | `Unity Graph Parser — 完整开发文档.md` | Original full design document | --- ## Known limitations - **Semantic search needs Python.** The Rust bridge stores and searches vectors, but the embedding model runs in Python. There is no in-process Rust embedder. - **No ANN index on the semantic table** — LanceDB's Rust `create_index` call is skipped due to an API mismatch, so semantic search is brute-force cosine. Fine at ~8 k chunks; revisit for much larger projects. - **`--strict-relations=all` is a deep audit, not a default.** Domain checks are calibrated for this project family; on an unfamiliar codebase expect to review findings before treating them as defects. - **Script parsing is regex-based**, not a C# compiler — field extraction can miss unusual formatting. - **The default ignore pattern `**/Temp/**` also matches an OS temp directory.** Checking a project out under `…/Temp/…` — or parsing any project whose parent folder is named `Temp` — silently yields a near-empty graph (`0 guids indexed`), because the `.meta` scan is skipped. Pass explicit patterns with `-i` in that situation. - Trae and Claude Code skill layouts follow the documented community/standard conventions; if your harness version differs, adjust the generated copies. ## License **木兰宽松许可证,第 1 版**(Mulan Permissive Software License v1, SPDX: `MulanPSL-1.0`)— full text in [`LICENSE`](LICENSE), and declared in `Cargo.toml` as `license = "MulanPSL-1.0"`. It is a permissive license: use, modify and redistribute freely, including commercially, provided the copyright and license notices are retained and modifications are noted. See for the canonical text. > Note: the repository was originally documented as MIT. If MIT is the intended > license, replace `LICENSE` with the MIT text and set > `license = "MIT"` in `Cargo.toml` — the two must agree.