Skip to content

Commit

Permalink
Merge pull request #45 from MrTrueChina/1.0.1-禁用碰撞器后延迟取消碰撞功能
Browse files Browse the repository at this point in the history
修改移除碰撞器为延迟到下一次检测时执行
  • Loading branch information
MrTrueChina authored Nov 10, 2021
2 parents b40bd62 + 49e24e0 commit 11bb7e5
Show file tree
Hide file tree
Showing 30 changed files with 1,062 additions and 143 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ internal partial class Quadtree : MonoBehaviour
/// <summary>
/// 碰撞器到节点的映射表
/// </summary>
private readonly Dictionary<QuadtreeCollider, QuadtreeNode> collidersToNodes = new Dictionary<QuadtreeCollider, QuadtreeNode>();
private Dictionary<QuadtreeCollider, QuadtreeNode> collidersToNodes = new Dictionary<QuadtreeCollider, QuadtreeNode>();
/// <summary>
/// 需要移除的碰撞器节点
/// </summary>
private readonly List<QuadtreeCollider> needRemoveColliders = new List<QuadtreeCollider>();

private void Awake()
{
Expand All @@ -29,6 +33,12 @@ private void Update()
// 更新四叉树
UpdateQuadtree();

// 移除需要移除的碰撞器
RemoveNeedRemoveColliders();

// 移除所有已被销毁的碰撞器的映射
RemoveDestroyedCollidersToNodes();

// 进行检测
Detect();
}
Expand All @@ -38,8 +48,8 @@ private void Update()
/// </summary>
private void UpdateQuadtree()
{
// 从根节点开始更新碰撞器位置
QuadtreeNode.OperationResult positionResult = root.UpdatePosition();
// 从根节点开始更新碰撞器位置并移除已经被销毁的碰撞器
QuadtreeNode.OperationResult positionResult = root.UpdatePositionAndRemoveDestroyedCollider();

// 更新映射表
collidersToNodes.OverlayMerge(positionResult.CollidersToNodes).RemoveOnValueIsNull();
Expand All @@ -52,6 +62,35 @@ private void UpdateQuadtree()
// 但这些节点需要进行最大半径更新,否则新节点的最大半径是负无穷,会导致生长的这一帧碰撞检测错误
}

/// <summary>
/// 移除需要移除的碰撞器
/// </summary>
private void RemoveNeedRemoveColliders()
{
// 遍历所有需要移除的碰撞器并移除
needRemoveColliders.ForEach(node =>
{
// 不是 null 的移除,是 null 的是因为碰撞机被销毁而需要移除的,这些需要用另外的方式处理
if(node != null)
{
// 移除碰撞机并在需要的时候进行合并
ImmediateRemoveColliderWithMerge(node);
}
});

// 清空需要移除的碰撞机列表
needRemoveColliders.Clear();
}

/// <summary>
/// 移除所有已被销毁的碰撞器的映射
/// </summary>
private void RemoveDestroyedCollidersToNodes()
{
// 从映射表里过滤出碰撞器不为 null 的映射,组成新的映射表
collidersToNodes = collidersToNodes.Where(pair => pair.Key != null).ToDictionary(pair => pair.Key, pair => pair.Value);
}

/// <summary>
/// 进行碰撞检测
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,38 @@ public static QuadtreeNode.OperationResult AddCollider(QuadtreeCollider collider
}

/// <summary>
/// 从四叉树中移除碰撞器,符合条件时会合并节点
/// 移除碰撞器,到下次进行检测时才会真正移除碰撞器,符合条件时会合并节点
/// </summary>
/// <param name="collider"></param>
public static void RemoveColliderWithMerge(QuadtreeCollider collider)
{
// 如果没有实例,不进行处理,这一步是必须的,否则在游戏关闭时会发生销毁时四叉树实例一次次出现,进而导致异常
if (instance == null)
{
return;
}

// 添加到需要移除的碰撞器列表里
Instance.needRemoveColliders.Add(collider);
}

/// <summary>
/// 立即从四叉树中移除碰撞器,符合条件时会合并节点
/// </summary>
/// <param name="collider"></param>
/// <returns></returns>
public static QuadtreeNode.OperationResult RemoveColliderWithMerge(QuadtreeCollider collider)
public static QuadtreeNode.OperationResult ImmediateRemoveColliderWithMerge(QuadtreeCollider collider)
{
return RemoveCollider(collider, true);
return ImmediateRemoveCollider(collider, true);
}

/// <summary>
/// 从四叉树中移除碰撞器,不进行合并
/// 立即从四叉树中移除碰撞器,不进行合并
/// </summary>
/// <param name="collider"></param>
internal static QuadtreeNode.OperationResult RemoveColliderWithOutMerge(QuadtreeCollider collider)
internal static QuadtreeNode.OperationResult ImmediateRemoveColliderWithOutMerge(QuadtreeCollider collider)
{
return RemoveCollider(collider, false);
return ImmediateRemoveCollider(collider, false);
}

