LANTERN realignment P5: Bathynaut suit body onto Player.prefab (in place)

New PlayerRigTools.BuildBathynautPlayer (menu: ProjectM/Animation) - forks the
proven EnemyRigTools recipe: strips the Synty-soldier skeleton+SMRs from
Player.prefab IN PLACE (GUID preserved; ghost/authoring/CC/Animator/
RigDefinitionAuthoring untouched), flattens the SpaceSoldier_HelmetArmor suit
body under the root, keeps the -0.90 feet offset, re-slots
M_SuitFrame_Bathynaut_Animated on every SMR, adopts the shared CharactersAvatar
(AC_PlayerTopDown muscle clips retarget). 17 inactive Synty variant SMRs stripped.
Both frames share this visual until per-frame models land; the brass-dome/tank
kitbash attachments are a follow-up /art-dev export.

Play-verified in DevSandbox: suit renders + idles correctly under the LANTERN
murk, Rukhanka rig baked with all 9 drive params, dev overlay alive in the
renamed scene, no console errors.

=> Movement/animation tuning is now UNBLOCKED (the operator's next priority).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 16:31:07 -07:00
parent 12d5b96b71
commit 210d307406
2 changed files with 2666 additions and 2559 deletions
@@ -62,6 +62,71 @@ namespace ProjectM.EditorTools
Debug.Log("[PlayerRigTools] AC_PlayerTopDown: IsAttacking + idle-based MeleeSwing built (no un-keyed-bone collapse).");
}
/// <summary>LANTERN P5 (realignment): swap the Bathynaut suit body onto Player.prefab IN PLACE (GUID
/// preserved — the subscene PlayerSpawner refs never break). Mirrors the proven EnemyRigTools.BuildOne
/// recipe: strip the old Synty-soldier skeleton + SMR children (every ROOT component — ghost/authoring/
/// CC/Animator/RigDefinitionAuthoring — stays), flatten the suit body's skeleton + SMRs under the player
/// root, keep the feet-on-ground Root offset, re-slot the deformation material, and adopt the clone's
/// avatar (same shared humanoid Characters avatar — AC_PlayerTopDown's muscle clips retarget). Both
/// frames share this visual until per-frame player models land. Idempotent / re-runnable.</summary>
[MenuItem("ProjectM/Animation/Player - Build Bathynaut Player (LANTERN)")]
public static void BuildBathynautPlayer()
{
const string synty = "Assets/Synty/PolygonSciFiSpace/Prefabs/Characters/SM_Chr_SpaceSoldier_HelmetArmor_Male_01.prefab";
const string matPath = "Assets/_Project/Materials/M_SuitFrame_Bathynaut_Animated.mat";
const string output = "Assets/_Project/Prefabs/Player.prefab";
const float rootY = -0.90f; // the player's existing feet-on-ground offset (entity origin = capsule center)
var syntyAsset = AssetDatabase.LoadAssetAtPath<GameObject>(synty);
var mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
if (syntyAsset == null) { Debug.LogError($"[PlayerRigTools] Suit body prefab missing: {synty}"); return; }
if (mat == null) { Debug.LogError($"[PlayerRigTools] Deformation material missing: {matPath}"); return; }
var root = PrefabUtility.LoadPrefabContents(output);
try
{
// Strip ONLY the old visual (skeleton + SMR children); root components stay untouched.
var kids = new System.Collections.Generic.List<Transform>();
foreach (Transform c in root.transform) kids.Add(c);
foreach (var c in kids) Object.DestroyImmediate(c.gameObject);
// Clone the suit body (unlinked) and flatten its skeleton + SMRs under the player root.
var clone = (GameObject)Object.Instantiate(syntyAsset);
var srcAnimator = clone.GetComponentInChildren<Animator>();
var avatar = srcAnimator != null ? srcAnimator.avatar : null;
var children = new System.Collections.Generic.List<Transform>();
foreach (Transform c in clone.transform) children.Add(c);
foreach (var c in children) c.SetParent(root.transform, false); // keep local transforms
Object.DestroyImmediate(clone);
// Feet-on-ground: offset the un-keyed Root bone.
var rootBone = root.transform.Find("Root");
if (rootBone != null) { var lp = rootBone.localPosition; lp.y = rootY; rootBone.localPosition = lp; }
else Debug.LogWarning("[PlayerRigTools] No 'Root' bone after the swap; feet offset skipped.");
// Deformation material on every SMR slot (else unskinned-static under Entities Graphics).
foreach (var smr in root.GetComponentsInChildren<SkinnedMeshRenderer>(true))
{
var mats = smr.sharedMaterials;
for (int i = 0; i < mats.Length; i++) mats[i] = mat;
smr.sharedMaterials = mats;
}
var anim = root.GetComponent<Animator>();
if (anim != null && avatar != null) anim.avatar = avatar; // controller (AC_PlayerTopDown) stays
PrefabUtility.SaveAsPrefabAsset(root, output);
Debug.Log("[PlayerRigTools] Bathynaut suit body swapped onto Player.prefab (in place).");
}
finally
{
PrefabUtility.UnloadPrefabContents(root);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
static AnimationClip FindIdleClip(AnimatorController ac)
{
foreach (var c in ac.layers[0].stateMachine.states)