103 lines
4.1 KiB
C#
103 lines
4.1 KiB
C#
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using UnityEngine;
|
|
|
|
namespace MSCO
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
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<WheelInfo> Wheels = new List<WheelInfo>();
|
|
}
|
|
|
|
readonly Dictionary<int, Vehicle> vehicles = new Dictionary<int, Vehicle>();
|
|
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<SyncedBody> 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);
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
}
|