diff --git a/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBone.cs b/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBone.cs new file mode 100644 index 0000000..dc5ad63 --- /dev/null +++ b/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBone.cs @@ -0,0 +1,718 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace ScarletMansion.GamePatch.DynamicBone { + +[AddComponentMenu("Dynamic Bone/Dynamic Bone")] +public class DynamicBone : MonoBehaviour +{ +#if UNITY_5_3_OR_NEWER + [Tooltip("The root of the transform hierarchy to apply physics.")] +#endif + public Transform m_Root = null; + +#if UNITY_5_3_OR_NEWER + [Tooltip("Internal physics simulation rate.")] +#endif + public float m_UpdateRate = 60.0f; + + public enum UpdateMode + { + Normal, + AnimatePhysics, + UnscaledTime, + Default + } + public UpdateMode m_UpdateMode = UpdateMode.Default; + +#if UNITY_5_3_OR_NEWER + [Tooltip("How much the bones slowed down.")] +#endif + [Range(0, 1)] + public float m_Damping = 0.1f; + public AnimationCurve m_DampingDistrib = null; + +#if UNITY_5_3_OR_NEWER + [Tooltip("How much the force applied to return each bone to original orientation.")] +#endif + [Range(0, 1)] + public float m_Elasticity = 0.1f; + public AnimationCurve m_ElasticityDistrib = null; + +#if UNITY_5_3_OR_NEWER + [Tooltip("How much bone's original orientation are preserved.")] +#endif + [Range(0, 1)] + public float m_Stiffness = 0.1f; + public AnimationCurve m_StiffnessDistrib = null; + +#if UNITY_5_3_OR_NEWER + [Tooltip("How much character's position change is ignored in physics simulation.")] +#endif + [Range(0, 1)] + public float m_Inert = 0; + public AnimationCurve m_InertDistrib = null; + +#if UNITY_5_3_OR_NEWER + [Tooltip("How much the bones slowed down when collide.")] +#endif + public float m_Friction = 0; + public AnimationCurve m_FrictionDistrib = null; + +#if UNITY_5_3_OR_NEWER + [Tooltip("Each bone can be a sphere to collide with colliders. Radius describe sphere's size.")] +#endif + public float m_Radius = 0; + public AnimationCurve m_RadiusDistrib = null; + +#if UNITY_5_3_OR_NEWER + [Tooltip("If End Length is not zero, an extra bone is generated at the end of transform hierarchy.")] +#endif + public float m_EndLength = 0; + +#if UNITY_5_3_OR_NEWER + [Tooltip("If End Offset is not zero, an extra bone is generated at the end of transform hierarchy.")] +#endif + public Vector3 m_EndOffset = Vector3.zero; + +#if UNITY_5_3_OR_NEWER + [Tooltip("The force apply to bones. Partial force apply to character's initial pose is cancelled out.")] +#endif + public Vector3 m_Gravity = Vector3.zero; + +#if UNITY_5_3_OR_NEWER + [Tooltip("The force apply to bones.")] +#endif + public Vector3 m_Force = Vector3.zero; + +#if UNITY_5_3_OR_NEWER + [Tooltip("Collider objects interact with the bones.")] +#endif + public List m_Colliders = null; + +#if UNITY_5_3_OR_NEWER + [Tooltip("Bones exclude from physics simulation.")] +#endif + public List m_Exclusions = null; + + + public enum FreezeAxis + { + None, X, Y, Z + } +#if UNITY_5_3_OR_NEWER + [Tooltip("Constrain bones to move on specified plane.")] +#endif + public FreezeAxis m_FreezeAxis = FreezeAxis.None; + +#if UNITY_5_3_OR_NEWER + [Tooltip("Disable physics simulation automatically if character is far from camera or player.")] +#endif + public bool m_DistantDisable = false; + public Transform m_ReferenceObject = null; + public float m_DistanceToObject = 20; + + Vector3 m_LocalGravity = Vector3.zero; + Vector3 m_ObjectMove = Vector3.zero; + Vector3 m_ObjectPrevPosition = Vector3.zero; + float m_BoneTotalLength = 0; + float m_ObjectScale = 1.0f; + float m_Time = 0; + float m_Weight = 1.0f; + bool m_DistantDisabled = false; + + class Particle + { + public Transform m_Transform = null; + public int m_ParentIndex = -1; + public float m_Damping = 0; + public float m_Elasticity = 0; + public float m_Stiffness = 0; + public float m_Inert = 0; + public float m_Friction = 0; + public float m_Radius = 0; + public float m_BoneLength = 0; + public bool m_isCollide = false; + + public Vector3 m_Position = Vector3.zero; + public Vector3 m_PrevPosition = Vector3.zero; + public Vector3 m_EndOffset = Vector3.zero; + public Vector3 m_InitLocalPosition = Vector3.zero; + public Quaternion m_InitLocalRotation = Quaternion.identity; + } + + List m_Particles = new List(); + + void Start() + { + SetupParticles(); + } + + void FixedUpdate() + { + if (m_UpdateMode == UpdateMode.AnimatePhysics) + PreUpdate(); + } + + void Update() + { + if (m_UpdateMode != UpdateMode.AnimatePhysics) + PreUpdate(); + } + + void LateUpdate() + { + if (m_DistantDisable) + CheckDistance(); + + if (m_Weight > 0 && !(m_DistantDisable && m_DistantDisabled)) + { +#if UNITY_5_3_OR_NEWER + float dt = m_UpdateMode == UpdateMode.UnscaledTime ? Time.unscaledDeltaTime : Time.deltaTime; +#else + float dt = Time.deltaTime; +#endif + UpdateDynamicBones(dt); + } + } + + void PreUpdate() + { + if (m_Weight > 0 && !(m_DistantDisable && m_DistantDisabled)) + InitTransforms(); + } + + void CheckDistance() + { + Transform rt = m_ReferenceObject; + if (rt == null && Camera.main != null) + rt = Camera.main.transform; + if (rt != null) + { + float d = (rt.position - transform.position).sqrMagnitude; + bool disable = d > m_DistanceToObject * m_DistanceToObject; + if (disable != m_DistantDisabled) + { + if (!disable) + ResetParticlesPosition(); + m_DistantDisabled = disable; + } + } + } + + void OnEnable() + { + ResetParticlesPosition(); + } + + void OnDisable() + { + InitTransforms(); + } + + void OnValidate() + { + m_UpdateRate = Mathf.Max(m_UpdateRate, 0); + m_Damping = Mathf.Clamp01(m_Damping); + m_Elasticity = Mathf.Clamp01(m_Elasticity); + m_Stiffness = Mathf.Clamp01(m_Stiffness); + m_Inert = Mathf.Clamp01(m_Inert); + m_Friction = Mathf.Clamp01(m_Friction); + m_Radius = Mathf.Max(m_Radius, 0); + + if (Application.isEditor && Application.isPlaying) + { + InitTransforms(); + SetupParticles(); + } + } + + void OnDrawGizmosSelected() + { + if (!enabled || m_Root == null) + return; + + if (Application.isEditor && !Application.isPlaying && transform.hasChanged) + { + InitTransforms(); + SetupParticles(); + } + + Gizmos.color = Color.white; + for (int i = 0; i < m_Particles.Count; ++i) + { + Particle p = m_Particles[i]; + if (p.m_ParentIndex >= 0) + { + Particle p0 = m_Particles[p.m_ParentIndex]; + Gizmos.DrawLine(p.m_Position, p0.m_Position); + } + if (p.m_Radius > 0) + Gizmos.DrawWireSphere(p.m_Position, p.m_Radius * m_ObjectScale); + } + } + + public void SetWeight(float w) + { + if (m_Weight != w) + { + if (w == 0) + InitTransforms(); + else if (m_Weight == 0) + ResetParticlesPosition(); + m_Weight = w; + } + } + + public float GetWeight() + { + return m_Weight; + } + + void UpdateDynamicBones(float t) + { + if (m_Root == null) + return; + + m_ObjectScale = Mathf.Abs(transform.lossyScale.x); + m_ObjectMove = transform.position - m_ObjectPrevPosition; + m_ObjectPrevPosition = transform.position; + + int loop = 1; + float timeVar = 1; + + if (m_UpdateMode == UpdateMode.Default) + { + if (m_UpdateRate > 0) + { + timeVar = Time.deltaTime * m_UpdateRate; + } + else + { + timeVar = Time.deltaTime; + } + } + else + { + if (m_UpdateRate > 0) + { + float dt = 1.0f / m_UpdateRate; + m_Time += t; + loop = 0; + + while (m_Time >= dt) + { + m_Time -= dt; + if (++loop >= 3) + { + m_Time = 0; + break; + } + } + } + } + + if (loop > 0) + { + for (int i = 0; i < loop; ++i) + { + UpdateParticles1(timeVar); + UpdateParticles2(timeVar); + m_ObjectMove = Vector3.zero; + } + } + else + { + SkipUpdateParticles(); + } + + ApplyParticlesToTransforms(); + } + + public void SetupParticles() + { + m_Particles.Clear(); + if (m_Root == null) + return; + + m_LocalGravity = m_Root.InverseTransformDirection(m_Gravity); + m_ObjectScale = Mathf.Abs(transform.lossyScale.x); + m_ObjectPrevPosition = transform.position; + m_ObjectMove = Vector3.zero; + m_BoneTotalLength = 0; + AppendParticles(m_Root, -1, 0); + UpdateParameters(); + } + + void AppendParticles(Transform b, int parentIndex, float boneLength) + { + Particle p = new Particle(); + p.m_Transform = b; + p.m_ParentIndex = parentIndex; + if (b != null) + { + p.m_Position = p.m_PrevPosition = b.position; + p.m_InitLocalPosition = b.localPosition; + p.m_InitLocalRotation = b.localRotation; + } + else // end bone + { + Transform pb = m_Particles[parentIndex].m_Transform; + if (m_EndLength > 0) + { + Transform ppb = pb.parent; + if (ppb != null) + p.m_EndOffset = pb.InverseTransformPoint((pb.position * 2 - ppb.position)) * m_EndLength; + else + p.m_EndOffset = new Vector3(m_EndLength, 0, 0); + } + else + { + p.m_EndOffset = pb.InverseTransformPoint(transform.TransformDirection(m_EndOffset) + pb.position); + } + p.m_Position = p.m_PrevPosition = pb.TransformPoint(p.m_EndOffset); + } + + if (parentIndex >= 0) + { + boneLength += (m_Particles[parentIndex].m_Transform.position - p.m_Position).magnitude; + p.m_BoneLength = boneLength; + m_BoneTotalLength = Mathf.Max(m_BoneTotalLength, boneLength); + } + + int index = m_Particles.Count; + m_Particles.Add(p); + + if (b != null) + { + for (int i = 0; i < b.childCount; ++i) + { + Transform child = b.GetChild(i); + bool exclude = false; + if (m_Exclusions != null) + { + exclude = m_Exclusions.Contains(child); + } + if (!exclude) + AppendParticles(child, index, boneLength); + else if (m_EndLength > 0 || m_EndOffset != Vector3.zero) + AppendParticles(null, index, boneLength); + } + + if (b.childCount == 0 && (m_EndLength > 0 || m_EndOffset != Vector3.zero)) + AppendParticles(null, index, boneLength); + } + } + + public void UpdateParameters() + { + if (m_Root == null) + return; + + m_LocalGravity = m_Root.InverseTransformDirection(m_Gravity); + + for (int i = 0; i < m_Particles.Count; ++i) + { + Particle p = m_Particles[i]; + p.m_Damping = m_Damping; + p.m_Elasticity = m_Elasticity; + p.m_Stiffness = m_Stiffness; + p.m_Inert = m_Inert; + p.m_Friction = m_Friction; + p.m_Radius = m_Radius; + + if (m_BoneTotalLength > 0) + { + float a = p.m_BoneLength / m_BoneTotalLength; + if (m_DampingDistrib != null && m_DampingDistrib.keys.Length > 0) + p.m_Damping *= m_DampingDistrib.Evaluate(a); + if (m_ElasticityDistrib != null && m_ElasticityDistrib.keys.Length > 0) + p.m_Elasticity *= m_ElasticityDistrib.Evaluate(a); + if (m_StiffnessDistrib != null && m_StiffnessDistrib.keys.Length > 0) + p.m_Stiffness *= m_StiffnessDistrib.Evaluate(a); + if (m_InertDistrib != null && m_InertDistrib.keys.Length > 0) + p.m_Inert *= m_InertDistrib.Evaluate(a); + if (m_FrictionDistrib != null && m_FrictionDistrib.keys.Length > 0) + p.m_Friction *= m_FrictionDistrib.Evaluate(a); + if (m_RadiusDistrib != null && m_RadiusDistrib.keys.Length > 0) + p.m_Radius *= m_RadiusDistrib.Evaluate(a); + } + + p.m_Damping = Mathf.Clamp01(p.m_Damping); + p.m_Elasticity = Mathf.Clamp01(p.m_Elasticity); + p.m_Stiffness = Mathf.Clamp01(p.m_Stiffness); + p.m_Inert = Mathf.Clamp01(p.m_Inert); + p.m_Friction = Mathf.Clamp01(p.m_Friction); + p.m_Radius = Mathf.Max(p.m_Radius, 0); + } + } + + void InitTransforms() + { + for (int i = 0; i < m_Particles.Count; ++i) + { + Particle p = m_Particles[i]; + if (p.m_Transform != null) + { + p.m_Transform.localPosition = p.m_InitLocalPosition; + p.m_Transform.localRotation = p.m_InitLocalRotation; + } + } + } + + void ResetParticlesPosition() + { + for (int i = 0; i < m_Particles.Count; ++i) + { + Particle p = m_Particles[i]; + if (p.m_Transform != null) + { + p.m_Position = p.m_PrevPosition = p.m_Transform.position; + } + else // end bone + { + Transform pb = m_Particles[p.m_ParentIndex].m_Transform; + p.m_Position = p.m_PrevPosition = pb.TransformPoint(p.m_EndOffset); + } + p.m_isCollide = false; + } + m_ObjectPrevPosition = transform.position; + } + + void UpdateParticles1(float timeVar) + { + Vector3 force = m_Gravity; + Vector3 fdir = m_Gravity.normalized; + Vector3 rf = m_Root.TransformDirection(m_LocalGravity); + Vector3 pf = fdir * Mathf.Max(Vector3.Dot(rf, fdir), 0); // project current gravity to rest gravity + force -= pf; // remove projected gravity + force = (force + m_Force) * (m_ObjectScale * timeVar); + + for (int i = 0; i < m_Particles.Count; ++i) + { + Particle p = m_Particles[i]; + if (p.m_ParentIndex >= 0) + { + // verlet integration + Vector3 v = p.m_Position - p.m_PrevPosition; + Vector3 rmove = m_ObjectMove * p.m_Inert; + p.m_PrevPosition = p.m_Position + rmove; + float damping = p.m_Damping; + if (p.m_isCollide) + { + damping += p.m_Friction; + if (damping > 1) + damping = 1; + p.m_isCollide = false; + } + p.m_Position += v * (1 - damping) + force + rmove; + } + else + { + p.m_PrevPosition = p.m_Position; + p.m_Position = p.m_Transform.position; + } + } + } + + void UpdateParticles2(float timeVar) + { + Plane movePlane = new Plane(); + + for (int i = 1; i < m_Particles.Count; ++i) + { + Particle p = m_Particles[i]; + Particle p0 = m_Particles[p.m_ParentIndex]; + + float restLen; + if (p.m_Transform != null) + restLen = (p0.m_Transform.position - p.m_Transform.position).magnitude; + else + restLen = p0.m_Transform.localToWorldMatrix.MultiplyVector(p.m_EndOffset).magnitude; + + // keep shape + float stiffness = Mathf.Lerp(1.0f, p.m_Stiffness, m_Weight); + if (stiffness > 0 || p.m_Elasticity > 0) + { + Matrix4x4 m0 = p0.m_Transform.localToWorldMatrix; + m0.SetColumn(3, p0.m_Position); + Vector3 restPos; + if (p.m_Transform != null) + restPos = m0.MultiplyPoint3x4(p.m_Transform.localPosition); + else + restPos = m0.MultiplyPoint3x4(p.m_EndOffset); + + Vector3 d = restPos - p.m_Position; + p.m_Position += d * (p.m_Elasticity * timeVar); + + if (stiffness > 0) + { + d = restPos - p.m_Position; + float len = d.magnitude; + float maxlen = restLen * (1 - stiffness) * 2; + if (len > maxlen) + p.m_Position += d * ((len - maxlen) / len); + } + } + + // collide + if (m_Colliders != null) + { + float particleRadius = p.m_Radius * m_ObjectScale; + for (int j = 0; j < m_Colliders.Count; ++j) + { + DynamicBoneColliderBase c = m_Colliders[j]; + if (c != null && c.enabled) + p.m_isCollide |= c.Collide(ref p.m_Position, particleRadius); + } + } + + // freeze axis, project to plane + if (m_FreezeAxis != FreezeAxis.None) + { + switch (m_FreezeAxis) + { + case FreezeAxis.X: + movePlane.SetNormalAndPosition(p0.m_Transform.right, p0.m_Position); + break; + case FreezeAxis.Y: + movePlane.SetNormalAndPosition(p0.m_Transform.up, p0.m_Position); + break; + case FreezeAxis.Z: + movePlane.SetNormalAndPosition(p0.m_Transform.forward, p0.m_Position); + break; + } + p.m_Position -= movePlane.normal * movePlane.GetDistanceToPoint(p.m_Position); + } + + // keep length + Vector3 dd = p0.m_Position - p.m_Position; + float leng = dd.magnitude; + if (leng > 0) + p.m_Position += dd * ((leng - restLen) / leng); + } + } + + // only update stiffness and keep bone length + void SkipUpdateParticles() + { + for (int i = 0; i < m_Particles.Count; ++i) + { + Particle p = m_Particles[i]; + if (p.m_ParentIndex >= 0) + { + p.m_PrevPosition += m_ObjectMove; + p.m_Position += m_ObjectMove; + + Particle p0 = m_Particles[p.m_ParentIndex]; + + float restLen; + if (p.m_Transform != null) + restLen = (p0.m_Transform.position - p.m_Transform.position).magnitude; + else + restLen = p0.m_Transform.localToWorldMatrix.MultiplyVector(p.m_EndOffset).magnitude; + + // keep shape + float stiffness = Mathf.Lerp(1.0f, p.m_Stiffness, m_Weight); + if (stiffness > 0) + { + Matrix4x4 m0 = p0.m_Transform.localToWorldMatrix; + m0.SetColumn(3, p0.m_Position); + Vector3 restPos; + if (p.m_Transform != null) + restPos = m0.MultiplyPoint3x4(p.m_Transform.localPosition); + else + restPos = m0.MultiplyPoint3x4(p.m_EndOffset); + + Vector3 d = restPos - p.m_Position; + float len = d.magnitude; + float maxlen = restLen * (1 - stiffness) * 2; + if (len > maxlen) + p.m_Position += d * ((len - maxlen) / len); + } + + // keep length + Vector3 dd = p0.m_Position - p.m_Position; + float leng = dd.magnitude; + if (leng > 0) + p.m_Position += dd * ((leng - restLen) / leng); + } + else + { + p.m_PrevPosition = p.m_Position; + p.m_Position = p.m_Transform.position; + } + } + } + + static Vector3 MirrorVector(Vector3 v, Vector3 axis) + { + return v - axis * (Vector3.Dot(v, axis) * 2); + } + + void ApplyParticlesToTransforms() + { +#if !UNITY_5_4_OR_NEWER + // detect negative scale + Vector3 ax = Vector3.right; + Vector3 ay = Vector3.up; + Vector3 az = Vector3.forward; + bool nx = false, ny = false, nz = false; + + Vector3 loosyScale = transform.lossyScale; + if (loosyScale.x < 0 || loosyScale.y < 0 || loosyScale.z < 0) + { + Transform mirrorObject = transform; + do + { + Vector3 ls = mirrorObject.localScale; + nx = ls.x < 0; + if (nx) + ax = mirrorObject.right; + ny = ls.y < 0; + if (ny) + ay = mirrorObject.up; + nz = ls.z < 0; + if (nz) + az = mirrorObject.forward; + if (nx || ny || nz) + break; + + mirrorObject = mirrorObject.parent; + } + while (mirrorObject != null); + } +#endif + + for (int i = 1; i < m_Particles.Count; ++i) + { + Particle p = m_Particles[i]; + Particle p0 = m_Particles[p.m_ParentIndex]; + + if (p0.m_Transform.childCount <= 1) // do not modify bone orientation if has more then one child + { + Vector3 v; + if (p.m_Transform != null) + v = p.m_Transform.localPosition; + else + v = p.m_EndOffset; + Vector3 v2 = p.m_Position - p0.m_Position; +#if !UNITY_5_4_OR_NEWER + if (nx) + v2 = MirrorVector(v2, ax); + if (ny) + v2 = MirrorVector(v2, ay); + if (nz) + v2 = MirrorVector(v2, az); +#endif + Quaternion rot = Quaternion.FromToRotation(p0.m_Transform.TransformDirection(v), v2); + p0.m_Transform.rotation = rot * p0.m_Transform.rotation; + } + + if (p.m_Transform != null) + p.m_Transform.position = p.m_Position; + } + } +} + +} \ No newline at end of file diff --git a/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBoneCollider.cs b/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBoneCollider.cs new file mode 100644 index 0000000..289d8a0 --- /dev/null +++ b/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBoneCollider.cs @@ -0,0 +1,239 @@ +using UnityEngine; + +namespace ScarletMansion.GamePatch.DynamicBone { + +[AddComponentMenu("Dynamic Bone/Dynamic Bone Collider")] +public class DynamicBoneCollider : DynamicBoneColliderBase +{ +#if UNITY_5_3_OR_NEWER + [Tooltip("The radius of the sphere or capsule.")] +#endif + public float m_Radius = 0.5f; + +#if UNITY_5_3_OR_NEWER + [Tooltip("The height of the capsule.")] +#endif + public float m_Height = 0; + + void OnValidate() + { + m_Radius = Mathf.Max(m_Radius, 0); + m_Height = Mathf.Max(m_Height, 0); + } + + public override bool Collide(ref Vector3 particlePosition, float particleRadius) + { + float radius = m_Radius * Mathf.Abs(transform.lossyScale.x); + float h = m_Height * 0.5f - m_Radius; + if (h <= 0) + { + if (m_Bound == Bound.Outside) + return OutsideSphere(ref particlePosition, particleRadius, transform.TransformPoint(m_Center), radius); + else + return InsideSphere(ref particlePosition, particleRadius, transform.TransformPoint(m_Center), radius); + } + else + { + Vector3 c0 = m_Center; + Vector3 c1 = m_Center; + + switch (m_Direction) + { + case Direction.X: + c0.x -= h; + c1.x += h; + break; + case Direction.Y: + c0.y -= h; + c1.y += h; + break; + case Direction.Z: + c0.z -= h; + c1.z += h; + break; + } + if (m_Bound == Bound.Outside) + return OutsideCapsule(ref particlePosition, particleRadius, transform.TransformPoint(c0), transform.TransformPoint(c1), radius); + else + return InsideCapsule(ref particlePosition, particleRadius, transform.TransformPoint(c0), transform.TransformPoint(c1), radius); + } + } + + static bool OutsideSphere(ref Vector3 particlePosition, float particleRadius, Vector3 sphereCenter, float sphereRadius) + { + float r = sphereRadius + particleRadius; + float r2 = r * r; + Vector3 d = particlePosition - sphereCenter; + float len2 = d.sqrMagnitude; + + // if is inside sphere, project onto sphere surface + if (len2 > 0 && len2 < r2) + { + float len = Mathf.Sqrt(len2); + particlePosition = sphereCenter + d * (r / len); + return true; + } + return false; + } + + static bool InsideSphere(ref Vector3 particlePosition, float particleRadius, Vector3 sphereCenter, float sphereRadius) + { + float r = sphereRadius - particleRadius; + float r2 = r * r; + Vector3 d = particlePosition - sphereCenter; + float len2 = d.sqrMagnitude; + + // if is outside sphere, project onto sphere surface + if (len2 > r2) + { + float len = Mathf.Sqrt(len2); + particlePosition = sphereCenter + d * (r / len); + return true; + } + return false; + } + + static bool OutsideCapsule(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius) + { + float r = capsuleRadius + particleRadius; + float r2 = r * r; + Vector3 dir = capsuleP1 - capsuleP0; + Vector3 d = particlePosition - capsuleP0; + float t = Vector3.Dot(d, dir); + + if (t <= 0) + { + // check sphere1 + float len2 = d.sqrMagnitude; + if (len2 > 0 && len2 < r2) + { + float len = Mathf.Sqrt(len2); + particlePosition = capsuleP0 + d * (r / len); + return true; + } + } + else + { + float dl = dir.sqrMagnitude; + if (t >= dl) + { + // check sphere2 + d = particlePosition - capsuleP1; + float len2 = d.sqrMagnitude; + if (len2 > 0 && len2 < r2) + { + float len = Mathf.Sqrt(len2); + particlePosition = capsuleP1 + d * (r / len); + return true; + } + } + else if (dl > 0) + { + // check cylinder + t /= dl; + d -= dir * t; + float len2 = d.sqrMagnitude; + if (len2 > 0 && len2 < r2) + { + float len = Mathf.Sqrt(len2); + particlePosition += d * ((r - len) / len); + return true; + } + } + } + return false; + } + + static bool InsideCapsule(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius) + { + float r = capsuleRadius - particleRadius; + float r2 = r * r; + Vector3 dir = capsuleP1 - capsuleP0; + Vector3 d = particlePosition - capsuleP0; + float t = Vector3.Dot(d, dir); + + if (t <= 0) + { + // check sphere1 + float len2 = d.sqrMagnitude; + if (len2 > r2) + { + float len = Mathf.Sqrt(len2); + particlePosition = capsuleP0 + d * (r / len); + return true; + } + } + else + { + float dl = dir.sqrMagnitude; + if (t >= dl) + { + // check sphere2 + d = particlePosition - capsuleP1; + float len2 = d.sqrMagnitude; + if (len2 > r2) + { + float len = Mathf.Sqrt(len2); + particlePosition = capsuleP1 + d * (r / len); + return true; + } + } + else if (dl > 0) + { + // check cylinder + t /= dl; + d -= dir * t; + float len2 = d.sqrMagnitude; + if (len2 > r2) + { + float len = Mathf.Sqrt(len2); + particlePosition += d * ((r - len) / len); + return true; + } + } + } + return false; + } + + void OnDrawGizmosSelected() + { + if (!enabled) + return; + + if (m_Bound == Bound.Outside) + Gizmos.color = Color.yellow; + else + Gizmos.color = Color.magenta; + float radius = m_Radius * Mathf.Abs(transform.lossyScale.x); + float h = m_Height * 0.5f - m_Radius; + if (h <= 0) + { + Gizmos.DrawWireSphere(transform.TransformPoint(m_Center), radius); + } + else + { + Vector3 c0 = m_Center; + Vector3 c1 = m_Center; + + switch (m_Direction) + { + case Direction.X: + c0.x -= h; + c1.x += h; + break; + case Direction.Y: + c0.y -= h; + c1.y += h; + break; + case Direction.Z: + c0.z -= h; + c1.z += h; + break; + } + Gizmos.DrawWireSphere(transform.TransformPoint(c0), radius); + Gizmos.DrawWireSphere(transform.TransformPoint(c1), radius); + } + } +} + +} \ No newline at end of file diff --git a/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBoneColliderBase.cs b/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBoneColliderBase.cs new file mode 100644 index 0000000..1b53961 --- /dev/null +++ b/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBoneColliderBase.cs @@ -0,0 +1,39 @@ +using UnityEngine; + +namespace ScarletMansion.GamePatch.DynamicBone { + +public class DynamicBoneColliderBase : MonoBehaviour +{ + public enum Direction + { + X, Y, Z + } + +#if UNITY_5_3_OR_NEWER + [Tooltip("The axis of the capsule's height.")] +#endif + public Direction m_Direction = Direction.Y; + +#if UNITY_5_3_OR_NEWER + [Tooltip("The center of the sphere or capsule, in the object's local space.")] +#endif + public Vector3 m_Center = Vector3.zero; + + public enum Bound + { + Outside, + Inside + } + +#if UNITY_5_3_OR_NEWER + [Tooltip("Constrain bones to outside bound or inside bound.")] +#endif + public Bound m_Bound = Bound.Outside; + + public virtual bool Collide(ref Vector3 particlePosition, float particleRadius) + { + return false; + } +} + +} \ No newline at end of file diff --git a/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBonePlaneCollider.cs b/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBonePlaneCollider.cs new file mode 100644 index 0000000..6a9b685 --- /dev/null +++ b/ScarletMansion/ScarletMansion/GamePatch/DynamicBone/DynamicBonePlaneCollider.cs @@ -0,0 +1,80 @@ +using UnityEngine; + +namespace ScarletMansion.GamePatch.DynamicBone { + +[AddComponentMenu("Dynamic Bone/Dynamic Bone Plane Collider")] +public class DynamicBonePlaneCollider : DynamicBoneColliderBase +{ + void OnValidate() + { + } + + public override bool Collide(ref Vector3 particlePosition, float particleRadius) + { + Vector3 normal = Vector3.up; + switch (m_Direction) + { + case Direction.X: + normal = transform.right; + break; + case Direction.Y: + normal = transform.up; + break; + case Direction.Z: + normal = transform.forward; + break; + } + + Vector3 p = transform.TransformPoint(m_Center); + Plane plane = new Plane(normal, p); + float d = plane.GetDistanceToPoint(particlePosition); + + if (m_Bound == Bound.Outside) + { + if (d < 0) + { + particlePosition -= normal * d; + return true; + } + } + else + { + if (d > 0) + { + particlePosition -= normal * d; + return true; + } + } + return false; + } + + void OnDrawGizmosSelected() + { + if (!enabled) + return; + + if (m_Bound == Bound.Outside) + Gizmos.color = Color.yellow; + else + Gizmos.color = Color.magenta; + + Vector3 normal = Vector3.up; + switch (m_Direction) + { + case Direction.X: + normal = transform.right; + break; + case Direction.Y: + normal = transform.up; + break; + case Direction.Z: + normal = transform.forward; + break; + } + + Vector3 p = transform.TransformPoint(m_Center); + Gizmos.DrawLine(p, p + normal); + } +} + +} \ No newline at end of file diff --git a/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletGohei.cs b/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletGohei.cs new file mode 100644 index 0000000..5089e95 --- /dev/null +++ b/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletGohei.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Unity.Netcode; +using UnityEngine; + +namespace ScarletMansion.GamePatch.Items { + + public class ScarletGohei : Shovel { + + [Header("Alert")] + public AudioClip alertAudioClip; + public LayerMask enemyLayerMask; + public float enemyCheckTimer = 0.5f; + public float enemyCheckPause = 8f; + public float enemyCheckRange = 16f; + private float checkTime; + + [Header("Animation")] + public SkinnedMeshRenderer skinnedMeshRenderer; + public float flashDuration = 1f; + + public override void Update(){ + base.Update(); + + if (!isPocketed && playerHeldBy && playerHeldBy.IsOwner) { + if (Time.time >= checkTime) { + // check enemies + //var enemies = RoundManager.Instance.SpawnedEnemies; + var enemies = Physics.OverlapSphere(transform.position, enemyCheckRange, enemyLayerMask); + var foundEnemy = enemies.Select(e => e.GetComponent()).Any(e => e && !e.isEnemyDead); + + // found enemy + if (foundEnemy){ + AlertServerRpc(); + checkTime = Time.time + enemyCheckPause; + } else { + checkTime = Time.time + enemyCheckTimer; + } + + } + } + } + + IEnumerator FlashRed(){ + var mat = skinnedMeshRenderer.material; + var startColor = mat.color; + var t = 0f; + var halfDuration = flashDuration * 0.5f; + + while(t < halfDuration) { + mat.color = Color.Lerp(startColor, Color.red, t / halfDuration); + yield return null; + t += Time.deltaTime; + } + + while(t < flashDuration) { + mat.color = Color.Lerp(Color.red, Color.white, (t - halfDuration) / halfDuration); + yield return null; + t += Time.deltaTime; + } + + mat.color = Color.white; + } + + [ServerRpc(RequireOwnership = false)] + public void AlertServerRpc(){ + AlertClientRpc(); + } + + public void AlertClientRpc(){ + StartCoroutine(FlashRed()); + RoundManager.Instance.PlayAudibleNoise(transform.position, 12f, 0.9f); + shovelAudio.PlayOneShot(alertAudioClip); + WalkieTalkie.TransmitOneShotAudio(shovelAudio, alertAudioClip); + } + + } +}