/// <summary>
Expand All @@ -74,7 +90,7 @@ internal static QuadtreeNode.OperationResult RemoveColliderWithOutMerge(Quadtree
/// <param name="collider"></param>
/// <param name="withMerge">是否需要在需要合并的时候进行合并</param>
/// <returns></returns>
internal static QuadtreeNode.OperationResult RemoveCollider(QuadtreeCollider collider, bool withMerge)
internal static QuadtreeNode.OperationResult ImmediateRemoveCollider(QuadtreeCollider collider, bool withMerge)
{
// 如果没有实例,不进行处理,这一步是必须的,否则在游戏关闭时会发生销毁时四叉树实例一次次出现,进而导致异常
if(instance == null)
Expand All @@ -92,11 +108,11 @@ internal static QuadtreeNode.OperationResult RemoveCollider(QuadtreeCollider col
QuadtreeNode.OperationResult result;
if (withMerge)
{
result = Instance.collidersToNodes[collider].RemoveColliderFromSelfWithMerge(collider);
result = Instance.collidersToNodes[collider].ImmediateRemoveColliderFromSelfWithMerge(collider);
}
else
{
result = Instance.collidersToNodes[collider].RemoveColliderFromSelfWithOutMerge(collider);
result = Instance.collidersToNodes[collider].ImmediateRemoveColliderFromSelfWithOutMerge(collider);
}

if (result.Success)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal partial class QuadtreeNode
/// </summary>
/// <param name="collider"></param>
/// <returns></returns>
internal OperationResult RemoveColliderFromSelfWithOutMerge(QuadtreeCollider collider)
internal OperationResult ImmediateRemoveColliderFromSelfWithOutMerge(QuadtreeCollider collider)
{
bool listResult = colliders.Remove(collider);

Expand All @@ -39,10 +39,10 @@ internal OperationResult RemoveColliderFromSelfWithOutMerge(QuadtreeCollider col
/// </summary>
/// <param name="collider"></param>
/// <returns></returns>
internal OperationResult RemoveColliderFromSelfWithMerge(QuadtreeCollider collider)
internal OperationResult ImmediateRemoveColliderFromSelfWithMerge(QuadtreeCollider collider)
{
// 移除碰撞器
OperationResult result = RemoveColliderFromSelfWithOutMerge(collider);
OperationResult result = ImmediateRemoveColliderFromSelfWithOutMerge(collider);

// 移除失败,不需要合并,直接返回结果
if(!result.Success)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,24 @@ internal partial class QuadtreeNode
/// 更新碰撞器位置
/// </summary>
/// <returns></returns>
internal OperationResult UpdatePosition()
internal OperationResult UpdatePositionAndRemoveDestroyedCollider()
{
if (HaveChildren())
{
// 有子节点,通知子节点更新碰撞器位置
return UpdateChildrenPosition();
return UpdateChildrenPositionAndRemoveDestroyedCollider();
}
else
{
// 没有子节点,更新当前节点内的碰撞器位置
return UpdateSelfPosition();
return UpdateSelfPositionAndRemoveDestroyedCollider();
}
}

/// <summary>
/// 更新子节点的碰撞器位置
/// </summary>
private OperationResult UpdateChildrenPosition()
private OperationResult UpdateChildrenPositionAndRemoveDestroyedCollider()
{
// 更新碰撞器位置必定成功
OperationResult result = new OperationResult(true);
Expand All @@ -36,7 +36,7 @@ private OperationResult UpdateChildrenPosition()
foreach (QuadtreeNode child in children)
{
// 通知子节点更新碰撞器位置
OperationResult childResult = child.UpdatePosition();
OperationResult childResult = child.UpdatePositionAndRemoveDestroyedCollider();

// 将子节更新导致的映射表更新合并到总结果里
result.CollidersToNodes.OverlayMerge(childResult.CollidersToNodes);
Expand All @@ -60,11 +60,14 @@ private OperationResult UpdateChildrenPosition()
/// <summary>
/// 更新当前节点内的碰撞器的位置
/// </summary>
private OperationResult UpdateSelfPosition()
private OperationResult UpdateSelfPositionAndRemoveDestroyedCollider()
{
// 更新碰撞器位置必定成功
OperationResult result = new OperationResult(true);

// 移除所有为空的碰撞器,这些是在外部被销毁的碰撞器
colliders.RemoveAll(collider => collider == null);

// 移除所有当前节点保存的、已经离开当前节点范围的碰撞器,并将这些碰撞器保存起来
OperationResult removeResult = GetAndRemoveCollidersOutOfArea();

Expand Down Expand Up @@ -101,7 +104,7 @@ private OperationResult GetAndRemoveCollidersOutOfArea()
foreach (QuadtreeCollider collider in new List<QuadtreeCollider>(result.CollidersToNodes.Keys))
{
// 移除碰撞器
OperationResult removeResult = Quadtree.RemoveColliderWithOutMerge(collider);
OperationResult removeResult = Quadtree.ImmediateRemoveColliderWithOutMerge(collider);

// 记录映射表的变化
result.CollidersToNodes.OverlayMerge(removeResult.CollidersToNodes);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace MtC.Tools.QuadtreeCollider.Test
{
/// <summary>
/// 当发生碰撞时销毁物体的组件,用于测试延迟禁用碰撞器的功能
/// </summary>
[RequireComponent(typeof(CircleQuadtreeCollider))]
public class DestroyOnCollisionEnter : MonoBehaviour, IOnQuadtreeCollisionEnter
{
public void OnQuadtreeCollisionEnter(QuadtreeCollider collider)
{
Destroy(gameObject);

Debug.Log(gameObject.name + " 销毁");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace MtC.Tools.QuadtreeCollider.Test
{
/// <summary>
/// 当发生碰撞时禁用碰撞器的组件,用于测试延迟禁用碰撞器的功能
/// </summary>
[RequireComponent(typeof(CircleQuadtreeCollider))]
public class DisableColliderOnCollisionEnter : MonoBehaviour, IOnQuadtreeCollisionEnter
{
public void OnQuadtreeCollisionEnter(QuadtreeCollider collider)
{
GetComponent<CircleQuadtreeCollider>().enabled = false;

Debug.Log(gameObject.name + " 禁用碰撞器");
}
}
}
Loading

0 comments on commit 11bb7e5

Please sign in to comment.