using UnityEngine; namespace MSCO { /// /// Figur eines Mitspielers im Stil der My-Summer-Car-Leute (selbst gebaut, keine NPC-Kopie): /// Karohemd/Arbeitsjacke, Jeans, Arbeitsschuhe, Schnurrbart, Kappe oder Haare, leichter Bauch. /// Aus Kopfposition (Kamera) und Bodenhöhe wird jedes Frame die Pose berechnet: Ducken beugt die Knie, /// nach vorne Lehnen neigt den Oberkörper, Laufen bewegt Beine und Arme, beim Halten greift der rechte Arm /// zum Item, im Auto sitzt die Figur. /// public class RemotePlayer : MonoBehaviour { const float Smoothing = 15f; const float StandingEyeHeight = 1.65f; const float MaxHeadPitch = 55f; // Körpermaße (Meter) const float ThighLength = 0.41f, ShinLength = 0.41f; // im Stehen fast gestreckt const float UpperArmLength = 0.3f, ForearmLength = 0.29f; const float TorsoLength = 0.55f; // Hüfte bis Hals const float HipWidth = 0.1f, ShoulderWidth = 0.21f; const float AnkleHeight = 0.07f; const float LegReach = ThighLength + ShinLength + AnkleHeight - 0.02f; // Hüfthöhe bei gestrecktem Bein static readonly Color Skin = new Color(0.86f, 0.66f, 0.52f); static readonly Color Jeans = new Color(0.2f, 0.26f, 0.38f); static readonly Color Boots = new Color(0.22f, 0.15f, 0.09f); static readonly Color Sole = new Color(0.07f, 0.06f, 0.05f); static readonly Color Dark = new Color(0.08f, 0.08f, 0.08f); static readonly Color Metal = new Color(0.62f, 0.62f, 0.6f); static readonly Color[] HairColors = { new Color(0.23f, 0.15f, 0.09f), new Color(0.45f, 0.33f, 0.2f), new Color(0.12f, 0.1f, 0.08f), new Color(0.55f, 0.5f, 0.45f) }; Color jacket; int variant; Transform headPivot, view; Transform pelvis, torso, belt, neck, belly, collar, placket, buckle; readonly Transform[] thigh = new Transform[2], shin = new Transform[2], boot = new Transform[2]; readonly Transform[] upperArm = new Transform[2], forearm = new Transform[2], hand = new Transform[2]; readonly Transform[] sole = new Transform[2], cuff = new Transform[2], shoulderBall = new Transform[2]; Vector3 targetFeet; Vector3 targetHead; Vector3 headWorld; float targetYaw, targetPitch, pitch; Quaternion targetViewRot = Quaternion.identity; PlayerState last; // Fahrzeug, in dem die Figur sitzt (setzt der Manager jedes Frame; null = läuft frei) public Transform Seat; Transform lastSeat; Vector3 seatHead; Quaternion seatView = Quaternion.identity; Vector3 lastPosition; float walkPhase, walkAmount, speed; float airAmount; // 0 = steht am Boden, 1 = in der Luft (Sprung, Fallen) 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 model != null ? model.Description : "MSCO figure, variant " + variant; } } /// Mitspieler als echte MSC-Menschen zeigen (Konsole "model game|classic", gespeichert). 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 public static RemotePlayer Create(string name, Color jacketColor, PlayerState state, int variant) { var root = new GameObject(name); var rp = root.AddComponent(); 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); rp.headWorld = rp.targetHead; rp.pitch = rp.targetPitch; rp.view.rotation = rp.targetViewRot; rp.lastPosition = root.transform.position; rp.Pose(0f); return rp; } void Build() { Transform r = transform; Color jacketDark = jacket * 0.7f; jacketDark.a = 1f; Color hairColor = HairColors[variant % HairColors.Length]; bool wearsCap = variant % 3 != 2; bool sunglasses = variant % 4 == 1; Texture2D plaid = Plaid(jacket); // Körper: Karohemd/Jacke, Jeans, Gürtel, leichter Bauch (wie die Leute in MSC) pelvis = Capsule("Pelvis", r, Jeans); torso = Textured(Capsule("Chest", r, Color.white), plaid, new Vector2(4f, 4f)); belly = Textured(Capsule("Belly", r, Color.white), plaid, new Vector2(4f, 2f)); belt = Primitive(PrimitiveType.Cylinder, "Belt", r, new Color(0.2f, 0.13f, 0.08f)); // rund um den Bauch buckle = Box("Buckle", r, Metal); placket = Box("Buttons", r, jacketDark); collar = Capsule("Collar", r, jacketDark); neck = Capsule("Neck", r, Skin); for (int i = 0; i < 2; i++) { thigh[i] = Capsule("Thigh", r, Jeans); shin[i] = Capsule("Shin", r, Jeans * 0.92f); boot[i] = Capsule("Boot", r, Boots); // abgerundete Arbeitsstiefel statt Klötze sole[i] = Capsule("Sole", r, Sole); upperArm[i] = Textured(Capsule("UpperArm", r, Color.white), plaid, new Vector2(2f, 3f)); forearm[i] = Textured(Capsule("Forearm", r, Color.white), plaid, new Vector2(2f, 3f)); cuff[i] = Capsule("Cuff", r, jacketDark); shoulderBall[i] = Textured(Sphere("Shoulder", r, Color.white, 0.13f), plaid, new Vector2(2f, 2f)); // runde Schulter hand[i] = Sphere("Hand", r, Skin, 0.1f); } // Kopf: dreht mit Blick (hoch/runter), Gesicht nach +Z headPivot = new GameObject("Head").transform; headPivot.SetParent(r, false); Transform skull = Sphere("Skull", headPivot, Skin, 0.24f); skull.localScale = new Vector3(0.225f, 0.27f, 0.25f); Transform jaw = Sphere("Jaw", headPivot, Skin, 0.2f); jaw.localPosition = new Vector3(0f, -0.07f, 0.022f); jaw.localScale = new Vector3(0.19f, 0.14f, 0.2f); Transform hair = Sphere("Hair", headPivot, hairColor, 0.24f); hair.localPosition = new Vector3(0f, 0.028f, -0.032f); hair.localScale = new Vector3(0.235f, 0.255f, 0.235f); for (int i = 0; i < 2; i++) { float side = i == 0 ? -1f : 1f; Transform ear = Sphere("Ear", headPivot, Skin * 0.96f, 0.05f); ear.localPosition = new Vector3(side * 0.112f, -0.005f, 0f); ear.localScale = new Vector3(0.035f, 0.065f, 0.05f); Transform eye = Sphere("Eye", headPivot, Dark, 0.026f); eye.localPosition = new Vector3(side * 0.045f, 0.018f, 0.112f); Transform brow = Box("Brow", headPivot, hairColor); brow.localPosition = new Vector3(side * 0.046f, 0.046f, 0.117f); brow.localScale = new Vector3(0.045f, 0.012f, 0.012f); brow.localRotation = Quaternion.Euler(0f, 0f, side * -8f); } Transform nose = Box("Nose", headPivot, Skin * 0.93f); nose.localPosition = new Vector3(0f, -0.012f, 0.128f); nose.localScale = new Vector3(0.035f, 0.055f, 0.045f); // Der typische Schnurrbart Transform mustache = Box("Mustache", headPivot, hairColor); mustache.localPosition = new Vector3(0f, -0.054f, 0.12f); mustache.localScale = new Vector3(0.085f, 0.024f, 0.03f); Transform mouth = Box("Mouth", headPivot, new Color(0.45f, 0.25f, 0.22f)); mouth.localPosition = new Vector3(0f, -0.082f, 0.114f); mouth.localScale = new Vector3(0.05f, 0.01f, 0.01f); if (sunglasses) { Transform glasses = Box("Sunglasses", headPivot, Dark); glasses.localPosition = new Vector3(0f, 0.018f, 0.121f); glasses.localScale = new Vector3(0.15f, 0.035f, 0.012f); } if (wearsCap) { // Kappe ("lippis") in der Jackenfarbe Transform cap = Sphere("Cap", headPivot, jacketDark, 0.25f); cap.localPosition = new Vector3(0f, 0.08f, -0.005f); cap.localScale = new Vector3(0.25f, 0.16f, 0.26f); Transform brim = Box("CapBrim", headPivot, jacketDark); brim.localPosition = new Vector3(0f, 0.07f, 0.15f); brim.localScale = new Vector3(0.2f, 0.014f, 0.1f); brim.localRotation = Quaternion.Euler(8f, 0f, 0f); // Schirm leicht nach unten Color patchColor = Color.Lerp(jacketDark, Color.white, 0.35f); Transform patch = Box("CapPatch", headPivot, patchColor); patch.localPosition = new Vector3(0f, 0.115f, 0.118f); patch.localScale = new Vector3(0.06f, 0.035f, 0.01f); patch.localRotation = Quaternion.Euler(-25f, 0f, 0f); } // Unsichtbarer "Blick" mit der exakten Kameradrehung - daran hängt das gehaltene Item view = new GameObject("View").transform; 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(true)) if (modelRoot == null || !r.transform.IsChildOf(modelRoot)) r.enabled = false; } /// Kleines Karomuster (Flanellhemd) in der Grundfarbe; wird auf die Kapseln gekachelt. static Texture2D Plaid(Color baseColor) { const int size = 16; var tex = new Texture2D(size, size, TextureFormat.RGB24, false); // Weiches Flanell-Karo: wenig Kontrast, dünne Linien - wirkt aus der Entfernung wie Stoff statt Schachbrett Color light = Color.Lerp(baseColor, Color.white, 0.1f); Color dark = baseColor * 0.78f; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { bool bandX = (x / 4) % 2 == 0, bandY = (y / 4) % 2 == 0; Color c = bandX && bandY ? dark : bandX || bandY ? baseColor : light; if (x % 8 == 1 || y % 8 == 1) c = Color.Lerp(c, Color.black, 0.25f); // dünne dunkle Linien c.a = 1f; tex.SetPixel(x, y, c); } } tex.filterMode = FilterMode.Bilinear; tex.wrapMode = TextureWrapMode.Repeat; tex.Apply(); return tex; } static Transform Textured(Transform part, Texture2D tex, Vector2 tiling) { Material m = part.GetComponent().material; m.mainTexture = tex; m.mainTextureScale = tiling; return part; } static Transform Capsule(string name, Transform parent, Color color) { return Primitive(PrimitiveType.Capsule, name, parent, color); } static Transform Box(string name, Transform parent, Color color) { return Primitive(PrimitiveType.Cube, name, parent, color); } static Transform Sphere(string name, Transform parent, Color color, float size) { Transform t = Primitive(PrimitiveType.Sphere, name, parent, color); t.localScale = Vector3.one * size; return t; } static Transform Primitive(PrimitiveType type, string name, Transform parent, Color color) { GameObject go = GameObject.CreatePrimitive(type); go.name = name; Destroy(go.GetComponent()); // stört sonst Physik/Trigger var renderer = go.GetComponent(); renderer.material.color = color; // Matt wie Stoff/Haut statt glänzendem Plastik (Standard-Shader) if (renderer.material.HasProperty("_Glossiness")) renderer.material.SetFloat("_Glossiness", 0.08f); if (renderer.material.HasProperty("_Metallic")) renderer.material.SetFloat("_Metallic", 0f); renderer.receiveShadows = true; go.transform.SetParent(parent, false); return go.transform; } // ---------------------------------------------------------------- Zustand public void SetTarget(PlayerState state) { last = state; targetHead = state.CamPos; // Füße: Bodenhöhe vom Spieler, waagerecht an der Spielerposition (Lehnen = Kopf woanders als Füße). // Liegt der Kopf unplausibel weit weg (andere Kamera), Füße unter den Kopf. float feetY = state.FeetY != 0f ? state.FeetY : state.CamPos.y - StandingEyeHeight; var feet = new Vector3(state.Position.x, feetY, state.Position.z); var horizontal = new Vector3(state.CamPos.x - feet.x, 0f, state.CamPos.z - feet.z); if (horizontal.sqrMagnitude > 0.8f * 0.8f || state.Seated) feet = new Vector3(state.CamPos.x, feetY, state.CamPos.z); targetFeet = feet; // Blickrichtung ohne seitliches Kippen Vector3 forward = state.CamRot * Vector3.forward; Vector3 flat = new Vector3(forward.x, 0f, forward.z); targetYaw = flat.sqrMagnitude > 0.0001f ? Quaternion.LookRotation(flat).eulerAngles.y : state.RotationY; targetPitch = Mathf.Clamp(-Mathf.Asin(Mathf.Clamp(forward.y, -1f, 1f)) * Mathf.Rad2Deg, -MaxHeadPitch, MaxHeadPitch); targetViewRot = state.CamRot; } public void Tick(float dt) { if (Seat != null && last.SeatId != 0) { TickSeated(dt); UpdateWalk(dt); Pose(dt); return; } lastSeat = null; // Bei großen Sprüngen (Teleport, Laden) direkt setzen statt gleiten bool snap = (transform.position - targetFeet).sqrMagnitude > 100f; float t = snap ? 1f : Mathf.Clamp01(dt * Smoothing); transform.position = Vector3.Lerp(transform.position, targetFeet, t); transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(0f, targetYaw, 0f), t); headWorld = snap ? targetHead : Vector3.Lerp(headWorld, targetHead, t); pitch = Mathf.Lerp(pitch, targetPitch, t); view.position = headWorld; view.rotation = Quaternion.Slerp(view.rotation, targetViewRot, t); UpdateWalk(dt); Pose(dt); } /// /// Im Fahrzeug: relativ zum Fahrzeug glätten und jedes Frame an dessen aktuelle Lage setzen. In Weltkoordinaten /// hinkt die Figur einem fahrenden Auto um Tempo / Glättung hinterher (bei 90 km/h fast 2 m) und ruckelt. /// void TickSeated(float dt) { bool fresh = lastSeat != Seat; // gerade eingestiegen: direkt auf den Sitz lastSeat = Seat; float t = fresh ? 1f : Mathf.Clamp01(dt * Smoothing); seatHead = Vector3.Lerp(seatHead, last.SeatHead, t); seatView = Quaternion.Slerp(seatView, last.SeatView, t); Vector3 up = Seat.up; Quaternion viewRot = Seat.rotation * seatView; Vector3 forward = viewRot * Vector3.forward; Vector3 flat = forward - up * Vector3.Dot(forward, up); if (flat.sqrMagnitude < 0.0001f) flat = Seat.forward; headWorld = Seat.TransformPoint(seatHead); transform.rotation = Quaternion.LookRotation(flat, up); // neigt sich mit dem Auto (Berg, Kurve) transform.position = headWorld - up * 1.2f; pitch = Mathf.Clamp(-Mathf.Asin(Mathf.Clamp(Vector3.Dot(forward.normalized, up), -1f, 1f)) * Mathf.Rad2Deg, -MaxHeadPitch, MaxHeadPitch); view.position = headWorld; view.rotation = viewRot; } void UpdateWalk(float dt) { Vector3 moved = transform.position - lastPosition; lastPosition = transform.position; moved.y = 0f; speed = dt > 0f ? moved.magnitude / dt : 0f; if (speed > 12f || last.Seated) speed = 0f; // Teleport oder im Auto walkAmount = Mathf.Lerp(walkAmount, Mathf.Clamp01(speed / 2.5f), Mathf.Clamp01(dt * 8f)); walkPhase += speed * dt * 3.4f; } // ---------------------------------------------------------------- Pose (alles im lokalen Raum der Figur) void Pose(float dt) { Vector3 head = transform.InverseTransformPoint(headWorld) + new Vector3(0f, -0.02f, -0.03f); Vector3 neckTop = head + new Vector3(0f, -0.12f, -0.01f); Vector3 neckBase = head + new Vector3(0f, -0.2f, -0.02f); // Hüfte: beim Lehnen nach hinten ausweichen, beim Ducken absinken (Füße bleiben am Boden) Vector3 hip; float air = 0f; if (last.Seated) { hip = neckBase + new Vector3(0f, -TorsoLength, -0.08f); } else { var lean = new Vector3(neckBase.x, 0f, neckBase.z); hip = -lean * 0.25f; float horizontal = (new Vector3(neckBase.x - hip.x, 0f, neckBase.z - hip.z)).magnitude; float vertical = Mathf.Sqrt(Mathf.Max(0.04f, TorsoLength * TorsoLength - horizontal * horizontal)); // Ist der Kopf höher, als die Beine reichen (Sprung, Fallen), hebt die ganze Figur ab - vorher blieben die // Füße am Boden und nur der Oberkörper ging nach oben hip.y = Mathf.Max(0.3f, neckBase.y - vertical); air = Mathf.Max(0f, hip.y - LegReach); } airAmount = Mathf.Lerp(airAmount, last.Seated ? 0f : Mathf.Clamp01(air / 0.12f), dt > 0f ? Mathf.Clamp01(dt * 10f) : 1f); Vector3 up = (neckBase - hip).normalized; Vector3 right = Vector3.right; Vector3 forward = Vector3.Cross(right, up).normalized; // Rumpf Quaternion torsoRot = Quaternion.LookRotation(forward, up); // Brust (breit oben) und Bauch (etwas tiefer = leichter Bauch) - fließend statt Klotz mit Kugel Segment(torso, hip + up * 0.26f, neckBase - up * 0.04f, 0.37f, 0.22f); Segment(belly, hip + up * 0.07f + forward * 0.015f, hip + up * 0.3f + forward * 0.015f, 0.33f, 0.25f); belt.localPosition = hip + up * 0.06f; belt.localRotation = torsoRot; belt.localScale = new Vector3(0.34f, 0.024f, 0.27f); // Zylinder: 0.048 m hoch, elliptisch buckle.localPosition = hip + up * 0.06f + forward * 0.136f; buckle.localRotation = torsoRot; buckle.localScale = new Vector3(0.055f, 0.04f, 0.012f); placket.localPosition = Vector3.Lerp(hip + up * 0.12f, neckBase, 0.6f) + forward * 0.124f; placket.localRotation = torsoRot; placket.localScale = new Vector3(0.022f, 0.3f, 0.01f); Segment(pelvis, hip - right * (HipWidth * 0.8f) - up * 0.02f, hip + right * (HipWidth * 0.8f) - up * 0.02f, 0.2f, 0.22f); Segment(collar, neckBase - up * 0.02f, neckBase + up * 0.035f, 0.17f, 0.15f); Segment(neck, neckBase, neckTop, 0.1f, 0.1f); // 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++) { float side = i == 0 ? -1f : 1f; float phase = i == 0 ? swing : -swing; // Beine Vector3 hipJoint = hip + right * (HipWidth * side); Vector3 ankle; if (last.Seated) ankle = hipJoint + new Vector3(0f, -0.4f, 0.48f); else { ankle = new Vector3(HipWidth * side, AnkleHeight + Mathf.Max(0f, Mathf.Cos(walkPhase + i * Mathf.PI)) * 0.07f * walkAmount, phase * 0.3f); // In der Luft: Füße unter der Hüfte mitnehmen und die Knie etwas anziehen ankle.y += air + 0.1f * airAmount; ankle.z -= 0.06f * airAmount; } 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); Segment(sole[i], ankle + new Vector3(0f, -0.07f, -0.075f), ankle + new Vector3(0f, -0.07f, 0.155f), 0.125f, 0.03f); // Arme Vector3 shoulder = neckBase - up * 0.06f + right * (ShoulderWidth * side); shoulderBall[i].localPosition = shoulder + up * 0.005f; shoulderBall[i].localRotation = torsoRot; shoulderBall[i].localScale = new Vector3(0.125f, 0.12f, 0.13f); Vector3 handTarget; Vector3 heldPos; Quaternion heldRot; if (i == 1 && TryGetHeldPose(out heldPos, out heldRot)) handTarget = transform.InverseTransformPoint(heldPos); else if (last.Seated) handTarget = shoulder + new Vector3(-side * 0.05f, -0.3f, 0.38f); // Hände nach vorne (Lenkrad) else handTarget = shoulder + new Vector3(side * (0.06f + 0.12f * airAmount), -(UpperArmLength + ForearmLength) + 0.05f + 0.14f * airAmount, 0.02f - phase * 0.22f); // Nicht weiter als die Armlänge Vector3 reach = handTarget - shoulder; float maxReach = UpperArmLength + ForearmLength - 0.01f; if (reach.magnitude > maxReach) handTarget = shoulder + reach.normalized * maxReach; 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); } /// Zwei-Knochen-Gelenk (Knie/Ellbogen): Punkt zwischen a und b, zur Seite "bend" geknickt. static Vector3 Joint(Vector3 a, Vector3 b, float lengthA, float lengthB, Vector3 bend) { Vector3 ab = b - a; float d = Mathf.Clamp(ab.magnitude, 0.01f, lengthA + lengthB - 0.001f); Vector3 dir = ab.normalized; // Abstand vom Startpunkt entlang ab und seitlicher Abstand (Kosinussatz) float along = (lengthA * lengthA - lengthB * lengthB + d * d) / (2f * d); float sideways = Mathf.Sqrt(Mathf.Max(0f, lengthA * lengthA - along * along)); Vector3 bendDir = bend - dir * Vector3.Dot(bend, dir); if (bendDir.sqrMagnitude < 0.0001f) bendDir = Vector3.forward - dir * Vector3.Dot(Vector3.forward, dir); return a + dir * along + bendDir.normalized * sideways; } /// Kapsel von a nach b spannen (lokaler Raum der Figur). static void Segment(Transform part, Vector3 a, Vector3 b, float width, float depth) { Vector3 dir = b - a; float length = dir.magnitude; part.localPosition = (a + b) * 0.5f; part.localRotation = length > 0.0001f ? Quaternion.FromToRotation(Vector3.up, dir / length) : Quaternion.identity; part.localScale = new Vector3(width, length * 0.5f + Mathf.Min(width, depth) * 0.5f, depth); } /// Wo das gehaltene Item beim Spieler gerade ist (relativ zum Blick). public bool TryGetHeldPose(out Vector3 pos, out Quaternion rot) { pos = Vector3.zero; rot = Quaternion.identity; if (last.HeldId == 0) return false; pos = view.TransformPoint(last.HeldLocalPos); rot = view.rotation * last.HeldLocalRot; return true; } } }