using System; using System.Collections.Generic; using System.IO; using System.Linq; using kt.Physics; using kt.Serialization; using kt.Utility; using UnityEngine; public class DynamicYureBone : MonoBehaviour, IDynamicBone { public DynamicBoneStatus status { get { return this.Status; } } private string ColliderRootName { get { return base.name.Replace("_SM_", string.Empty) + " Collider"; } } public void Init(TBody body, bool particle_setup, params Transform[] rootbones) { this.isEventExecAuto = false; foreach (Transform root_bone in rootbones) { this.AddRootBone(root_bone); } this.Body = body; this.ColliderSearchRoot = this.Body.m_trBones; this.InitLimbColiders(); if (particle_setup) { this.SetupParticles(); } } private void InitLimbColiders() { foreach (LimbColliderMgr.LimbType limbType in (LimbColliderMgr.LimbType[])Enum.GetValues(typeof(LimbColliderMgr.LimbType))) { DynamicYureBone.LimbColliderInfo limbColliderInfo = new DynamicYureBone.LimbColliderInfo(); limbColliderInfo.isEnable = true; limbColliderInfo.limbType = limbType; limbColliderInfo.collider = this.Body.limbColliderMgr.GetCollider(limbType); this.LimbColliderInfoList.Add(limbColliderInfo); } } private void Start() { if (this.NodeDataList.Count == 0) { this.AddRootBone(base.transform); } if (!this.IsParticleSetUped && this.isEventExecAuto) { this.SetupParticles(); } } public void Save(StreamWriter writer) { string value = JsonUtility.ToJson(this.Status, true); writer.Write(value); } public void SaveCollider(StreamWriter writer) { DynamicBoneColliderData dynamicBoneColliderData = new DynamicBoneColliderData(); dynamicBoneColliderData.colliderStatusList = (from col in this.colliderList where col != null select col.status).ToList(); dynamicBoneColliderData.limbEnableList = this.LimbColliderInfoList; Dictionary dictionary = new Dictionary(); foreach (ANativeColliderBase anativeColliderBase in this.colliderList) { Transform parent = anativeColliderBase.transform.parent; Transform parent2 = parent.parent; dictionary[parent2] = parent; anativeColliderBase.transform.SetParent(parent2, true); } string value = JsonUtility.ToJson(dynamicBoneColliderData, true); writer.Write(value); foreach (ANativeColliderBase anativeColliderBase2 in this.colliderList) { Transform parent3 = anativeColliderBase2.transform.parent; anativeColliderBase2.transform.SetParent(dictionary[parent3], true); } } public void Load(StreamReader reader) { string json = reader.ReadToEnd(); this.Status = JsonUtility.FromJson(json); this.UpdateParameters(); } public void LoadCollider(StreamReader reader) { if (!this.ColliderSearchRoot) { DebugUtility.Assert.Call("DynamicYureBone:コライダーの親が設定されてません"); } string json = reader.ReadToEnd(); DynamicBoneColliderData dynamicBoneColliderData = JsonUtility.FromJson(json); if (this.colliderList == null) { this.colliderList = new List(); } else { for (int i = 0; i < this.colliderList.Count; i++) { UnityEngine.Object.DestroyImmediate(this.colliderList[i].gameObject); } this.colliderList.Clear(); } List colliderStatusList = dynamicBoneColliderData.colliderStatusList; for (int j = 0; j < colliderStatusList.Count; j++) { GameObject gameObject = new GameObject("Collider"); NativeColliderStatus nativeColliderStatus = colliderStatusList[j]; ANativeColliderBase anativeColliderBase = null; switch (nativeColliderStatus.colliderType) { case NativeColliderStatus.ColliderType.Plane: anativeColliderBase = gameObject.AddComponent(); break; case NativeColliderStatus.ColliderType.Capsule: anativeColliderBase = gameObject.AddComponent(); break; case NativeColliderStatus.ColliderType.Sphere: anativeColliderBase = gameObject.AddComponent(); break; case NativeColliderStatus.ColliderType.MaidPropCol: { NativeMaidPropCollider nativeMaidPropCollider = gameObject.AddComponent(); if (this.Body) { nativeMaidPropCollider.SetChara(this.Body.maid); } anativeColliderBase = nativeMaidPropCollider; break; } } if (anativeColliderBase) { anativeColliderBase.SetStatus(nativeColliderStatus, this.Body); this.colliderList.Add(anativeColliderBase); this.CreateColliderRoot(anativeColliderBase); } } this.LimbColliderInfoList = dynamicBoneColliderData.limbEnableList; for (int k = 0; k < this.LimbColliderInfoList.Count; k++) { LimbColliderMgr.LimbType limbType = this.LimbColliderInfoList[k].limbType; this.LimbColliderInfoList[k].collider = this.Body.limbColliderMgr.GetCollider(limbType); } } private void CreateColliderRoot(ANativeColliderBase collider) { Transform parent = collider.transform.parent; Transform transform = parent.Find(this.ColliderRootName); if (!transform) { transform = new GameObject(this.ColliderRootName).transform; transform.SetParent(parent, false); this.ColliderRoots.Add(transform); } collider.transform.SetParent(transform, true); } private void AddRootBone(Transform root_bone) { if (!this.RootBones.Contains(root_bone)) { this.NodeDataList.Add(new DynamicYureBone.NodeData(root_bone)); this.RootBones.Add(root_bone); } } private void SetupParticles() { this.ObjectScale = Mathf.Abs(base.transform.lossyScale.x); this.ObjectPrevPosition = base.transform.position; this.ObjectMove = Vector3.zero; this.MaxParticleCount = 0; foreach (DynamicYureBone.NodeData nodeData in this.NodeDataList) { nodeData.particles.Clear(); nodeData.defaultRotation = nodeData.rootBone.rotation; nodeData.localGravity = nodeData.rootBone.InverseTransformDirection(this.status.gravity); this.AppendParticles(nodeData, nodeData.rootBone, -1, 0f); } this.UpdateParameters(); this.IsParticleSetUped = true; } private void AppendParticles(DynamicYureBone.NodeData data, Transform b, int parentIndex, float boneLength) { DynamicYureBone.Particle particle = new DynamicYureBone.Particle(); particle.transform = b; particle.parentIndex = parentIndex; List particles = data.particles; if (b != null) { particle.position = (particle.prevPosition = b.position); particle.initLocalPosition = b.localPosition; particle.initLocalRotation = b.localRotation; } else { Transform transform = particles[parentIndex].transform; if (this.status.endLength > 0f) { Transform parent = transform.parent; if (parent != null) { particle.endOffset = transform.InverseTransformPoint(transform.position * 2f - parent.position) * this.status.endLength; } else { particle.endOffset = new Vector3(this.status.endLength, 0f, 0f); } } else { particle.endOffset = transform.InverseTransformPoint(base.transform.TransformDirection(this.status.endOffset) + transform.position); } particle.position = (particle.prevPosition = transform.TransformPoint(particle.endOffset)); } if (parentIndex >= 0) { boneLength += (particles[parentIndex].transform.position - particle.position).magnitude; particle.boneLength = boneLength; data.boneTotalLength = Mathf.Max(data.boneTotalLength, boneLength); } int count = particles.Count; particles.Add(particle); this.MaxParticleCount = Mathf.Max(this.MaxParticleCount, particles.Count); if (b != null) { for (int i = 0; i < b.childCount; i++) { bool flag = false; if (this.exclusions != null) { for (int j = 0; j < this.exclusions.Count; j++) { Transform x = this.exclusions[j]; if (x == b.GetChild(i)) { flag = true; break; } } } if (!flag) { this.AppendParticles(data, b.GetChild(i), count, boneLength); } else if (this.status.endLength > 0f || this.status.endOffset != Vector3.zero) { this.AppendParticles(data, null, count, boneLength); } } if (b.childCount == 0 && (this.status.endLength > 0f || this.status.endOffset != Vector3.zero)) { this.AppendParticles(data, null, count, boneLength); } } } public void UpdateParameters() { foreach (DynamicYureBone.NodeData nodeData in this.NodeDataList) { nodeData.localGravity = Quaternion.Inverse(nodeData.defaultRotation) * this.status.gravity; for (int i = 0; i < nodeData.particles.Count; i++) { DynamicYureBone.Particle particle = nodeData.particles[i]; particle.damping = this.status.damping; particle.elasticity = this.status.elasticity; particle.stiffness = this.status.stiffness; particle.inert = this.status.inert; particle.radius = this.status.radius; if (nodeData.boneTotalLength > 0f) { float time = particle.boneLength / nodeData.boneTotalLength; if (this.status.dampingCurve != null && this.status.dampingCurve.keys.Length > 0) { particle.damping *= this.status.dampingCurve.Evaluate(time); } if (this.status.elasticityCurve != null && this.status.elasticityCurve.keys.Length > 0) { particle.elasticity *= this.status.elasticityCurve.Evaluate(time); } if (this.status.stiffnessCurve != null && this.status.stiffnessCurve.keys.Length > 0) { particle.stiffness *= this.status.stiffnessCurve.Evaluate(time); } if (this.status.inertCurve != null && this.status.inertCurve.keys.Length > 0) { particle.inert *= this.status.inertCurve.Evaluate(time); } if (this.status.radiusCurve != null && this.status.radiusCurve.keys.Length > 0) { particle.radius *= this.status.radiusCurve.Evaluate(time); } } particle.damping = Mathf.Clamp01(particle.damping); particle.elasticity = Mathf.Clamp01(particle.elasticity); particle.stiffness = Mathf.Clamp01(particle.stiffness); particle.inert = Mathf.Clamp01(particle.inert); particle.radius = Mathf.Max(particle.radius, 0f); } } } private void OnDestroy() { this.UnInit(); } public void UnInit() { for (int i = 0; i < this.ColliderRoots.Count; i++) { if (this.ColliderRoots[i]) { UnityEngine.Object.DestroyImmediate(this.ColliderRoots[i].gameObject); } } this.ColliderRoots.Clear(); this.colliderList.Clear(); this.LimbColliderInfoList.Clear(); } private void OnEnable() { this.ResetParticlesPosition(); } private void OnDisable() { this.InitTransforms(); } public void PhysicsReset() { this.ResetParticlesPosition(); } private void ResetParticlesPosition() { foreach (DynamicYureBone.NodeData nodeData in this.NodeDataList) { List particles = nodeData.particles; for (int i = 0; i < particles.Count; i++) { DynamicYureBone.Particle particle = particles[i]; if (particle.transform != null) { particle.position = (particle.prevPosition = particle.transform.position); } else { Transform transform = particles[particle.parentIndex].transform; particle.position = (particle.prevPosition = transform.TransformPoint(particle.endOffset)); } } } this.ObjectPrevPosition = base.transform.position; } private void FixedUpdate() { if (this.isEventExecAuto && this.updateMode == DynamicYureBone.UpdateMode.AnimatePhysics) { this.PreUpdate(); } } private void Update() { if (this.isEventExecAuto && this.updateMode != DynamicYureBone.UpdateMode.AnimatePhysics) { this.PreUpdate(); } } public void PreUpdate() { if (this.Weight > 0f && (!this.distantDisable || !this.DistantDisabled)) { this.InitTransforms(); } } private void InitTransforms() { foreach (DynamicYureBone.NodeData nodeData in this.NodeDataList) { for (int i = 0; i < nodeData.particles.Count; i++) { DynamicYureBone.Particle particle = nodeData.particles[i]; if (particle.transform != null) { particle.transform.localPosition = particle.initLocalPosition; particle.transform.localRotation = particle.initLocalRotation; } } } } private void LateUpdate() { if (!this.isEventExecAuto) { return; } this.DynamicUpdate(); } public void DynamicUpdate() { if (!base.enabled) { return; } if (!this.IsParticleSetUped) { this.SetupParticles(); } if (!this.isEventExecAuto) { this.PreUpdate(); } if (this.distantDisable) { this.CheckDistance(); } if (this.Weight > 0f && (!this.distantDisable || !this.DistantDisabled)) { float t = (this.updateMode != DynamicYureBone.UpdateMode.UnscaledTime) ? Time.deltaTime : Time.unscaledDeltaTime; this.UpdateDynamicBones(t); } } private void CheckDistance() { Transform transform = this.referenceObject; if (transform == null && Camera.main != null) { transform = Camera.main.transform; } if (transform != null) { float sqrMagnitude = (transform.position - base.transform.position).sqrMagnitude; bool flag = sqrMagnitude > this.distanceToObject * this.distanceToObject; if (flag != this.DistantDisabled) { if (!flag) { this.ResetParticlesPosition(); } this.DistantDisabled = flag; } } } private void UpdateDynamicBones(float t) { this.ObjectScale = Mathf.Abs(base.transform.lossyScale.x); this.ObjectMove = base.transform.position - this.ObjectPrevPosition; this.ObjectPrevPosition = base.transform.position; int num = 1; if (this.updateRate > 0f) { float num2 = 1f / this.updateRate; this.Timer += t; num = 0; while (this.Timer >= num2) { this.Timer -= num2; if (++num >= 3) { this.Timer = 0f; break; } } } for (int i = 0; i < this.MaxParticleCount; i++) { if (num > 0) { this.UpdateParticles1(i); this.UpdateParticles2(i); } else { this.SkipUpdateParticles(i); } } this.ObjectMove = Vector3.zero; this.ApplyParticlesToTransforms(); } private void UpdateParticles1(int index) { Vector3 normalized = this.status.gravity.normalized; foreach (DynamicYureBone.NodeData nodeData in this.NodeDataList) { List particles = nodeData.particles; if (index < particles.Count) { DynamicYureBone.Particle particle = particles[index]; Vector3 vector = this.status.gravity; Vector3 lhs = nodeData.rootBone.TransformDirection(nodeData.localGravity); Vector3 b = normalized * Mathf.Max(Vector3.Dot(lhs, normalized), 0f); vector -= b; vector = (vector + this.status.force) * this.ObjectScale; if (particle.parentIndex >= 0) { Vector3 a = particle.position - particle.prevPosition; Vector3 b2 = this.ObjectMove * particle.inert; particle.prevPosition = particle.position + b2; particle.position += a * (1f - particle.damping) + vector + b2; } else { particle.prevPosition = particle.position; particle.position = particle.transform.position; } } } } private void UpdateParticles2(int index) { if (index == 0) { return; } Plane plane = default(Plane); foreach (DynamicYureBone.NodeData nodeData in this.NodeDataList) { List particles = nodeData.particles; if (index < particles.Count) { DynamicYureBone.Particle particle = particles[index]; DynamicYureBone.Particle particle2 = particles[particle.parentIndex]; float magnitude; if (particle.transform != null) { magnitude = (particle2.transform.position - particle.transform.position).magnitude; } else { magnitude = particle2.transform.localToWorldMatrix.MultiplyVector(particle.endOffset).magnitude; } float num = Mathf.Lerp(1f, particle.stiffness, this.Weight); if (num > 0f || particle.elasticity > 0f) { Matrix4x4 localToWorldMatrix = particle2.transform.localToWorldMatrix; localToWorldMatrix.SetColumn(3, particle2.position); Vector3 a; if (particle.transform != null) { a = localToWorldMatrix.MultiplyPoint3x4(particle.transform.localPosition); } else { a = localToWorldMatrix.MultiplyPoint3x4(particle.endOffset); } Vector3 a2 = a - particle.position; particle.position += a2 * particle.elasticity; if (num > 0f) { a2 = a - particle.position; float magnitude2 = a2.magnitude; float num2 = magnitude * (1f - num) * 2f; if (magnitude2 > num2) { particle.position += a2 * ((magnitude2 - num2) / magnitude2); } } } float radius = particle.radius * this.ObjectScale; if (this.colliderList != null) { for (int i = 0; i < this.colliderList.Count; i++) { ANativeColliderBase anativeColliderBase = this.colliderList[i]; if (anativeColliderBase != null && anativeColliderBase.enabled) { anativeColliderBase.Collide(ref particle.position, radius); } } } if (this.floorPlaneColider) { this.floorPlaneColider.Collide(ref particle.position, radius); } for (int j = 0; j < this.LimbColliderInfoList.Count; j++) { if (this.LimbColliderInfoList[j].isEnable) { this.LimbColliderInfoList[j].collider.Collide(ref particle.position, radius); } } if (this.status.freezeAxis != DynamicBoneStatus.FreezeAxis.None) { DynamicBoneStatus.FreezeAxis freezeAxis = this.status.freezeAxis; if (freezeAxis != DynamicBoneStatus.FreezeAxis.X) { if (freezeAxis != DynamicBoneStatus.FreezeAxis.Y) { if (freezeAxis == DynamicBoneStatus.FreezeAxis.Z) { plane.SetNormalAndPosition(particle2.transform.forward, particle2.position); } } else { plane.SetNormalAndPosition(particle2.transform.up, particle2.position); } } else { plane.SetNormalAndPosition(particle2.transform.right, particle2.position); } particle.position -= plane.normal * plane.GetDistanceToPoint(particle.position); } Vector3 a3 = particle2.position - particle.position; float magnitude3 = a3.magnitude; if (magnitude3 > 0f) { particle.position += a3 * ((magnitude3 - magnitude) / magnitude3); } } } } private static Vector3 MirrorVector(Vector3 v, Vector3 axis) { return v - axis * (Vector3.Dot(v, axis) * 2f); } private void SkipUpdateParticles(int index) { foreach (DynamicYureBone.NodeData nodeData in this.NodeDataList) { List particles = nodeData.particles; if (index < particles.Count) { DynamicYureBone.Particle particle = particles[index]; if (particle.parentIndex >= 0) { particle.prevPosition += this.ObjectMove; particle.position += this.ObjectMove; DynamicYureBone.Particle particle2 = particles[particle.parentIndex]; float magnitude; if (particle.transform != null) { magnitude = (particle2.transform.position - particle.transform.position).magnitude; } else { magnitude = particle2.transform.localToWorldMatrix.MultiplyVector(particle.endOffset).magnitude; } float num = Mathf.Lerp(1f, particle.stiffness, this.Weight); if (num > 0f) { Matrix4x4 localToWorldMatrix = particle2.transform.localToWorldMatrix; localToWorldMatrix.SetColumn(3, particle2.position); Vector3 a; if (particle.transform != null) { a = localToWorldMatrix.MultiplyPoint3x4(particle.transform.localPosition); } else { a = localToWorldMatrix.MultiplyPoint3x4(particle.endOffset); } Vector3 a2 = a - particle.position; float magnitude2 = a2.magnitude; float num2 = magnitude * (1f - num) * 2f; if (magnitude2 > num2) { particle.position += a2 * ((magnitude2 - num2) / magnitude2); } } Vector3 a3 = particle2.position - particle.position; float magnitude3 = a3.magnitude; if (magnitude3 > 0f) { particle.position += a3 * ((magnitude3 - magnitude) / magnitude3); } } else { particle.prevPosition = particle.position; particle.position = particle.transform.position; } } } } private void ApplyParticlesToTransforms() { foreach (DynamicYureBone.NodeData nodeData in this.NodeDataList) { List particles = nodeData.particles; for (int i = 1; i < particles.Count; i++) { DynamicYureBone.Particle particle = particles[i]; DynamicYureBone.Particle particle2 = particles[particle.parentIndex]; if (particle2.transform.childCount <= 1) { Vector3 direction; if (particle.transform != null) { direction = particle.transform.localPosition; } else { direction = particle.endOffset; } Vector3 toDirection = particle.position - particle2.position; Quaternion lhs = Quaternion.FromToRotation(particle2.transform.TransformDirection(direction), toDirection); particle2.transform.rotation = lhs * particle2.transform.rotation; } if (particle.transform != null) { particle.transform.position = particle.position; } } } } public float GetWeight() { return this.Weight; } public void SetWeight(float w) { if (this.Weight != w) { if (w == 0f) { this.InitTransforms(); } else if (this.Weight == 0f) { this.ResetParticlesPosition(); } this.Weight = w; } } public const string STATUS_FILE_EXTENTION = "dbconf"; public const string COLLIDER_FILE_EXTENTION = "dbcol"; [SerializeField] private TBody Body; [SerializeField] private Transform ColliderSearchRoot; [SerializeField] [Header("揺れボーンリスト")] [ReadOnly] [Space] private List RootBones = new List(); [Header("更新頻度。0だと現在のFPSに依存する")] [Tooltip("Internal physics simulation rate.")] public float updateRate; public DynamicYureBone.UpdateMode updateMode; [Header("適用してるパラメータ設定ファイル")] [ReadOnly] public string loadConfigFile = string.Empty; [SerializeField] private DynamicBoneStatus Status = new DynamicBoneStatus(); [Header("適用してる当たり判定設定ファイル")] [ReadOnly] public string loadColliderFile = string.Empty; [SerializeField] private List LimbColliderInfoList = new List(); [Tooltip("Collider objects interact with the bones.")] public List colliderList; [Header("床コライダー")] public NativePlaneCollider floorPlaneColider; private List ColliderRoots = new List(); [Header("物理計算から特別に除外するノード(子供も動かなくなる)")] [Tooltip("Bones exclude from physics simulation.")] public List exclusions; [Header("距離による物理自動無効化")] [Tooltip("Disable physics simulation automatically if character is far from camera or player.")] public bool distantDisable; public Transform referenceObject; public float distanceToObject = 20f; private Vector3 ObjectMove = Vector3.zero; private Vector3 ObjectPrevPosition = Vector3.zero; private float ObjectScale = 1f; private float Timer; private float Weight = 1f; private bool DistantDisabled; private List NodeDataList = new List(); [Header("自動で物理処理を更新するか")] public bool isEventExecAuto = true; private int MaxParticleCount; private bool IsParticleSetUped; [Serializable] public class LimbColliderInfo : ASerializationVersionControl { public override int FixVersion { get { return 1000; } } [ReadOnly] public LimbColliderMgr.LimbType limbType; public bool isEnable; [NonSerialized] public NativeCapsuleCollider collider; } public enum UpdateMode { Normal, AnimatePhysics, UnscaledTime } public class Particle { public Transform transform; public int parentIndex = -1; public float damping; public float elasticity; public float stiffness; public float inert; public float radius; public float boneLength; public Vector3 position = Vector3.zero; public Vector3 prevPosition = Vector3.zero; public Vector3 endOffset = Vector3.zero; public Vector3 initLocalPosition = Vector3.zero; public Quaternion initLocalRotation = Quaternion.identity; } private class NodeData { public NodeData(Transform root) { this.rootBone = root; this.defaultRotation = root.rotation; } public DynamicYureBone.Particle GetParticle(int index) { return this.particles[index]; } public readonly Transform rootBone; public List particles = new List(); public Vector3 localGravity; public float boneTotalLength; public Quaternion defaultRotation; } }