Created
October 9, 2020 17:52
-
-
Save Epicguru/aef4ef03f5880c9fa47a4b9d5f36e4f1 to your computer and use it in GitHub Desktop.
Unity physics advanced Raycast helper.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using UnityEngine; | |
/// <summary> | |
/// Contains a dual linecast method, which is designed to calculate hits on both front and back faces | |
/// of colliders. Useful for testing depth of objects (such as checking how thick a wall is) or doing advanced | |
/// ballistic simulation (such as a projectile penetrating a surface and deflecting based on how far it traveled through the object). | |
/// | |
/// Also useful to work around the problem of raycasts not detecting hits when started inside a collider. | |
/// In that situation, the back face will be detected by this method. | |
/// | |
/// However, this method is limited. Currently, it does support concave mesh colliders where the ray will intersect the object | |
/// more than once (imagine a doughnut, with the ray going through one side, out into the middle, and back into the other side). | |
/// However, convex mesh colliders and all other 3D collider types are fully supported. | |
/// </summary> | |
public static class RaycastUtils | |
{ | |
public const int MAX_RAY_HITS = 512; | |
private static readonly RaycastHit[] cacheA = new RaycastHit[MAX_RAY_HITS], cacheB = new RaycastHit[MAX_RAY_HITS]; | |
private static readonly Dictionary<Collider, int> colliderToIndex = new Dictionary<Collider, int>(); | |
public static int DualLinecastAll(DualRaycastHit[] hits, Vector3 start, Vector3 end, LayerMask mask, QueryTriggerInteraction triggerInteraction) | |
{ | |
if (hits == null) | |
return 0; | |
const bool SKIP_ERROR_CHECKS = false; | |
Vector3 direction = (end - start); | |
float distance = direction.magnitude; | |
int forwardsHitCount = Physics.RaycastNonAlloc(start, direction, cacheA, distance, mask, triggerInteraction); | |
int backwardsHitCount = Physics.RaycastNonAlloc(end, -direction, cacheB, distance, mask, triggerInteraction); | |
colliderToIndex.Clear(); | |
int dualCount = 0; | |
for (int i = 0; i < forwardsHitCount; i++) | |
{ | |
var hit = cacheA[i]; | |
if (!SKIP_ERROR_CHECKS && colliderToIndex.ContainsKey(hit.collider)) | |
{ | |
Debug.LogWarning($"Forward raycast hit collider '{hit.collider.gameObject.name}' more than once. Concave meshes are not yet supported in DualLinecast.", hit.collider); | |
continue; | |
} | |
colliderToIndex.Add(hit.collider, i); | |
if (dualCount >= hits.Length) | |
{ | |
dualCount++; | |
break; | |
} | |
var obj = new DualRaycastHit(); | |
obj.FrontHit = hit; | |
hits[dualCount] = obj; | |
dualCount++; | |
} | |
for (int i = 0; i < backwardsHitCount; i++) | |
{ | |
var hit = cacheB[i]; | |
if (colliderToIndex.TryGetValue(hit.collider, out int index)) | |
{ | |
// Found back side! | |
var obj = hits[index]; | |
if (!SKIP_ERROR_CHECKS && obj.HasBackHit) | |
Debug.LogWarning($"Multiple back facing hits on collider '{hit.collider.gameObject.name}'. Concave meshes are not yet supported in DualLinecast.", hit.collider); | |
obj.BackHit = hit; | |
hits[index] = obj; | |
} | |
else | |
{ | |
if (dualCount >= hits.Length) | |
{ | |
dualCount++; | |
break; | |
} | |
// Hit only on the way back. This can happen if the start position is inside a collider. | |
var obj = new DualRaycastHit(); | |
obj.BackHit = hit; | |
hits[dualCount] = obj; | |
dualCount++; | |
} | |
} | |
return dualCount; | |
} | |
} | |
public struct DualRaycastHit | |
{ | |
public bool HasFrontHit | |
{ | |
get | |
{ | |
return FrontHit.collider != null; | |
} | |
} | |
public bool HasBackHit | |
{ | |
get | |
{ | |
return BackHit.collider != null; | |
} | |
} | |
public bool IsValid | |
{ | |
get | |
{ | |
return HasFrontHit || HasBackHit; | |
} | |
} | |
public RaycastHit FrontHit; | |
public RaycastHit BackHit; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment