using System.Collections.Generic; using System.Reflection; using UnityEngine; namespace MSCO { /// /// Räder an Fahrzeugen, die ein anderer Spieler fährt, drehen sich mit. Bei uns wird so ein Auto nur verschoben /// (kinematisch), die Rad-Physik des Spiels (CarDynamics "Wheel") dreht die Räder dann nicht. Wir drehen das /// Rad-Modell passend zur Geschwindigkeit um seine Achse - nach dem Spiel (LateUpdate), ohne dessen Werte zu ändern. /// public class WheelSpin { const float CacheRefresh = 5f; // Räder können ab-/angebaut werden class WheelInfo { public Transform Model; public Transform Hub; // Transform der Wheel-Komponente (Fahrtrichtung) public float Radius; public float Angle; public Quaternion Base; // Drehung ohne unsere Rollbewegung public Quaternion Written; // zuletzt von uns gesetzt public bool HasWritten; } class Vehicle { public float BuiltAt; public readonly List Wheels = new List(); } readonly Dictionary vehicles = new Dictionary(); static System.Type wheelType; static FieldInfo modelField, radiusField; static bool lookedUp; public void Reset() { vehicles.Clear(); } public void Apply(ObjectSync objects, float dt) { if (!Lookup()) return; List driven = objects.RemoteDrivenVehicles(); if (driven.Count == 0) { if (vehicles.Count > 0) vehicles.Clear(); return; } foreach (SyncedBody b in driven) { Vehicle v; if (!vehicles.TryGetValue(b.Id, out v) || Time.time - v.BuiltAt > CacheRefresh) { v = Build(b.Rb.transform); vehicles[b.Id] = v; } Vector3 vel = b.TargetVel; foreach (WheelInfo w in v.Wheels) { if (w.Model == null || w.Hub == null) continue; float speed = Vector3.Dot(vel, w.Hub.forward); w.Angle = Mathf.Repeat(w.Angle + speed / w.Radius * Mathf.Rad2Deg * dt, 360f); if (float.IsNaN(w.Angle) || float.IsInfinity(w.Angle)) w.Angle = 0f; // Hat das Spiel das Modell in diesem Frame selbst gedreht (z.B. Lenkung), ist das die neue Basis Quaternion current = w.Model.localRotation; if (!w.HasWritten || current != w.Written) w.Base = current; w.Written = w.Base * Quaternion.Euler(w.Angle, 0f, 0f); w.Model.localRotation = w.Written; w.HasWritten = true; } } } static Vehicle Build(Transform root) { var v = new Vehicle { BuiltAt = Time.time }; foreach (Component c in root.GetComponentsInChildren(wheelType, true)) { var model = modelField.GetValue(c) as GameObject; float radius = radiusField != null ? (float)radiusField.GetValue(c) : 0.3f; if (model == null || radius < 0.05f || radius > 2f) continue; v.Wheels.Add(new WheelInfo { Model = model.transform, Hub = c.transform, Radius = radius }); } return v; } static bool Lookup() { if (lookedUp) return wheelType != null; lookedUp = true; foreach (Assembly a in System.AppDomain.CurrentDomain.GetAssemblies()) { if (a.GetName().Name != "Assembly-CSharp") continue; wheelType = a.GetType("Wheel"); break; } if (wheelType == null) { MscoLog.Warning("[MSCO] Wheel type not found - remote wheels will not spin"); return false; } modelField = wheelType.GetField("model", BindingFlags.Public | BindingFlags.Instance); radiusField = wheelType.GetField("radius", BindingFlags.Public | BindingFlags.Instance); if (modelField == null) { wheelType = null; return false; } return true; } } }