- Home /
Raycast - arrow stopped by walls (layer) but not by units (layer)
Hi everyone.
I've a unit in 2d game. An archer that can shoot arrows to other units. We've the following entities, each with a specific layer:
UNITS
OBSTACLES
TARGET (that is a UNIT)
When the archer tries to shoot it check the distance with a racast.
RaycastHit2D hit = Physics2D.Raycast(shootRayOrigin, headingNormalized, Unit.attackRange - colliderSize, layerMaskRanged);
Then I simply check if hit == Target.
It works as supposed, and when I fire I check if the enemy is in range, and if the ray is not blocked. => If so the Archer shoots.
BUT my other UNITS currently stops the ray.
I'd like to have a ray that is blocked by the OBSTACLE layer, but sends back all the UNITS hit before this OBSTACLE (then I'll check if TARGET is one of these UNITS).
Is it possible to achieve this with a single raycast? Or do I need to make 2 of them, one to check for OBSTACLES, and the second to check for UNITS?
Ray Example: ----U1----U2-----O-----U3--> MAX RANGE ----- U4
The raycast should be able to hit both hit U1 and U2, but shouldn't hit U3 and U4. (Currently my ray can hit just U1, that stops the ray).
Answer by zach-r-d · Jul 11, 2015 at 10:37 PM
The easiest way to do this is to use Physics2D.RaycastAll. It may be necessary sort the results by distance; I know that the 3D equivalent doesn't return the results sorted.
With the sorted results, it's simply a matter of iterating through them until hitting an obstacle or the end of the array. So in the example provided, the sorted array would contain U1, U2, O, U3 (U4 would not be in it due to the max distance parameter). Iterating through with a loop would perform the desired operation on U1, then U2, and then break out of the loop upon reaching O.
Yes I knew about the Raycast non-sorted results. I didn't thought about sorting them myself. It's a nice solution but it can use some efficiency if I've to sort out too many objects caught by the ray. If there's a more efficient solution it would be great. Thank you very much for your answer.
Sorting arrays is actually quite fast. Using the built-in array sorting function:
System.Array.Sort(hitResults, (r1, r2) => r1.distance - r2.distance);
will sort based on the Introsort algorithm, which runs in O(n log n) time. Furthermore, depending on the spatial partitioning strategy of Unity's physics engine, a method involving multiple raycasts will likely be slower than a single RaycastAll, and will be more error prone in either case. It is possible to implement a custom spatial acceleration data structure, but this should only be a last resort.
I made as follows, using linq:
tokenHits = tokenHits.OrderBy(tokenHit => DistanceFromTargetSqr(tokenHit)).ToArray();
The DistanceFromTargetSqr simply returns the sqrmagnitude. I thought it would be faster in this way.
private float DistanceFromTargetSqr(TokenView$$anonymous$$odel tokenHit)
{
return Vector2.Sqr$$anonymous$$agnitude(tokenHit.position - Target.position);
}
I tested and it worked. Not sure here. I'm not an expert. Is System.Array.Sort quicker? If so I'll use it.
Array.Sort will be faster than LINQ since it's in-place rather than having to go from array to enumerator and back to an array. It's also worth noting that the distances are already precomputed in a RaycastHit2D, so there is actually less overhead in using them versus manually computing two square magnitudes.