Add formal enemies, remaining formal partners and synthesized UI SFX
- UI SFX v3: procedurally synthesized one-shots (Tools/synth_ui_sfx.py) wired to buttons, rewards, errors and red dots via UiSfx, gated by the SFX setting. - Five formal enemies (Hornlet, Dashlet, Shellhorn, HornCaptain, GreatHorn): Hunyuan 50k -> 2000-tri XPbr, DGXA TokenRig, Idle/Walk/Attack, nested per type in PrototypeArena; PrototypeBattle.Enemy gains presentation-only Moving/Strikes. - Formal partners for all ten pets in the arena and portraits, including new VineSprite, ThunderLeopard and TideWhale. - Pipeline fixes: reject local TokenRig fallback, drop loose mesh islands, configurable Unity run_script timeout, companion static stage. - Also includes concurrent work from another tool in this working tree (FlameLion, enemy combat FX, challenge mode, home explorer, server updates). - Resources registered in Doc/ResDesc_*.md; verified on isolated :18081. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,17 @@ using Object = UnityEngine.Object;
|
||||
/// <summary>Bake an upright, grounded, periodic in-place gait onto this exact bind.</summary>
|
||||
public static class FinalizeAnimalWalk
|
||||
{
|
||||
[Serializable] public sealed class QuadrupedRecipe
|
||||
{
|
||||
public string assetRoot,id,rootBone,frontRightFoot,hindLeftFoot,frontLeftFoot,hindRightFoot,outputDir;
|
||||
public float height,walkStride,walkLift,walkSeconds,walkSourceWeight,walkSwingFraction;
|
||||
}
|
||||
public static string RunQuadruped(string recipePath)
|
||||
{
|
||||
var r=JsonUtility.FromJson<QuadrupedRecipe>(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../..",recipePath))));
|
||||
if(!(r.walkSwingFraction>0 && r.walkSwingFraction<=.25f))throw new ArgumentException("Four-beat swing must be at most a quarter cycle");
|
||||
return RunCore(r.assetRoot,r.id,r.height,r.rootBone,new[]{r.frontRightFoot,r.hindLeftFoot,r.frontLeftFoot,r.hindRightFoot},r.walkStride,r.walkLift,r.walkSeconds,r.outputDir,true,r.walkSourceWeight,true,r.walkSwingFraction);
|
||||
}
|
||||
const string Root = "Assets/Bundles/Characters/Animal/RockBall";
|
||||
sealed class Pose
|
||||
{
|
||||
@@ -48,6 +59,10 @@ public static class FinalizeAnimalWalk
|
||||
|
||||
public static string RunFor(string assetRoot,string actor,float height,string rootBone,string rightFoot,string leftFoot,
|
||||
float strideMeters,float liftMeters,float cycleSeconds,string reportDirectory,bool calibrateBind=false,float sourceWeight=1f)
|
||||
=>RunCore(assetRoot,actor,height,rootBone,new[]{rightFoot,leftFoot},strideMeters,liftMeters,cycleSeconds,reportDirectory,calibrateBind,sourceWeight,false,.2f);
|
||||
|
||||
static string RunCore(string assetRoot,string actor,float height,string rootBone,string[] footNames,
|
||||
float strideMeters,float liftMeters,float cycleSeconds,string reportDirectory,bool calibrateBind,float sourceWeight,bool fourBeat,float swingFraction)
|
||||
{
|
||||
var asset=AssetDatabase.LoadAssetAtPath<GameObject>(assetRoot+"/Rigged/"+actor+"_Rigged.fbx");
|
||||
var model=Object.Instantiate(asset);
|
||||
@@ -57,7 +72,9 @@ public static class FinalizeAnimalWalk
|
||||
if(animator!=null) animator.enabled=false;
|
||||
var bones=model.GetComponentsInChildren<Transform>().Where(t=>t!=model.transform && (t.name=="Armature" || t.name.StartsWith("bone_"))).ToArray();
|
||||
var root=bones.First(t=>t.name==rootBone);
|
||||
var feet=new[] { bones.First(t=>t.name==rightFoot),bones.First(t=>t.name==leftFoot) };
|
||||
var feet=footNames.Select(name=>bones.First(t=>t.name==name)).ToArray();
|
||||
if(feet.Distinct().Count()!=feet.Length)throw new ArgumentException("Foot mappings must be distinct");
|
||||
var footPositions=feet.Select(f=>f.position).ToArray();var footRotations=feet.Select(f=>f.rotation).ToArray();
|
||||
var bindPose=Read(bones);
|
||||
var bindRotation=root.rotation;
|
||||
var bindPosition=root.position;
|
||||
@@ -88,6 +105,7 @@ public static class FinalizeAnimalWalk
|
||||
}
|
||||
}
|
||||
else root.rotation=bindRotation*Quaternion.Inverse(reference)*root.rotation;
|
||||
if(fourBeat)root.rotation=bindRotation;
|
||||
root.position=new Vector3(bindPosition.x,root.position.y,bindPosition.z);
|
||||
root.position+=Vector3.up*(bindBottom-Bottom(model));
|
||||
poses[f]=Read(bones);
|
||||
@@ -117,10 +135,24 @@ public static class FinalizeAnimalWalk
|
||||
p.q[i]=a.q[i]*Quaternion.Slerp(Quaternion.identity,Quaternion.Inverse(end.q[i])*begin.q[i],blend);
|
||||
}
|
||||
Write(bones,p);
|
||||
if(fourBeat)root.rotation=bindRotation;
|
||||
// Author an explicit alternating short step in the OFFLINE clip bake.
|
||||
// Bone mapping and dimensions belong to this character's recipe.
|
||||
for(int foot=0;foot<feet.Length;foot++)
|
||||
{
|
||||
if(fourBeat)
|
||||
{
|
||||
// Four separate swing windows leave the other three paws supporting.
|
||||
float u=Mathf.Repeat(t-foot*.25f,1);
|
||||
bool swinging=u<swingFraction;
|
||||
float advance=swinging ? Mathf.SmoothStep(-.5f,.5f,u/swingFraction) : .5f-(u-swingFraction)/(1-swingFraction);
|
||||
float lift=swinging ? Mathf.Pow(Mathf.Sin(Mathf.PI*u/swingFraction),2) : 0;
|
||||
for(int b=0;b<bones.Length;b++)if(bones[b]!=feet[foot] && bones[b].IsChildOf(feet[foot]))
|
||||
{bones[b].localPosition=bindPose.p[b];bones[b].localRotation=bindPose.q[b];bones[b].localScale=bindPose.s[b];}
|
||||
feet[foot].position=footPositions[foot]+(Vector3.back*(advance*strideMeters)+Vector3.up*(lift*liftMeters))*metersToBind;
|
||||
feet[foot].rotation=footRotations[foot];
|
||||
continue;
|
||||
}
|
||||
float phase=2*Mathf.PI*t+foot*Mathf.PI;
|
||||
float swing=Mathf.Max(0,Mathf.Cos(phase));
|
||||
feet[foot].position+=Vector3.back*(Mathf.Sin(phase)*strideMeters*.5f*metersToBind)
|
||||
@@ -162,7 +194,7 @@ public static class FinalizeAnimalWalk
|
||||
AnimationUtility.SetAnimationClipSettings(clip,settings);
|
||||
EditorUtility.SetDirty(clip);
|
||||
AssetDatabase.SaveAssets();
|
||||
string report=$"frames={first}..{last} length={clip.length:F4} phaseRms={error:F3} sourceInitialRootTilt={referenceTilt:F3}; allBoneBindCalibration={calibrateBind} sourceWeight={sourceWeight:F3}; root bind calibration + in-place XZ + per-frame grounded skin + periodic closure; authored short-foot stride={strideMeters:F3}m lift={liftMeters:F3}m cycle={cycleSeconds:F3}s";
|
||||
string report=$"frames={first}..{last} length={clip.length:F4} phaseRms={error:F3} sourceInitialRootTilt={referenceTilt:F3}; allBoneBindCalibration={calibrateBind} sourceWeight={sourceWeight:F3}; root bind calibration + in-place XZ + per-frame grounded skin + periodic closure; feet={feet.Length} fourBeat={fourBeat} swingFraction={swingFraction:F3}; authored short-foot stride={strideMeters:F3}m lift={liftMeters:F3}m cycle={cycleSeconds:F3}s";
|
||||
string outPath=Path.GetFullPath(Path.Combine(Application.dataPath,"../../",reportDirectory,"walk_processing.txt"));
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
||||
File.WriteAllText(outPath,report);
|
||||
|
||||
Reference in New Issue
Block a user