├── Assets.meta ├── Assets ├── Scripts.meta └── Scripts │ ├── Runtime.meta │ └── Runtime │ ├── BoxCastSensor.cs │ ├── BoxCastSensor.cs.meta │ ├── BoxOverlapSensor.cs │ ├── BoxOverlapSensor.cs.meta │ ├── CastSensor.cs │ ├── CastSensor.cs.meta │ ├── OverlapSensor.cs │ ├── OverlapSensor.cs.meta │ ├── PhysicsSensor.cs │ ├── PhysicsSensor.cs.meta │ ├── PhysicsSensorUtils.cs │ ├── PhysicsSensorUtils.cs.meta │ ├── SensorKit.asmdef │ ├── SensorKit.asmdef.meta │ ├── SphereCastSensor.cs │ ├── SphereCastSensor.cs.meta │ ├── SphereOverlapSensor.cs │ └── SphereOverlapSensor.cs.meta ├── Images.meta ├── Images ├── ClassDiagram.png ├── ClassDiagram.png.meta ├── Gizmo.png ├── Gizmo.png.meta ├── Params.png ├── Params.png.meta ├── SensorKitLogo.gif └── SensorKitLogo.gif.meta ├── LICENSE ├── LICENSE.meta ├── README-ru.md ├── README-ru.md.meta ├── README.md ├── README.md.meta ├── package.json └── package.json.meta /Assets.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d8af223fd5181a541a904b8c6f3d63cc 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 644dca48a6e46734e9814b735ab4c454 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b84444e8ebb7c7f45bdf02cdcd871a6a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/BoxCastSensor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace ThreeDISevenZeroR.SensorKit 4 | { 5 | /// 6 | /// Sensor which casts box 7 | /// Behaves like a Physics.BoxCast, Physics.BoxCastNonAlloc, depending on your settings 8 | /// 9 | public class BoxCastSensor : CastSensor 10 | { 11 | /// 12 | /// Half extents of box 13 | /// 14 | [Tooltip("Half extents of box")] 15 | public Vector3 halfExtents; 16 | 17 | protected override int DoCast(Ray ray, RaycastHit[] hitArray) 18 | { 19 | var scale = transform.lossyScale; 20 | 21 | if (maxResults == 1) 22 | { 23 | #if UNITY_2019_1_OR_NEWER 24 | return PhysicsScene. 25 | #else 26 | return Physics. 27 | #endif 28 | BoxCast(ray.origin, PhysicsSensorUtils.GetScaledBoxRadius(halfExtents, scale), ray.direction, 29 | out hitArray[0], transform.rotation, PhysicsSensorUtils.GetCastDistance(maxDistance, scale), 30 | layerMask, queryTriggerInteraction) ? 1 : 0; 31 | } 32 | 33 | #if UNITY_2019_1_OR_NEWER 34 | return PhysicsScene.BoxCast 35 | #else 36 | return Physics.BoxCastNonAlloc 37 | #endif 38 | (ray.origin, PhysicsSensorUtils.GetScaledBoxRadius(halfExtents, scale), 39 | ray.direction, hitArray, transform.rotation, PhysicsSensorUtils.GetCastDistance(maxDistance, scale), 40 | layerMask, queryTriggerInteraction); 41 | } 42 | 43 | #if UNITY_EDITOR 44 | protected override void DrawColliderShape(Vector3 position, Quaternion rotation, Vector3 scale) 45 | { 46 | PhysicsSensorUtils.DrawBoxGizmo(position, rotation, scale, halfExtents); 47 | } 48 | #endif 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/BoxCastSensor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 17f18238b953a044cb6eb47fa4c7401c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/BoxOverlapSensor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace ThreeDISevenZeroR.SensorKit 4 | { 5 | /// 6 | /// Sensor which checks for colliders inside its volume, behaves like a Physics.OverlapBox 7 | /// 8 | public class BoxOverlapSensor : OverlapSensor 9 | { 10 | /// 11 | /// Half extents of box 12 | /// 13 | [Tooltip("Half extents of box")] 14 | public Vector3 halfExtents; 15 | 16 | protected override int DoOverlapCheck(Vector3 center, Collider[] colliders) 17 | { 18 | #if UNITY_2019_1_OR_NEWER 19 | return PhysicsScene.OverlapBox 20 | #else 21 | return Physics.OverlapBoxNonAlloc 22 | #endif 23 | (center, PhysicsSensorUtils.GetScaledBoxRadius(halfExtents, transform.lossyScale), 24 | colliders, transform.rotation, layerMask, queryTriggerInteraction); 25 | } 26 | 27 | #if UNITY_EDITOR 28 | protected override void DrawColliderShape(Vector3 position, Quaternion rotation, Vector3 scale) 29 | { 30 | PhysicsSensorUtils.DrawBoxGizmo(position, rotation, scale, halfExtents); 31 | } 32 | #endif 33 | } 34 | } -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/BoxOverlapSensor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1f33dc0f5e82b314a8a98d761b8b2ba5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/CastSensor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace ThreeDISevenZeroR.SensorKit 4 | { 5 | /// 6 | /// Abstract class for any cast sensor 7 | /// Cast sensor can detect objects at specified distance 8 | /// 9 | public abstract class CastSensor : PhysicsSensor 10 | { 11 | private static readonly RaycastHit[] emptyRayHits = new RaycastHit[0]; 12 | 13 | /// 14 | /// Transform which overrides ray cast direction. Makes possible to cast rotated shapes 15 | /// If null, uses sensor object for direction 16 | /// 17 | [Tooltip("Transform which overrides ray cast direction. Makes possible to cast rotated shapes\n" + 18 | "If null, uses sensor object for direction")] 19 | public Transform rayDirectionOverride; 20 | 21 | /// 22 | /// Maximum cast distance 23 | /// 24 | [Tooltip("Maximum cast distance")] 25 | public float maxDistance = Mathf.Infinity; 26 | 27 | private RaycastHit[] rayHits = emptyRayHits; 28 | private bool outdatedColliders; 29 | 30 | /// 31 | /// Actual ray that will be fired on update 32 | /// 33 | public Ray Ray 34 | { 35 | get 36 | { 37 | return new Ray(transform.position, CastDirection * 38 | new Vector3(0, 0, transform.lossyScale.z > 0 ? 1 : -1)); 39 | } 40 | } 41 | 42 | /// 43 | /// Array with all hits that have been detected during sensor update 44 | /// This array is cached, and guaranteed to be at least HitCount long 45 | /// 46 | public RaycastHit[] RayHits 47 | { 48 | get 49 | { 50 | EnsureArrayCapacity(ref rayHits); 51 | return rayHits; 52 | } 53 | } 54 | 55 | /// 56 | /// Returns first RayHit 57 | /// Convenience method when maxCount is 1 58 | /// 59 | public RaycastHit RayHit 60 | { 61 | get 62 | { 63 | return HitCount > 0 ? RayHits[0] : default(RaycastHit); 64 | } 65 | } 66 | 67 | /// 68 | /// Returns closest RayHit 69 | /// Since NonAlloc methods returns array with no order, 70 | /// method finds most closest hit in result array 71 | /// 72 | public RaycastHit ClosestRayHit 73 | { 74 | get 75 | { 76 | if (hitCount == 1) 77 | { 78 | return rayHits[0]; 79 | } 80 | 81 | if (hitCount == 0) 82 | { 83 | return default(RaycastHit); 84 | } 85 | 86 | var closestIndex = 0; 87 | var closestDistance = float.MaxValue; 88 | 89 | for (var i = 0; i < hitCount; i++) 90 | { 91 | var distance = rayHits[i].distance; 92 | 93 | if (distance < closestDistance) 94 | { 95 | closestIndex = i; 96 | closestDistance = distance; 97 | } 98 | } 99 | 100 | return rayHits[closestIndex]; 101 | } 102 | } 103 | 104 | /// 105 | /// Direction in which ray will be casted 106 | /// Either rotation used by this object, or rotation from rayDirectionOverride 107 | /// 108 | public Quaternion CastDirection 109 | { 110 | get { return rayDirectionOverride ? rayDirectionOverride.rotation : transform.rotation; } 111 | } 112 | 113 | /// 114 | /// Actual distance of cast, with respect of object scale 115 | /// 116 | public float CastDistance 117 | { 118 | get { return PhysicsSensorUtils.GetCastDistance(maxDistance, transform.lossyScale); } 119 | } 120 | 121 | public override Collider[] HitColliders 122 | { 123 | get 124 | { 125 | if (outdatedColliders) 126 | { 127 | UpdateCollidersArray(); 128 | outdatedColliders = false; 129 | } 130 | 131 | return hitColliders; 132 | } 133 | } 134 | 135 | private void Start() 136 | { 137 | if (!lazyAllocation) 138 | { 139 | EnsureArrayCapacity(ref hitColliders); 140 | EnsureArrayCapacity(ref rayHits); 141 | } 142 | } 143 | 144 | public override int UpdateSensor() 145 | { 146 | EnsureArrayCapacity(ref hitColliders); 147 | EnsureArrayCapacity(ref rayHits); 148 | hitCount = DoCast(Ray, rayHits); 149 | outdatedColliders = true; 150 | return hitCount; 151 | } 152 | 153 | private void UpdateCollidersArray() 154 | { 155 | for (var i = 0; i < hitCount; i++) 156 | { 157 | hitColliders[i] = rayHits[i].collider; 158 | } 159 | 160 | for (var i = hitCount; i < hitColliders.Length; i++) 161 | { 162 | hitColliders[i] = null; 163 | } 164 | } 165 | 166 | protected abstract int DoCast(Ray ray, RaycastHit[] hit); 167 | 168 | #if UNITY_EDITOR 169 | 170 | private RaycastHit[] gizmoRayHits = emptyRayHits; 171 | 172 | private void OnDrawGizmosSelected() 173 | { 174 | var castDistance = CastDistance; 175 | 176 | if (float.IsPositiveInfinity(castDistance)) 177 | { 178 | castDistance = 1000000f; 179 | } 180 | 181 | var castRay = Ray; 182 | EnsureArrayCapacity(ref gizmoRayHits); 183 | var gizmoHitCount = DoCast(castRay, gizmoRayHits); 184 | var rayEnd = castRay.GetPoint(castDistance); 185 | 186 | if (gizmoHitCount > 0) 187 | { 188 | for (var i = 0; i < gizmoHitCount; i++) 189 | { 190 | var gizmoHit = gizmoRayHits[i]; 191 | var collisionPoint = castRay.GetPoint(gizmoHit.distance); 192 | 193 | Gizmos.color = PhysicsSensorUtils.hasHitColor; 194 | Gizmos.DrawLine(castRay.origin, collisionPoint); 195 | DrawColliderShape(collisionPoint, transform.rotation, transform.lossyScale); 196 | Gizmos.color = PhysicsSensorUtils.rayEndColor; 197 | Gizmos.DrawLine(collisionPoint, rayEnd); 198 | 199 | PhysicsSensorUtils.DrawNormal(gizmoHit); 200 | PhysicsSensorUtils.DrawCollisionPoints(collisionPoint, gizmoHit); 201 | PhysicsSensorUtils.HighlightMeshVertices(gizmoHit); 202 | PhysicsSensorUtils.DrawHitInfo(gizmoHit, collisionPoint); 203 | } 204 | } 205 | else 206 | { 207 | Gizmos.color = PhysicsSensorUtils.noHitColor; 208 | Gizmos.DrawLine(castRay.origin, rayEnd); 209 | DrawColliderShape(rayEnd, transform.rotation, transform.lossyScale); 210 | } 211 | } 212 | 213 | protected virtual void DrawColliderShape(Vector3 position, Quaternion rotation, Vector3 scale) 214 | { 215 | // noop 216 | } 217 | #endif 218 | } 219 | } -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/CastSensor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b8b9d6b7f5ba06e4db3320c1d2dc5c73 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/OverlapSensor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace ThreeDISevenZeroR.SensorKit 4 | { 5 | /// 6 | /// Abstract class for any overlap sensor 7 | /// Overlap sensor can detect objects at specified location 8 | /// 9 | public abstract class OverlapSensor : PhysicsSensor 10 | { 11 | private void Start() 12 | { 13 | if (!lazyAllocation) 14 | { 15 | EnsureArrayCapacity(ref hitColliders); 16 | } 17 | } 18 | 19 | public override int UpdateSensor() 20 | { 21 | EnsureArrayCapacity(ref hitColliders); 22 | hitCount = DoOverlapCheck(transform.position, hitColliders); 23 | return hitCount; 24 | } 25 | 26 | protected abstract int DoOverlapCheck(Vector3 center, Collider[] colliders); 27 | 28 | #if UNITY_EDITOR 29 | private static Collider[] gizmoCollider = new Collider[1]; 30 | 31 | private void OnDrawGizmosSelected() 32 | { 33 | var count = DoOverlapCheck(transform.position, gizmoCollider); 34 | Gizmos.color = count != 0 ? PhysicsSensorUtils.hasHitColor : PhysicsSensorUtils.noHitColor; 35 | DrawColliderShape(transform.position, transform.rotation, transform.lossyScale); 36 | } 37 | 38 | protected virtual void DrawColliderShape(Vector3 position, Quaternion rotation, Vector3 scale) 39 | { 40 | // noop 41 | } 42 | #endif 43 | } 44 | } -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/OverlapSensor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8dafbeec559bc1b46918fbe057b6f467 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/PhysicsSensor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Animations; 3 | 4 | namespace ThreeDISevenZeroR.SensorKit 5 | { 6 | /// 7 | /// Base class for any physics sensor 8 | /// Provides shared collider detection logic that can be obtained from any sensor 9 | /// 10 | public abstract class PhysicsSensor : MonoBehaviour 11 | { 12 | private static readonly Collider[] emptyColliders = new Collider[0]; 13 | 14 | /// 15 | /// If false, all arrays will be allocated on object creation. 16 | /// If true, all arrays will be allocated on first access. 17 | /// 18 | #if UNITY_2019_2_OR_NEWER 19 | [NotKeyable] 20 | #endif 21 | [SerializeField] 22 | [Tooltip("If false, all arrays will be allocated on object creation.\n" + 23 | "If true, all arrays will be allocated on first access.")] 24 | protected bool lazyAllocation; 25 | 26 | /// 27 | /// Maximum amount of detected hits 28 | /// Every sensor uses no allocation per cast, and it is important to know maximum amount of 29 | /// objects this sensor is able to detect, to preallocate array 30 | /// Changing this property at runtime recreates array, so try to not touch if not necessary 31 | /// 32 | [Tooltip("Maximum amount of detected hits\n" + 33 | "Every sensor uses no allocation per cast, and it is important to know maximum amount of objects " + 34 | "this sensor is able to detect, to preallocate array. " + 35 | "Changing this property at runtime recreates array, so try to not touch if not necessary")] 36 | public int maxResults = 1; 37 | 38 | /// 39 | /// Layer mask which sensor will use 40 | /// 41 | [Tooltip("Layer mask which sensor will use")] 42 | public LayerMask layerMask = Physics.DefaultRaycastLayers; 43 | 44 | /// 45 | /// Should this sensor detect triggers 46 | /// 47 | [Tooltip("Should this sensor detect triggers")] 48 | public QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal; 49 | 50 | protected int hitCount; 51 | protected Collider[] hitColliders = emptyColliders; 52 | 53 | #if UNITY_2019_1_OR_NEWER 54 | 55 | private bool isCustomPhysicsSceneSet; 56 | private PhysicsScene customPhysicsScene; 57 | 58 | /// 59 | /// Is lazy allocation enabled in inspector 60 | /// 61 | public bool UsesLazyAllocation 62 | { 63 | get { return lazyAllocation; } 64 | } 65 | 66 | /// 67 | /// Physics scene used for physics checks 68 | /// Defaults to "Physics.defaultPhysicsScene" 69 | /// 70 | /// When set to different scene, it is user responsibility to correctly 71 | /// handle cases when PhysicsScene is destroyed, sensor will not switch to "Physics.defaultPhysicsScene" 72 | /// automatically 73 | /// 74 | public PhysicsScene PhysicsScene 75 | { 76 | get { return isCustomPhysicsSceneSet ? customPhysicsScene : Physics.defaultPhysicsScene; } 77 | set 78 | { 79 | customPhysicsScene = value; 80 | isCustomPhysicsSceneSet = true; 81 | } 82 | } 83 | #endif 84 | 85 | /// 86 | /// Is sensor detected something? 87 | /// Returns true when HitCount returns more than zero 88 | /// 89 | public bool HasHit 90 | { 91 | get { return HitCount > 0; } 92 | } 93 | 94 | /// 95 | /// Count of detected objects 96 | /// 97 | public int HitCount 98 | { 99 | get { return hitCount; } 100 | } 101 | 102 | /// 103 | /// Array with colliders of detected objects 104 | /// This array is cached, and guaranteed to be at least HitCount elements long 105 | /// 106 | public virtual Collider[] HitColliders 107 | { 108 | get 109 | { 110 | EnsureArrayCapacity(ref hitColliders); 111 | return hitColliders; 112 | } 113 | } 114 | 115 | /// 116 | /// First collider that was hit, if any 117 | /// 118 | public Collider HitCollider 119 | { 120 | get 121 | { 122 | return HitCount > 0 ? HitColliders[0] : null; 123 | } 124 | } 125 | 126 | /// 127 | /// Updates sensor state, fires sensor logic and stores result in its instance 128 | /// 129 | /// Count of found colliders, same as HitCount 130 | public abstract int UpdateSensor(); 131 | 132 | /// 133 | /// Ensures that specified array contains enough items to store at least maxResults count 134 | /// If static allocation is not used, 135 | /// 136 | protected void EnsureArrayCapacity(ref T[] array) 137 | { 138 | if (array == null || array.Length != maxResults) 139 | { 140 | array = new T[maxResults]; 141 | } 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/PhysicsSensor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e72ea7ed1b7fb854ca5dc22df1b8142a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/PhysicsSensorUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | #if UNITY_EDITOR 3 | using UnityEditor; 4 | #endif 5 | 6 | using UnityEngine; 7 | 8 | namespace ThreeDISevenZeroR.SensorKit 9 | { 10 | /// 11 | /// Utility methods which used in classes derived from PhysicsSensor 12 | /// 13 | public static class PhysicsSensorUtils 14 | { 15 | public static Color noHitColor = new Color(1f, 0.5f, 0.5f, 0.5f); 16 | public static Color rayEndColor = new Color(1f, 1f, 0.5f, 0.5f); 17 | public static Color hasHitColor = new Color(0.5f, 1f, 0.5f, 0.5f); 18 | public static Color normalColor = new Color(0.25f, 0.75f, 1f); 19 | public static Color hitPositionColor = new Color(1f, 0.5f, 1f, 0.5f); 20 | public static Color triangleColor = new Color(1, 1, 1, 0.5f); 21 | 22 | public static float GetCastDistance(float distance, Vector3 scale) 23 | { 24 | return Mathf.Abs(distance * scale.z); 25 | } 26 | 27 | public static float GetScaledCapsuleRadius(float radius, Vector3 scale) 28 | { 29 | return Mathf.Abs(radius * (scale.y < scale.z ? scale.z : scale.y)); 30 | } 31 | 32 | public static float GetScaledSphereRadius(float radius, Vector3 scale) 33 | { 34 | return Mathf.Abs(radius * (scale.y < scale.x 35 | ? scale.x < scale.z ? scale.z : scale.x 36 | : scale.y < scale.z 37 | ? scale.z 38 | : scale.y)); 39 | } 40 | 41 | public static Vector3 GetScaledBoxRadius(Vector3 extents, Vector3 scale) 42 | { 43 | return new Vector3(extents.x * Mathf.Abs(scale.x), 44 | extents.y * Mathf.Abs(scale.y), 45 | extents.z * Mathf.Abs(scale.z)); 46 | } 47 | 48 | public static void GetCapsulePoints(Vector3 origin, Quaternion rotation, float width, float xScale, 49 | out Vector3 p1, out Vector3 p2) 50 | { 51 | var offset = rotation * new Vector3(Mathf.Abs(width * xScale), 0); 52 | p1 = origin + offset; 53 | p2 = origin - offset; 54 | } 55 | 56 | #if UNITY_EDITOR 57 | 58 | private static readonly List sharedVertices = new List(8192); 59 | private static readonly List sharedIndices = new List(8192); 60 | private static readonly List sharedIndicesTemp = new List(8192); 61 | private static Mesh lastInspectedMesh; 62 | 63 | public static void DrawCollisionPoints(Vector3 collisionPoint, RaycastHit hit) 64 | { 65 | Gizmos.color = hasHitColor; 66 | Gizmos.DrawSphere(collisionPoint, 0.025f); 67 | 68 | Gizmos.color = hitPositionColor; 69 | Gizmos.DrawLine(collisionPoint, hit.point); 70 | Gizmos.DrawSphere(hit.point, 0.025f); 71 | } 72 | 73 | public static void DrawNormal(RaycastHit hit) 74 | { 75 | Gizmos.color = normalColor; 76 | Handles.color = normalColor; 77 | var normalEnd = hit.point + hit.normal * 0.5f; 78 | Gizmos.DrawLine(hit.point, hit.point + hit.normal * 0.5f); 79 | Handles.ConeHandleCap(0, normalEnd, Quaternion.LookRotation(hit.normal), 0.1f, Event.current.type); 80 | } 81 | 82 | public static void HighlightMeshVertices(RaycastHit hit) 83 | { 84 | var meshCollider = hit.collider as MeshCollider; 85 | 86 | if (meshCollider != null) 87 | { 88 | // Convex 89 | if(hit.triangleIndex == -1) 90 | return; 91 | 92 | var mesh = meshCollider.sharedMesh; 93 | 94 | if (mesh != lastInspectedMesh) 95 | { 96 | lastInspectedMesh = mesh; 97 | mesh.GetVertices(sharedVertices); 98 | sharedIndices.Clear(); 99 | 100 | for (var i = 0; i < mesh.subMeshCount; i++) 101 | { 102 | mesh.GetTriangles(sharedIndicesTemp, i, true); 103 | sharedIndices.AddRange(sharedIndicesTemp); 104 | } 105 | } 106 | 107 | var triangleStart = hit.triangleIndex * 3; 108 | var normalOffset = hit.normal * 0.001f; 109 | var v0 = meshCollider.transform.TransformPoint(sharedVertices[sharedIndices[triangleStart]]) + 110 | normalOffset; 111 | var v1 = meshCollider.transform.TransformPoint(sharedVertices[sharedIndices[triangleStart + 1]]) + 112 | normalOffset; 113 | var v2 = meshCollider.transform.TransformPoint(sharedVertices[sharedIndices[triangleStart + 2]]) + 114 | normalOffset; 115 | 116 | Gizmos.color = triangleColor; 117 | Gizmos.DrawLine(v0, v1); 118 | Gizmos.DrawLine(v1, v2); 119 | Gizmos.DrawLine(v2, v0); 120 | } 121 | } 122 | 123 | public static void DrawHitInfo(RaycastHit hit, Vector3 position) 124 | { 125 | if (hit.collider == null) 126 | return; 127 | 128 | var sceneView = SceneView.currentDrawingSceneView; 129 | var sceneCamera = sceneView != null ? SceneView.currentDrawingSceneView.camera : Camera.current; 130 | 131 | if (sceneCamera == null) 132 | return; 133 | 134 | var offset = sceneCamera.WorldToScreenPoint(position); 135 | 136 | if (offset.z > 0) 137 | { 138 | Handles.BeginGUI(); 139 | var rect = new Rect(offset.x - 64,-offset.y + sceneCamera.pixelHeight + 16, 140 | 140, 56); 141 | var boundsRect = Rect.MinMaxRect(8, 8, 142 | sceneCamera.pixelWidth - 8, sceneCamera.pixelHeight - 8); 143 | 144 | if (rect.x < boundsRect.x) rect.x = boundsRect.x; 145 | if (rect.y < boundsRect.y) rect.y = boundsRect.y; 146 | if (rect.xMax > boundsRect.xMax) rect.x = boundsRect.xMax - rect.width; 147 | if (rect.yMax > boundsRect.yMax) rect.y = boundsRect.yMax - rect.height; 148 | 149 | #if UNITY_2019_3_OR_NEWER // New GUI 150 | var textHeight = 22; 151 | var textOffset = 6; 152 | #else 153 | var textHeight = 16; 154 | var textOffset = 8; 155 | #endif 156 | 157 | GUI.color = Color.black * 0.6f; 158 | GUI.Box(new Rect(rect.x, rect.y, rect.width, rect.height), 159 | GUIContent.none, EditorStyles.textField); 160 | GUI.color = Color.white; 161 | GUI.Label(new Rect(rect.x + 8, rect.y + textOffset, 132, textHeight), 162 | hit.collider.gameObject.name, EditorStyles.whiteLabel); 163 | GUI.Label(new Rect(rect.x + 8, rect.y + textOffset + 12, 132, textHeight), 164 | "distance: " + hit.distance, EditorStyles.whiteLabel); 165 | GUI.Label(new Rect(rect.x + 8, rect.y + textOffset + 24, 132, textHeight), 166 | "triangleIndex: " + hit.triangleIndex, EditorStyles.whiteLabel); 167 | Handles.EndGUI(); 168 | } 169 | } 170 | 171 | public static void DrawCapsuleGizmo(Vector3 position, Quaternion rotation, Vector3 scale, float width, 172 | float radius) 173 | { 174 | Vector3 p1; 175 | Vector3 p2; 176 | 177 | GetCapsulePoints(position, rotation, width, scale.x, out p1, out p2); 178 | var castRadius = GetScaledCapsuleRadius(radius, scale); 179 | 180 | DrawSphereGizmo(p1, rotation, Vector3.one, castRadius); 181 | DrawSphereGizmo(p2, rotation, Vector3.one, castRadius); 182 | 183 | Gizmos.DrawLine(p1 + rotation * Vector3.up * castRadius, p2 + rotation * Vector3.up * castRadius); 184 | Gizmos.DrawLine(p1 + rotation * Vector3.down * castRadius, p2 + rotation * Vector3.down * castRadius); 185 | Gizmos.DrawLine(p1 + rotation * Vector3.forward * castRadius, p2 + rotation * Vector3.forward * castRadius); 186 | Gizmos.DrawLine(p1 + rotation * Vector3.back * castRadius, p2 + rotation * Vector3.back * castRadius); 187 | } 188 | 189 | public static void DrawSphereGizmo(Vector3 position, Quaternion rotation, Vector3 scale, float radius) 190 | { 191 | Gizmos.matrix = Matrix4x4.TRS(position, rotation, Vector3.one); 192 | Gizmos.DrawWireSphere(Vector3.zero, GetScaledSphereRadius(radius, scale)); 193 | Gizmos.matrix = Matrix4x4.identity; 194 | } 195 | 196 | public static void DrawBoxGizmo(Vector3 position, Quaternion rotation, Vector3 scale, Vector3 halfExtents) 197 | { 198 | Gizmos.matrix = Matrix4x4.TRS(position, rotation, scale); 199 | Gizmos.DrawWireCube(Vector3.zero, halfExtents * 2); 200 | Gizmos.matrix = Matrix4x4.identity; 201 | } 202 | #endif 203 | } 204 | } -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/PhysicsSensorUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8fc8e7ebec5bb43459ed0ab2134a28aa 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/SensorKit.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SensorKit" 3 | } 4 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/SensorKit.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 837f17ccb9b9efc4aba9bed7453cfacf 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/SphereCastSensor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace ThreeDISevenZeroR.SensorKit 4 | { 5 | /// 6 | /// Sensor which casts ray, sphere or capsule. 7 | /// Behaves like a Physics.Raycast, Physics.RaycastNonAlloc, Physics.SphereCast, Physics.SphereCastNonAlloc, 8 | /// Physics.CapsuleCast, Physics.CapsuleCastNonAlloc, depending on your settings 9 | /// 10 | public class SphereCastSensor : CastSensor 11 | { 12 | /// 13 | /// Radius of sphere, when sphere radius is zero, behaves like a ray 14 | /// 15 | [Tooltip("Radius of sphere, when sphere radius is zero, behaves like a ray")] 16 | public float radius; 17 | 18 | /// 19 | /// Width of sphere, when non zero, makes this sensor behave like a capsule, when zero, behaves like a sphere 20 | /// 21 | [Tooltip("Width of sphere, when non zero, makes this sensor behave like a capsule, when zero, behaves like a sphere")] 22 | public float width; 23 | 24 | protected override int DoCast(Ray ray, RaycastHit[] hitArray) 25 | { 26 | var scale = transform.lossyScale; 27 | var castDistance = PhysicsSensorUtils.GetCastDistance(maxDistance, scale); 28 | 29 | if (width != 0) 30 | { 31 | Vector3 p1; 32 | Vector3 p2; 33 | 34 | PhysicsSensorUtils.GetCapsulePoints(ray.origin, transform.rotation, width, scale.x, out p1, out p2); 35 | 36 | if (hitArray.Length == 1) 37 | { 38 | 39 | #if UNITY_2019_1_OR_NEWER 40 | return PhysicsScene. 41 | #else 42 | return Physics. 43 | #endif 44 | CapsuleCast(p1, p2, PhysicsSensorUtils.GetScaledCapsuleRadius(radius, scale), 45 | ray.direction, out hitArray[0], castDistance, layerMask, queryTriggerInteraction) ? 1 : 0; 46 | } 47 | 48 | #if UNITY_2019_1_OR_NEWER 49 | return PhysicsScene.CapsuleCast 50 | #else 51 | return Physics.CapsuleCastNonAlloc 52 | #endif 53 | (p1, p2, PhysicsSensorUtils.GetScaledCapsuleRadius(radius, scale), 54 | ray.direction, hitArray, castDistance, layerMask, queryTriggerInteraction); 55 | } 56 | 57 | if (radius != 0) 58 | { 59 | if (hitArray.Length == 1) 60 | { 61 | #if UNITY_2019_1_OR_NEWER 62 | return PhysicsScene.SphereCast 63 | #else 64 | return Physics.SphereCast 65 | #endif 66 | (ray.origin, PhysicsSensorUtils.GetScaledSphereRadius(radius, scale), 67 | ray.direction, out hitArray[0], castDistance, layerMask, queryTriggerInteraction) ? 1 : 0; 68 | } 69 | 70 | #if UNITY_2019_1_OR_NEWER 71 | return PhysicsScene.SphereCast 72 | #else 73 | return Physics.SphereCastNonAlloc 74 | #endif 75 | (ray.origin, PhysicsSensorUtils.GetScaledSphereRadius(radius, scale), 76 | ray.direction, hitArray, castDistance, layerMask, queryTriggerInteraction); 77 | } 78 | 79 | if (hitArray.Length == 1) 80 | { 81 | #if UNITY_2019_1_OR_NEWER 82 | return PhysicsScene. 83 | #else 84 | return Physics. 85 | #endif 86 | Raycast(ray.origin, ray.direction, out hitArray[0], 87 | castDistance, layerMask, queryTriggerInteraction) ? 1 : 0; 88 | } 89 | 90 | #if UNITY_2019_1_OR_NEWER 91 | return PhysicsScene.Raycast 92 | #else 93 | return Physics.RaycastNonAlloc 94 | #endif 95 | (ray.origin, ray.direction, hitArray, 96 | castDistance, layerMask, queryTriggerInteraction); 97 | } 98 | 99 | #if UNITY_EDITOR 100 | protected override void DrawColliderShape(Vector3 position, Quaternion rotation, Vector3 scale) 101 | { 102 | if (width != 0) 103 | { 104 | PhysicsSensorUtils.DrawCapsuleGizmo(position, rotation, scale, width, radius); 105 | } 106 | else if (radius != 0) 107 | { 108 | PhysicsSensorUtils.DrawSphereGizmo(position, rotation, scale, radius); 109 | } 110 | } 111 | #endif 112 | } 113 | } -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/SphereCastSensor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e798c47116e069343b9cb18f33d0ad4d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/SphereOverlapSensor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace ThreeDISevenZeroR.SensorKit 4 | { 5 | /// 6 | /// Sensor which checks for colliders inside its volume, sphere or capsule, depending on your settings. 7 | /// Behaves like a Physics.OverlapSphereNonAlloc, Physics.OverlapCapsuleNonAlloc respectively 8 | /// 9 | public class SphereOverlapSensor : OverlapSensor 10 | { 11 | /// 12 | /// Radius of sphere 13 | /// 14 | [Tooltip("Radius of sphere")] 15 | public float radius; 16 | 17 | /// 18 | /// Width of sphere, when non zero, makes this sensor behave like a capsule 19 | /// 20 | [Tooltip("Width of sphere, when non zero, makes this sensor behave like a capsule")] 21 | public float width; 22 | 23 | protected override int DoOverlapCheck(Vector3 center, Collider[] colliders) 24 | { 25 | var scale = transform.lossyScale; 26 | 27 | if (width != 0) 28 | { 29 | Vector3 p1; 30 | Vector3 p2; 31 | 32 | PhysicsSensorUtils.GetCapsulePoints(center, transform.rotation, width, scale.x, out p1, out p2); 33 | 34 | #if UNITY_2019_1_OR_NEWER 35 | return PhysicsScene.OverlapCapsule 36 | #else 37 | return Physics.OverlapCapsuleNonAlloc 38 | #endif 39 | (p1, p2, PhysicsSensorUtils.GetScaledCapsuleRadius(radius, scale), 40 | colliders, layerMask, queryTriggerInteraction); 41 | } 42 | 43 | #if UNITY_2019_1_OR_NEWER 44 | return PhysicsScene.OverlapSphere 45 | #else 46 | return Physics.OverlapSphereNonAlloc 47 | #endif 48 | (center, PhysicsSensorUtils.GetScaledSphereRadius(radius, scale), 49 | colliders, layerMask, queryTriggerInteraction); 50 | } 51 | 52 | #if UNITY_EDITOR 53 | protected override void DrawColliderShape(Vector3 position, Quaternion rotation, Vector3 scale) 54 | { 55 | if (width > 0) 56 | { 57 | PhysicsSensorUtils.DrawCapsuleGizmo(position, rotation, scale, width, radius); 58 | } 59 | else 60 | { 61 | PhysicsSensorUtils.DrawSphereGizmo(position, rotation, scale, radius); 62 | } 63 | } 64 | #endif 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Assets/Scripts/Runtime/SphereOverlapSensor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f0dddb6cff5960645b1cba50c548cfb0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Images.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c1395fc2e248f954bbb81b8634e0c0ad 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Images/ClassDiagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3DI70R/Unity-SensorKit/0cdb31dc7f4bea8586914335af9e649830bacbfa/Images/ClassDiagram.png -------------------------------------------------------------------------------- /Images/ClassDiagram.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7cbc815c2cbdbfc4ea5e178c228981e7 3 | TextureImporter: 4 | fileIDToRecycleName: {} 5 | externalObjects: {} 6 | serializedVersion: 7 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 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: -1 35 | aniso: -1 36 | mipBias: -100 37 | wrapU: -1 38 | wrapV: -1 39 | wrapW: -1 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | platformSettings: 61 | - serializedVersion: 2 62 | buildTarget: DefaultTexturePlatform 63 | maxTextureSize: 2048 64 | resizeAlgorithm: 0 65 | textureFormat: -1 66 | textureCompression: 1 67 | compressionQuality: 50 68 | crunchedCompression: 0 69 | allowsAlphaSplitting: 0 70 | overridden: 0 71 | androidETC2FallbackOverride: 0 72 | spriteSheet: 73 | serializedVersion: 2 74 | sprites: [] 75 | outline: [] 76 | physicsShape: [] 77 | bones: [] 78 | spriteID: 79 | vertices: [] 80 | indices: 81 | edges: [] 82 | weights: [] 83 | spritePackingTag: 84 | pSDRemoveMatte: 0 85 | pSDShowRemoveMatteOption: 0 86 | userData: 87 | assetBundleName: 88 | assetBundleVariant: 89 | -------------------------------------------------------------------------------- /Images/Gizmo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3DI70R/Unity-SensorKit/0cdb31dc7f4bea8586914335af9e649830bacbfa/Images/Gizmo.png -------------------------------------------------------------------------------- /Images/Gizmo.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 834948512c40cd94eabf68ec3ded4494 3 | TextureImporter: 4 | fileIDToRecycleName: {} 5 | externalObjects: {} 6 | serializedVersion: 7 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 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: -1 35 | aniso: -1 36 | mipBias: -100 37 | wrapU: -1 38 | wrapV: -1 39 | wrapW: -1 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | platformSettings: 61 | - serializedVersion: 2 62 | buildTarget: DefaultTexturePlatform 63 | maxTextureSize: 2048 64 | resizeAlgorithm: 0 65 | textureFormat: -1 66 | textureCompression: 1 67 | compressionQuality: 50 68 | crunchedCompression: 0 69 | allowsAlphaSplitting: 0 70 | overridden: 0 71 | androidETC2FallbackOverride: 0 72 | spriteSheet: 73 | serializedVersion: 2 74 | sprites: [] 75 | outline: [] 76 | physicsShape: [] 77 | bones: [] 78 | spriteID: 79 | vertices: [] 80 | indices: 81 | edges: [] 82 | weights: [] 83 | spritePackingTag: 84 | pSDRemoveMatte: 0 85 | pSDShowRemoveMatteOption: 0 86 | userData: 87 | assetBundleName: 88 | assetBundleVariant: 89 | -------------------------------------------------------------------------------- /Images/Params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3DI70R/Unity-SensorKit/0cdb31dc7f4bea8586914335af9e649830bacbfa/Images/Params.png -------------------------------------------------------------------------------- /Images/Params.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: da04bb0a0805e314aaf8dfeb2a181f8e 3 | TextureImporter: 4 | fileIDToRecycleName: {} 5 | externalObjects: {} 6 | serializedVersion: 7 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 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: -1 35 | aniso: -1 36 | mipBias: -100 37 | wrapU: -1 38 | wrapV: -1 39 | wrapW: -1 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | platformSettings: 61 | - serializedVersion: 2 62 | buildTarget: DefaultTexturePlatform 63 | maxTextureSize: 2048 64 | resizeAlgorithm: 0 65 | textureFormat: -1 66 | textureCompression: 1 67 | compressionQuality: 50 68 | crunchedCompression: 0 69 | allowsAlphaSplitting: 0 70 | overridden: 0 71 | androidETC2FallbackOverride: 0 72 | spriteSheet: 73 | serializedVersion: 2 74 | sprites: [] 75 | outline: [] 76 | physicsShape: [] 77 | bones: [] 78 | spriteID: 79 | vertices: [] 80 | indices: 81 | edges: [] 82 | weights: [] 83 | spritePackingTag: 84 | pSDRemoveMatte: 0 85 | pSDShowRemoveMatteOption: 0 86 | userData: 87 | assetBundleName: 88 | assetBundleVariant: 89 | -------------------------------------------------------------------------------- /Images/SensorKitLogo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3DI70R/Unity-SensorKit/0cdb31dc7f4bea8586914335af9e649830bacbfa/Images/SensorKitLogo.gif -------------------------------------------------------------------------------- /Images/SensorKitLogo.gif.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3eb9295a5b978df4e98fe451b177278f 3 | TextureImporter: 4 | fileIDToRecycleName: {} 5 | externalObjects: {} 6 | serializedVersion: 7 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 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: -1 35 | aniso: -1 36 | mipBias: -100 37 | wrapU: -1 38 | wrapV: -1 39 | wrapW: -1 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | platformSettings: 61 | - serializedVersion: 2 62 | buildTarget: DefaultTexturePlatform 63 | maxTextureSize: 2048 64 | resizeAlgorithm: 0 65 | textureFormat: -1 66 | textureCompression: 1 67 | compressionQuality: 50 68 | crunchedCompression: 0 69 | allowsAlphaSplitting: 0 70 | overridden: 0 71 | androidETC2FallbackOverride: 0 72 | spriteSheet: 73 | serializedVersion: 2 74 | sprites: [] 75 | outline: [] 76 | physicsShape: [] 77 | bones: [] 78 | spriteID: 79 | vertices: [] 80 | indices: 81 | edges: [] 82 | weights: [] 83 | spritePackingTag: 84 | pSDRemoveMatte: 0 85 | pSDShowRemoveMatteOption: 0 86 | userData: 87 | assetBundleName: 88 | assetBundleVariant: 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 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 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1b21519a6d92b744d96f9c6ae1d24352 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README-ru.md: -------------------------------------------------------------------------------- 1 | ![Logo](Images/SensorKitLogo.gif) 2 | [![openupm](https://img.shields.io/npm/v/ru.threedisevenzeror.sensorkit?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/ru.threedisevenzeror.sensorkit/) 3 | 4 | Утилитарные компоненты для работы с Raycast'ами как с игровыми обьектами 5 | 6 | # Зачем нужна 7 | Эта библиотека оборачивает методы Physics.**X**Cast and Physics.Overlap**X** в MonoBehavior, и превращает игровой обьект в конфигурируемый сенсор. 8 | Благодаря ей можно писать код который меньше думает о том как стрельнуть луч, и больше о том, как реагировать если луч куда то попал. 9 | 10 | # Особенности 11 | * **Выносит логику работы с cast'ами и overlap'ами в отдельный класс.** 12 | * **Несколько уровней наследования позволяет абстрагировать тип сенсора (Cast или Overlap) и его форму (Луч, Сфера, Капсула, Куб).** 13 | * **Спроектирована для встраивание в существующую логику** 14 | * **Корректно применяет на себя все transform'ы, так же, как это бы делал коллайдер.** 15 | * **Детальное Gizmo для того чтобы видеть абсолютно всю информацию о том, как себя поведёт каст.** 16 | * **Автоматический выбор метода в зависимости от настроек сенсора.** 17 | * **В случае если желаемое количество детектов > 1, используются NonAlloc версии методов с переиспользуемым массивом.** 18 | * **Можно анимировать параметры каста при помощи аниматора, как и любой другой обьект** 19 | 20 | --- 21 | 22 | # Использование 23 | - Добавьте на сцену нужный компонент и настройте параметры детекта 24 | - **SphereCastSensor** | **BoxCastSensor** | **SphereOverlapSensor** | **BoxOverlapSensor** 25 | - Добавьте своему игровому обьекту поле того же типа, присвойте туда этот сенсор 26 | - Вызывайте `UpdateSensor()` когда нужно сделать новый каст/overlap 27 | - Используйте полученную информацию как угодно 28 | 29 | ```CS 30 | public class SensorTest : MonoBehaviour 31 | { 32 | public CastSensor groundSensor; 33 | 34 | public void Update() 35 | { 36 | groundSensor.UpdateSensor(); 37 | 38 | if (groundSensor.HasHit) 39 | { 40 | var normal = groundSensor.RayHit.normal; 41 | // ground movement logic 42 | } 43 | else 44 | { 45 | // airborn movement logic 46 | } 47 | } 48 | } 49 | ``` 50 | 51 | # Архитектура 52 | Библиотека спроектирована таким образом, чтобы дать возможность быстро итерировать логику связанную с Cast'ами и Overlap'ами, и быстро переключаться между различными способами взаимодействия с физическим миром не модифицируя при этом код. 53 | 54 | ![Class Diagram](Images/ClassDiagram.png) 55 | ###### Ray/Capsule касты отсутствуют, потому что являются производными от SphereCast'а. В случае если Radius = 0, используется RayCast, а в случае если Width > 0, используется CapsuleCast. Похожая логика используется и для Overlap'ов 56 | 57 | Каждый из уровней наследования предлагает функционал, который позволит получать общую информацию о детекте независимо от типа сенсора. Таким образом например, в случае если необходимо всего лишь детектить присутствие какого то обьекта, PhysicsSensor позволит получать общую информацию независимую от типа сенсора (Есть ли попадание и список обьектов). Для кардинальной смены логики, например смены BoxOverlap на SphereCast, не потребуется изменять оригинальный исходник, а просто поменять ссылку на сенсор. 58 | 59 | Сенсор не обновляется сам по себе, и для его обновления необходимо вызывать `UpdateSensor()`. На это есть несколько причин: 60 | - Все игровые обьекты используют касты внутри своей логики, и невозможно предугадать в какое время и месте они это сделают. 61 | - Каст может происходить не каждый кадр 62 | - Иногда необходимо сделать каст, и хранить результат 63 | 64 | # Параметры 65 | Сорян, русская документация служит черновиком для английской, а параметры я описывал сразу там, ну в общем там глянь 66 | 67 | # Требования 68 | Unity **2018.1** или выше 69 | 70 | # Установка 71 | 72 | * **Git**: 73 | В случае если у вас версия Unity 2018.1 и выше и установлен git, можете добавить строчку в **Packages\manifest.json** 74 | ``` 75 | "ru.threedisevenzeror.sensorkit": "https://github.com/3DI70R/SensorKit.git" 76 | ``` 77 | И все компоненты будут загружены и подключены как модуль, не засоряя папку проекта. 78 | 79 | * **Как пакет**: 80 | Просто скачайте этот репозиторий как Zip, распакуйте, и в Package Manager'е выберите "Add package from disk..." указав "package.json" 81 | * **OpenUPM**: 82 | Пакет так же доступен в [репозитории OpenUPM](https://openupm.com). Можно установить его через [openupm-cli](https://github.com/openupm/openupm-cli). 83 | ``` 84 | openupm add ru.threedisevenzeror.sensorkit 85 | ``` 86 | 87 | В противном случае, можно просто скопировать файлы из `Assets/Scripts/Runtime` и вставить в проект, некрасиво, лениво, зато быстро 88 | 89 | # Changelog 90 | * 1.0.0 91 | - Первая версия 92 | * 1.1.0 93 | - Фича: Поддержка переопределения PhysicsScene для каста в другой сцене 94 | - Фича: Поддержка переопределения направления луча для Cast сенсоров, для возможности кастить фигуры направленные в сторону отличную от луча 95 | - Багфикс: RayCast/SphereCast/CapsuleCast считали собственный Ray вместо посчитанного заранее 96 | * 1.1.1 97 | - Багфикс: NullReference при активации Gizmos в GameView 98 | * 1.1.2 99 | - Багфикс: CastSensor хранил **static** массив. Это прямо таки большой и жирный баг который как то прошёл мимо меня. Сорян. (Хранился только последний результат каста и данные терялись если их не использовать до следующего вызова) 100 | - Багфикс: На 2019.3 текст в окошке Gizmo был немного обрезан из за обновлённого UI 101 | * 1.1.3 102 | - Улучшение: Форматирование для документации, Всплывающие окошки в с документацией у полей 103 | - Улучшение: Обновление стиля окошка с информацией о попадании, также теперь он не обрезается о границу окна 104 | - Улучшение: Кэширование вершин для отображения треугольников 105 | - Улучшение: "Lazy Allocation" теперь только для чтения, нельзя анимировать и можно проставить только из инспектора, так как его значение используется только при инициализации компонента 106 | * 1.1.4 107 | - Багфикс: Out of bounds exception при попытке отрисовать треугольник на Convex MeshCollider (при таком случае, triangleIndex == -1) -------------------------------------------------------------------------------- /README-ru.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bdafa638e1fed764aa855e8497631d8b 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](Images/SensorKitLogo.gif) 2 | [![openupm](https://img.shields.io/npm/v/ru.threedisevenzeror.sensorkit?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/ru.threedisevenzeror.sensorkit/) 3 | 4 | Utility components for working with Raycasts using GameObjects 5 | 6 | # What is this 7 | This library wraps Physics.**X**Cast and Physics.Overlap**X** methods into MonoBehavior, and acts like a configurable sensor. 8 | With that, you can write logic that is less focused on ray casting logic, and more on reaction when ray hit something. 9 | 10 | # Features 11 | * **Wraps all cast/overlap logic into separate class** 12 | * **Multiple inheritance levels allows to write logic that is independent of sensor type (Cast or Overlap) and its shape (Ray, Sphere, Capsule, Box)** 13 | * **Designed for embedding into existing logic** 14 | * **Correctly applies all transforms, like collider with similar shape** 15 | * **Highly detailed gizmo, so you can see how sensor acts** 16 | * **Automatic method selection based on sensor settings** 17 | * **Uses NonAlloc methods with reusable arrays for Multi-Object casts** 18 | * **Since this is simple behavior, you can animate its values like any other object** 19 | 20 | # Usage 21 | - Add desired sensor to scene, and configure its detection params 22 | - **SphereCastSensor** | **BoxCastSensor** | **SphereOverlapSensor** | **BoxOverlapSensor** 23 | - Create field in your behavior with same type, assign your sensor in inspector 24 | - Call `UpdateSensor()` when you need to update your sensor 25 | - Use obtained information any way you want 26 | 27 | ```CS 28 | public class CharacterObject : MonoBehaviour 29 | { 30 | public CastSensor groundSensor; 31 | 32 | public void Update() 33 | { 34 | groundSensor.UpdateSensor(); 35 | 36 | if (groundSensor.HasHit) 37 | { 38 | var normal = groundSensor.RayHit.normal; 39 | // ground movement logic 40 | } 41 | else 42 | { 43 | // airborn movement logic 44 | } 45 | } 46 | } 47 | ``` 48 | 49 | # Parameters 50 | ![Sensor parameters](Images/Params.png) 51 | 52 | Nearly all of sensor parameters are actually Cast/Overlap method parameters, so i think if you need documentation for those, you can easily read detailed info in Unity Documentation. However, there is unique parameters: 53 | - **Max Results** - Since sensor can detect multiple objects at once, it needs to preallocate array for specific object count, to avoid garbage generation for each cast. 54 | - **Lazy Allocation** - If there is chance that your sensor will be unused, you may tick this checkbox, to delay array allocation untill first use. This option may save you few nanoseconds, if you are desperate for performance. 55 | - **Ray Direction Override** - For Cast* sensors, ability to separate ray cast direction from sensor rotation. 56 | 57 | 58 | # Gizmo 59 | Each sensor has detailed gizmo, to aid sensor configuration and allow quick visualization of current sensor behavior 60 | 61 | _Gizmo is visible only on selected game objects for peformance reasons_ 62 | ![Gizmo](Images/Gizmo.png) 63 | 64 | Colors: 65 | - **Red gizmo**: Nothing Detected 66 | - **Green line**: Distance that cast has moved untill detection 67 | - **Green dot**: Point of object stop distance 68 | - **Yellow line**: Leftover distance, that cast could reach if there were no collision 69 | - **Blue arrow**: Hit normal 70 | - **Purple point**: Actual hit position 71 | - **White triangle**: Highlight of the triangle that was hit (If it is mesh collider) 72 | 73 | # Architecture 74 | This library is designed to allow faster Cast/Overlap iteration cycles, and give ability to easily swap between sensor types without code modification. 75 | 76 | ![Class Diagram](Images/ClassDiagram.png) 77 | ###### Ray/Capsule casts are absent because they are SphereCast's special cases. If Radius = 0, Ray cast is used for detection, if Width > 0, Capsule cast is used. Similar logic applies to overlap sensors. 78 | 79 | Every inheritance level has logic that allows to work with results independently of sensor type. So, if you just need to check for object prescence, you can use base class - PhysicsSensor just to get basic information about hit (Is detected something, Collider of detected object). With that, you can assign any sensor to that property, and if you need to change from BoxOverlap to SphereCast, you dont even need to modify your sources, you can just swap your sensor. 80 | 81 | Sensor **is not updated automatically**, you need to call `UpdateSensor()` for that. Here is reasons: 82 | - Every game object uses casts in his own lifecycle, which can be in update/fixed update/coroutine/whatever 83 | - There is no guarantee that sensor will be used every frame 84 | - Often you want to keep last cast information untill next update 85 | 86 | # Requirements 87 | At least Unity **2018.1** or higher. **2019.1** is required for overriding PhysicsScene 88 | 89 | # Installation 90 | * **Git (2018.3+)**: 91 | If git is installed, you can add following entry in **Packages\manifest.json** 92 | ``` 93 | "ru.threedisevenzeror.sensorkit": "https://github.com/3DI70R/SensorKit.git" 94 | ``` 95 | And all these scripts will be included in your project as dependency. 96 | * **Local Package (2018.3+):** 97 | Just download this repository as ZIP, extract, and then add it via "Add package from disk..." from Package Manager 98 | * **OpenUPM**: 99 | The package is available on the [openupm registry](https://openupm.com). It's recommended to install it via [openupm-cli](https://github.com/openupm/openupm-cli). 100 | ``` 101 | openupm add ru.threedisevenzeror.sensorkit 102 | ``` 103 | 104 | Otherwise, you can just copy files from `Assets/Scripts/Runtime` to your project. 105 | 106 | # Changelog 107 | * 1.0.0 108 | - Initial release 109 | * 1.1.0 110 | - Feature: Ability to override PhysicsScene used by sensors (only for Unity 2019.1+) 111 | - Feature: Ability to override ray cast direction to allow non ray aligned shapes 112 | - Bugfix: RayCast/SphereCast/CapsuleCast didn't used provided precalculated Ray instance and calculated its own version (which is identical, so it is just small performance fix) 113 | * 1.1.1 114 | - Bugfix: null pointer exception while gizmos are enabled in game view 115 | * 1.1.2 116 | - Bugfix: CastSensor stored rayHits in **static** array. This is a big glaring bug that i didn't noticed. Sorry. (Basically, you lose cast information for previous sensors if you didn't used it right away) 117 | - Bugfix: Text was slightly clipped in 2019.3 due to UI update 118 | * 1.1.3 119 | - Improvement: Documentation formatting fixes, Tooltips for editor 120 | - Improvement: Hit information window style update, also now it doesn't clip out of view 121 | - Improvement: Vertex caching for hit information overlay 122 | - Improvement: "Lazy Allocation" now readonly, non keyable and can be set only from inspector, because it is used only during component initialization 123 | * 1.1.4 124 | - Bugfix: Out of bounds exception when triangle highlight gizmo tries to display triangle on convex mesh (triangleIndex is -1 in this case) -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 575675869e0d9194088154ce713669c6 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ru.threedisevenzeror.sensorkit", 3 | "displayName": "Sensor Kit", 4 | "version": "1.1.4", 5 | "unity": "2018.1", 6 | "description": "Collection of wrapped Physics.* methods with ability to place casts in scene and configure parameters without hardcoding values", 7 | "keywords": ["Physics", "Raycast", "Utility"], 8 | "category": "Physics", 9 | "author" : { 10 | "name": "3DI70R", 11 | "email": "ThreeDISevenZeroR@gmail.com" 12 | } 13 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 62bc50da73473274e8848ad41b9de22c 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------