diff options
author | chai <215380520@qq.com> | 2023-05-12 10:32:11 +0800 |
---|---|---|
committer | chai <215380520@qq.com> | 2023-05-12 10:32:11 +0800 |
commit | 2fc9585797067730f28b03b0727bf05f9deed091 (patch) | |
tree | 8807e37b85ba922045eaa17ac445dd0a1d2d730c /WorldlineKeepers/Assets/Scripts/Physics | |
parent | 2a1cd4fda8a4a8e649910d16b4dfa1ce7ae63543 (diff) |
+ worldline keepers
Diffstat (limited to 'WorldlineKeepers/Assets/Scripts/Physics')
25 files changed, 1304 insertions, 0 deletions
diff --git a/WorldlineKeepers/Assets/Scripts/Physics/FastBoxCollider.cs b/WorldlineKeepers/Assets/Scripts/Physics/FastBoxCollider.cs new file mode 100644 index 0000000..df84e0d --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/FastBoxCollider.cs @@ -0,0 +1,90 @@ +using mh; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class FastBoxCollider : MonoBehaviour, IQuadTreeObject +{ + + [SerializeField] private ColliderType m_Type; + [SerializeField] private Vector2 m_Offset; + [SerializeField] private Vector2 m_Size; + + public Vector2 center + { + get + { + Vector3 pos = transform.position + m_Offset.ToVector3(); + return pos; + } + } + + public Vector2 offset => m_Offset; + public Vector2 size => m_Size; + + public Vector4 bound + { + get + { + Vector3 pos = transform.position + m_Offset.ToVector3(); + Vector4 b = new Vector4(); + b.x = pos.x; + b.y = pos.y; + b.z = size.x; + b.w = size.y; + return b; + } + } + + public Vector4 box + { + get + { + Vector2 c = center; + Vector4 b = new Vector4(); + b.x = c.x; + b.y = c.y; + b.z = size.x; + b.w = size.y; + return b; + } + } + + public void Awake() + { + if (m_Type == ColliderType.Collider) + { + PhysicsManager.Instance.AddCollider(this); + } + else if (m_Type == ColliderType.Hurtbox) + { + PhysicsManager.Instance.AddHurtboxes(this); + } + } + + public void OnDestroy() + { + if (m_Type == ColliderType.Collider) + { + PhysicsManager.Instance.RemoveCollider(this); + } + else if (m_Type == ColliderType.Hurtbox) + { + PhysicsManager.Instance.RemoveHurtbox(this); + } + } + + private void OnDrawGizmos() + { + Color c = Gizmos.color; + + if(m_Type == ColliderType.Hurtbox) + { + Gizmos.color = Color.red; + } + Gizmos.DrawWireCube(transform.position + m_Offset.ToVector3(), m_Size.ToVector3()); + + Gizmos.color = c; + } + +}
\ No newline at end of file diff --git a/WorldlineKeepers/Assets/Scripts/Physics/FastBoxCollider.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/FastBoxCollider.cs.meta new file mode 100644 index 0000000..c4a5b19 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/FastBoxCollider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 310ae84e35289454197ed61078698811 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/FastCircleCollider.cs b/WorldlineKeepers/Assets/Scripts/Physics/FastCircleCollider.cs new file mode 100644 index 0000000..dde49f9 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/FastCircleCollider.cs @@ -0,0 +1,82 @@ +using mh; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class FastCircleCollider : MonoBehaviour, IQuadTreeObject +{ + + [SerializeField] private ColliderType m_Type; + [SerializeField] private float m_Radius; + [SerializeField] private Vector2 m_Offset; + + public Vector2 center + { + get + { + Vector3 pos = transform.position + m_Offset.ToVector3(); + return pos; + } + } + + public float radius => m_Radius; + + public Vector4 bound + { + get + { + Vector3 pos = transform.position + m_Offset.ToVector3(); + Vector4 b = new Vector4(); + b.x = pos.x; + b.y = pos.y; + b.z = m_Radius * 2; + b.w = m_Radius * 2; + return b; + } + } + + public Vector3 circle + { + get + { + Vector3 c = new Vector3(); + Vector2 ct = center; + c.x = ct.x; + c.y = ct.y; + c.z = radius; + return c; + } + } + + public Vector2 offset => m_Offset; + + public void Awake() + { + if(m_Type == ColliderType.Collider) + { + PhysicsManager.Instance.AddCollider(this); + } + else if(m_Type == ColliderType.Hurtbox) + { + PhysicsManager.Instance.AddHurtboxes(this); + } + } + + public void OnDestroy() + { + if (m_Type == ColliderType.Collider) + { + PhysicsManager.Instance.RemoveCollider(this); + } + else if (m_Type == ColliderType.Hurtbox) + { + PhysicsManager.Instance.RemoveHurtbox(this); + } + } + + private void OnDrawGizmos() + { + Gizmos.DrawWireSphere(transform.position + m_Offset.ToVector3(), m_Radius); + } + +}
\ No newline at end of file diff --git a/WorldlineKeepers/Assets/Scripts/Physics/FastCircleCollider.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/FastCircleCollider.cs.meta new file mode 100644 index 0000000..8457227 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/FastCircleCollider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: defd292dd15a961418d8ac5721b28712 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager.cs b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager.cs new file mode 100644 index 0000000..279ebbb --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager.cs @@ -0,0 +1,46 @@ +using mh; +using System.Collections; +using System.Collections.Generic; +using Unity.VisualScripting; +using UnityEngine; + +public enum ColliderType +{ + Collider, + Hurtbox, +} + +public partial class PhysicsManager : Singleton<PhysicsManager> +{ + // 四叉树筛选结果 + public List<IQuadTreeObject> sharedRetriveResults => m_SharedRetriveResults; + private List<IQuadTreeObject> m_SharedRetriveResults = new List<IQuadTreeObject>(); + + public PhysicsManager() + { + m_CollisionQuadtree = new PhysicsQuadtree(new Vector4(0, 0, 30, 30)); + m_HurtboxQuadtree = new PhysicsQuadtree(new Vector4(0, 0, 30, 30)); + } + + public System.Func<Vector4, bool> GetRetriverByType(ColliderType type) + { + if (type == ColliderType.Collider) + return RetriveColliders; + else if (type == ColliderType.Hurtbox) + return RetriveHurtboxes; + else + return null; + } + + public void Update() + { + m_CollisionQuadtree.UpdateQuadtree(); + m_HurtboxQuadtree.UpdateQuadtree(); + } + + public void Debug() + { + m_CollisionQuadtree.Debug(); + } + +}
\ No newline at end of file diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager.cs.meta new file mode 100644 index 0000000..1bc7b8b --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 309b3570728b5ca40b98a4799850238f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_Collide.cs b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_Collide.cs new file mode 100644 index 0000000..4710b2e --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_Collide.cs @@ -0,0 +1,186 @@ +using mh; +using System.Collections; +using System.Collections.Generic; +using Unity.VisualScripting.Antlr3.Runtime.Tree; +using UnityEngine; +using UnityEngine.UIElements; + +/// <summary> +/// 物理查询 +/// </summary> +public partial class PhysicsManager : Singleton<PhysicsManager> +{ + // 碰撞检测结果 + private List<IQuadTreeObject> m_SharedCollideResults = new List<IQuadTreeObject>(); + + public static Vector4 GetCircleBound(Vector3 circle) + { + float size = circle.z * 2; + return new Vector4(circle.x, circle.y, size, size); + } + + public static Vector4 GetBoxBound(Vector4 box) + { + return box; + } + + public static Vector4 GetRayBound(Vector4 ray) + { + Vector2 tr = ray.xy() + ray.zw(); + Vector4 bound = new Vector4(); + bound.x = (tr.x + ray.x) / 2; + bound.y = (tr.y + ray.y) / 2; + bound.z = Mathf.Max(Mathf.Abs(tr.x - ray.x), 1); + bound.w = Mathf.Max(Mathf.Abs(tr.y - ray.y), 1); + return bound; + } + + public static Vector4 GetRaySegment(Vector4 ray) + { + Vector2 tr = ray.xy() + ray.zw(); + Vector4 seg = new Vector4(); + seg.x = ray.x; + seg.y = ray.y; + seg.z = tr.x; + seg.w = tr.y; + return seg; + } + + public static Vector4 GetPointBound(Vector2 point) + { + Vector4 bound = new Vector4(); + bound.x = point.x; + bound.y = point.y; + bound.z = 1; + bound.w = 1; + return bound; + } + + /// <summary> + /// 把x,y,w,h的box转换为lowerx,higherx,lowery,highery + /// </summary> + /// <returns></returns> + public static Vector4 GetBoxRange(Vector4 box) + { + Vector4 Range = new(); + Range.x = box.x - box.z / 2; + Range.y = box.x + box.z / 2; + Range.z = box.y - box.w / 2; + Range.w = box.y + box.w / 2; + return Range; + } + + public ref readonly List<IQuadTreeObject> CircleCast(ColliderType target, Vector3 circle) + { + m_SharedCollideResults.Clear(); + var retriver = GetRetriverByType(target); + if(retriver != null) + { + if (retriver(GetCircleBound(circle))) + { + for(int i = 0; i < m_SharedRetriveResults.Count; ++i) + { + var collider = m_SharedRetriveResults[i]; + if(collider != null) + { + if(collider is FastCircleCollider) + { + if(CircleVsCircle((collider as FastCircleCollider).circle, circle)) + { + m_SharedCollideResults.Add(collider); + } + } + else if(collider is FastBoxCollider) + { + if (BoxVsCircle((collider as FastBoxCollider).box, circle)) + { + m_SharedCollideResults.Add(collider); + } + } + } + } + } + } + return ref m_SharedCollideResults; + } + + public ref readonly List<IQuadTreeObject> BoxCast(ColliderType target, Vector4 box) + { + m_SharedCollideResults.Clear(); + var retriver = GetRetriverByType(target); + if (retriver != null) + { + if (retriver(GetBoxBound(box))) + { + for (int i = 0; i < m_SharedRetriveResults.Count; ++i) + { + var collider = m_SharedRetriveResults[i]; + if (collider != null) + { + if (collider is FastCircleCollider) + { + if (BoxVsCircle(box, (collider as FastCircleCollider).circle)) + { + m_SharedCollideResults.Add(collider); + } + } + else if (collider is FastBoxCollider) + { + if (BoxVsBox(box, (collider as FastBoxCollider).box)) + { + m_SharedCollideResults.Add(collider); + } + } + } + } + } + } + return ref m_SharedCollideResults; + } + + /// <summary> + /// x,y dir.x dir.y(dir带长度) + /// </summary> + /// <param name="target"></param> + /// <param name="line"></param> + /// <returns></returns> + public ref readonly List<IQuadTreeObject> RayCast(ColliderType target, Vector4 ray) + { + m_SharedCollideResults.Clear(); + var retriver = GetRetriverByType(target); + if(retriver != null) + { + if(retriver(GetRayBound(ray))) + { + for (int i = 0; i < m_SharedRetriveResults.Count; ++i) + { + var collider = m_SharedRetriveResults[i]; + if (collider != null) + { + if (collider is FastCircleCollider) + { + if (RayVsCircle(ray, (collider as FastCircleCollider).circle)) + { + m_SharedCollideResults.Add(collider); + } + } + else if (collider is FastBoxCollider) + { + if (RayVsBox(ray, (collider as FastBoxCollider).box)) + { + m_SharedCollideResults.Add(collider); + } + } + } + } + } + } + return ref m_SharedCollideResults; + } + + public ref readonly List<IQuadTreeObject> PointCast(ColliderType target, Vector2 point) + { + return ref m_SharedCollideResults; + } + +}
\ No newline at end of file diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_Collide.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_Collide.cs.meta new file mode 100644 index 0000000..d5f9202 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_Collide.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d0c6870f58c8c6469810ba2ca63cbc7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionDetection.cs b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionDetection.cs new file mode 100644 index 0000000..d5fd913 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionDetection.cs @@ -0,0 +1,183 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +/// <summary> +/// 碰撞检测 +/// </summary> +public partial class PhysicsManager : Singleton<PhysicsManager> +{ + + /// <summary> + /// circle x,y,radius + /// </summary> + /// <param name="pos1"></param> + /// <param name="r1"></param> + /// <param name="pos2"></param> + /// <param name="r2"></param> + /// <returns></returns> + public static bool CircleVsCircle(Vector2 pos1, float r1, Vector2 pos2, float r2) + { + return (pos1 - pos2).magnitude <= r1 + r2; + } + + public static bool CircleVsCircle(Vector3 c1, Vector3 c2) + { + return (c1.xy() - c2.xy()).magnitude <= c1.z + c2.z; + } + + /// <summary> + /// intersection是r2对于r1 + /// </summary> + /// <param name="b1"></param> + /// <param name="b2"></param> + /// <param name="intersection"></param> + /// <returns></returns> + public static bool BoxVsBox(Vector4 b1, Vector4 b2, out Vector2 intersection) + { + float b1w = b1.z / 2f, b1h = b1.w / 2f, b2w = b2.z / 2f, b2h = b2.w / 2f; + float distX = b2.x - b1.x; + float distY = b2.y - b1.y; + if(Mathf.Abs(distX) < b1w + b2w && Mathf.Abs(distY) < b1h +b2h) + { + intersection = new Vector2(); + intersection.x = Mathf.Sign(distX) * (b1w + b2w - Mathf.Abs(distX)); + intersection.y = Mathf.Sign(distY) * (b1h + b2h - Mathf.Abs(distY)); + return true; + } + intersection = Vector2.zero; + return false; + } + + public static bool BoxVsBox(Vector4 b1, Vector4 b2) + { + float b1w = b1.z / 2f, b1h = b1.w / 2f, b2w = b2.z / 2f, b2h = b2.w / 2f; + float distX = b2.x - b1.x; + float distY = b2.y - b1.y; + if (Mathf.Abs(distX) < b1w + b2w && Mathf.Abs(distY) < b1h + b2h) + { + return true; + } + return false; + } + + public static bool BoxVsCircle(Vector4 box, Vector2 pos, float radius) + { + Vector4 boxScaled = box; + boxScaled.z = box.z + radius * 2; + boxScaled.w = box.w + radius * 2; + if (!IsPointInsideBox(boxScaled, pos)) + return false; + Vector2 v = MathUtils.Abs(pos - box.xy()); + Vector2 u = MathUtils.Max(v - box.zw(), 0); + return Vector2.Dot(u, u) < radius * radius; + } + + public static bool RayVsCircle(Vector4 ray, Vector3 circle) + { + Vector4 seg = GetRaySegment(ray); + Vector2 dir = ray.zw().normalized; + Vector2 center = circle.xy(); + float u = Vector2.Dot(center - seg.xy(), dir); + Vector2 near = new Vector2(); + if(u <= 0) + { + near = seg.xy(); + } + else if(u >= ray.zw().magnitude) + { + near = seg.zw(); + } + else + { + near = seg.xy() + dir * u; + } + return (near - center).sqrMagnitude <= circle.z * circle.z; + } + + + // From Real-time Collision Detection, p179 + // box2d + public static bool RayVsBox(Vector4 ray, Vector4 box) + { + float tmin = float.MinValue; + float tmax = float.MaxValue; + Vector2 dir = ray.zw().normalized; + Vector4 range = GetBoxRange(box); //lowerx,higherx,lowery,highery + Vector2 lowerBound = new Vector2(range.x, range.z); + Vector2 upperBound = new Vector2(range.y, range.w); + //Vector2 normal = new Vector2(); + Vector2 p = ray.xy(); + + for (int i = 0; i < 2; ++i) + { + + if (dir[i] < float.Epsilon) // 和x-slab平行 + { + if (p[i] < lowerBound[i] || p[i] > upperBound[i]) + { + return false; + } + } + else + { + float inv_d = 1f / dir[i]; + float t1 = (lowerBound[i] - p[i]) * inv_d; + float t2 = (upperBound[i] - p[i]) * inv_d; + float s = -1f; + if (t1 > t2) + { + MathUtils.Swap(ref t1, ref t2); + s = 1f; + } + if (t1 > tmin) + { + //normal = Vector2.zero; + //normal[i] = s; + tmin = t1; + } + + tmax = Mathf.Min(tmax, t2); + + if (tmin > tmax) + { + return false; + } + } + } + + // Does the ray start inside the box? + // Does the ray intersect beyond the max fraction? + if (tmin < 0 || ray.zw().magnitude < tmin) + { + if (IsPointInsideBox(box, p)) + return true; + return false; + } + //// Intersection. + //output->fraction = tmin; + //output->normal = normal; + + return true; + } + + /// <summary> + /// box x,y,w,h circle x,y,raduis + /// </summary> + /// <param name="box"></param> + /// <param name="circle"></param> + /// <returns></returns> + public static bool BoxVsCircle(Vector4 box, Vector3 circle) + { + return BoxVsCircle(box, circle.xy(), circle.z); + } + + public static bool IsPointInsideBox(Vector4 box, Vector2 point) + { + return point.x >= box.x - box.z / 2f + && point.x <= box.x + box.z / 2f + && point.y >= box.y - box.w / 2f + && point.y <= box.y + box.w / 2f; + } + +} diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionDetection.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionDetection.cs.meta new file mode 100644 index 0000000..7be9116 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionDetection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2577861427359c9459ea0fa471de4040 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionTree.cs b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionTree.cs new file mode 100644 index 0000000..f372f3a --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionTree.cs @@ -0,0 +1,45 @@ +using mh; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public partial class PhysicsManager : Singleton<PhysicsManager> +{ + + private PhysicsQuadtree m_CollisionQuadtree; + + public Vector4 collisionQuadtreeRange + { + set + { + m_CollisionQuadtree.quadtreeRange = value; + } + } + + public void AddCollider(IQuadTreeObject collider) + { + m_CollisionQuadtree.AddObject(collider); + } + + public void RemoveCollider(IQuadTreeObject collider) + { + m_CollisionQuadtree.RemoveObject(collider); + } + + public bool RetriveColliders(ref List<IQuadTreeObject> returnObjs, IQuadTreeObject obj) + { + return m_CollisionQuadtree.Retrive(ref returnObjs, obj); + } + + public bool RetriveColliders(ref List<IQuadTreeObject> returnObjs, Vector4 bound) + { + return m_CollisionQuadtree.Retrive(ref returnObjs, bound); + } + + public bool RetriveColliders(Vector4 bound) + { + m_SharedRetriveResults.Clear(); + return m_CollisionQuadtree.Retrive(ref m_SharedRetriveResults, bound); + } + +}
\ No newline at end of file diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionTree.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionTree.cs.meta new file mode 100644 index 0000000..1ac4a70 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_CollisionTree.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb174e1114e8e3d439bdb43fa7d609b8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_HurtboxTree.cs b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_HurtboxTree.cs new file mode 100644 index 0000000..ca13935 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_HurtboxTree.cs @@ -0,0 +1,40 @@ +using mh; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public partial class PhysicsManager : Singleton<PhysicsManager> +{ + + private PhysicsQuadtree m_HurtboxQuadtree; + + public Vector4 hurtboxQuadtreeRange + { + set + { + m_HurtboxQuadtree.quadtreeRange = value; + } + } + + public void AddHurtboxes(IQuadTreeObject hurtbox) + { + m_HurtboxQuadtree.AddObject(hurtbox); + } + + public void RemoveHurtbox(IQuadTreeObject hurtbox) + { + m_HurtboxQuadtree.RemoveObject(hurtbox); + } + + public bool RetriveHurtboxes(ref List<IQuadTreeObject> returnObjs, Vector4 bound) + { + return m_HurtboxQuadtree.Retrive(ref returnObjs, bound); + } + + public bool RetriveHurtboxes(Vector4 bound) + { + m_SharedRetriveResults.Clear(); + return m_HurtboxQuadtree.Retrive(ref m_SharedRetriveResults, bound); + } + +} diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_HurtboxTree.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_HurtboxTree.cs.meta new file mode 100644 index 0000000..39cd699 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsManager_HurtboxTree.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9fc247f3e6aafbd45a935ef4b1ced70c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsQuadtree.cs b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsQuadtree.cs new file mode 100644 index 0000000..5cd374e --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsQuadtree.cs @@ -0,0 +1,87 @@ +using mh; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +/// <summary> +/// 鍩轰簬鍥惧舰鐨勫洓鍙夋爲 +/// </summary> +class PhysicsQuadtree +{ + /// <summary> + /// 杈圭晫 + /// </summary> + public Vector4 quadtreeRange { set { m_QuadtreeRange = value; } } + private Vector4 m_QuadtreeRange; + + /// <summary> + /// 鏍硅妭鐐 + /// </summary> + private Quadtree m_Quadtree; + + /// <summary> + /// 鎵樼鐨勫叏閮ㄥ璞★紝浣嗕笉涓瀹氫細鍔犲叆鍒版爲閲岄潰锛堜腑蹇冪偣鍦ㄨ竟鐣屽鐨勪笉鍔犲叆鏍戯級 + /// </summary> + private List<IQuadTreeObject> m_Objects = new List<IQuadTreeObject>(); + + public PhysicsQuadtree(Vector4 range) + { + m_QuadtreeRange = range; + m_Quadtree = new Quadtree(0, range, true); + } + + public void AddObject(IQuadTreeObject obj) + { + m_Objects.Add(obj); + } + + public void RemoveObject(IQuadTreeObject obj) + { + m_Objects.Remove(obj); + } + + public void UpdateQuadtree() + { + m_Quadtree.Clear(false); + m_Quadtree.Rebound(m_QuadtreeRange); + InsertAllObjects(); + } + + private void InsertAllObjects() + { + Vector4 bound = m_Quadtree.bound; + for (int i = 0; i < m_Objects.Count; i++) + { + IQuadTreeObject obj = m_Objects[i]; + // 绠鍗曠殑蹇界暐涓績鍦ㄨ竟鐣屽鐨刼bject + if (PhysicsManager.IsPointInsideBox(bound, obj.bound.xy())) + { + m_Quadtree.Insert(obj); + } + } + } + + public void Debug() + { + Color c = Gizmos.color; + Gizmos.color = Color.green; + m_Quadtree?.Iterate((t) => + { + Vector3 pos = new Vector3(t.x, t.y, 0); + Vector3 size = new Vector3(t.w, t.h, 1); + Gizmos.DrawWireCube(pos, size); + }); + Gizmos.color = c; + } + + public bool Retrive(ref List<IQuadTreeObject> returnObjs, IQuadTreeObject obj) + { + return m_Quadtree.Retrieve(ref returnObjs, obj); + } + + public bool Retrive(ref List<IQuadTreeObject> returnObjs, Vector4 bound) + { + return m_Quadtree.Retrieve(ref returnObjs, bound); + } + +}
\ No newline at end of file diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsQuadtree.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsQuadtree.cs.meta new file mode 100644 index 0000000..c9d1346 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsQuadtree.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 07c60bb0f452e9a4a8cec80be4c6183a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsSystem.cs b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsSystem.cs new file mode 100644 index 0000000..5c7283f --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsSystem.cs @@ -0,0 +1,23 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class PhysicsSystem : SingletonMB<PhysicsSystem> +{ + + protected override void Awake() + { + base.Awake(); + } + + protected override void OnDestroy() + { + base.OnDestroy(); + } + + protected override void DoWhenOnDestroy() + { + base.DoWhenOnDestroy(); + } + +} diff --git a/WorldlineKeepers/Assets/Scripts/Physics/PhysicsSystem.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsSystem.cs.meta new file mode 100644 index 0000000..098c3e2 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/PhysicsSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 424982e85f480904d9712074785f9227 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/Quadtree.cs b/WorldlineKeepers/Assets/Scripts/Physics/Quadtree.cs new file mode 100644 index 0000000..a012d26 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/Quadtree.cs @@ -0,0 +1,292 @@ +using JetBrains.Annotations; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Unity.VisualScripting; +using UnityEngine; + +// https://gamedev.stackexchange.com/questions/6345/quad-tree-vs-grid-based-collision-detection + +namespace mh +{ + + public interface IQuadTreeObject + { + public Vector4 bound { get; } + } + + /// <summary> + /// 四叉树空间分割 + /// </summary> + public class Quadtree + { + public const int kMaxObjectsPerBlock = 4; + public const int kMaxLevel = 5; + + private int m_Level; + private Vector4 m_Bounds; // x,y,z,w => posx,posy,width,height + private Quadtree[] m_SubTrees; // 从右上角开始逆时针索引 + private List<IQuadTreeObject> m_Objects; // 非叶节点的为0 + private bool m_IsRoot; + + public Vector4 bound { get { return m_Bounds; } } + public float x { get { return m_Bounds.x; } } + public float y { get { return m_Bounds.y; } } + public float w { get { return m_Bounds.z; } } + public float h { get { return m_Bounds.w; } } + public float halfW { get { return w / 2; } } + public float halfH { get { return h / 2; } } + public float left { get { return x - halfW; } } + public float right { get { return x + halfW; } } + public float top { get { return y + halfH; } } + public float bottom { get { return y - halfH; } } + + public bool isRoot { get { return m_IsRoot; } } + + private static Queue<List<IQuadTreeObject>> m_QuadtreeObjPool = new Queue<List<IQuadTreeObject>>(); + private static Queue<Quadtree> m_QuadtreePool = new Queue<Quadtree>(); + + private Quadtree QueryQuadtree(int level, Vector4 bounds) + { + if(m_QuadtreePool.Count == 0) + { + return new Quadtree(level, bounds); + } + Quadtree tree = m_QuadtreePool.Dequeue(); + tree.m_Level = level; + tree.m_Bounds = bounds; + if (tree.m_Objects == null) + tree.m_Objects = QueryQuadtreeObjList(); + return tree; + } + + private void RecycleQuadtree(ref Quadtree tree) + { + tree.Clear(); + m_QuadtreePool.Enqueue(tree); + tree = null; + } + + private List<IQuadTreeObject> QueryQuadtreeObjList() + { + if(m_QuadtreeObjPool.Count == 0) + { + return new List<IQuadTreeObject>(); + } + List<IQuadTreeObject> list = m_QuadtreeObjPool.Dequeue(); + return list; + } + + private void RecycleQuadtreeObjList(ref List<IQuadTreeObject> list) + { + list.Clear(); + m_QuadtreeObjPool.Enqueue(list); + list = null; + } + + public Quadtree(int level, Vector4 bounds, bool isRoot = false) + { + m_Level = level; + m_Bounds = bounds; + m_SubTrees = new Quadtree[4]; + m_Objects = QueryQuadtreeObjList(); + m_IsRoot = isRoot; + } + + /// <summary> + /// 更改边界,只适用于最外层的tree + /// </summary> + /// <param name="bounds"></param> + public void Rebound(Vector4 bounds) + { + if(!m_IsRoot) + { + Debug.LogError("Quadtree.Rebound()只能运用于最外层"); + return; + } + m_Bounds = bounds; + } + + public void Clear(bool clearObjectList = true) + { + if (clearObjectList) + RecycleQuadtreeObjList(ref m_Objects); + else + m_Objects.Clear(); + for (int i = 0; i < m_SubTrees.Length; i++) + { + if (m_SubTrees[i] != null) + { + //m_SubTrees[i].Clear(); + //m_SubTrees[i] = null; + RecycleQuadtree(ref m_SubTrees[i]); + } + } + } + + public void Split() + { + float subWidth = (m_Bounds.z / 2); + float subHeight = (m_Bounds.w / 2); + float x = m_Bounds.x; + float y = m_Bounds.y; + m_SubTrees[0] = QueryQuadtree(m_Level + 1, new Vector4(x + subWidth / 2, y + subHeight / 2, subWidth, subHeight)); + m_SubTrees[1] = QueryQuadtree(m_Level + 1, new Vector4(x - subWidth / 2, y + subHeight / 2, subWidth, subHeight)); + m_SubTrees[2] = QueryQuadtree(m_Level + 1, new Vector4(x - subWidth / 2, y - subHeight / 2, subWidth, subHeight)); + m_SubTrees[3] = QueryQuadtree(m_Level + 1, new Vector4(x + subWidth / 2, y - subHeight / 2, subWidth, subHeight)); + } + + /// <summary> + /// 0表示没法完全放在一个subTree: subtree交界或者整个越界 + /// </summary> + /// <param name="bound"></param> + /// <returns></returns> + public int GetSubtreeIndices(Vector4 bound) + { + int indices = 0; + float halfw = bound.z / 2; + float halfh = bound.w / 2; + float lowerx = bound.x - halfw; + float higherx = bound.x + halfw; + float lowery = bound.y - halfh; + float highery = bound.y + halfh; + bool startIsNorth = highery > y; + bool startIsWest = lowerx < x; + bool endIsEast = higherx > x; + bool endIsSouth = lowery < y; + //top-right quad + if (startIsNorth && endIsEast) + { + indices |= 1; + } + + //top-left quad + if (startIsWest && startIsNorth) + { + indices |= 1 << 1; + } + + //bottom-left quad + if (startIsWest && endIsSouth) + { + indices |= 1 << 2; + } + + //bottom-right quad + if (endIsEast && endIsSouth) + { + indices |= 1 << 3; + } + + return indices; + } + + public void Insert(IQuadTreeObject obj) + { + if (m_SubTrees[0] != null) + { + int indices = GetSubtreeIndices(obj.bound); + for(int i = 0; i < 4; i++) + { + if((indices & (1 << i)) != 0) + { + m_SubTrees[i].Insert(obj); + } + } + return; + } + + m_Objects.Add(obj); + + if (m_Objects.Count > kMaxObjectsPerBlock && m_Level < kMaxLevel) // 本层满了之后重新排布层内对象 + { + if (m_SubTrees[0] == null) Split(); + + for (int i = m_Objects.Count - 1; i >= 0; i--) + { + int indices = GetSubtreeIndices(m_Objects[i].bound); + for (int j = 0; j < 4; j++) + { + if ((indices & (1 << j)) != 0) + { + m_SubTrees[j].Insert(m_Objects[i]); + } + } + } + + m_Objects.Clear(); + } + } + + /// <summary> + /// 获得可能和obj碰撞的对象(不包括自己) + /// </summary> + /// <param name="returnObjs"></param> + /// <param name="obj"></param> + /// <returns></returns> + public bool Retrieve(ref List<IQuadTreeObject> returnObjs, IQuadTreeObject obj) + { + for(int i = 0; i < m_Objects.Count; ++i) + { + if (!returnObjs.Contains(m_Objects[i]) && obj != m_Objects[i]) + { + returnObjs.Add(m_Objects[i]); + } + } + if (m_SubTrees[0] != null) + { + int indices = GetSubtreeIndices(obj.bound); + for (int i = 0; i < 4; i++) + { + if ((indices & (1 << i)) != 0) + { + m_SubTrees[i].Retrieve(ref returnObjs, obj); + } + } + } + return returnObjs.Count > 0; + } + + /// <summary> + /// 获得可能和obj碰撞的对象(不包括自己) + /// </summary> + /// <param name="returnObjs"></param> + /// <param name="obj"></param> + /// <returns></returns> + public bool Retrieve(ref List<IQuadTreeObject> returnObjs, Vector4 bound) + { + for (int i = 0; i < m_Objects.Count; ++i) // 根节点count==0 + { + if (!returnObjs.Contains(m_Objects[i])) + { + returnObjs.Add(m_Objects[i]); + } + } + if (m_SubTrees[0] != null) + { + int indices = GetSubtreeIndices(bound); + for (int i = 0; i < 4; i++) + { + if ((indices & (1 << i)) != 0) + { + m_SubTrees[i].Retrieve(ref returnObjs, bound); + } + } + } + return returnObjs.Count > 0; + } + + public void Iterate(System.Action<Quadtree> action) + { + action?.Invoke(this); + m_SubTrees[0]?.Iterate(action); + m_SubTrees[1]?.Iterate(action); + m_SubTrees[2]?.Iterate(action); + m_SubTrees[3]?.Iterate(action); + } + + } +}
\ No newline at end of file diff --git a/WorldlineKeepers/Assets/Scripts/Physics/Quadtree.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/Quadtree.cs.meta new file mode 100644 index 0000000..89087fb --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/Quadtree.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5df24784c1c96ae4c963844001b3ef30 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/TestQuadtree.cs b/WorldlineKeepers/Assets/Scripts/Physics/TestQuadtree.cs new file mode 100644 index 0000000..2ea967e --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/TestQuadtree.cs @@ -0,0 +1,32 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using mh; +using MH; +using Unity.VisualScripting; + +namespace mh +{ + [DefaultExecutionOrder(-1000)] + public class TestQuadtree : MonoBehaviour + { + + private void Awake() + { + } + + private void FixedUpdate() + { + var pos = UnitManager.hero.transform.position; + PhysicsManager.Instance.collisionQuadtreeRange = new Vector4(pos.x, pos.y, 30, 20); + PhysicsManager.Instance.hurtboxQuadtreeRange = new Vector4(pos.x, pos.y, 30, 20); + PhysicsManager.Instance.Update(); + } + + private void OnDrawGizmos() + { + PhysicsManager.Instance.Debug(); + } + } + +} diff --git a/WorldlineKeepers/Assets/Scripts/Physics/TestQuadtree.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/TestQuadtree.cs.meta new file mode 100644 index 0000000..ed81efe --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/TestQuadtree.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f88bde6dc59a579488b9b0aa6906f913 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/TestSpirits.cs b/WorldlineKeepers/Assets/Scripts/Physics/TestSpirits.cs new file mode 100644 index 0000000..3f313ae --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/TestSpirits.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; + +public class TestSpirits : MonoBehaviour +{ + public SpiritScript prefab; + + public static List<SpiritScript> spirits = new List<SpiritScript>(); + + private const int kMaxCount = 500; + + // Start is called before the first frame update + void Start() + { + int count = kMaxCount - spirits.Count; + for (int i = 0; i < count; ++i) + { + float x = UnityEngine.Random.Range(-20, 10); + float y = UnityEngine.Random.Range(-20, 10); + SpiritScript go = Instantiate(prefab) as SpiritScript; + go.transform.position = new Vector3(x, y, 0); + go.transform.parent = this.transform; + go.gameObject.SetActive(true); + } + StartCoroutine(CoSpawn(5)); + } + + IEnumerator CoSpawn(float interval) + { + while (true) + { + int count = kMaxCount - spirits.Count; + for (int i = 0; i < count; ++i) + { + float x = UnityEngine.Random.Range(-20, 10); + float y = UnityEngine.Random.Range(-20, 10); + SpiritScript go = Instantiate(prefab) as SpiritScript; + go.transform.position = new Vector3(x, y, 0); + go.transform.parent = this.transform; + go.gameObject.SetActive(true); + } + + yield return new WaitForSeconds(interval); + } + } + + private void FixedUpdate() + { + for(int i = 0; i < spirits.Count; ++i) + { + spirits[i].Tick(); + } + } + +} diff --git a/WorldlineKeepers/Assets/Scripts/Physics/TestSpirits.cs.meta b/WorldlineKeepers/Assets/Scripts/Physics/TestSpirits.cs.meta new file mode 100644 index 0000000..479bb7a --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/TestSpirits.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 339ab6a313449b84fb9f51c3b6a1980b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Physics/unity-quadtree-master.meta b/WorldlineKeepers/Assets/Scripts/Physics/unity-quadtree-master.meta new file mode 100644 index 0000000..cbdce18 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Physics/unity-quadtree-master.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2fb02811893802a4bb3f07b115453535 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: |