Add prototype gameplay loop with character/battlefield assets and server game APIs.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,19 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
"""将角色 XPbr / 烘焙产物安装到 AIClient/Assets/Bundles/Characters/{ActorName}/。
|
||||
"""将角色 XPbr / 烘焙产物安装到 Bundles/Characters。
|
||||
|
||||
人物:Characters/Human/{ActorName}/
|
||||
不传 --kind 时落到 Characters/{ActorName}/(兼容 MaleExplorer)。
|
||||
|
||||
优先识别 XPbr 产物(推荐):
|
||||
{ActorName}.fbx
|
||||
{ActorName}_base.png # RGB albedo, A=AO/透明
|
||||
{ActorName}_mix.png # RG sphere-normal, B=metallic, A=roughness
|
||||
{ActorName}.mat # Test/XPbr
|
||||
{ActorName}.prefab # Unity 菜单生成
|
||||
{ActorName}.prefab # Pipeline SetupCharacter 生成
|
||||
|
||||
概念图留在 Doc/Design/Character/,不要拷进 Bundles。
|
||||
|
||||
兼容旧五通道烘焙目录(_color/_normal/_ao/_metallic/_roughness)。
|
||||
|
||||
用法:
|
||||
python Tools/install_character_bundles.py -a MaleExplorer -b Tools/output/actor_game/actor_xpbr
|
||||
python Tools/install_character_bundles.py -a CaoCao --kind Human -b Tools/output/caocao_game/caocao_xpbr -c Doc/Design/Character/CaoCao/caocao.png
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -35,22 +38,32 @@ def _pick_first(files: list[Path], predicate) -> Path | None:
|
||||
return None
|
||||
|
||||
|
||||
def _safe_seg(name: str, label: str) -> str:
|
||||
text = (name or "").strip()
|
||||
if not text or any(ch in text for ch in r'\/:*?"<>|'):
|
||||
raise ValueError(f"非法 {label}: {name!r}")
|
||||
return text
|
||||
|
||||
|
||||
def install(
|
||||
*,
|
||||
actor: str,
|
||||
bake_dir: Path,
|
||||
kind: str | None = None,
|
||||
concept: Path | None = None,
|
||||
dry_run: bool = False,
|
||||
) -> dict:
|
||||
actor = actor.strip()
|
||||
if not actor or any(ch in actor for ch in r'\/:*?"<>|'):
|
||||
raise ValueError(f"非法 ActorName: {actor!r}")
|
||||
actor = _safe_seg(actor, "ActorName")
|
||||
kind = _safe_seg(kind, "Kind") if kind else None
|
||||
|
||||
bake_dir = bake_dir.expanduser().resolve()
|
||||
if not bake_dir.is_dir():
|
||||
raise FileNotFoundError(f"资源目录不存在: {bake_dir}")
|
||||
|
||||
dest = _repo_root() / "AIClient" / "Assets" / "Bundles" / "Characters" / actor
|
||||
dest = _repo_root() / "AIClient" / "Assets" / "Bundles" / "Characters"
|
||||
if kind:
|
||||
dest = dest / kind
|
||||
dest = dest / actor
|
||||
files = list(bake_dir.iterdir())
|
||||
|
||||
sources: dict[str, Path] = {}
|
||||
@@ -126,12 +139,14 @@ def install(
|
||||
result = {
|
||||
"ok": True,
|
||||
"actor": actor,
|
||||
"kind": kind,
|
||||
"mode": mode,
|
||||
"dest": str(dest.relative_to(_repo_root())).replace("\\", "/"),
|
||||
"files": copied,
|
||||
"hint": (
|
||||
f"在 Unity 执行菜单 AIGame/M3/Setup Character Prefab + Art Collector "
|
||||
f"生成挂 XPbr 的 {actor}.prefab;并登记 Doc/ResDesc_Model.md"
|
||||
"Unity Pipeline: unity command run_script --file AgentScripts/SetupCharacter.cs "
|
||||
f"--entry SetupCharacter.Run --args {json.dumps([kind or '', actor])}"
|
||||
";并登记 Doc/ResDesc_Model.md"
|
||||
),
|
||||
}
|
||||
if concept is not None:
|
||||
@@ -141,7 +156,13 @@ def install(
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
p = argparse.ArgumentParser(description="安装角色资源到 Bundles/Characters/{Actor}")
|
||||
p.add_argument("--actor", "-a", required=True, help="角色目录名 / 寻址名,如 MaleExplorer")
|
||||
p.add_argument("--actor", "-a", required=True, help="角色目录名 / 寻址名,如 CaoCao")
|
||||
p.add_argument(
|
||||
"--kind",
|
||||
"-k",
|
||||
default=None,
|
||||
help="人物用 Human,落到 Characters/{Kind}/{Actor};省略则为 Characters/{Actor}",
|
||||
)
|
||||
p.add_argument(
|
||||
"--bake-dir",
|
||||
"-b",
|
||||
@@ -160,6 +181,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
result = install(
|
||||
actor=args.actor,
|
||||
bake_dir=Path(args.bake_dir),
|
||||
kind=args.kind,
|
||||
concept=Path(args.concept) if args.concept else None,
|
||||
dry_run=args.dry_run,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user