├── .gitignore ├── Images ├── DebugInfo.png ├── WNCBoxCollider.png ├── WNCPointCollider.png ├── WNCBoxColliderGizmo.png ├── CollisionLayerConfig.png ├── WorldUnitCollision2DSystem.png ├── DebugInfo.png.meta ├── WNCBoxCollider.png.meta ├── CollisionLayerConfig.png.meta ├── WNCBoxColliderGizmo.png.meta ├── WNCPointCollider.png.meta └── WorldUnitCollision2DSystem.png.meta ├── Example ├── BaseExample │ ├── Square.png │ ├── Bullet.prefab.meta │ ├── Enemy.prefab.meta │ ├── ExampleScene.unity.meta │ ├── WorldUnitCollisionSystem.prefab.meta │ ├── scripts.meta │ ├── CollisionLayerConfig.asset.meta │ ├── scripts │ │ ├── Bullet.cs.meta │ │ ├── Enemy.cs.meta │ │ ├── Player.cs.meta │ │ ├── GameManager.cs.meta │ │ ├── BulletObjectPool.cs.meta │ │ ├── EnemyPoolManager.cs.meta │ │ ├── Bullet.cs │ │ ├── Enemy.cs │ │ ├── EnemyPoolManager.cs │ │ ├── BulletObjectPool.cs │ │ ├── GameManager.cs │ │ └── Player.cs │ ├── CollisionLayerConfig.asset │ ├── WorldUnitCollisionSystem.prefab │ ├── Square.png.meta │ ├── Bullet.prefab │ ├── Enemy.prefab │ └── ExampleScene.unity └── BaseExample.meta ├── README.md.meta ├── Core.meta ├── Example.meta ├── Images.meta ├── Core ├── Colliders.meta ├── CameraUtil.cs.meta ├── WorldUnit.cs.meta ├── Colliders │ ├── WNCBoxCollider.cs.meta │ ├── WNCPointCollider.cs.meta │ ├── AbsctractCollider.cs.meta │ ├── AbsctractCollider.cs │ ├── WNCPointCollider.cs │ └── WNCBoxCollider.cs ├── CollisionLayerConfigSO.cs.meta ├── WorldUnitCollision2DSystem.cs.meta ├── CameraUtil.cs ├── WorldUnit.cs ├── CollisionLayerConfigSO.cs └── WorldUnitCollision2DSystem.cs ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | LICENSE.meta -------------------------------------------------------------------------------- /Images/DebugInfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuxiang123/WorldUnitCollision2DSystem/HEAD/Images/DebugInfo.png -------------------------------------------------------------------------------- /Images/WNCBoxCollider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuxiang123/WorldUnitCollision2DSystem/HEAD/Images/WNCBoxCollider.png -------------------------------------------------------------------------------- /Images/WNCPointCollider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuxiang123/WorldUnitCollision2DSystem/HEAD/Images/WNCPointCollider.png -------------------------------------------------------------------------------- /Example/BaseExample/Square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuxiang123/WorldUnitCollision2DSystem/HEAD/Example/BaseExample/Square.png -------------------------------------------------------------------------------- /Images/WNCBoxColliderGizmo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuxiang123/WorldUnitCollision2DSystem/HEAD/Images/WNCBoxColliderGizmo.png -------------------------------------------------------------------------------- /Images/CollisionLayerConfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuxiang123/WorldUnitCollision2DSystem/HEAD/Images/CollisionLayerConfig.png -------------------------------------------------------------------------------- /Images/WorldUnitCollision2DSystem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuxiang123/WorldUnitCollision2DSystem/HEAD/Images/WorldUnitCollision2DSystem.png -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 53d43e178ec299d47a91c5af980391c8 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Core.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c2b781c4e32265142b5fefc3142c9539 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Example.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f047ec0d0cf84df45a1a959dc16e251e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Images.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eb05870c683e6f14c92fc9ec8501b9f8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Core/Colliders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ccdb431608dd79a4a961a8428d5bc171 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Example/BaseExample/Bullet.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cc997c8d5c8ab2e45ae729f05a476a5f 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Example/BaseExample/Enemy.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ec0b1e212e4406a4e94f8a982525d5cd 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Example/BaseExample.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f5b6daf6926bc124ca0100e261ba1d36 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Example/BaseExample/ExampleScene.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 11979e630c4b9bb46bc272d5225c739b 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Example/BaseExample/WorldUnitCollisionSystem.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b4f5bf8b2b1127f46a163e87d87eb623 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Example/BaseExample/scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e5e527865b6e2084e96dbfb39be90e26 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Example/BaseExample/CollisionLayerConfig.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c7e4f7bd649fc0f41966827dca7be11d 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 0 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Core/CameraUtil.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2424d859a3ec5664da668b4f42d0551b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Core/WorldUnit.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9b5e2f9453170f94392dc3f5ff708bf2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Core/Colliders/WNCBoxCollider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 582f6cb176d1c2741bbb188c9c04cfad 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Core/Colliders/WNCPointCollider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 14ac46b56af43f24cb64f85f57d97e02 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Core/CollisionLayerConfigSO.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 16a8c5de567fe564abe5d1ad481a2167 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Core/WorldUnitCollision2DSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2b3c9e968b30e0d4ab02ecae7117c342 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Core/Colliders/AbsctractCollider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 628ed38817e7aed41b5e1924b97a79f6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Example/BaseExample/scripts/Bullet.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 73640d515f048644aa5c62150bef0e3e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Example/BaseExample/scripts/Enemy.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 83813b50246d4f94399d0ad530b08d07 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Example/BaseExample/scripts/Player.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 724314f3fbf91d24a82ac824f4796ab6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Example/BaseExample/scripts/GameManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b13833515624b7247900ca1026c79c6a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Example/BaseExample/scripts/BulletObjectPool.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c219dbeb7dbcc414cb91b7bafe6d8a54 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Example/BaseExample/scripts/EnemyPoolManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0880c0769bd53cb4999e5034509e4fdd 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Core/CameraUtil.cs: -------------------------------------------------------------------------------- 1 | 2 | using UnityEngine; 3 | 4 | namespace WorldUnitCollision2DSystem 5 | { 6 | public static class CameraUtil 7 | { 8 | private static Camera _camera; 9 | 10 | // 当前目标是否超出摄像机范围 11 | public static bool IsOutOfCamera(Vector2 target) 12 | { 13 | if (!_camera) 14 | { 15 | _camera = Camera.main; 16 | } 17 | var targetScreenPos = _camera!.WorldToScreenPoint(target); 18 | return targetScreenPos.x < -1 || targetScreenPos.x > Screen.width + 1 || targetScreenPos.y < -1 || targetScreenPos.y > Screen.height + 1; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BaseExample/CollisionLayerConfig.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &11400000 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 11500000, guid: 16a8c5de567fe564abe5d1ad481a2167, type: 3} 13 | m_Name: CollisionLayerConfig 14 | m_EditorClassIdentifier: 15 | ActiveCollisionLayers: 16 | - Enemy 17 | PassiveCollisionLayers: 18 | - PlayerBullet 19 | CollisionLayerConfigs: 20 | - ActiveLayer: Enemy 21 | CollisionLayers: 22 | - PlayerBullet 23 | -------------------------------------------------------------------------------- /Core/Colliders/AbsctractCollider.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using Sirenix.OdinInspector; 4 | using UnityEngine; 5 | 6 | namespace WorldUnitCollision2DSystem 7 | { 8 | public class AbstractCollider : MonoBehaviour 9 | { 10 | [Required] public string LayerName; 11 | public Action OnTrigger; 12 | 13 | private void Start() 14 | { 15 | var CollisionLayerConfigSO = WorldUnitCollision2DSystem.Instance.CollisionLayerConfigSo; 16 | if (!CollisionLayerConfigSO.ActiveCollisionLayers.Contains(LayerName) && !CollisionLayerConfigSO.PassiveCollisionLayers.Contains(LayerName)) 17 | { 18 | Debug.LogError($"碰撞层配置文件中没有找到LayerName为{LayerName}的碰撞层配置"); 19 | return; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Example/BaseExample/scripts/Bullet.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace WorldUnitCollision2DSystem.Example 4 | { 5 | public class Bullet : MonoBehaviour 6 | { 7 | public float speed = 20f; 8 | private Vector2 direction; 9 | 10 | private void Update() 11 | { 12 | // 使用2D向量来更新子弹位置 13 | transform.position += (Vector3)direction * speed * Time.deltaTime; 14 | } 15 | 16 | public void SetDirection(Vector2 newDirection) 17 | { 18 | direction = newDirection.normalized; 19 | // 设置子弹的旋转,使其朝向移动方向 20 | float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; 21 | transform.rotation = Quaternion.Euler(0, 0, angle); 22 | } 23 | 24 | void OnBecameInvisible() 25 | { 26 | // 使用对象池回收子弹 27 | BulletObjectPool.Instance.ReturnToPool(this.gameObject); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Example/BaseExample/scripts/Enemy.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace WorldUnitCollision2DSystem.Example 4 | { 5 | public class Enemy : MonoBehaviour 6 | { 7 | public float moveSpeed = 3f; 8 | 9 | private Transform _player; 10 | private WNCBoxCollider _boxCollision; 11 | 12 | private void Awake() 13 | { 14 | _player = GameObject.FindGameObjectWithTag("Player").transform; 15 | _boxCollision = GetComponent(); 16 | _boxCollision.OnTrigger += (bullet, layerName) => 17 | { 18 | if (layerName == "PlayerBullet") 19 | { 20 | EnemyPoolManager.Instance.ReturnToPool(gameObject); 21 | BulletObjectPool.Instance.ReturnToPool(bullet); 22 | } 23 | }; 24 | } 25 | 26 | private void Update() 27 | { 28 | // 敌人朝玩家移动 29 | transform.position = Vector2.MoveTowards(transform.position, _player.position, moveSpeed * Time.deltaTime); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 fuxiang123 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Core/Colliders/WNCPointCollider.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector; 2 | using UnityEngine; 3 | 4 | namespace WorldUnitCollision2DSystem 5 | { 6 | public class WNCPointCollider : AbstractCollider 7 | { 8 | [LabelText("是否在屏幕外禁用")]public bool DisableWhenOutOfCamera = true; 9 | private Vector2Int _currentIndex; 10 | 11 | private readonly Vector2Int _impossibleIndex = new(int.MaxValue, int.MaxValue); 12 | 13 | void OnDisable() 14 | { 15 | if (_currentIndex != _impossibleIndex) WorldUnitCollision2DSystem.Instance.RemoveCollider(_currentIndex, LayerName, this); 16 | } 17 | 18 | void Update() 19 | { 20 | if (DisableWhenOutOfCamera && CameraUtil.IsOutOfCamera(transform.position)) 21 | { 22 | if (_currentIndex != _impossibleIndex) 23 | { 24 | WorldUnitCollision2DSystem.Instance.RemoveCollider(_currentIndex, LayerName, this); 25 | _currentIndex = _impossibleIndex; 26 | } 27 | return; 28 | } 29 | 30 | // 更新子弹所在的WorldUnit 31 | var index = WorldUnitCollision2DSystem.Instance.GetWorldUnitIndex(transform.position); 32 | if (_currentIndex == _impossibleIndex || index != _currentIndex) 33 | { 34 | WorldUnitCollision2DSystem.Instance.AddCollider(transform.position, LayerName, this); 35 | if (_currentIndex != _impossibleIndex) WorldUnitCollision2DSystem.Instance.RemoveCollider(_currentIndex, LayerName, this); 36 | _currentIndex = index; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Example/BaseExample/WorldUnitCollisionSystem.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &1235147260086750720 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 8836716509608513818} 12 | - component: {fileID: 711950268436279629} 13 | m_Layer: 0 14 | m_Name: WorldUnitCollisionSystem 15 | m_TagString: Untagged 16 | m_Icon: {fileID: 0} 17 | m_NavMeshLayer: 0 18 | m_StaticEditorFlags: 0 19 | m_IsActive: 1 20 | --- !u!4 &8836716509608513818 21 | Transform: 22 | m_ObjectHideFlags: 0 23 | m_CorrespondingSourceObject: {fileID: 0} 24 | m_PrefabInstance: {fileID: 0} 25 | m_PrefabAsset: {fileID: 0} 26 | m_GameObject: {fileID: 1235147260086750720} 27 | serializedVersion: 2 28 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 29 | m_LocalPosition: {x: 22.363071, y: -21.676456, z: -0.8221353} 30 | m_LocalScale: {x: 1, y: 1, z: 1} 31 | m_ConstrainProportionsScale: 0 32 | m_Children: [] 33 | m_Father: {fileID: 0} 34 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 35 | --- !u!114 &711950268436279629 36 | MonoBehaviour: 37 | m_ObjectHideFlags: 0 38 | m_CorrespondingSourceObject: {fileID: 0} 39 | m_PrefabInstance: {fileID: 0} 40 | m_PrefabAsset: {fileID: 0} 41 | m_GameObject: {fileID: 1235147260086750720} 42 | m_Enabled: 1 43 | m_EditorHideFlags: 0 44 | m_Script: {fileID: 11500000, guid: 2b3c9e968b30e0d4ab02ecae7117c342, type: 3} 45 | m_Name: 46 | m_EditorClassIdentifier: 47 | CollisionLayerConfigSO: {fileID: 11400000, guid: c7e4f7bd649fc0f41966827dca7be11d, 48 | type: 2} 49 | unitWidth: 1 50 | worldUnitRemoveTime: 5 51 | ShowDebugInfo: 1 52 | -------------------------------------------------------------------------------- /Example/BaseExample/scripts/EnemyPoolManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | 4 | namespace WorldUnitCollision2DSystem.Example 5 | { 6 | public class EnemyPoolManager : MonoBehaviour 7 | { 8 | public static EnemyPoolManager Instance; 9 | public GameObject enemyPrefab; 10 | public int initSize = 10; 11 | 12 | private List enemyPool = new List(); 13 | private int activeEnemyCount; 14 | 15 | private void Awake() 16 | { 17 | Instance = this; 18 | for (int i = 0; i < initSize; i++) 19 | { 20 | GameObject enemy = Instantiate(enemyPrefab); 21 | enemy.SetActive(false); 22 | enemyPool.Add(enemy); 23 | } 24 | } 25 | 26 | public GameObject GetFromPool() 27 | { 28 | activeEnemyCount++; 29 | if (enemyPool.Count > 0) 30 | { 31 | var index = enemyPool.Count - 1; 32 | GameObject enemy = enemyPool[index]; 33 | enemyPool.RemoveAt(index); 34 | enemy.SetActive(true); 35 | return enemy; 36 | } 37 | else 38 | { 39 | GameObject enemy = Instantiate(enemyPrefab); 40 | return enemy; 41 | } 42 | } 43 | 44 | public void ReturnToPool(GameObject enemy) 45 | { 46 | enemy.SetActive(false); 47 | enemyPool.Add(enemy); 48 | activeEnemyCount--; 49 | } 50 | 51 | // 在屏幕显示当前敌人数量 52 | private void OnGUI() 53 | { 54 | GUIStyle style = new GUIStyle(); 55 | style.fontSize = 24; // 增大字体大小 56 | style.normal.textColor = Color.white; // 设置文字颜色为白色 57 | GUI.Label(new Rect(Screen.width - 200, Screen.height - 100, 200, 20), "当前敌人数量: " + activeEnemyCount, style); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Example/BaseExample/scripts/BulletObjectPool.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace WorldUnitCollision2DSystem.Example 6 | { 7 | public class BulletObjectPool : MonoBehaviour 8 | { 9 | public static BulletObjectPool Instance; 10 | public GameObject bulletPrefab; 11 | public int initSize = 20; 12 | 13 | private List bulletPool = new List(); 14 | private int activeBulletCount; 15 | 16 | private void Awake() 17 | { 18 | Instance = this; 19 | for (int i = 0; i < initSize; i++) 20 | { 21 | GameObject bullet = Instantiate(bulletPrefab); 22 | bullet.SetActive(false); 23 | bulletPool.Add(bullet); 24 | } 25 | } 26 | 27 | public GameObject GetFromPool() 28 | { 29 | activeBulletCount++; 30 | if (bulletPool.Count > 0) 31 | { 32 | var index = bulletPool.Count - 1; 33 | GameObject bullet = bulletPool[index]; 34 | bulletPool.RemoveAt(index); 35 | bullet.SetActive(true); 36 | return bullet; 37 | } 38 | else 39 | { 40 | GameObject bullet = Instantiate(bulletPrefab); 41 | return bullet; 42 | } 43 | } 44 | 45 | public void ReturnToPool(GameObject bullet) 46 | { 47 | activeBulletCount--; 48 | bullet.SetActive(false); 49 | bulletPool.Add(bullet); 50 | } 51 | 52 | // 在屏幕显示当前子弹数量 53 | private void OnGUI() 54 | { 55 | GUIStyle style = new GUIStyle(); 56 | style.fontSize = 24; // 增大字体大小 57 | style.normal.textColor = Color.white; // 设置文字颜色为白色 58 | GUI.Label(new Rect(Screen.width - 200, Screen.height - 50, 200, 20), "当前子弹数量: " + activeBulletCount, style); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Example/BaseExample/scripts/GameManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace WorldUnitCollision2DSystem.Example 4 | { 5 | public class GameManager : MonoBehaviour 6 | { 7 | public float spawnInterval = 2f; 8 | public int enemiesToSpawn = 3; // 每次生成的敌人数量 9 | public bool isSpawning = true; // 控制生成状态 10 | 11 | private void Start() 12 | { 13 | InvokeRepeating("SpawnEnemy", 0f, spawnInterval); 14 | } 15 | 16 | private void Update() 17 | { 18 | CalFPS(); 19 | } 20 | 21 | void SpawnEnemy() 22 | { 23 | if (!isSpawning) return; // 如果停止生成,则直接返回 24 | 25 | for (int i = 0; i < enemiesToSpawn; i++) // 循环生成多个敌人 26 | { 27 | Vector2 spawnPosition = GetSpawnPosition(); // 获取屏幕边缘生成位置 28 | // 从对象池获取敌人实例 29 | GameObject enemy = EnemyPoolManager.Instance.GetFromPool(); 30 | enemy.transform.position = spawnPosition; 31 | enemy.transform.rotation = Quaternion.identity; 32 | } 33 | } 34 | 35 | private Vector2 GetSpawnPosition() 36 | { 37 | float x = Random.Range(0, Screen.width); 38 | float y = Random.Range(0, Screen.height); 39 | Vector2 screenPosition = new Vector2(x, y); 40 | Vector2 worldPosition = Camera.main.ScreenToWorldPoint(screenPosition); 41 | 42 | return worldPosition; 43 | } 44 | 45 | private int count = 0; 46 | private float timer = 0f; 47 | private float showTime = 1f; 48 | private int fps = 0; 49 | 50 | // 在屏幕右下显示当前FPS 51 | private void CalFPS() 52 | { 53 | count++; 54 | timer += Time.deltaTime; 55 | if (timer >= showTime) 56 | { 57 | fps = Mathf.RoundToInt(count / timer); 58 | count = 0; 59 | timer = 0f; 60 | } 61 | } 62 | 63 | private void OnGUI() 64 | { 65 | GUIStyle style = new GUIStyle(); 66 | style.fontSize = 24; // 增大字体大小 67 | style.normal.textColor = Color.white; // 设置文字颜色为白色 68 | GUI.Label(new Rect(Screen.width - 200, Screen.height - 150, 200, 20), "当前FPS: " + fps, style); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /Example/BaseExample/scripts/Player.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace WorldUnitCollision2DSystem.Example 4 | { 5 | public class Player : MonoBehaviour 6 | { 7 | public float moveSpeed = 5f; 8 | public float attackSpeed = 0.1f; // 攻击速度 9 | public int bulletCount = 5; // 同时发射的子弹数量 10 | public float spreadAngle = 15f; // 子弹散射角度 11 | private Vector2 movement; 12 | private float lastAttackTime; 13 | 14 | private void Update() 15 | { 16 | HandleMovement(); 17 | 18 | if (Input.GetMouseButton(0) && Time.time >= lastAttackTime + attackSpeed) // 按住左键射击 19 | { 20 | Shoot(); 21 | lastAttackTime = Time.time; // 更新最后攻击时间 22 | } 23 | } 24 | 25 | void HandleMovement() 26 | { 27 | // 获取输入 28 | float moveX = 0f; 29 | float moveY = 0f; 30 | 31 | if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) 32 | { 33 | moveY = 1f; 34 | } 35 | if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) 36 | { 37 | moveY = -1f; 38 | } 39 | if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) 40 | { 41 | moveX = -1f; 42 | } 43 | if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) 44 | { 45 | moveX = 1f; 46 | } 47 | 48 | movement = new Vector2(moveX, moveY).normalized; 49 | 50 | // 移动玩家 51 | transform.position += new Vector3(movement.x, movement.y, 0) * moveSpeed * Time.deltaTime; 52 | } 53 | 54 | void Shoot() 55 | { 56 | // 获取鼠标位置 57 | Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); 58 | // 计算方向 59 | Vector2 baseDirection = (mousePosition - transform.position).normalized; 60 | 61 | for (int i = 0; i < bulletCount; i++) 62 | { 63 | // 计算散射角度 64 | float angle = spreadAngle * (i - (bulletCount - 1) / 2f) / (bulletCount - 1); 65 | Vector2 direction = Quaternion.Euler(0, 0, angle) * baseDirection; 66 | 67 | // 从对象池获取子弹实例 68 | GameObject bullet = BulletObjectPool.Instance.GetFromPool(); 69 | bullet.transform.position = transform.position; 70 | bullet.transform.rotation = Quaternion.identity; 71 | // 设置子弹方向 72 | bullet.GetComponent().SetDirection(direction); 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WorldUnitCollision2DSystem 2 | 3 | ## 本系统依赖[Odin Inspector](https://assetstore.unity.com/packages/tools/utilities/odin-inspector-and-serializer-89041)运行。如果想使用不依赖odin的版本,请[点击这里](https://github.com/fuxiang123/WorldUnitCollision2DSystem/tree/withoutOdin)。 4 | 5 | WorldUnitCollision2DSystem 是一个基于网格的2D碰撞检测系统。它通过一系列使用限制,来换取在碰撞检测上的性能提升。 6 | 7 | 一般情况下可以满足同屏2000 - 3000个碰撞体之间的碰撞检测需求。 8 | 9 | ## 快速开始 10 | 11 | 将本仓库克隆或下载后直接放入项目中Assets文件夹下,然后直接运行Example文件夹下的内容,或者遵循以下步骤。 12 | 13 | 1. 从Example文件夹中,将 `WorldUnitCollisionSystem` 预制体 拖入到你的场景中。 14 | 2. 为你的敌人添加 `WNCBoxCollider` 组件,并设置 `LayerName` 为 `Enemy`。 15 | 3. 为你的玩家子弹添加 `WNCPointCollider` 组件,并设置 `LayerName` 为 `PlayerBullet`。 16 | 4. 查看后续内容,学会编辑碰撞层配置文件。 17 | 18 | ## CollisionLayerConfigSO碰撞层配置文件 19 | 20 | 碰撞层配置文件是一个 `ScriptableObject`,用来配置碰撞层之间的碰撞关系。通过在 `Project` 窗口中右键,选择 `Create -> WorldUnitCollision2DSystem -> CollisionLayerConfigSO` 来创建一个碰撞层配置文件。 21 | 22 | 将CollisionLayerConfigSO拖动到游戏场景中的WorldUnitCollisionSystem组件里即可生效。 23 | 24 | ![碰撞层配置文件](./Images/CollisionLayerConfig.png) 25 | 26 | ### 主动碰撞层 27 | 28 | 主动碰撞层会在检测到碰撞后,触发自身的OnTrigger方法。在两个相互碰撞的层级之间,你应该选择数量较少的那一个作为主动碰撞层,以减少计算量。 29 | 30 | 例如,在一个弹幕地狱游戏中,有很多子弹和较少的敌人,那么你应该选择敌人所在的层级作为主动碰撞层。反之,如果有很多敌人,而子弹较少,那么你应该选择子弹所在的层级作为主动碰撞层。 31 | 32 | ### 被动碰撞层 33 | 34 | 被动碰撞层不会触发OnTrigger方法,但是当它被其他主动碰撞层碰撞时,会触发其他主动碰撞层的OnTrigger方法。 35 | 36 | ### 碰撞层配置 37 | 38 | 通过ActiveLayer字段设置一个主动碰撞层,通过CollisionLayers字段,设置可以与它碰撞的其他碰撞层(这个碰撞层可以是主动碰撞层,也可以是被动碰撞层)。 39 | 40 | 41 | ## WorldUnitCollision2DSystem 42 | 43 | ![矩形碰撞器](./Images/WorldUnitCollision2DSystem.png) 44 | 45 | WorldUnitCollision2DSystem是负责碰撞检测的核心系统。它通过将空间拆分成一个个网格,并只对在同一个网格中的物体进行碰撞检测,从而减少了碰撞检测的次数。 46 | 47 | 长时间未存在碰撞物体的网格会自动回收,你可以自己调整网格大小和网格存活时间,来获取最好的效果。但一般情况下,你只需要拖入CollisionLayerConfigSO(碰撞层配置文件)来配置碰撞层,其他保持默认即可。 48 | 49 | 通过showDebugInfo字段,可以开启或者关闭调试信息。调试信息开启的状态如下。每个网格的左下角表示当前网格的索引(为Vector2Int类型),右下角表示当前网格的碰撞物体数量。 50 | 51 | ![调试信息](./Images/DebugInfo.png) 52 | 53 | ## 碰撞器 54 | 55 | ### 注意:点碰撞器与点碰撞器之间不会发生碰撞。 56 | 57 | 碰撞器通过一个名为OnTrigger的Action来触发碰撞检测。你可以在其他组件中获取碰撞器,并订阅OnTrigger来处理碰撞后的逻辑。 58 | 59 | 详细内容可以参考 `Example` 文件夹下的 `Enemy` 脚本。 60 | 61 | ### 点碰撞器 62 | 63 | ![点碰撞器](./Images/WNCPointCollider.png) 64 | 65 | 在很多射击游戏中,子弹的体积较小,可以近似看作一个点。在不要求高精度碰撞检测的情况下,推荐使用点碰撞器来检测子弹与敌人之间的碰撞。 66 | 67 | 点状碰撞器拥有最好的性能,如果有可能,请尽量使用点碰撞器来处理较小体积的碰撞物体。如果你的物体比较大,那么可以将其他碰撞器(如矩形碰撞器)稍微调大一点进行弥补。 68 | 69 | 使用点状碰撞器只需要设置LayerName字段即可。 70 | 71 | ### 矩形碰撞器 72 | 73 | ![矩形碰撞器](./Images/WNCBoxCollider.png) 74 | 75 | 矩形碰撞器可以处理大多数情况下的碰撞检测需求。 76 | 77 | 通过Width和Height字段,可以设置矩形碰撞器的尺寸。 78 | 通过Offset字段,可以调整矩形碰撞器的位置。 79 | 80 | 你可以通过unity Editor中的绿色方框,来直观地看到碰撞器的位置和尺寸。 81 | 82 | ![矩形碰撞器](./Images/WNCBoxColliderGizmo.png) 83 | 84 | ### 其他碰撞器 85 | 86 | 在需要大量物体碰撞的场景中,一般不会对碰撞有很高的精度要求,矩形碰撞器可以满足绝大多数需求。其他碰撞器使用的场景较小,同时也会耗费更高的性能开销,目前暂无计划实现。如果你真的有需求可以通过issue进行反馈。 -------------------------------------------------------------------------------- /Core/Colliders/WNCBoxCollider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Sirenix.OdinInspector; 3 | using UnityEngine; 4 | using UnityEngine.Serialization; 5 | 6 | namespace WorldUnitCollision2DSystem 7 | { 8 | public class WNCBoxCollider : AbstractCollider 9 | { 10 | public float width = 1; 11 | public float height = 1; 12 | public Vector2 offset; 13 | [FormerlySerializedAs("DisableWhenOutOfCamera")] [LabelText("是否在屏幕外禁用")]public bool disableWhenOutOfCamera = true; 14 | private HashSet _worldUnits; 15 | void OnDisable() 16 | { 17 | if (_worldUnits != null) 18 | { 19 | WorldUnitCollision2DSystem.Instance.RemoveCollider(_worldUnits, LayerName, this); 20 | _worldUnits = null; 21 | } 22 | } 23 | 24 | void Update() 25 | { 26 | if (disableWhenOutOfCamera && CameraUtil.IsOutOfCamera(transform.position)) 27 | { 28 | if (_worldUnits != null) 29 | { 30 | WorldUnitCollision2DSystem.Instance.RemoveCollider(_worldUnits, LayerName, this); 31 | _worldUnits = null; 32 | } 33 | return; 34 | } 35 | 36 | var bounds = GetBounds(); 37 | var curWorldUnits = WorldUnitCollision2DSystem.Instance.GetWorldUnitGroup(bounds); 38 | if (_worldUnits == null || !curWorldUnits.SetEquals(_worldUnits)) 39 | { 40 | if (_worldUnits != null) WorldUnitCollision2DSystem.Instance.RemoveCollider(_worldUnits, LayerName, this); 41 | WorldUnitCollision2DSystem.Instance.AddCollider(curWorldUnits, LayerName, this); 42 | _worldUnits = curWorldUnits; 43 | } 44 | } 45 | 46 | public CollisionBounds GetBounds() 47 | { 48 | var xMin = transform.position.x - width / 2 + offset.x; 49 | var xMax = transform.position.x + width / 2 + offset.x; 50 | var yMin = transform.position.y - height / 2 + offset.y; 51 | var yMax = transform.position.y + height / 2 + offset.y; 52 | return new CollisionBounds(xMin, xMax, yMin, yMax); 53 | } 54 | 55 | #if UNITY_EDITOR 56 | // 绘制碰撞区域 57 | void OnDrawGizmosSelected() 58 | { 59 | var bounds = GetBounds(); 60 | Gizmos.color = Color.green; 61 | Vector2 topLeft = new Vector2(bounds.XMin, bounds.YMax); 62 | Vector2 topRight = new Vector2(bounds.XMax, bounds.YMax); 63 | Vector2 bottomLeft = new Vector2(bounds.XMin, bounds.YMin); 64 | Vector2 bottomRight = new Vector2(bounds.XMax, bounds.YMin); 65 | 66 | Gizmos.DrawLine(topLeft, topRight); 67 | Gizmos.DrawLine(topRight, bottomRight); 68 | Gizmos.DrawLine(bottomRight, bottomLeft); 69 | Gizmos.DrawLine(bottomLeft, topLeft); 70 | } 71 | #endif 72 | } 73 | } -------------------------------------------------------------------------------- /Images/DebugInfo.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: be1c62df7709a62448167dde2ea1a283 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | flipGreenChannel: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | vTOnly: 0 28 | ignoreMipmapLimit: 0 29 | grayScaleToAlpha: 0 30 | generateCubemap: 6 31 | cubemapConvolution: 0 32 | seamlessCubemap: 0 33 | textureFormat: 1 34 | maxTextureSize: 2048 35 | textureSettings: 36 | serializedVersion: 2 37 | filterMode: 1 38 | aniso: 1 39 | mipBias: 0 40 | wrapU: 0 41 | wrapV: 0 42 | wrapW: 0 43 | nPOTScale: 1 44 | lightmap: 0 45 | compressionQuality: 50 46 | spriteMode: 0 47 | spriteExtrude: 1 48 | spriteMeshType: 1 49 | alignment: 0 50 | spritePivot: {x: 0.5, y: 0.5} 51 | spritePixelsToUnits: 100 52 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 53 | spriteGenerateFallbackPhysicsShape: 1 54 | alphaUsage: 1 55 | alphaIsTransparency: 0 56 | spriteTessellationDetail: -1 57 | textureType: 0 58 | textureShape: 1 59 | singleChannelComponent: 0 60 | flipbookRows: 1 61 | flipbookColumns: 1 62 | maxTextureSizeSet: 0 63 | compressionQualitySet: 0 64 | textureFormatSet: 0 65 | ignorePngGamma: 0 66 | applyGammaDecoding: 0 67 | swizzle: 50462976 68 | cookieLightType: 0 69 | platformSettings: 70 | - serializedVersion: 3 71 | buildTarget: DefaultTexturePlatform 72 | maxTextureSize: 2048 73 | resizeAlgorithm: 0 74 | textureFormat: -1 75 | textureCompression: 1 76 | compressionQuality: 50 77 | crunchedCompression: 0 78 | allowsAlphaSplitting: 0 79 | overridden: 0 80 | ignorePlatformSupport: 0 81 | androidETC2FallbackOverride: 0 82 | forceMaximumCompressionQuality_BC6H_BC7: 0 83 | - serializedVersion: 3 84 | buildTarget: Standalone 85 | maxTextureSize: 2048 86 | resizeAlgorithm: 0 87 | textureFormat: -1 88 | textureCompression: 1 89 | compressionQuality: 50 90 | crunchedCompression: 0 91 | allowsAlphaSplitting: 0 92 | overridden: 0 93 | ignorePlatformSupport: 0 94 | androidETC2FallbackOverride: 0 95 | forceMaximumCompressionQuality_BC6H_BC7: 0 96 | spriteSheet: 97 | serializedVersion: 2 98 | sprites: [] 99 | outline: [] 100 | physicsShape: [] 101 | bones: [] 102 | spriteID: 103 | internalID: 0 104 | vertices: [] 105 | indices: 106 | edges: [] 107 | weights: [] 108 | secondaryTextures: [] 109 | nameFileIdTable: {} 110 | mipmapLimitGroupName: 111 | pSDRemoveMatte: 0 112 | userData: 113 | assetBundleName: 114 | assetBundleVariant: 115 | -------------------------------------------------------------------------------- /Images/WNCBoxCollider.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2544943288068574585324fcfe24015b 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | flipGreenChannel: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | vTOnly: 0 28 | ignoreMipmapLimit: 0 29 | grayScaleToAlpha: 0 30 | generateCubemap: 6 31 | cubemapConvolution: 0 32 | seamlessCubemap: 0 33 | textureFormat: 1 34 | maxTextureSize: 2048 35 | textureSettings: 36 | serializedVersion: 2 37 | filterMode: 1 38 | aniso: 1 39 | mipBias: 0 40 | wrapU: 0 41 | wrapV: 0 42 | wrapW: 0 43 | nPOTScale: 1 44 | lightmap: 0 45 | compressionQuality: 50 46 | spriteMode: 0 47 | spriteExtrude: 1 48 | spriteMeshType: 1 49 | alignment: 0 50 | spritePivot: {x: 0.5, y: 0.5} 51 | spritePixelsToUnits: 100 52 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 53 | spriteGenerateFallbackPhysicsShape: 1 54 | alphaUsage: 1 55 | alphaIsTransparency: 0 56 | spriteTessellationDetail: -1 57 | textureType: 0 58 | textureShape: 1 59 | singleChannelComponent: 0 60 | flipbookRows: 1 61 | flipbookColumns: 1 62 | maxTextureSizeSet: 0 63 | compressionQualitySet: 0 64 | textureFormatSet: 0 65 | ignorePngGamma: 0 66 | applyGammaDecoding: 0 67 | swizzle: 50462976 68 | cookieLightType: 0 69 | platformSettings: 70 | - serializedVersion: 3 71 | buildTarget: DefaultTexturePlatform 72 | maxTextureSize: 2048 73 | resizeAlgorithm: 0 74 | textureFormat: -1 75 | textureCompression: 1 76 | compressionQuality: 50 77 | crunchedCompression: 0 78 | allowsAlphaSplitting: 0 79 | overridden: 0 80 | ignorePlatformSupport: 0 81 | androidETC2FallbackOverride: 0 82 | forceMaximumCompressionQuality_BC6H_BC7: 0 83 | - serializedVersion: 3 84 | buildTarget: Standalone 85 | maxTextureSize: 2048 86 | resizeAlgorithm: 0 87 | textureFormat: -1 88 | textureCompression: 1 89 | compressionQuality: 50 90 | crunchedCompression: 0 91 | allowsAlphaSplitting: 0 92 | overridden: 0 93 | ignorePlatformSupport: 0 94 | androidETC2FallbackOverride: 0 95 | forceMaximumCompressionQuality_BC6H_BC7: 0 96 | spriteSheet: 97 | serializedVersion: 2 98 | sprites: [] 99 | outline: [] 100 | physicsShape: [] 101 | bones: [] 102 | spriteID: 103 | internalID: 0 104 | vertices: [] 105 | indices: 106 | edges: [] 107 | weights: [] 108 | secondaryTextures: [] 109 | nameFileIdTable: {} 110 | mipmapLimitGroupName: 111 | pSDRemoveMatte: 0 112 | userData: 113 | assetBundleName: 114 | assetBundleVariant: 115 | -------------------------------------------------------------------------------- /Images/CollisionLayerConfig.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aa4b6916505b79b44aa7f472b84f9f1e 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | flipGreenChannel: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | vTOnly: 0 28 | ignoreMipmapLimit: 0 29 | grayScaleToAlpha: 0 30 | generateCubemap: 6 31 | cubemapConvolution: 0 32 | seamlessCubemap: 0 33 | textureFormat: 1 34 | maxTextureSize: 2048 35 | textureSettings: 36 | serializedVersion: 2 37 | filterMode: 1 38 | aniso: 1 39 | mipBias: 0 40 | wrapU: 0 41 | wrapV: 0 42 | wrapW: 0 43 | nPOTScale: 1 44 | lightmap: 0 45 | compressionQuality: 50 46 | spriteMode: 0 47 | spriteExtrude: 1 48 | spriteMeshType: 1 49 | alignment: 0 50 | spritePivot: {x: 0.5, y: 0.5} 51 | spritePixelsToUnits: 100 52 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 53 | spriteGenerateFallbackPhysicsShape: 1 54 | alphaUsage: 1 55 | alphaIsTransparency: 0 56 | spriteTessellationDetail: -1 57 | textureType: 0 58 | textureShape: 1 59 | singleChannelComponent: 0 60 | flipbookRows: 1 61 | flipbookColumns: 1 62 | maxTextureSizeSet: 0 63 | compressionQualitySet: 0 64 | textureFormatSet: 0 65 | ignorePngGamma: 0 66 | applyGammaDecoding: 0 67 | swizzle: 50462976 68 | cookieLightType: 0 69 | platformSettings: 70 | - serializedVersion: 3 71 | buildTarget: DefaultTexturePlatform 72 | maxTextureSize: 2048 73 | resizeAlgorithm: 0 74 | textureFormat: -1 75 | textureCompression: 1 76 | compressionQuality: 50 77 | crunchedCompression: 0 78 | allowsAlphaSplitting: 0 79 | overridden: 0 80 | ignorePlatformSupport: 0 81 | androidETC2FallbackOverride: 0 82 | forceMaximumCompressionQuality_BC6H_BC7: 0 83 | - serializedVersion: 3 84 | buildTarget: Standalone 85 | maxTextureSize: 2048 86 | resizeAlgorithm: 0 87 | textureFormat: -1 88 | textureCompression: 1 89 | compressionQuality: 50 90 | crunchedCompression: 0 91 | allowsAlphaSplitting: 0 92 | overridden: 0 93 | ignorePlatformSupport: 0 94 | androidETC2FallbackOverride: 0 95 | forceMaximumCompressionQuality_BC6H_BC7: 0 96 | spriteSheet: 97 | serializedVersion: 2 98 | sprites: [] 99 | outline: [] 100 | physicsShape: [] 101 | bones: [] 102 | spriteID: 103 | internalID: 0 104 | vertices: [] 105 | indices: 106 | edges: [] 107 | weights: [] 108 | secondaryTextures: [] 109 | nameFileIdTable: {} 110 | mipmapLimitGroupName: 111 | pSDRemoveMatte: 0 112 | userData: 113 | assetBundleName: 114 | assetBundleVariant: 115 | -------------------------------------------------------------------------------- /Images/WNCBoxColliderGizmo.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7fcc9767d03b3df4bae2cb381056e9b6 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | flipGreenChannel: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | vTOnly: 0 28 | ignoreMipmapLimit: 0 29 | grayScaleToAlpha: 0 30 | generateCubemap: 6 31 | cubemapConvolution: 0 32 | seamlessCubemap: 0 33 | textureFormat: 1 34 | maxTextureSize: 2048 35 | textureSettings: 36 | serializedVersion: 2 37 | filterMode: 1 38 | aniso: 1 39 | mipBias: 0 40 | wrapU: 0 41 | wrapV: 0 42 | wrapW: 0 43 | nPOTScale: 1 44 | lightmap: 0 45 | compressionQuality: 50 46 | spriteMode: 0 47 | spriteExtrude: 1 48 | spriteMeshType: 1 49 | alignment: 0 50 | spritePivot: {x: 0.5, y: 0.5} 51 | spritePixelsToUnits: 100 52 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 53 | spriteGenerateFallbackPhysicsShape: 1 54 | alphaUsage: 1 55 | alphaIsTransparency: 0 56 | spriteTessellationDetail: -1 57 | textureType: 0 58 | textureShape: 1 59 | singleChannelComponent: 0 60 | flipbookRows: 1 61 | flipbookColumns: 1 62 | maxTextureSizeSet: 0 63 | compressionQualitySet: 0 64 | textureFormatSet: 0 65 | ignorePngGamma: 0 66 | applyGammaDecoding: 0 67 | swizzle: 50462976 68 | cookieLightType: 0 69 | platformSettings: 70 | - serializedVersion: 3 71 | buildTarget: DefaultTexturePlatform 72 | maxTextureSize: 2048 73 | resizeAlgorithm: 0 74 | textureFormat: -1 75 | textureCompression: 1 76 | compressionQuality: 50 77 | crunchedCompression: 0 78 | allowsAlphaSplitting: 0 79 | overridden: 0 80 | ignorePlatformSupport: 0 81 | androidETC2FallbackOverride: 0 82 | forceMaximumCompressionQuality_BC6H_BC7: 0 83 | - serializedVersion: 3 84 | buildTarget: Standalone 85 | maxTextureSize: 2048 86 | resizeAlgorithm: 0 87 | textureFormat: -1 88 | textureCompression: 1 89 | compressionQuality: 50 90 | crunchedCompression: 0 91 | allowsAlphaSplitting: 0 92 | overridden: 0 93 | ignorePlatformSupport: 0 94 | androidETC2FallbackOverride: 0 95 | forceMaximumCompressionQuality_BC6H_BC7: 0 96 | spriteSheet: 97 | serializedVersion: 2 98 | sprites: [] 99 | outline: [] 100 | physicsShape: [] 101 | bones: [] 102 | spriteID: 103 | internalID: 0 104 | vertices: [] 105 | indices: 106 | edges: [] 107 | weights: [] 108 | secondaryTextures: [] 109 | nameFileIdTable: {} 110 | mipmapLimitGroupName: 111 | pSDRemoveMatte: 0 112 | userData: 113 | assetBundleName: 114 | assetBundleVariant: 115 | -------------------------------------------------------------------------------- /Images/WNCPointCollider.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ce9107ada6d14bc4fa2cf3fc5a8fc5a6 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | flipGreenChannel: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | vTOnly: 0 28 | ignoreMipmapLimit: 0 29 | grayScaleToAlpha: 0 30 | generateCubemap: 6 31 | cubemapConvolution: 0 32 | seamlessCubemap: 0 33 | textureFormat: 1 34 | maxTextureSize: 2048 35 | textureSettings: 36 | serializedVersion: 2 37 | filterMode: 1 38 | aniso: 1 39 | mipBias: 0 40 | wrapU: 0 41 | wrapV: 0 42 | wrapW: 0 43 | nPOTScale: 1 44 | lightmap: 0 45 | compressionQuality: 50 46 | spriteMode: 0 47 | spriteExtrude: 1 48 | spriteMeshType: 1 49 | alignment: 0 50 | spritePivot: {x: 0.5, y: 0.5} 51 | spritePixelsToUnits: 100 52 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 53 | spriteGenerateFallbackPhysicsShape: 1 54 | alphaUsage: 1 55 | alphaIsTransparency: 0 56 | spriteTessellationDetail: -1 57 | textureType: 0 58 | textureShape: 1 59 | singleChannelComponent: 0 60 | flipbookRows: 1 61 | flipbookColumns: 1 62 | maxTextureSizeSet: 0 63 | compressionQualitySet: 0 64 | textureFormatSet: 0 65 | ignorePngGamma: 0 66 | applyGammaDecoding: 0 67 | swizzle: 50462976 68 | cookieLightType: 0 69 | platformSettings: 70 | - serializedVersion: 3 71 | buildTarget: DefaultTexturePlatform 72 | maxTextureSize: 2048 73 | resizeAlgorithm: 0 74 | textureFormat: -1 75 | textureCompression: 1 76 | compressionQuality: 50 77 | crunchedCompression: 0 78 | allowsAlphaSplitting: 0 79 | overridden: 0 80 | ignorePlatformSupport: 0 81 | androidETC2FallbackOverride: 0 82 | forceMaximumCompressionQuality_BC6H_BC7: 0 83 | - serializedVersion: 3 84 | buildTarget: Standalone 85 | maxTextureSize: 2048 86 | resizeAlgorithm: 0 87 | textureFormat: -1 88 | textureCompression: 1 89 | compressionQuality: 50 90 | crunchedCompression: 0 91 | allowsAlphaSplitting: 0 92 | overridden: 0 93 | ignorePlatformSupport: 0 94 | androidETC2FallbackOverride: 0 95 | forceMaximumCompressionQuality_BC6H_BC7: 0 96 | spriteSheet: 97 | serializedVersion: 2 98 | sprites: [] 99 | outline: [] 100 | physicsShape: [] 101 | bones: [] 102 | spriteID: 103 | internalID: 0 104 | vertices: [] 105 | indices: 106 | edges: [] 107 | weights: [] 108 | secondaryTextures: [] 109 | nameFileIdTable: {} 110 | mipmapLimitGroupName: 111 | pSDRemoveMatte: 0 112 | userData: 113 | assetBundleName: 114 | assetBundleVariant: 115 | -------------------------------------------------------------------------------- /Images/WorldUnitCollision2DSystem.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7c34c0eab7db6d34684d3cc6cd729f93 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | flipGreenChannel: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | vTOnly: 0 28 | ignoreMipmapLimit: 0 29 | grayScaleToAlpha: 0 30 | generateCubemap: 6 31 | cubemapConvolution: 0 32 | seamlessCubemap: 0 33 | textureFormat: 1 34 | maxTextureSize: 2048 35 | textureSettings: 36 | serializedVersion: 2 37 | filterMode: 1 38 | aniso: 1 39 | mipBias: 0 40 | wrapU: 0 41 | wrapV: 0 42 | wrapW: 0 43 | nPOTScale: 1 44 | lightmap: 0 45 | compressionQuality: 50 46 | spriteMode: 0 47 | spriteExtrude: 1 48 | spriteMeshType: 1 49 | alignment: 0 50 | spritePivot: {x: 0.5, y: 0.5} 51 | spritePixelsToUnits: 100 52 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 53 | spriteGenerateFallbackPhysicsShape: 1 54 | alphaUsage: 1 55 | alphaIsTransparency: 0 56 | spriteTessellationDetail: -1 57 | textureType: 0 58 | textureShape: 1 59 | singleChannelComponent: 0 60 | flipbookRows: 1 61 | flipbookColumns: 1 62 | maxTextureSizeSet: 0 63 | compressionQualitySet: 0 64 | textureFormatSet: 0 65 | ignorePngGamma: 0 66 | applyGammaDecoding: 0 67 | swizzle: 50462976 68 | cookieLightType: 0 69 | platformSettings: 70 | - serializedVersion: 3 71 | buildTarget: DefaultTexturePlatform 72 | maxTextureSize: 2048 73 | resizeAlgorithm: 0 74 | textureFormat: -1 75 | textureCompression: 1 76 | compressionQuality: 50 77 | crunchedCompression: 0 78 | allowsAlphaSplitting: 0 79 | overridden: 0 80 | ignorePlatformSupport: 0 81 | androidETC2FallbackOverride: 0 82 | forceMaximumCompressionQuality_BC6H_BC7: 0 83 | - serializedVersion: 3 84 | buildTarget: Standalone 85 | maxTextureSize: 2048 86 | resizeAlgorithm: 0 87 | textureFormat: -1 88 | textureCompression: 1 89 | compressionQuality: 50 90 | crunchedCompression: 0 91 | allowsAlphaSplitting: 0 92 | overridden: 0 93 | ignorePlatformSupport: 0 94 | androidETC2FallbackOverride: 0 95 | forceMaximumCompressionQuality_BC6H_BC7: 0 96 | spriteSheet: 97 | serializedVersion: 2 98 | sprites: [] 99 | outline: [] 100 | physicsShape: [] 101 | bones: [] 102 | spriteID: 103 | internalID: 0 104 | vertices: [] 105 | indices: 106 | edges: [] 107 | weights: [] 108 | secondaryTextures: [] 109 | nameFileIdTable: {} 110 | mipmapLimitGroupName: 111 | pSDRemoveMatte: 0 112 | userData: 113 | assetBundleName: 114 | assetBundleVariant: 115 | -------------------------------------------------------------------------------- /Example/BaseExample/Square.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2c2f8f7054dc8bc43af819346294f9d0 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 0 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | flipGreenChannel: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | vTOnly: 0 28 | ignoreMipmapLimit: 0 29 | grayScaleToAlpha: 0 30 | generateCubemap: 6 31 | cubemapConvolution: 0 32 | seamlessCubemap: 0 33 | textureFormat: 1 34 | maxTextureSize: 2048 35 | textureSettings: 36 | serializedVersion: 2 37 | filterMode: 1 38 | aniso: 1 39 | mipBias: 0 40 | wrapU: 1 41 | wrapV: 1 42 | wrapW: 0 43 | nPOTScale: 0 44 | lightmap: 0 45 | compressionQuality: 50 46 | spriteMode: 1 47 | spriteExtrude: 1 48 | spriteMeshType: 1 49 | alignment: 0 50 | spritePivot: {x: 0.5, y: 0.5} 51 | spritePixelsToUnits: 5 52 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 53 | spriteGenerateFallbackPhysicsShape: 1 54 | alphaUsage: 1 55 | alphaIsTransparency: 1 56 | spriteTessellationDetail: -1 57 | textureType: 8 58 | textureShape: 1 59 | singleChannelComponent: 0 60 | flipbookRows: 1 61 | flipbookColumns: 1 62 | maxTextureSizeSet: 0 63 | compressionQualitySet: 0 64 | textureFormatSet: 0 65 | ignorePngGamma: 0 66 | applyGammaDecoding: 0 67 | swizzle: 50462976 68 | cookieLightType: 0 69 | platformSettings: 70 | - serializedVersion: 3 71 | buildTarget: DefaultTexturePlatform 72 | maxTextureSize: 2048 73 | resizeAlgorithm: 0 74 | textureFormat: -1 75 | textureCompression: 1 76 | compressionQuality: 50 77 | crunchedCompression: 0 78 | allowsAlphaSplitting: 0 79 | overridden: 0 80 | ignorePlatformSupport: 0 81 | androidETC2FallbackOverride: 0 82 | forceMaximumCompressionQuality_BC6H_BC7: 0 83 | - serializedVersion: 3 84 | buildTarget: Standalone 85 | maxTextureSize: 2048 86 | resizeAlgorithm: 0 87 | textureFormat: -1 88 | textureCompression: 1 89 | compressionQuality: 50 90 | crunchedCompression: 0 91 | allowsAlphaSplitting: 0 92 | overridden: 0 93 | ignorePlatformSupport: 0 94 | androidETC2FallbackOverride: 0 95 | forceMaximumCompressionQuality_BC6H_BC7: 0 96 | spriteSheet: 97 | serializedVersion: 2 98 | sprites: [] 99 | outline: [] 100 | physicsShape: [] 101 | bones: [] 102 | spriteID: 5e97eb03825dee720800000000000000 103 | internalID: 0 104 | vertices: [] 105 | indices: 106 | edges: [] 107 | weights: [] 108 | secondaryTextures: [] 109 | nameFileIdTable: {} 110 | mipmapLimitGroupName: 111 | pSDRemoveMatte: 0 112 | userData: 113 | assetBundleName: 114 | assetBundleVariant: 115 | -------------------------------------------------------------------------------- /Core/WorldUnit.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace WorldUnitCollision2DSystem 5 | { 6 | // 对象池管理类 7 | public class WorldUnitObjectPool 8 | { 9 | private readonly List _pool = new(); 10 | 11 | public WorldUnit GetObject(Vector2Int index, float size, float xMin, float yMin) 12 | { 13 | WorldUnit unit; 14 | if (_pool.Count > 0) 15 | { 16 | unit = _pool[^1]; 17 | _pool.RemoveAt(_pool.Count - 1); 18 | unit.Reset(index, size, xMin, yMin); 19 | } 20 | else 21 | { 22 | unit = new WorldUnit(index, size, xMin, yMin); 23 | } 24 | return unit; 25 | } 26 | 27 | public void ReturnObject(WorldUnit unit) 28 | { 29 | _pool.Add(unit); 30 | } 31 | } 32 | 33 | // 单个网格 34 | public class WorldUnit 35 | { 36 | public Vector2Int Index; 37 | public float Size; // 格子大小 38 | public CollisionBounds CollisionBounds; 39 | public int ObjectCount { get; private set; } = 0; 40 | // 长时间没有出现碰撞的话,进行销毁 41 | public float LastCollisionTime = 0; 42 | 43 | public WorldUnit(Vector2Int index, float size, float xMin, float yMin) 44 | { 45 | this.Index = index; 46 | this.Size = size; 47 | CollisionBounds.XMin = xMin; 48 | CollisionBounds.XMax = xMin + size; 49 | CollisionBounds.YMin = yMin; 50 | CollisionBounds.YMax = yMin + size; 51 | } 52 | 53 | public void Reset(Vector2Int index, float size, float xMin, float yMin) 54 | { 55 | this.Index = index; 56 | this.Size = size; 57 | CollisionBounds.XMin = xMin; 58 | CollisionBounds.XMax = xMin + size; 59 | CollisionBounds.YMin = yMin; 60 | CollisionBounds.YMax = yMin + size; 61 | ObjectCount = 0; // 重置物体计数 62 | } 63 | 64 | // 根据layerName存储物体 65 | public Dictionary> LayerObjects = new(); 66 | 67 | public void AddCollider(string layerName, AbstractCollider cld) 68 | { 69 | ObjectCount++; 70 | if (!LayerObjects.ContainsKey(layerName)) 71 | { 72 | LayerObjects.Add(layerName, new HashSet()); 73 | } 74 | LayerObjects[layerName].Add(cld); 75 | } 76 | 77 | public void RemoveCollider(string layerName, AbstractCollider cld) 78 | { 79 | if (!LayerObjects.ContainsKey(layerName)) return; 80 | if (LayerObjects[layerName].Contains(cld)) 81 | { 82 | LayerObjects[layerName].Remove(cld); 83 | ObjectCount--; 84 | } 85 | } 86 | 87 | public void Clear(string layerName) 88 | { 89 | ObjectCount -= LayerObjects[layerName].Count; 90 | LayerObjects[layerName].Clear(); 91 | } 92 | 93 | public void ClearAll() 94 | { 95 | ObjectCount = 0; 96 | foreach (var item in LayerObjects) 97 | { 98 | item.Value.Clear(); 99 | } 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /Example/BaseExample/Bullet.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &957270325262730545 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 6032977897062458999} 12 | - component: {fileID: 2805831734243524538} 13 | - component: {fileID: 3207980425291855861} 14 | - component: {fileID: 1277530426100780109} 15 | m_Layer: 0 16 | m_Name: Bullet 17 | m_TagString: Untagged 18 | m_Icon: {fileID: 0} 19 | m_NavMeshLayer: 0 20 | m_StaticEditorFlags: 0 21 | m_IsActive: 1 22 | --- !u!4 &6032977897062458999 23 | Transform: 24 | m_ObjectHideFlags: 0 25 | m_CorrespondingSourceObject: {fileID: 0} 26 | m_PrefabInstance: {fileID: 0} 27 | m_PrefabAsset: {fileID: 0} 28 | m_GameObject: {fileID: 957270325262730545} 29 | serializedVersion: 2 30 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 31 | m_LocalPosition: {x: -6.3748026, y: 2.54, z: 0.021226775} 32 | m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} 33 | m_ConstrainProportionsScale: 0 34 | m_Children: [] 35 | m_Father: {fileID: 0} 36 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 37 | --- !u!114 &2805831734243524538 38 | MonoBehaviour: 39 | m_ObjectHideFlags: 0 40 | m_CorrespondingSourceObject: {fileID: 0} 41 | m_PrefabInstance: {fileID: 0} 42 | m_PrefabAsset: {fileID: 0} 43 | m_GameObject: {fileID: 957270325262730545} 44 | m_Enabled: 1 45 | m_EditorHideFlags: 0 46 | m_Script: {fileID: 11500000, guid: 73640d515f048644aa5c62150bef0e3e, type: 3} 47 | m_Name: 48 | m_EditorClassIdentifier: 49 | speed: 20 50 | --- !u!212 &3207980425291855861 51 | SpriteRenderer: 52 | m_ObjectHideFlags: 0 53 | m_CorrespondingSourceObject: {fileID: 0} 54 | m_PrefabInstance: {fileID: 0} 55 | m_PrefabAsset: {fileID: 0} 56 | m_GameObject: {fileID: 957270325262730545} 57 | m_Enabled: 1 58 | m_CastShadows: 0 59 | m_ReceiveShadows: 0 60 | m_DynamicOccludee: 1 61 | m_StaticShadowCaster: 0 62 | m_MotionVectors: 1 63 | m_LightProbeUsage: 1 64 | m_ReflectionProbeUsage: 1 65 | m_RayTracingMode: 0 66 | m_RayTraceProcedural: 0 67 | m_RenderingLayerMask: 1 68 | m_RendererPriority: 0 69 | m_Materials: 70 | - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} 71 | m_StaticBatchInfo: 72 | firstSubMesh: 0 73 | subMeshCount: 0 74 | m_StaticBatchRoot: {fileID: 0} 75 | m_ProbeAnchor: {fileID: 0} 76 | m_LightProbeVolumeOverride: {fileID: 0} 77 | m_ScaleInLightmap: 1 78 | m_ReceiveGI: 1 79 | m_PreserveUVs: 0 80 | m_IgnoreNormalsForChartDetection: 0 81 | m_ImportantGI: 0 82 | m_StitchLightmapSeams: 1 83 | m_SelectedEditorRenderState: 0 84 | m_MinimumChartSize: 4 85 | m_AutoUVMaxDistance: 0.5 86 | m_AutoUVMaxAngle: 89 87 | m_LightmapParameters: {fileID: 0} 88 | m_SortingLayerID: 0 89 | m_SortingLayer: 0 90 | m_SortingOrder: 0 91 | m_Sprite: {fileID: 21300000, guid: 2c2f8f7054dc8bc43af819346294f9d0, type: 3} 92 | m_Color: {r: 0, g: 1, b: 0.13536787, a: 1} 93 | m_FlipX: 0 94 | m_FlipY: 0 95 | m_DrawMode: 0 96 | m_Size: {x: 0.5, y: 0.5} 97 | m_AdaptiveModeThreshold: 0.5 98 | m_SpriteTileMode: 0 99 | m_WasSpriteAssigned: 1 100 | m_MaskInteraction: 0 101 | m_SpriteSortPoint: 0 102 | --- !u!114 &1277530426100780109 103 | MonoBehaviour: 104 | m_ObjectHideFlags: 0 105 | m_CorrespondingSourceObject: {fileID: 0} 106 | m_PrefabInstance: {fileID: 0} 107 | m_PrefabAsset: {fileID: 0} 108 | m_GameObject: {fileID: 957270325262730545} 109 | m_Enabled: 1 110 | m_EditorHideFlags: 0 111 | m_Script: {fileID: 11500000, guid: 14ac46b56af43f24cb64f85f57d97e02, type: 3} 112 | m_Name: 113 | m_EditorClassIdentifier: 114 | LayerName: PlayerBullet 115 | -------------------------------------------------------------------------------- /Example/BaseExample/Enemy.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &1158270053580576071 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 3325859767640905422} 12 | - component: {fileID: 7955356660780016797} 13 | - component: {fileID: 3356812062940992355} 14 | - component: {fileID: 1602864597379335353} 15 | m_Layer: 0 16 | m_Name: Enemy 17 | m_TagString: Untagged 18 | m_Icon: {fileID: 0} 19 | m_NavMeshLayer: 0 20 | m_StaticEditorFlags: 0 21 | m_IsActive: 1 22 | --- !u!4 &3325859767640905422 23 | Transform: 24 | m_ObjectHideFlags: 0 25 | m_CorrespondingSourceObject: {fileID: 0} 26 | m_PrefabInstance: {fileID: 0} 27 | m_PrefabAsset: {fileID: 0} 28 | m_GameObject: {fileID: 1158270053580576071} 29 | serializedVersion: 2 30 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 31 | m_LocalPosition: {x: -6.3748026, y: 1.3543708, z: 0.021226775} 32 | m_LocalScale: {x: 1, y: 1, z: 1} 33 | m_ConstrainProportionsScale: 0 34 | m_Children: [] 35 | m_Father: {fileID: 0} 36 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 37 | --- !u!114 &7955356660780016797 38 | MonoBehaviour: 39 | m_ObjectHideFlags: 0 40 | m_CorrespondingSourceObject: {fileID: 0} 41 | m_PrefabInstance: {fileID: 0} 42 | m_PrefabAsset: {fileID: 0} 43 | m_GameObject: {fileID: 1158270053580576071} 44 | m_Enabled: 1 45 | m_EditorHideFlags: 0 46 | m_Script: {fileID: 11500000, guid: 83813b50246d4f94399d0ad530b08d07, type: 3} 47 | m_Name: 48 | m_EditorClassIdentifier: 49 | moveSpeed: 1 50 | --- !u!212 &3356812062940992355 51 | SpriteRenderer: 52 | m_ObjectHideFlags: 0 53 | m_CorrespondingSourceObject: {fileID: 0} 54 | m_PrefabInstance: {fileID: 0} 55 | m_PrefabAsset: {fileID: 0} 56 | m_GameObject: {fileID: 1158270053580576071} 57 | m_Enabled: 1 58 | m_CastShadows: 0 59 | m_ReceiveShadows: 0 60 | m_DynamicOccludee: 1 61 | m_StaticShadowCaster: 0 62 | m_MotionVectors: 1 63 | m_LightProbeUsage: 1 64 | m_ReflectionProbeUsage: 1 65 | m_RayTracingMode: 0 66 | m_RayTraceProcedural: 0 67 | m_RenderingLayerMask: 1 68 | m_RendererPriority: 0 69 | m_Materials: 70 | - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} 71 | m_StaticBatchInfo: 72 | firstSubMesh: 0 73 | subMeshCount: 0 74 | m_StaticBatchRoot: {fileID: 0} 75 | m_ProbeAnchor: {fileID: 0} 76 | m_LightProbeVolumeOverride: {fileID: 0} 77 | m_ScaleInLightmap: 1 78 | m_ReceiveGI: 1 79 | m_PreserveUVs: 0 80 | m_IgnoreNormalsForChartDetection: 0 81 | m_ImportantGI: 0 82 | m_StitchLightmapSeams: 1 83 | m_SelectedEditorRenderState: 0 84 | m_MinimumChartSize: 4 85 | m_AutoUVMaxDistance: 0.5 86 | m_AutoUVMaxAngle: 89 87 | m_LightmapParameters: {fileID: 0} 88 | m_SortingLayerID: 0 89 | m_SortingLayer: 0 90 | m_SortingOrder: 0 91 | m_Sprite: {fileID: 21300000, guid: 2c2f8f7054dc8bc43af819346294f9d0, type: 3} 92 | m_Color: {r: 1, g: 0.495283, b: 0.495283, a: 1} 93 | m_FlipX: 0 94 | m_FlipY: 0 95 | m_DrawMode: 0 96 | m_Size: {x: 0.04, y: 0.04} 97 | m_AdaptiveModeThreshold: 0.5 98 | m_SpriteTileMode: 0 99 | m_WasSpriteAssigned: 1 100 | m_MaskInteraction: 0 101 | m_SpriteSortPoint: 0 102 | --- !u!114 &1602864597379335353 103 | MonoBehaviour: 104 | m_ObjectHideFlags: 0 105 | m_CorrespondingSourceObject: {fileID: 0} 106 | m_PrefabInstance: {fileID: 0} 107 | m_PrefabAsset: {fileID: 0} 108 | m_GameObject: {fileID: 1158270053580576071} 109 | m_Enabled: 1 110 | m_EditorHideFlags: 0 111 | m_Script: {fileID: 11500000, guid: 582f6cb176d1c2741bbb188c9c04cfad, type: 3} 112 | m_Name: 113 | m_EditorClassIdentifier: 114 | LayerName: Enemy 115 | width: 1 116 | height: 1 117 | offset: {x: 0, y: 0} 118 | -------------------------------------------------------------------------------- /Core/CollisionLayerConfigSO.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Sirenix.OdinInspector; 6 | using UnityEngine; 7 | 8 | namespace WorldUnitCollision2DSystem 9 | { 10 | [Serializable] 11 | public class CollisionLayerConfig 12 | { 13 | [LabelText("当前主动碰撞层")] 14 | [ValueDropdown("GetActiveLayers")] 15 | [ValidateInput("ValidateActiveLayerExist", "只能配置主动碰撞层")] 16 | public string ActiveLayer; 17 | 18 | [LabelText("与当前主动层碰撞的其他层")] 19 | [ValueDropdown("GetCollisionLayers")] 20 | [ValidateInput("ValidateCollisionLayersDuplicate", "不能有重复的碰撞层配置")] 21 | [ValidateInput("ValidateCollisionLayerExist", "当前碰撞层不存在")] 22 | [ValidateInput("ValidateNotActiveLayer", "不能配置当前主动层")] 23 | public List CollisionLayers; 24 | 25 | private List _activeCollisionLayers; 26 | private List _passiveCollisionLayers; 27 | public void SetCollisionLayers(List activeCollisionLayers, List passiveCollisionLayers) 28 | { 29 | _activeCollisionLayers = activeCollisionLayers; 30 | _passiveCollisionLayers = passiveCollisionLayers; 31 | } 32 | 33 | private List GetActiveLayers() 34 | { 35 | return _activeCollisionLayers; 36 | } 37 | 38 | // 获取所有碰撞层,但不包括当前CurrentActiveLayer 39 | private List GetCollisionLayers() 40 | { 41 | return _activeCollisionLayers.Where(layer => layer != ActiveLayer).Concat(_passiveCollisionLayers).ToList(); 42 | } 43 | 44 | // 验证碰撞层,不能有重复的碰撞层 45 | private bool ValidateCollisionLayersDuplicate(List collisionLayers) 46 | { 47 | return collisionLayers.Distinct().Count() == collisionLayers.Count; 48 | } 49 | 50 | // 验证碰撞层, 配置的主动碰撞层是否存在 51 | private bool ValidateActiveLayerExist(string activeLayer) 52 | { 53 | return _activeCollisionLayers.Contains(activeLayer); 54 | } 55 | 56 | // 验证碰撞层, 配置的碰撞层是否存在 57 | private bool ValidateCollisionLayerExist(List collisionLayers) 58 | { 59 | var allLayers = _activeCollisionLayers.Concat(_passiveCollisionLayers).ToList(); 60 | foreach (var layer in collisionLayers) 61 | { 62 | if (!allLayers.Contains(layer)) 63 | { 64 | return false; 65 | } 66 | } 67 | return true; 68 | } 69 | 70 | // 验证碰撞层,不能配置当前主动层 71 | private bool ValidateNotActiveLayer(List collisionLayers) 72 | { 73 | return !collisionLayers.Contains(ActiveLayer); 74 | } 75 | } 76 | 77 | [CreateAssetMenu(fileName = "CollisionLayerConfig", menuName = "WorldUnitCollision2DSystem/CollisionLayerConfig")] 78 | public class CollisionLayerConfigSO : ScriptableObject 79 | { 80 | 81 | [LabelText("主动碰撞层名称")] 82 | [OnValueChanged("OnCollisionLayersChanged")] 83 | [ValidateInput("ValidateLayerDuplicate", "不能有重复的碰撞层")] 84 | [ValidateInput("ValidateLayerEmpty", "不能有为空的碰撞层")] 85 | public List ActiveCollisionLayers; 86 | 87 | [LabelText("被动碰撞层名称")] 88 | [OnValueChanged("OnCollisionLayersChanged")] 89 | [ValidateInput("ValidateLayerDuplicate", "不能有重复的碰撞层")] 90 | [ValidateInput("ValidateLayerEmpty", "不能有为空的碰撞层")] 91 | public List PassiveCollisionLayers; 92 | 93 | [LabelText("碰撞层配置"), TableList, Space(10)] 94 | [OnValueChanged("OnCollisionLayersChanged")] 95 | [ValidateInput("ValidateLayerConfigDuplicate", "不能有重复的碰撞层配置")] 96 | [ValidateInput("ValidateLayerConfigEmpty", "不能有为空的碰撞层配置")] 97 | public List CollisionLayerConfigs; 98 | 99 | private void OnEnable() 100 | { 101 | foreach (var config in CollisionLayerConfigs) 102 | { 103 | config.SetCollisionLayers(ActiveCollisionLayers, PassiveCollisionLayers); 104 | } 105 | } 106 | 107 | // 获取碰撞层相关配置 108 | public CollisionLayerConfig GetCollisionLayerConfig(string layerName) 109 | { 110 | return CollisionLayerConfigs.Find(config => config.ActiveLayer == layerName); 111 | } 112 | 113 | private void OnCollisionLayersChanged() 114 | { 115 | foreach (var config in CollisionLayerConfigs) 116 | { 117 | config.SetCollisionLayers(ActiveCollisionLayers, PassiveCollisionLayers); 118 | } 119 | } 120 | 121 | 122 | // 验证碰撞层,不能有重复的层 123 | private bool ValidateLayerDuplicate(List layers) 124 | { 125 | return layers.Distinct().Count() == layers.Count; 126 | } 127 | 128 | // 验证碰撞层,不能有为空的层 129 | private bool ValidateLayerEmpty(List layers) 130 | { 131 | return layers.All(layer => !string.IsNullOrEmpty(layer)); 132 | } 133 | 134 | // 验证碰撞层配置, 不能有重复的层 135 | private bool ValidateLayerConfigDuplicate(List collisionLayerConfigs) 136 | { 137 | var layers = new List(); 138 | foreach (var config in collisionLayerConfigs) 139 | { 140 | if (layers.Contains(config.ActiveLayer)) 141 | { 142 | return false; 143 | } 144 | layers.Add(config.ActiveLayer); 145 | } 146 | return true; 147 | } 148 | 149 | // 验证碰撞层配置,不能有为空的层 150 | private bool ValidateLayerConfigEmpty(List collisionLayerConfigs) 151 | { 152 | foreach (var config in collisionLayerConfigs) 153 | { 154 | if (string.IsNullOrEmpty(config.ActiveLayer)) 155 | { 156 | return false; 157 | } 158 | } 159 | return true; 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /Core/WorldUnitCollision2DSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Sirenix.OdinInspector; 4 | using UnityEditor; 5 | using UnityEngine; 6 | using UnityEngine.Serialization; 7 | 8 | namespace WorldUnitCollision2DSystem 9 | { 10 | // 碰撞边界 11 | public struct CollisionBounds 12 | { 13 | public CollisionBounds(float XMin, float XMax, float YMin, float YMax) 14 | { 15 | this.XMin = XMin; 16 | this.XMax = XMax; 17 | this.YMin = YMin; 18 | this.YMax = YMax; 19 | } 20 | public float XMin; 21 | public float XMax; 22 | public float YMin; 23 | public float YMax; 24 | } 25 | 26 | // 网格碰撞系统 27 | public class WorldUnitCollision2DSystem : MonoBehaviour 28 | { 29 | public static WorldUnitCollision2DSystem Instance; 30 | [FormerlySerializedAs("CollisionLayerConfigSO")] [LabelText("碰撞层配置文件")] public CollisionLayerConfigSO CollisionLayerConfigSo; 31 | [SerializeField, LabelText("网格大小")] private float unitWidth = 1; 32 | [SerializeField, LabelText("网格存活时间")] private float worldUnitRemoveTime = 5; 33 | 34 | private readonly Dictionary WorldUnits = new(); 35 | // 需要删除的网格 36 | private readonly List _worldUnitsToRemove = new(); 37 | // 待触发碰撞的物体 38 | private readonly List _triggerActionList = new(); 39 | // worldUnit对象池 40 | private readonly WorldUnitObjectPool _worldUnitObjectPool = new(); 41 | #if UNITY_EDITOR 42 | [FormerlySerializedAs("ShowDebugInfo")] public bool showDebugInfo = true; 43 | #endif 44 | void Awake() 45 | { 46 | Instance = this; 47 | } 48 | 49 | void Update() 50 | { 51 | CheckCollision(); 52 | TriggerAllCollision(); 53 | RemoveWorldUnits(); 54 | } 55 | 56 | // 触发所有的碰撞事件 57 | private void TriggerAllCollision() 58 | { 59 | _triggerActionList.ForEach(e => 60 | { 61 | e?.Invoke(); 62 | }); 63 | _triggerActionList.Clear(); 64 | } 65 | 66 | // 移除长时间不用的网格 67 | private void RemoveWorldUnits() 68 | { 69 | if (worldUnitRemoveTime <= 0) return; 70 | foreach (var item in _worldUnitsToRemove) 71 | { 72 | _worldUnitObjectPool.ReturnObject(item); 73 | WorldUnits.Remove(item.Index); 74 | } 75 | _worldUnitsToRemove.Clear(); 76 | } 77 | 78 | // 遍历所有格子,检查碰撞 79 | private void CheckCollision() 80 | { 81 | // 遍历所有网格,计算碰撞 82 | foreach (var unit in WorldUnits) 83 | { 84 | var worldUnit = unit.Value; 85 | HandleWorldUnitRemove(worldUnit); 86 | 87 | // 遍历所有碰撞层配置 88 | foreach (var config in CollisionLayerConfigSo.CollisionLayerConfigs) 89 | { 90 | var layerName = config.ActiveLayer; 91 | var otherCollisionLayers = config.CollisionLayers; 92 | // 获取存储在当前网格的主动碰撞层物体 93 | if (!worldUnit.LayerObjects.TryGetValue(layerName, out var activeObjects)) continue; 94 | // 遍历当前层所有物体 95 | foreach (var activeObj in activeObjects) 96 | { 97 | // 通过碰撞层配置,计算所有的碰撞 98 | foreach (var otherCollisionLayer in otherCollisionLayers) 99 | { 100 | // 获取可能与当前物体碰撞的物体 101 | if (!worldUnit.LayerObjects.TryGetValue(otherCollisionLayer, out var otherObjects)) continue; 102 | foreach (var otherObj in otherObjects) 103 | { 104 | HandleObjectCollision(activeObj, otherObj); 105 | } 106 | } 107 | } 108 | } 109 | } 110 | } 111 | 112 | // 处理网格随时间销毁的逻辑 113 | void HandleWorldUnitRemove(WorldUnit worldUnit) 114 | { 115 | if (worldUnitRemoveTime <= 0) return; 116 | // 如果当前网格没有物体,则增加时间 117 | if (worldUnit.ObjectCount <= 0) 118 | { 119 | worldUnit.LastCollisionTime += Time.deltaTime; 120 | // 删除长时间没有物体的网格 121 | if (worldUnit.LastCollisionTime > worldUnitRemoveTime) 122 | { 123 | _worldUnitsToRemove.Add(worldUnit); 124 | } 125 | } 126 | else if (worldUnit.LastCollisionTime > 0) 127 | { 128 | worldUnit.LastCollisionTime = 0; 129 | } 130 | } 131 | 132 | // 处理两个物体的碰撞 133 | void HandleObjectCollision(AbstractCollider activeCld, AbstractCollider otherCld) 134 | { 135 | if (!activeCld.isActiveAndEnabled || !otherCld.isActiveAndEnabled) return; 136 | 137 | bool isCollision = false; 138 | 139 | if (activeCld is WNCBoxCollider && otherCld is WNCBoxCollider) 140 | { 141 | isCollision = IsCollision((activeCld as WNCBoxCollider).GetBounds(), 142 | (otherCld as WNCBoxCollider).GetBounds()); 143 | } else if (activeCld is WNCBoxCollider && otherCld is WNCPointCollider) 144 | { 145 | isCollision = IsCollision((activeCld as WNCBoxCollider).GetBounds(), otherCld.transform.position); 146 | } else if (activeCld is WNCPointCollider && otherCld is WNCBoxCollider) 147 | { 148 | isCollision = IsCollision((otherCld as WNCBoxCollider).GetBounds(), activeCld.transform.position); 149 | } 150 | 151 | if (isCollision) 152 | { 153 | _triggerActionList.Add(() => 154 | { 155 | if (activeCld.isActiveAndEnabled && otherCld.isActiveAndEnabled) 156 | activeCld.OnTrigger?.Invoke(otherCld.gameObject, otherCld.LayerName); 157 | }); 158 | } 159 | } 160 | 161 | bool IsCollision(CollisionBounds bounds, CollisionBounds otherBounds) 162 | { 163 | return bounds.XMax > otherBounds.XMin && bounds.XMin < otherBounds.XMax && 164 | bounds.YMax > otherBounds.YMin && bounds.YMin < otherBounds.YMax; 165 | } 166 | 167 | bool IsCollision(CollisionBounds bounds, Vector2 position) 168 | { 169 | return bounds.XMax > position.x && bounds.XMin < position.x && 170 | bounds.YMax > position.y && bounds.YMin < position.y; 171 | } 172 | 173 | 174 | // 获取当前位置的四周格子 175 | public HashSet GetWorldUnitGroup(WNCBoxCollider collision) 176 | { 177 | // 获取BoxCollider占据的所有格子 178 | var bounds = collision.GetBounds(); 179 | return GetWorldUnitGroup(bounds); 180 | } 181 | 182 | // 获取当前位置的四周格子 183 | public HashSet GetWorldUnitGroup(CollisionBounds bound) 184 | { 185 | // 获取BoxCollider占据的所有格子 186 | var XMin = bound.XMin; 187 | var XMax = bound.XMax; 188 | var YMin = bound.YMin; 189 | var YMax = bound.YMax; 190 | HashSet tempUnits = new HashSet 191 | { 192 | // 先计算四个角,防止跨边界的情况 193 | GetWorldUnit(new Vector2(XMin, YMin)), 194 | GetWorldUnit(new Vector2(XMax, YMax)), 195 | GetWorldUnit(new Vector2(XMin, YMax)), 196 | GetWorldUnit(new Vector2(XMax, YMin)) 197 | }; 198 | 199 | // 计算物体的四个边 200 | for (float x = XMin + unitWidth; x < XMax; x += unitWidth) 201 | { 202 | tempUnits.Add(GetWorldUnit(new Vector2(x, YMin))); 203 | tempUnits.Add(GetWorldUnit(new Vector2(x, YMax))); 204 | } 205 | 206 | for (float y = YMin + unitWidth; y < YMax; y += unitWidth) 207 | { 208 | tempUnits.Add(GetWorldUnit(new Vector2(XMin, y))); 209 | tempUnits.Add(GetWorldUnit(new Vector2(XMax, y))); 210 | } 211 | return tempUnits; 212 | } 213 | 214 | 215 | // 添加新网格 216 | public void AddWorldUnit(Vector2Int index) 217 | { 218 | var worldUnit = _worldUnitObjectPool.GetObject(index, unitWidth, index.x * unitWidth, index.y * unitWidth); 219 | WorldUnits.Add(index, worldUnit); 220 | } 221 | 222 | public Vector2Int GetWorldUnitIndex(Vector2 position) 223 | { 224 | return new Vector2Int((int)Math.Floor(position.x / unitWidth), (int)Math.Floor(position.y / unitWidth)); 225 | } 226 | 227 | // 添加一个点物体到网格 228 | public Vector2Int AddCollider(Vector2 position, string layerName, AbstractCollider cld) 229 | { 230 | if (!CollisionLayerConfigSo.PassiveCollisionLayers.Contains(layerName) && !CollisionLayerConfigSo.ActiveCollisionLayers.Contains(layerName)) 231 | { 232 | Debug.LogError("没有配置的碰撞层:" + layerName); 233 | return new Vector2Int(0, 0); 234 | } 235 | var worldUnit = GetWorldUnit(position); 236 | worldUnit.AddCollider(layerName, cld); 237 | return worldUnit.Index; 238 | } 239 | 240 | public HashSet AddCollider(HashSet worldUnits, string layerName, AbstractCollider cld) 241 | { 242 | foreach (var worldUnit in worldUnits) 243 | { 244 | worldUnit.AddCollider(layerName, cld); 245 | } 246 | return worldUnits; 247 | } 248 | 249 | // 从网格移除点物体 250 | public void RemoveCollider(Vector2 position, string layerName, AbstractCollider cld) 251 | { 252 | var worldUnit = GetWorldUnit(position); 253 | worldUnit.RemoveCollider(layerName, cld); 254 | } 255 | 256 | // 添加碰撞盒物体到网格 257 | public HashSet AddCollider(CollisionBounds collisionBound, string layerName, AbstractCollider cld) 258 | { 259 | if (!CollisionLayerConfigSo.ActiveCollisionLayers.Contains(layerName) && !CollisionLayerConfigSo.PassiveCollisionLayers.Contains(layerName)) 260 | { 261 | Debug.LogError("没有配置的碰撞层:" + layerName); 262 | return new HashSet(); 263 | } 264 | 265 | var units = GetWorldUnitGroup(collisionBound); 266 | foreach (var u in units) 267 | { 268 | u.AddCollider(layerName, cld); 269 | } 270 | return units; 271 | } 272 | 273 | // 移除碰撞盒物体 274 | public void RemoveCollider(CollisionBounds collisionBound, string layerName, AbstractCollider cld) 275 | { 276 | foreach (var item in GetWorldUnitGroup(collisionBound)) 277 | { 278 | item.RemoveCollider(layerName, cld); 279 | } 280 | } 281 | 282 | public void RemoveCollider(HashSet units, string layerName, AbstractCollider cld) 283 | { 284 | foreach (var item in units) 285 | { 286 | item.RemoveCollider(layerName, cld); 287 | } 288 | } 289 | 290 | 291 | // 根据空间获取网格 292 | private WorldUnit GetWorldUnit(Vector2 position) 293 | { 294 | var index = GetWorldUnitIndex(position); 295 | return GetWorldUnit(index); 296 | } 297 | 298 | private WorldUnit GetWorldUnit(Vector2Int index) 299 | { 300 | if (!WorldUnits.ContainsKey(index)) 301 | { 302 | AddWorldUnit(index); 303 | } 304 | return WorldUnits[index]; 305 | } 306 | 307 | public void Reset() 308 | { 309 | WorldUnits.Clear(); 310 | _worldUnitsToRemove.Clear(); 311 | _triggerActionList.Clear(); 312 | } 313 | 314 | #if UNITY_EDITOR 315 | void OnDrawGizmos() 316 | { 317 | if (this != null && !showDebugInfo) return; 318 | foreach (var unit in WorldUnits) 319 | { 320 | CollisionBounds bounds = unit.Value.CollisionBounds; 321 | var topLeft = new Vector2(bounds.XMin, bounds.YMax); 322 | var topRight = new Vector2(bounds.XMax, bounds.YMax); 323 | var bottomLeft = new Vector2(bounds.XMin, bounds.YMin); 324 | var bottomRight = new Vector2(bounds.XMax, bounds.YMin); 325 | 326 | Gizmos.DrawLine(topLeft, topRight); 327 | Gizmos.DrawLine(topRight, bottomRight); 328 | Gizmos.DrawLine(bottomRight, bottomLeft); 329 | Gizmos.DrawLine(bottomLeft, topLeft); 330 | Handles.Label(bottomLeft + new Vector2(0.2f, 0.2f), "(" + unit.Key.x + "," + unit.Key.y + "}"); 331 | Handles.Label(bottomRight + new Vector2(-0.2f, 0.2f), unit.Value.ObjectCount.ToString()); 332 | } 333 | } 334 | #endif 335 | } 336 | } -------------------------------------------------------------------------------- /Example/BaseExample/ExampleScene.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | OcclusionCullingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_OcclusionBakeSettings: 8 | smallestOccluder: 5 9 | smallestHole: 0.25 10 | backfaceThreshold: 100 11 | m_SceneGUID: 00000000000000000000000000000000 12 | m_OcclusionCullingData: {fileID: 0} 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_ObjectHideFlags: 0 16 | serializedVersion: 9 17 | m_Fog: 0 18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} 19 | m_FogMode: 3 20 | m_FogDensity: 0.01 21 | m_LinearFogStart: 0 22 | m_LinearFogEnd: 300 23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} 24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} 25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} 26 | m_AmbientIntensity: 1 27 | m_AmbientMode: 0 28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} 29 | m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} 30 | m_HaloStrength: 0.5 31 | m_FlareStrength: 1 32 | m_FlareFadeSpeed: 3 33 | m_HaloTexture: {fileID: 0} 34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} 35 | m_DefaultReflectionMode: 0 36 | m_DefaultReflectionResolution: 128 37 | m_ReflectionBounces: 1 38 | m_ReflectionIntensity: 1 39 | m_CustomReflection: {fileID: 0} 40 | m_Sun: {fileID: 0} 41 | m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641275, b: 0.5748172, a: 1} 42 | m_UseRadianceAmbientProbe: 0 43 | --- !u!157 &3 44 | LightmapSettings: 45 | m_ObjectHideFlags: 0 46 | serializedVersion: 12 47 | m_GIWorkflowMode: 1 48 | m_GISettings: 49 | serializedVersion: 2 50 | m_BounceScale: 1 51 | m_IndirectOutputScale: 1 52 | m_AlbedoBoost: 1 53 | m_EnvironmentLightingMode: 0 54 | m_EnableBakedLightmaps: 1 55 | m_EnableRealtimeLightmaps: 0 56 | m_LightmapEditorSettings: 57 | serializedVersion: 12 58 | m_Resolution: 2 59 | m_BakeResolution: 40 60 | m_AtlasSize: 1024 61 | m_AO: 0 62 | m_AOMaxDistance: 1 63 | m_CompAOExponent: 1 64 | m_CompAOExponentDirect: 0 65 | m_ExtractAmbientOcclusion: 0 66 | m_Padding: 2 67 | m_LightmapParameters: {fileID: 0} 68 | m_LightmapsBakeMode: 1 69 | m_TextureCompression: 1 70 | m_FinalGather: 0 71 | m_FinalGatherFiltering: 1 72 | m_FinalGatherRayCount: 256 73 | m_ReflectionCompression: 2 74 | m_MixedBakeMode: 2 75 | m_BakeBackend: 1 76 | m_PVRSampling: 1 77 | m_PVRDirectSampleCount: 32 78 | m_PVRSampleCount: 512 79 | m_PVRBounces: 2 80 | m_PVREnvironmentSampleCount: 256 81 | m_PVREnvironmentReferencePointCount: 2048 82 | m_PVRFilteringMode: 1 83 | m_PVRDenoiserTypeDirect: 1 84 | m_PVRDenoiserTypeIndirect: 1 85 | m_PVRDenoiserTypeAO: 1 86 | m_PVRFilterTypeDirect: 0 87 | m_PVRFilterTypeIndirect: 0 88 | m_PVRFilterTypeAO: 0 89 | m_PVREnvironmentMIS: 1 90 | m_PVRCulling: 1 91 | m_PVRFilteringGaussRadiusDirect: 1 92 | m_PVRFilteringGaussRadiusIndirect: 5 93 | m_PVRFilteringGaussRadiusAO: 2 94 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 95 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 96 | m_PVRFilteringAtrousPositionSigmaAO: 1 97 | m_ExportTrainingData: 0 98 | m_TrainingDataDestination: TrainingData 99 | m_LightProbeSampleCountMultiplier: 4 100 | m_LightingDataAsset: {fileID: 0} 101 | m_LightingSettings: {fileID: 0} 102 | --- !u!196 &4 103 | NavMeshSettings: 104 | serializedVersion: 2 105 | m_ObjectHideFlags: 0 106 | m_BuildSettings: 107 | serializedVersion: 3 108 | agentTypeID: 0 109 | agentRadius: 0.5 110 | agentHeight: 2 111 | agentSlope: 45 112 | agentClimb: 0.4 113 | ledgeDropHeight: 0 114 | maxJumpAcrossDistance: 0 115 | minRegionArea: 2 116 | manualCellSize: 0 117 | cellSize: 0.16666667 118 | manualTileSize: 0 119 | tileSize: 256 120 | buildHeightMesh: 0 121 | maxJobWorkers: 0 122 | preserveTilesOutsideBounds: 0 123 | debug: 124 | m_Flags: 0 125 | m_NavMeshData: {fileID: 0} 126 | --- !u!1 &237838858 127 | GameObject: 128 | m_ObjectHideFlags: 0 129 | m_CorrespondingSourceObject: {fileID: 0} 130 | m_PrefabInstance: {fileID: 0} 131 | m_PrefabAsset: {fileID: 0} 132 | serializedVersion: 6 133 | m_Component: 134 | - component: {fileID: 237838861} 135 | - component: {fileID: 237838860} 136 | - component: {fileID: 237838859} 137 | m_Layer: 0 138 | m_Name: Main Camera 139 | m_TagString: MainCamera 140 | m_Icon: {fileID: 0} 141 | m_NavMeshLayer: 0 142 | m_StaticEditorFlags: 0 143 | m_IsActive: 1 144 | --- !u!81 &237838859 145 | AudioListener: 146 | m_ObjectHideFlags: 0 147 | m_CorrespondingSourceObject: {fileID: 0} 148 | m_PrefabInstance: {fileID: 0} 149 | m_PrefabAsset: {fileID: 0} 150 | m_GameObject: {fileID: 237838858} 151 | m_Enabled: 1 152 | --- !u!20 &237838860 153 | Camera: 154 | m_ObjectHideFlags: 0 155 | m_CorrespondingSourceObject: {fileID: 0} 156 | m_PrefabInstance: {fileID: 0} 157 | m_PrefabAsset: {fileID: 0} 158 | m_GameObject: {fileID: 237838858} 159 | m_Enabled: 1 160 | serializedVersion: 2 161 | m_ClearFlags: 2 162 | m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} 163 | m_projectionMatrixMode: 1 164 | m_GateFitMode: 2 165 | m_FOVAxisMode: 0 166 | m_Iso: 200 167 | m_ShutterSpeed: 0.005 168 | m_Aperture: 16 169 | m_FocusDistance: 10 170 | m_FocalLength: 50 171 | m_BladeCount: 5 172 | m_Curvature: {x: 2, y: 11} 173 | m_BarrelClipping: 0.25 174 | m_Anamorphism: 0 175 | m_SensorSize: {x: 36, y: 24} 176 | m_LensShift: {x: 0, y: 0} 177 | m_NormalizedViewPortRect: 178 | serializedVersion: 2 179 | x: 0 180 | y: 0 181 | width: 1 182 | height: 1 183 | near clip plane: 0.3 184 | far clip plane: 1000 185 | field of view: 60 186 | orthographic: 1 187 | orthographic size: 40 188 | m_Depth: -1 189 | m_CullingMask: 190 | serializedVersion: 2 191 | m_Bits: 4294967295 192 | m_RenderingPath: -1 193 | m_TargetTexture: {fileID: 0} 194 | m_TargetDisplay: 0 195 | m_TargetEye: 3 196 | m_HDR: 1 197 | m_AllowMSAA: 1 198 | m_AllowDynamicResolution: 0 199 | m_ForceIntoRT: 0 200 | m_OcclusionCulling: 1 201 | m_StereoConvergence: 10 202 | m_StereoSeparation: 0.022 203 | --- !u!4 &237838861 204 | Transform: 205 | m_ObjectHideFlags: 0 206 | m_CorrespondingSourceObject: {fileID: 0} 207 | m_PrefabInstance: {fileID: 0} 208 | m_PrefabAsset: {fileID: 0} 209 | m_GameObject: {fileID: 237838858} 210 | serializedVersion: 2 211 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 212 | m_LocalPosition: {x: 0, y: 1, z: -20} 213 | m_LocalScale: {x: 1, y: 1, z: 1} 214 | m_ConstrainProportionsScale: 0 215 | m_Children: [] 216 | m_Father: {fileID: 0} 217 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 218 | --- !u!1 &385562518 219 | GameObject: 220 | m_ObjectHideFlags: 0 221 | m_CorrespondingSourceObject: {fileID: 0} 222 | m_PrefabInstance: {fileID: 0} 223 | m_PrefabAsset: {fileID: 0} 224 | serializedVersion: 6 225 | m_Component: 226 | - component: {fileID: 385562520} 227 | - component: {fileID: 385562519} 228 | m_Layer: 0 229 | m_Name: Manager 230 | m_TagString: Untagged 231 | m_Icon: {fileID: 0} 232 | m_NavMeshLayer: 0 233 | m_StaticEditorFlags: 0 234 | m_IsActive: 1 235 | --- !u!114 &385562519 236 | MonoBehaviour: 237 | m_ObjectHideFlags: 0 238 | m_CorrespondingSourceObject: {fileID: 0} 239 | m_PrefabInstance: {fileID: 0} 240 | m_PrefabAsset: {fileID: 0} 241 | m_GameObject: {fileID: 385562518} 242 | m_Enabled: 1 243 | m_EditorHideFlags: 0 244 | m_Script: {fileID: 11500000, guid: b13833515624b7247900ca1026c79c6a, type: 3} 245 | m_Name: 246 | m_EditorClassIdentifier: 247 | spawnInterval: 0.2 248 | enemiesToSpawn: 10 249 | isSpawning: 1 250 | --- !u!4 &385562520 251 | Transform: 252 | m_ObjectHideFlags: 0 253 | m_CorrespondingSourceObject: {fileID: 0} 254 | m_PrefabInstance: {fileID: 0} 255 | m_PrefabAsset: {fileID: 0} 256 | m_GameObject: {fileID: 385562518} 257 | serializedVersion: 2 258 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 259 | m_LocalPosition: {x: -6.3748026, y: 1.3543708, z: 0.021226775} 260 | m_LocalScale: {x: 1, y: 1, z: 1} 261 | m_ConstrainProportionsScale: 0 262 | m_Children: 263 | - {fileID: 1444471936} 264 | - {fileID: 1288724263} 265 | m_Father: {fileID: 0} 266 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 267 | --- !u!1 &689226245 268 | GameObject: 269 | m_ObjectHideFlags: 0 270 | m_CorrespondingSourceObject: {fileID: 0} 271 | m_PrefabInstance: {fileID: 0} 272 | m_PrefabAsset: {fileID: 0} 273 | serializedVersion: 6 274 | m_Component: 275 | - component: {fileID: 689226247} 276 | - component: {fileID: 689226246} 277 | m_Layer: 0 278 | m_Name: Directional Light 279 | m_TagString: Untagged 280 | m_Icon: {fileID: 0} 281 | m_NavMeshLayer: 0 282 | m_StaticEditorFlags: 0 283 | m_IsActive: 1 284 | --- !u!108 &689226246 285 | Light: 286 | m_ObjectHideFlags: 0 287 | m_CorrespondingSourceObject: {fileID: 0} 288 | m_PrefabInstance: {fileID: 0} 289 | m_PrefabAsset: {fileID: 0} 290 | m_GameObject: {fileID: 689226245} 291 | m_Enabled: 1 292 | serializedVersion: 10 293 | m_Type: 1 294 | m_Shape: 0 295 | m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} 296 | m_Intensity: 1 297 | m_Range: 10 298 | m_SpotAngle: 30 299 | m_InnerSpotAngle: 21.80208 300 | m_CookieSize: 10 301 | m_Shadows: 302 | m_Type: 2 303 | m_Resolution: -1 304 | m_CustomResolution: -1 305 | m_Strength: 1 306 | m_Bias: 0.05 307 | m_NormalBias: 0.4 308 | m_NearPlane: 0.2 309 | m_CullingMatrixOverride: 310 | e00: 1 311 | e01: 0 312 | e02: 0 313 | e03: 0 314 | e10: 0 315 | e11: 1 316 | e12: 0 317 | e13: 0 318 | e20: 0 319 | e21: 0 320 | e22: 1 321 | e23: 0 322 | e30: 0 323 | e31: 0 324 | e32: 0 325 | e33: 1 326 | m_UseCullingMatrixOverride: 0 327 | m_Cookie: {fileID: 0} 328 | m_DrawHalo: 0 329 | m_Flare: {fileID: 0} 330 | m_RenderMode: 0 331 | m_CullingMask: 332 | serializedVersion: 2 333 | m_Bits: 4294967295 334 | m_RenderingLayerMask: 1 335 | m_Lightmapping: 4 336 | m_LightShadowCasterMode: 0 337 | m_AreaSize: {x: 1, y: 1} 338 | m_BounceIntensity: 1 339 | m_ColorTemperature: 6570 340 | m_UseColorTemperature: 0 341 | m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} 342 | m_UseBoundingSphereOverride: 0 343 | m_UseViewFrustumForShadowCasterCull: 1 344 | m_ShadowRadius: 0 345 | m_ShadowAngle: 0 346 | --- !u!4 &689226247 347 | Transform: 348 | m_ObjectHideFlags: 0 349 | m_CorrespondingSourceObject: {fileID: 0} 350 | m_PrefabInstance: {fileID: 0} 351 | m_PrefabAsset: {fileID: 0} 352 | m_GameObject: {fileID: 689226245} 353 | serializedVersion: 2 354 | m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} 355 | m_LocalPosition: {x: 0, y: 3, z: 0} 356 | m_LocalScale: {x: 1, y: 1, z: 1} 357 | m_ConstrainProportionsScale: 0 358 | m_Children: [] 359 | m_Father: {fileID: 0} 360 | m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} 361 | --- !u!1 &1288724261 362 | GameObject: 363 | m_ObjectHideFlags: 0 364 | m_CorrespondingSourceObject: {fileID: 0} 365 | m_PrefabInstance: {fileID: 0} 366 | m_PrefabAsset: {fileID: 0} 367 | serializedVersion: 6 368 | m_Component: 369 | - component: {fileID: 1288724263} 370 | - component: {fileID: 1288724262} 371 | m_Layer: 0 372 | m_Name: EnemyPool 373 | m_TagString: Untagged 374 | m_Icon: {fileID: 0} 375 | m_NavMeshLayer: 0 376 | m_StaticEditorFlags: 0 377 | m_IsActive: 1 378 | --- !u!114 &1288724262 379 | MonoBehaviour: 380 | m_ObjectHideFlags: 0 381 | m_CorrespondingSourceObject: {fileID: 0} 382 | m_PrefabInstance: {fileID: 0} 383 | m_PrefabAsset: {fileID: 0} 384 | m_GameObject: {fileID: 1288724261} 385 | m_Enabled: 1 386 | m_EditorHideFlags: 0 387 | m_Script: {fileID: 11500000, guid: 0880c0769bd53cb4999e5034509e4fdd, type: 3} 388 | m_Name: 389 | m_EditorClassIdentifier: 390 | enemyPrefab: {fileID: 1158270053580576071, guid: ec0b1e212e4406a4e94f8a982525d5cd, type: 3} 391 | initSize: 10 392 | --- !u!4 &1288724263 393 | Transform: 394 | m_ObjectHideFlags: 0 395 | m_CorrespondingSourceObject: {fileID: 0} 396 | m_PrefabInstance: {fileID: 0} 397 | m_PrefabAsset: {fileID: 0} 398 | m_GameObject: {fileID: 1288724261} 399 | serializedVersion: 2 400 | m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} 401 | m_LocalPosition: {x: 28.737873, y: -23.030827, z: -0.8433621} 402 | m_LocalScale: {x: 1, y: 1, z: 1} 403 | m_ConstrainProportionsScale: 0 404 | m_Children: [] 405 | m_Father: {fileID: 385562520} 406 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 407 | --- !u!1 &1444471934 408 | GameObject: 409 | m_ObjectHideFlags: 0 410 | m_CorrespondingSourceObject: {fileID: 0} 411 | m_PrefabInstance: {fileID: 0} 412 | m_PrefabAsset: {fileID: 0} 413 | serializedVersion: 6 414 | m_Component: 415 | - component: {fileID: 1444471936} 416 | - component: {fileID: 1444471935} 417 | m_Layer: 0 418 | m_Name: BulletPoll 419 | m_TagString: Untagged 420 | m_Icon: {fileID: 0} 421 | m_NavMeshLayer: 0 422 | m_StaticEditorFlags: 0 423 | m_IsActive: 1 424 | --- !u!114 &1444471935 425 | MonoBehaviour: 426 | m_ObjectHideFlags: 0 427 | m_CorrespondingSourceObject: {fileID: 0} 428 | m_PrefabInstance: {fileID: 0} 429 | m_PrefabAsset: {fileID: 0} 430 | m_GameObject: {fileID: 1444471934} 431 | m_Enabled: 1 432 | m_EditorHideFlags: 0 433 | m_Script: {fileID: 11500000, guid: c219dbeb7dbcc414cb91b7bafe6d8a54, type: 3} 434 | m_Name: 435 | m_EditorClassIdentifier: 436 | bulletPrefab: {fileID: 957270325262730545, guid: cc997c8d5c8ab2e45ae729f05a476a5f, type: 3} 437 | initSize: 20 438 | --- !u!4 &1444471936 439 | Transform: 440 | m_ObjectHideFlags: 0 441 | m_CorrespondingSourceObject: {fileID: 0} 442 | m_PrefabInstance: {fileID: 0} 443 | m_PrefabAsset: {fileID: 0} 444 | m_GameObject: {fileID: 1444471934} 445 | serializedVersion: 2 446 | m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} 447 | m_LocalPosition: {x: 28.737873, y: -23.030827, z: -0.8433621} 448 | m_LocalScale: {x: 1, y: 1, z: 1} 449 | m_ConstrainProportionsScale: 0 450 | m_Children: [] 451 | m_Father: {fileID: 385562520} 452 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 453 | --- !u!1 &1880034726 454 | GameObject: 455 | m_ObjectHideFlags: 0 456 | m_CorrespondingSourceObject: {fileID: 0} 457 | m_PrefabInstance: {fileID: 0} 458 | m_PrefabAsset: {fileID: 0} 459 | serializedVersion: 6 460 | m_Component: 461 | - component: {fileID: 1880034727} 462 | - component: {fileID: 1880034728} 463 | - component: {fileID: 1880034729} 464 | m_Layer: 0 465 | m_Name: Player 466 | m_TagString: Player 467 | m_Icon: {fileID: 0} 468 | m_NavMeshLayer: 0 469 | m_StaticEditorFlags: 0 470 | m_IsActive: 1 471 | --- !u!4 &1880034727 472 | Transform: 473 | m_ObjectHideFlags: 0 474 | m_CorrespondingSourceObject: {fileID: 0} 475 | m_PrefabInstance: {fileID: 0} 476 | m_PrefabAsset: {fileID: 0} 477 | m_GameObject: {fileID: 1880034726} 478 | serializedVersion: 2 479 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 480 | m_LocalPosition: {x: 0, y: 0, z: 0} 481 | m_LocalScale: {x: 1, y: 1, z: 1} 482 | m_ConstrainProportionsScale: 0 483 | m_Children: [] 484 | m_Father: {fileID: 0} 485 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 486 | --- !u!114 &1880034728 487 | MonoBehaviour: 488 | m_ObjectHideFlags: 0 489 | m_CorrespondingSourceObject: {fileID: 0} 490 | m_PrefabInstance: {fileID: 0} 491 | m_PrefabAsset: {fileID: 0} 492 | m_GameObject: {fileID: 1880034726} 493 | m_Enabled: 1 494 | m_EditorHideFlags: 0 495 | m_Script: {fileID: 11500000, guid: 724314f3fbf91d24a82ac824f4796ab6, type: 3} 496 | m_Name: 497 | m_EditorClassIdentifier: 498 | moveSpeed: 10 499 | attackSpeed: 0.1 500 | bulletCount: 80 501 | spreadAngle: 360 502 | --- !u!212 &1880034729 503 | SpriteRenderer: 504 | m_ObjectHideFlags: 0 505 | m_CorrespondingSourceObject: {fileID: 0} 506 | m_PrefabInstance: {fileID: 0} 507 | m_PrefabAsset: {fileID: 0} 508 | m_GameObject: {fileID: 1880034726} 509 | m_Enabled: 1 510 | m_CastShadows: 0 511 | m_ReceiveShadows: 0 512 | m_DynamicOccludee: 1 513 | m_StaticShadowCaster: 0 514 | m_MotionVectors: 1 515 | m_LightProbeUsage: 1 516 | m_ReflectionProbeUsage: 1 517 | m_RayTracingMode: 0 518 | m_RayTraceProcedural: 0 519 | m_RenderingLayerMask: 1 520 | m_RendererPriority: 0 521 | m_Materials: 522 | - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} 523 | m_StaticBatchInfo: 524 | firstSubMesh: 0 525 | subMeshCount: 0 526 | m_StaticBatchRoot: {fileID: 0} 527 | m_ProbeAnchor: {fileID: 0} 528 | m_LightProbeVolumeOverride: {fileID: 0} 529 | m_ScaleInLightmap: 1 530 | m_ReceiveGI: 1 531 | m_PreserveUVs: 0 532 | m_IgnoreNormalsForChartDetection: 0 533 | m_ImportantGI: 0 534 | m_StitchLightmapSeams: 1 535 | m_SelectedEditorRenderState: 0 536 | m_MinimumChartSize: 4 537 | m_AutoUVMaxDistance: 0.5 538 | m_AutoUVMaxAngle: 89 539 | m_LightmapParameters: {fileID: 0} 540 | m_SortingLayerID: 0 541 | m_SortingLayer: 0 542 | m_SortingOrder: 0 543 | m_Sprite: {fileID: 21300000, guid: 2c2f8f7054dc8bc43af819346294f9d0, type: 3} 544 | m_Color: {r: 1, g: 1, b: 1, a: 1} 545 | m_FlipX: 0 546 | m_FlipY: 0 547 | m_DrawMode: 0 548 | m_Size: {x: 0.8, y: 0.8} 549 | m_AdaptiveModeThreshold: 0.5 550 | m_SpriteTileMode: 0 551 | m_WasSpriteAssigned: 1 552 | m_MaskInteraction: 0 553 | m_SpriteSortPoint: 0 554 | --- !u!1001 &3064770741197221681 555 | PrefabInstance: 556 | m_ObjectHideFlags: 0 557 | serializedVersion: 2 558 | m_Modification: 559 | serializedVersion: 3 560 | m_TransformParent: {fileID: 0} 561 | m_Modifications: 562 | - target: {fileID: 1235147260086750720, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 563 | propertyPath: m_Name 564 | value: WorldUnitCollisionSystem 565 | objectReference: {fileID: 0} 566 | - target: {fileID: 8836716509608513818, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 567 | propertyPath: m_LocalPosition.x 568 | value: 22.363071 569 | objectReference: {fileID: 0} 570 | - target: {fileID: 8836716509608513818, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 571 | propertyPath: m_LocalPosition.y 572 | value: -21.676456 573 | objectReference: {fileID: 0} 574 | - target: {fileID: 8836716509608513818, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 575 | propertyPath: m_LocalPosition.z 576 | value: -0.8221353 577 | objectReference: {fileID: 0} 578 | - target: {fileID: 8836716509608513818, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 579 | propertyPath: m_LocalRotation.w 580 | value: 1 581 | objectReference: {fileID: 0} 582 | - target: {fileID: 8836716509608513818, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 583 | propertyPath: m_LocalRotation.x 584 | value: 0 585 | objectReference: {fileID: 0} 586 | - target: {fileID: 8836716509608513818, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 587 | propertyPath: m_LocalRotation.y 588 | value: 0 589 | objectReference: {fileID: 0} 590 | - target: {fileID: 8836716509608513818, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 591 | propertyPath: m_LocalRotation.z 592 | value: 0 593 | objectReference: {fileID: 0} 594 | - target: {fileID: 8836716509608513818, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 595 | propertyPath: m_LocalEulerAnglesHint.x 596 | value: 0 597 | objectReference: {fileID: 0} 598 | - target: {fileID: 8836716509608513818, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 599 | propertyPath: m_LocalEulerAnglesHint.y 600 | value: 0 601 | objectReference: {fileID: 0} 602 | - target: {fileID: 8836716509608513818, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 603 | propertyPath: m_LocalEulerAnglesHint.z 604 | value: 0 605 | objectReference: {fileID: 0} 606 | m_RemovedComponents: [] 607 | m_RemovedGameObjects: [] 608 | m_AddedGameObjects: [] 609 | m_AddedComponents: [] 610 | m_SourcePrefab: {fileID: 100100000, guid: b4f5bf8b2b1127f46a163e87d87eb623, type: 3} 611 | --- !u!1660057539 &9223372036854775807 612 | SceneRoots: 613 | m_ObjectHideFlags: 0 614 | m_Roots: 615 | - {fileID: 237838861} 616 | - {fileID: 689226247} 617 | - {fileID: 1880034727} 618 | - {fileID: 385562520} 619 | - {fileID: 3064770741197221681} 620 | --------------------------------------------------------------------------------