
📝 核心代码实现
1. 空间哈希网格系统
public class SpatialHashGrid : MonoBehaviour
{
private Dictionary<Vector2Int, HashSet<Collider2D>> grid;
private float cellSize = 5f; // 根据游戏对象大小调整
private int gridWidth = 200;
private int gridHeight = 200;
void Awake()
{
grid = new Dictionary<Vector2Int, HashSet<Collider2D>>();
InitializeGrid();
}
private void InitializeGrid()
{
for (int x = 0; x < gridWidth; x++)
{
for (int y = 0; y < gridHeight; y++)
{
grid[new Vector2Int(x, y)] = new HashSet<Collider2D>();
}
}
}
public Vector2Int WorldToGrid(Vector3 worldPos)
{
int x = Mathf.FloorToInt(worldPos.x / cellSize);
int y = Mathf.FloorToInt(worldPos.y / cellSize);
return new Vector2Int(
Mathf.Clamp(x, 0, gridWidth - 1),
Mathf.Clamp(y, 0, gridHeight - 1)
);
}
public void AddToGrid(Collider2D collider)
{
Vector2Int gridPos = WorldToGrid(collider.transform.position);
if (grid.ContainsKey(gridPos))
{
grid[gridPos].Add(collider);
}
}
public void RemoveFromGrid(Collider2D collider, Vector2Int oldGridPos)
{
if (grid.ContainsKey(oldGridPos))
{
grid[oldGridPos].Remove(collider);
}
}
public HashSet<Collider2D> GetNearbyObjects(Vector3 position, int range = 1)
{
HashSet<Collider2D> nearby = new HashSet<Collider2D>();
Vector2Int centerGrid = WorldToGrid(position);
for (int x = -range; x <= range; x++)
{
for (int y = -range; y <= range; y++)
{
Vector2Int checkGrid = new Vector2Int(centerGrid.x + x, centerGrid.y + y);
if (grid.ContainsKey(checkGrid))
{
foreach (var obj in grid[checkGrid])
{
nearby.Add(obj);
}
}
}
}
return nearby;
}
}
2. 高效碰撞管理器
public class CollisionManager : MonoBehaviour
{
[Header("性能设置")]
public int maxCollisionChecksPerFrame = 100;
public float maxCheckDistance = 20f;
private SpatialHashGrid spatialGrid;
private ObjectPoolManager poolManager;
private Queue<CollisionPair> collisionQueue;
private List<Bullet> activeBullets;
private List<Monster> activeMonsters;
// 性能统计
public int CollisionChecksThisFrame { get; private set; }
public int TotalCollisionsDetected { get; private set; }
private struct CollisionPair
{
public Collider2D bullet;
public Collider2D monster;
public float priority; // 基于距离的优先级
}
void Awake()
{
spatialGrid = GetComponent<SpatialHashGrid>();
poolManager = GetComponent<ObjectPoolManager>();
collisionQueue = new Queue<CollisionPair>();
activeBullets = new List<Bullet>();
activeMonsters = new List<Monster>();
// 设置物理层
SetupPhysicsLayers();
}
void SetupPhysicsLayers()
{
// 子弹层 = 8, 怪物层 = 9
Physics2D.IgnoreLayerCollision(8, 8); // 子弹间不碰撞
Physics2D.IgnoreLayerCollision(9, 9); // 怪物间不碰撞
// 只保留子弹-怪物碰撞
}
void FixedUpdate()
{
CollisionChecksThisFrame = 0;
UpdateSpatialGrid();
ProcessCollisionDetection();
}
private void UpdateSpatialGrid()
{
// 清空网格
spatialGrid.ClearGrid();
// 重新添加所有活跃对象
foreach (var bullet in activeBullets)
{
if (bullet.gameObject.activeInHierarchy)
spatialGrid.AddToGrid(bullet.GetComponent<Collider2D>());
}
foreach (var monster in activeMonsters)
{
if (monster.gameObject.activeInHierarchy)
spatialGrid.AddToGrid(monster.GetComponent<Collider2D>());
}
}
private void ProcessCollisionDetection()
{
// 构建碰撞候选队列
BuildCollisionCandidates();
// 分帧处理碰撞检测
int checksThisFrame = 0;
while (collisionQueue.Count > 0 && checksThisFrame < maxCollisionChecksPerFrame)
{
var pair = collisionQueue.Dequeue();
if (CheckCollision(pair))
{
HandleCollision(pair.bullet, pair.monster);
TotalCollisionsDetected++;
}
checksThisFrame++;
}
CollisionChecksThisFrame = checksThisFrame;
}
private void BuildCollisionCandidates()
{
collisionQueue.Clear();
foreach (var bullet in activeBullets)
{
if (!bullet.gameObject.activeInHierarchy) continue;
var nearbyObjects = spatialGrid.GetNearbyObjects(bullet.transform.position);
foreach (var obj in nearbyObjects)
{
if (obj.gameObject.layer == 9) // 怪物层
{
float distance = Vector3.Distance(bullet.transform.position, obj.transform.position);
if (distance <= maxCheckDistance)
{
collisionQueue.Enqueue(new CollisionPair
{
bullet = bullet.GetComponent<Collider2D>(),
monster = obj,
priority = 1f / (distance + 0.1f) // 距离越近优先级越高
});
}
}
}
}
// 按优先级排序(可选,性能消耗较大时可省略)
// var sortedPairs = collisionQueue.OrderByDescending(p => p.priority);
// collisionQueue = new Queue<CollisionPair>(sortedPairs);
}
private bool CheckCollision(CollisionPair pair)
{
if (pair.bullet == null || pair.monster == null) return false;
// 快速距离检测
float sqrDistance = (pair.bullet.transform.position - pair.monster.transform.position).sqrMagnitude;
float combinedRadius = GetColliderRadius(pair.bullet) + GetColliderRadius(pair.monster);
return sqrDistance <= (combinedRadius * combinedRadius);
}
private float GetColliderRadius(Collider2D collider)
{
if (collider is CircleCollider2D circle)
return circle.radius * Mathf.Max(collider.transform.lossyScale.x, collider.transform.lossyScale.y);
else if (collider is BoxCollider2D box)
return Mathf.Max(box.size.x, box.size.y) * 0.5f * Mathf.Max(collider.transform.lossyScale.x, collider.transform.lossyScale.y);
return 1f; // 默认半径
}
private void HandleCollision(Collider2D bullet, Collider2D monster)
{
// 处理碰撞逻辑
var bulletComponent = bullet.GetComponent<Bullet>();
var monsterComponent = monster.GetComponent<Monster>();
if (bulletComponent != null && monsterComponent != null)
{
// 造成伤害
monsterComponent.TakeDamage(bulletComponent.Damage);
// 回收子弹到对象池
poolManager.ReturnBullet(bulletComponent);
// 播放特效
var effect = poolManager.GetHitEffect();
effect.transform.position = bullet.transform.position;
effect.Play();
}
}
// 注册/注销方法
public void RegisterBullet(Bullet bullet)
{
activeBullets.Add(bullet);
}
public void UnregisterBullet(Bullet bullet)
{
activeBullets.Remove(bullet);
}
public void RegisterMonster(Monster monster)
{
activeMonsters.Add(monster);
}
public void UnregisterMonster(Monster monster)
{
activeMonsters.Remove(monster);
}
}
3. 智能对象池系统
public class ObjectPoolManager : MonoBehaviour
{
[System.Serializable]
public class PoolSettings
{
public GameObject prefab;
public int initialSize = 50;
public int maxSize = 200;
public bool allowGrowth = true;
}
[Header("对象池配置")]
public PoolSettings bulletPool;
public PoolSettings hitEffectPool;
public PoolSettings damageTextPool;
private Dictionary<string, Queue<GameObject>> pools;
private Dictionary<string, PoolSettings> poolSettings;
private Dictionary<string, int> poolCounts;
void Awake()
{
InitializePools();
}
private void InitializePools()
{
pools = new Dictionary<string, Queue<GameObject>>();
poolSettings = new Dictionary<string, PoolSettings>();
poolCounts = new Dictionary<string, int>();
// 初始化各个对象池
CreatePool("Bullet", bulletPool);
CreatePool("HitEffect", hitEffectPool);
CreatePool("DamageText", damageTextPool);
}
private void CreatePool(string poolName, PoolSettings settings)
{
pools[poolName] = new Queue<GameObject>();
poolSettings[poolName] = settings;
poolCounts[poolName] = 0;
// 预创建对象
for (int i = 0; i < settings.initialSize; i++)
{
GameObject obj = Instantiate(settings.prefab, transform);
obj.SetActive(false);
pools[poolName].Enqueue(obj);
poolCounts[poolName]++;
}
}
public GameObject GetFromPool(string poolName)
{
if (!pools.ContainsKey(poolName))
{
Debug.LogError($"Pool {poolName} does not exist!");
return null;
}
GameObject obj = null;
var pool = pools[poolName];
var settings = poolSettings[poolName];
// 尝试从池中获取
while (pool.Count > 0)
{
obj = pool.Dequeue();
if (obj != null)
{
obj.SetActive(true);
return obj;
}
}
// 池为空,检查是否可以扩容
if (settings.allowGrowth && poolCounts[poolName] < settings.maxSize)
{
obj = Instantiate(settings.prefab, transform);
poolCounts[poolName]++;
obj.SetActive(true);
return obj;
}
Debug.LogWarning($"Pool {poolName} is exhausted!");
return null;
}
public void ReturnToPool(string poolName, GameObject obj)
{
if (!pools.ContainsKey(poolName))
{
Destroy(obj);
return;
}
obj.SetActive(false);
obj.transform.SetParent(transform);
pools[poolName].Enqueue(obj);
}
// 便捷方法
public Bullet GetBullet()
{
var obj = GetFromPool("Bullet");
return obj?.GetComponent<Bullet>();
}
public void ReturnBullet(Bullet bullet)
{
ReturnToPool("Bullet", bullet.gameObject);
}
public ParticleSystem GetHitEffect()
{
var obj = GetFromPool("HitEffect");
return obj?.GetComponent<ParticleSystem>();
}
}
4. 性能监控面板
public class PerformanceMonitor : MonoBehaviour
{
private CollisionManager collisionManager;
private ObjectPoolManager poolManager;
[Header("显示设置")]
public bool showDebugInfo = true;
public KeyCode toggleKey = KeyCode.F1;
// 性能数据
private float deltaTime;
private int lastFrameCollisionChecks;
void Start()
{
collisionManager = FindObjectOfType<CollisionManager>();
poolManager = FindObjectOfType<ObjectPoolManager>();
}
void Update()
{
deltaTime += (Time.unscaledDeltaTime - deltaTime) * 0.1f;
if (Input.GetKeyDown(toggleKey))
{
showDebugInfo = !showDebugInfo;
}
lastFrameCollisionChecks = collisionManager.CollisionChecksThisFrame;
}
void OnGUI()
{
if (!showDebugInfo) return;
GUILayout.BeginArea(new Rect(10, 10, 300, 200));
GUILayout.Label($"FPS: {Mathf.Ceil(1.0f / deltaTime)}");
GUILayout.Label($"碰撞检测/帧: {lastFrameCollisionChecks}");
GUILayout.Label($"总碰撞数: {collisionManager.TotalCollisionsDetected}");
GUILayout.Label($"活跃子弹: {FindObjectsOfType<Bullet>().Length}");
GUILayout.Label($"活跃怪物: {FindObjectsOfType<Monster>().Length}");
GUILayout.EndArea();
}
}
⚙️ 使用配置
// 在CollisionManager中的推荐设置
[Header("性能调优")]
public int maxCollisionChecksPerFrame = 50; // 根据目标帧率调整
public float maxCheckDistance = 15f; // 根据游戏场景大小调整
public float cellSize = 3f; // 根据游戏对象平均大小调整