Other players as real MSC characters

GameCharacter clones a MSC human (bodymesh + skeleton, no scripts, physics or
animation), puts it into its bind pose, aligns and scales it, and drives the
bones every frame from the joint points of the existing MSCO figure (hip,
knee, ankle, shoulder, elbow, hand, head pitch, torso lean) by rotating each
limb from its rest direction. Limb bones are picked geometrically, so bone
naming conventions do not matter. Variant per player id; console 'model
classic|game' switches, the MSCO figure is the automatic fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 10:36:50 +02:00
co-authored by Claude Opus 5.5
parent 983291a4b3
commit cd8c1293e0
6 changed files with 370 additions and 3 deletions
+35 -1
View File
@@ -62,7 +62,18 @@ namespace MSCO
public int HeldId { get { return last.HeldId; } }
public PlayerState LastState { get { return last; } }
public Vector3 HeadPosition { get { return headPivot.position; } }
public string ModelDescription { get { return "MSCO figure, variant " + variant; } }
public string ModelDescription { get { return model != null ? model.Description : "MSCO figure, variant " + variant; } }
/// <summary>Mitspieler als echte MSC-Menschen zeigen (Konsole "model game|classic", gespeichert).</summary>
public static bool UseGameModels
{
get { return PlayerPrefs.GetInt("MSCO_GameModels", 1) == 1; }
set { PlayerPrefs.SetInt("MSCO_GameModels", value ? 1 : 0); PlayerPrefs.Save(); }
}
// Echte Spielfigur (null = eigene MSCO-Figur sichtbar); die MSCO-Figur liefert dann nur noch die Gelenkpunkte
GameCharacter model;
readonly GameCharacter.Rig rig = new GameCharacter.Rig();
// ---------------------------------------------------------------- Aufbau
@@ -73,6 +84,7 @@ namespace MSCO
rp.jacket = jacketColor;
rp.variant = Mathf.Abs(variant);
rp.Build();
if (UseGameModels) rp.UseGameModel();
rp.SetTarget(state);
root.transform.position = rp.targetFeet;
root.transform.rotation = Quaternion.Euler(0f, rp.targetYaw, 0f);
@@ -180,6 +192,16 @@ namespace MSCO
view.SetParent(r, false);
}
void UseGameModel()
{
model = GameCharacter.Create(transform, variant);
if (model == null) return;
// Eigene Figur unsichtbar (sie bewegt sich weiter und gibt die Gelenke vor)
Transform modelRoot = transform.Find("MSCO_Model");
foreach (Renderer r in GetComponentsInChildren<Renderer>(true))
if (modelRoot == null || !r.transform.IsChildOf(modelRoot)) r.enabled = false;
}
/// <summary>Kleines Karomuster (Flanellhemd) in der Grundfarbe; wird auf die Kapseln gekachelt.</summary>
static Texture2D Plaid(Color baseColor)
{
@@ -379,6 +401,11 @@ namespace MSCO
// Kopf
headPivot.localPosition = head;
headPivot.localRotation = Quaternion.Euler(pitch, 0f, 0f);
rig.Hip = hip;
rig.NeckBase = neckBase;
rig.Head = head;
rig.Up = up;
rig.Pitch = pitch;
float swing = Mathf.Sin(walkPhase) * walkAmount * (1f - airAmount);
for (int i = 0; i < 2; i++)
@@ -400,6 +427,9 @@ namespace MSCO
}
Vector3 knee = Joint(hipJoint, ankle, ThighLength, ShinLength, Vector3.forward);
Segment(thigh[i], hipJoint, knee, 0.15f, 0.16f);
rig.HipJoint[i] = hipJoint;
rig.Knee[i] = knee;
rig.Ankle[i] = ankle;
Segment(shin[i], knee, ankle, 0.12f, 0.13f);
// Stiefel: Kapsel von der Ferse zur Spitze, darunter eine flache Sohle
Segment(boot[i], ankle + new Vector3(0f, -0.025f, -0.06f), ankle + new Vector3(0f, -0.03f, 0.14f), 0.12f, 0.1f);
@@ -427,12 +457,16 @@ namespace MSCO
Vector3 elbow = Joint(shoulder, handTarget, UpperArmLength, ForearmLength, new Vector3(side * 0.4f, -0.2f, -1f));
Segment(upperArm[i], shoulder, elbow, 0.11f, 0.11f);
rig.Shoulder[i] = shoulder;
rig.Elbow[i] = elbow;
rig.Hand[i] = handTarget;
Segment(forearm[i], elbow, handTarget, 0.095f, 0.095f);
Segment(cuff[i], Vector3.Lerp(elbow, handTarget, 0.82f), Vector3.Lerp(elbow, handTarget, 0.9f), 0.105f, 0.105f);
hand[i].localPosition = handTarget;
hand[i].localRotation = Quaternion.LookRotation(Vector3.forward, (handTarget - elbow).sqrMagnitude > 0f ? elbow - handTarget : Vector3.up);
hand[i].localScale = new Vector3(0.085f, 0.125f, 0.11f);
}
if (model != null) model.Apply(rig);
}
/// <summary>Zwei-Knochen-Gelenk (Knie/Ellbogen): Punkt zwischen a und b, zur Seite "bend" geknickt.</summary>