当前位置: 首页 > news >正文

Unity 大量子弹和小怪碰撞检测优化

image.png

核心代码实现

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, 怪物层 = 9Physics2D.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;                  // 根据游戏对象平均大小调整

http://www.dtcms.com/a/341212.html

相关文章:

  • GSPO:Towards scalable reinforcement learning for language models
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型和EasyOCR实现汽车牌照动态检测和识别(C#代码,UI界面版)
  • 使用UUP dump制作windows preview镜像
  • 手机、汽车如何实现卫星直连
  • imx6ull-驱动开发篇31——Linux异步通知
  • 玩转QEMU硬件模拟器 - Raspberry Pi OS驱动开发
  • 【项目复盘】【四轴飞行器设计】驱动开发部分
  • Redis 安装教程
  • 【数据结构之二叉树】
  • 【openssl】openssl CA.pl 签发证书操作步骤
  • redis执行lua脚本的原子性和数据库原子性的区别
  • [激光原理与应用-315]:光学设计 - SolidWorks, 光机系统设计的神器,打通光学与机械设计的闭环
  • Tomcat部署与HTTP协议详解
  • 佳维视工业一体机在公共交通系统配套中的应用
  • 疯狂星期四文案网第45天运营日记
  • LTspice仿真电路:(三十五)LED恒流驱动仿真(LT3497)
  • burpsuite+captcha-killer插件识别图片验证码进行爆破
  • AiPy 文档自动化处理实践:从 docx 到结构化 db 的高效转换方案
  • 华为仓颉语言的class(类)初步
  • ES Modules +案例分析
  • 【C++】动态导入Windows系统API的简单方法
  • Docker复杂安装--最详细的MySQL主从复制与Redis集群安装、主从复制、主从扩容主从缩容实战版
  • 03-dockerfile
  • 8月7日国赛全真模拟!2025“华数杯”数学建模竞赛,常用模型及算法总结
  • 网络连接的核心机制
  • Python 数据可视化:Matplotlib 与 Seaborn 实战
  • [TryHackMe](知识学习)Hacking with PowerShell
  • 浅显易懂——Redis、SpringDataRedis
  • 充值系统开源版,支持对接码支付,支持三级分销
  • 深入解析 Containerd 的工作原理