Physics Queries
Summary
Shows how to use the different kinds of physics queries: raycasts, overlaps, and sweeps.
Query Parameters
Each type of physics query uses a physics query parameters object that holds all the parameters needed to make the query. Different types of queries uses different query parameter clases.
Raycasts
Raycast queries test which colliders are hit by a ray moving from a starting point to an end point, or a starting point
along a direction vector for a specified distance. Raycasts use the ksRaycastParams object. The following example shows
how to use raycast queries in a server script to check if anything was hit, find the nearest hit, and find all hits. This
can also be used in a client script if you change all Physics
references to Room.Physics
(in client scripts, Physics
refers to Unity's Physics class).
Raycast Script
public void Raycast()
{
// Raycast upwards from the origin for a distance of 5.
ksRaycastParams raycastParams = new ksRaycastParams(ksVector3.Zero, ksVector3.Up, 5f);
// Check if anything was hit. Use this when you don't care what was hit.
if (Physics.RaycastAny(raycastParams))
{
// We hit something!
// Add logic here...
}
// Raycast from origin to end point.
raycastParams.Origin = new ksVector3(1f, 0f, 0f);
// Always set End after setting Origin. Setting Origin second will move the End point.
raycastParams.End = new ksVector3(0f, 1f, 0f);
// Find the nearest hit.
ksRaycastResult nearestHit;
if (Physics.RaycastNearest(raycastParams, out nearestHit))
{
// We hit something!
ksLog.Info(this, "Raycast nearest hit point: " + nearestHit.Point + ", distance: " + nearestHit.Distance +
", normal: " + nearestHit.Normal);
// Add logic here...
}
// Find all hits.
foreach (ksRaycastResult hit in Physics.Raycast(raycastParams))
{
ksLog.Info(this, "Raycast hit point: " + hit.Point + ", distance: " + hit.Distance +
", normal: " + hit.Normal);
// Add logic here...
}
}
The results of queries that return multiple hits are not sorted. The following example shows how to get the results of a Raycast sorted from nearest to farthest:
Raycast Script
public void RaycastSorted()
{
// Raycast from (-1, 5, 0) to (0, 2, -2)
ksRaycastParams raycastParams = new ksRaycastParams(new ksVector3(-1f, 5f, 0f), new ksVector3(0f, 2f, -2f));
ksQueryHitResults<ksRaycastResult> hits = Physics.Raycast(raycastParams);
hits.SortByDistance(); // Sort from nearest to farthest.
foreach (ksRaycastResult hit in hits)
{
ksLog.Info(this, "Raycast hit point: " + hit.Point + ", distance: " + hit.Distance +
", normal: " + hit.Normal);
// Add logic here...
}
}
Query Objects
Overlap and sweep queries use a query object. The query object can be one of three types:
- ksShape There are three types of shapes: ksSphere, ksBox, and ksCapsule.
- ksICollider The collider must be a sphere, box, capsule, or convex mesh. The collider's scaled offset and rotation are added to the query origin.
- ksIEntity One query is done with each of the entity's enabled colliders that are spheres, boxes, capsules, or convex meshes, and the results are combined.
Overlaps
Overlap queries test which colliders in a scene are overlapped by the query object geometry. Overlap queries use the ksOverlapParams object. It has two bools UseEntityPosition
and UseEntityRotation
that can be used to make the query use the entity's position and rotation when the query object is an entity or collider, instead of
supplying an origin and rotation. These bools are set to true by the constructors that take an entity or collider without also taking origin or rotation parameters.
The following example shows how to use overlap queries in a server script to check if anything is overlapping a sphere, and to find all colliders overlapping a
box. Like the raycast example, this can also be used in a client script if you change the Physics
references to Room.Physics
.
Overlap Script
public void Overlap()
{
// Overlap a sphere of radius 1, 10 units above the origin.
ksOverlapParams overlapParams = new ksOverlapParams(new ksSphere(1f), new ksVector3(0f, 10f, 0f),
ksQuaternion.Identity);
// Check if anything overlaps the sphere. Use this when you don't care what was overlapping.
if (Physics.OverlapAny(overlapParams))
{
// There was an overlap!
// Add logic here..
}
// Change the sphere to a 2x4x2 box centered on the origin.
overlapParams.Shape = new ksBox(2f, 4f, 2f);
overlapParams.Origin = ksVector3.Zero;
// Find all overlaps.
foreach (ksOverlapResult overlap in Physics.Overlap(overlapParams))
{
ksLog.Info(this, "Overlap with entity " + overlap.Entity.Id);
// Add logic here...
}
}
The following example shows how to use ksUnityCollider to wrap a Unity collider in the ksICollider interface to use it in an overlap query in a client script:
Client Entity Script
using UnityEngine;
using KS.Reactor.Client.Unity;
using KS.Reactor;
public class ceColliderOverlap : ksEntityScript
{
// Called after properties are initialized.
public override void Initialize()
{
// Get Unity's capsule collider.
CapsuleCollider capsule = GetComponent<CapsuleCollider>();
if (capsule == null)
{
return;
}
// Wrap the capsule in a ksUnityCollider that implements ksICollider so it can be used in Reactor queries.
ksUnityCollider collider = new ksUnityCollider(capsule);
// Construct overlap params using the collider. This also sets UseEntityPosition and UseEntityRotation to true
// and sets the ExcludeEntity to the collider's entity so it won't be included in results.
ksOverlapParams overlapParams = new ksOverlapParams(collider);
// Find all colliders on entities overlapping the capsule collider.
foreach (ksOverlapResult overlap in Room.Physics.Overlap(overlapParams))
{
// Add logic here...
}
}
}
Sweeps
Sweep queries test which colliders are hit when a query object moves from a starting point to an end point, or a starting point
along a direction vector for a specified distance. Sweep queries use the ksSweepParams object. It extends ksOverlapParams.
The following example shows how to use sweep queries in a server script to check if an entity moving down will hit anything, find
the nearest hit, and find all hits. Like the raycast and overlap examples, this can also be used in a client script if you change
the Physics
references to Room.Physics
. Also like raycasts, sweeps that return multiple results are not sorted, but can be sorted
from nearest to farthest by calling ksQueryHitResults<ksSweepResult>.SortByDistance()
.
Server Entity Script
public void Sweep()
{
// Sweep downwards from the entity's position a distance of .1. This constructor sets UseEntityPosition and
// UseEntityRotation to true.
ksSweepParams sweepParams = new ksSweepParams(Entity, ksVector3.Down, .1f);
// Check if anything was hit. Use this when you don't care what was hit.
if (Physics.SweepAny(sweepParams))
{
// We hit something!
// Add logic here...
}
// Sweep from (0, 5, 0) instead of the entity's position. We have to set UseEntityPosition to false before we
// can change the Origin.
sweepParams.UseEntityPosition = false;
sweepParams.Origin = new ksVector3(0f, 5f, 0f);
// Find the nearest hit.
ksSweepResult nearestHit;
if (Physics.SweepNearest(sweepParams, out nearestHit))
{
// We hit something!
ksLog.Info(this, "Sweep nearest hit point: " + nearestHit.Point + ", distance: " + nearestHit.Distance +
", normal: " + nearestHit.Normal);
// Add logic here...
}
// Find all hits.
foreach (ksSweepResult hit in Physics.Sweep(sweepParams))
{
ksLog.Info(this, "Sweep hit point: " + hit.Point + ", distance: " + hit.Distance +
", normal: " + hit.Normal);
// Add logic here...
}
}
Query Filters and Blocking vs Touching Hits
Query filters implement the ksIQueryFilter interface and are used to filter which colliders are included in the results. They also decide if a hit is blocking or touching. Sweeps and raycasts will return the closest blocking hit to the query origin and all touching hits between the origin and blocking hit. A use case for this would be a bullet that travels through a breaks glass but gets stopped when it hits a wall. Glass hits would be touching and wall hits would be blocking. By default if no query filter is provided, all hits are touching. Overlap queries that return multiple results will return all blocking and touching hits.
There are three types of query filters available you can use. You can also create your own filters.
- ksGroupMaskFilter All colliders that belong to at least one if the collision groups in a group mask are touching hits.
- ksCollisionFilter All colliders that collide with this filter are blocking hits, and all colliders that belong to one of the filter's notify groups are touching hits. For more information about using collision filters, see here.
- ksSimulationFilter Similar to the ksCollisionFilter, except it uses the collision filter from the query collider to determine which hits are touching and which block, and it also ignores blocking hits if the query collider is a trigger, and it excludes hits with non-trigger non-simulation colliders. Use this with entity queries to check for colliders the can collide with or receive overlap events for. This cannot be used with shape queries or raycasts.
The following example shows how to use a ksCollisionFilter with a raycast to shoot and distinguish between blocking and touching hits. The shot will destroy the touching-hit entities it passes through and call an RPC with the position of the blocking hit, which could be used on the client to render something where the shot hit. For this example script you will need to create a collision filter asset and assign it to the script in the inspector.
Server Entity Script
using KS.Reactor.Server;
using KS.Reactor;
public class seRaycastShoot : ksServerEntityScript
{
// This is the collision filter used to determine which raycast hits are blocking and which are touching. Assign
// this in the inspector. If left unassigned, all hits will be touching.
[ksEditable]
public ksCollisionFilter Filter;
// How far the raycast travels.
[ksEditable]
public float ShootDistance = 100f;
private ksRaycastParams m_raycastParams;
// Called when the script is attached.
public override void Initialize()
{
m_raycastParams = new ksRaycastParams();
m_raycastParams.Filter = Filter;
m_raycastParams.Distance = ShootDistance;
m_raycastParams.ExcludeEntity = Entity; // Exclude this entity from being hit.
}
public void Shoot()
{
// Shoot from the entity position, in the direction the entity is facing.
m_raycastParams.Origin = Transform.Position;
m_raycastParams.Direction = Transform.Forward();
ksQueryHitResults<ksRaycastResult> results = Physics.Raycast(m_raycastParams);
// Destroy the entities from touching hits.
foreach (ksRaycastResult hit in results.Touches)
{
hit.Entity.Destroy();
}
// Process the blocking hit if there is one.
if (results.HasBlock)
{
// Call RPC 0 at the block position.
ksRaycastResult block = results.Block;
Room.CallRPC(0, block.Point);
}
}
}
The following example shows how to create your own query filter. This filter excludes entities that have property 0 set to true, and treats hits a touching if either the query collider or the hit collider is a trigger, otherwise hits will block. The query collider is the collider used to make the query. If the query object is a collider, it's that collider. If the query object is an entity, it's whichever of the entity's colliders was used in the query. For shape queries and raycasts, it is null. The hit collider is the collider being tested for a hit.
Query Filter Script
using KS.Reactor;
public class SampleQueryFilter : ksIQueryFilter
{
public ksQueryHitTypes Filter(
ksICollider queryCollider,
ksICollider hitCollider,
ksQueryResultTypes resultType,
ksBaseQueryParams args)
{
// Ignore entities that have Property 0 set to true.
if (hitCollider.Entity.Properties[0].Bool)
{
return ksQueryHitTypes.NONE;
}
// If the hit collider or the query collider is a trigger, it's a touching hit, otherwise it's a block. We have
// to null check the query collider since it will be null for shape queries and raycasts.
return hitCollider.IsTrigger || (queryCollider != null && queryCollider.IsTrigger) ?
ksQueryHitTypes.TOUCH : ksQueryHitTypes.BLOCK;
}
}
Query Flags
Query flags can also be used to affect what results are returned. The following flags are available:
- ksQueryFlags.STATIC Test for hits with static entities. This is set by default.
- ksQueryFlag.DYNAMIC Test for hits with dynamic and kinematic rigid bodies. This is set by default.
- ksQueryFlag.EXCLUDE_OVERLAPS Exclude initial overlaps for raycasts and sweeps. Ignored for overlap queries. This is set by default.
- ksQueryFlags.NO_BLOCK Blocking hits will not stop the query.
- ksQueryFlags.EXCLUDE_TOUCHES Exclude touches from results.
Entity Query Collider Filters
Entity query collider filters implement the ksIEntityQueryColliderFilter interface and are used in entity queries to determine which of the
entity's enabled colliders are used in the query. They are not used if the query object is a collider or a shape. ksSimulationFilter is an
entity query collider filter that excludes non-simulation colliders. It also excludes triggers when ksQueryFlags.EXCLUDE_TOUCHES
is set.
The following example shows how to use an entity sweep to detect if an entity is standing on walkable ground. If something is detected below the entity, a slop calculation is done to determine if the ground can be walked on. This code could also be used in a player controller.
Server Entity Script
using KS.Reactor.Server;
using KS.Reactor;
public class seGroundDetection : ksServerEntityScript
{
[ksEditable]
public float MaxWalkSlope = 1f;
private ksSweepParams m_sweepParams;
// Called when the script is attached.
public override void Initialize()
{
// Create a sweep parameters object preconfigured to only check for objects the entity can collide with.
m_sweepParams = Physics.CreateSimulationQueryParams(Entity);
// Use the entity's rotation.
m_sweepParams.UseEntityRotation = true;
// Sweep down a short distance to detect ground.
m_sweepParams.Direction = ksVector3.Down;
m_sweepParams.Distance = .1f;
}
public bool DetectGround()
{
// Start the sweep slightly above the entity, because sweeps can miss detecting objects that start touching.
m_sweepParams.Origin = Transform.Position + ksVector3.Up * m_sweepParams.Distance / 2f;
// Get the nearest hit a short distance below the entity.
ksSweepResult hit;
if (!Physics.SweepNearest(m_sweepParams, out hit))
{
return false;
}
// Calculate slope from hit normal
float horizontal = hit.Normal.XZ.Magnitude();
if (horizontal == 0)
{
// If horizontal distance is 0, we're standing on flat ground.
return true;
}
float slope = hit.Normal.Y / horizontal;
return slope <= MaxWalkSlope;
}
}
Physics.CreateSimulationQueryParams
is a convenience function to create parameters configured to only return results an entity can collide with. You can pass
false as a second optional argument to include colliders the entity would generate overlap events for in results. It is equivalent to doing the following:
ksSweepParams sweepParams = new ksSweepParams();
sweepParams.Entity = Entity;
sweepParams.ExcludeEntity = Entity;
// Use a simulation filter as both the query filter and the query entity collider filter.
ksSimulationFilter simulationFilter = new ksSimulationFilter();
sweepParams.Filter = simulationFilter;
sweepParams.EntityColliderFilter = simulationFilter;
// Exclude touches so we don't get hits with colliders the entity would receive overlap events from.
sweepParams.Flags |= ksQueryFlags.EXCLUDE_TOUCHES;
The following example shows how to create your own entity query collider filter to exclude the entity's triggers.
Entity Query Collider Filter Script
using KS.Reactor;
public class SampleEntityQueryColliderFilter : ksIEntityQueryColliderFilter
{
public bool Filter(ksICollider queryCollider, ksOverlapParams args)
{
// Exclude the collider if it is a trigger.
return !queryCollider.IsTrigger;
}
}