Physics¶
NexusScript exposes Unity's physics: cast rays, find overlapping objects, push rigidbodies, and react to collisions and triggers. Semantics match Unity exactly.
Physics runs on the client
Queries and forces execute in each player's local simulation — Physics.Raycast sees that client's scene. This is perfect for local gameplay (what am I looking at, did my shot hit, is the ground below me). For anything that must be the same for everyone — who got hit, did the door open for all — drive it through an authoritative path: an NPC/server action, a networked world object, or Player reads (which come from the server's synced transforms). Don't let each client raycast and reach its own conclusion about a shared outcome.
Raycasts — "what's in this direction?"¶
RaycastHit hit;
if (Physics.Raycast(transform.position, transform.forward, out hit, 50f))
{
Vector3 where = hit.point; // world point of contact
Vector3 n = hit.normal; // surface normal
float dist = hit.distance;
GameObject obj = hit.collider.gameObject;
Debug.Log("Looking at " + obj.name + " at " + dist + "m");
}
The signature is the Unity one: Physics.Raycast(origin, direction, out hit, maxDistance, layerMask). maxDistance and layerMask are optional; omit out hit if you only need a yes/no.
| Cast | Use |
|---|---|
Physics.Raycast(origin, dir, out hit, dist, mask) |
single thin ray |
Physics.RaycastAll(origin, dir, dist) |
array of everything along the ray |
Physics.SphereCast(origin, radius, dir, out hit, dist, mask) |
thick ray (e.g. a ball's path) |
Physics.BoxCast · Physics.CapsuleCast |
swept box / capsule |
Physics.Linecast(start, end, out hit, mask) |
ray between two points |
Camera cam = Camera.main;
Ray ray = cam.ScreenPointToRay(Input.mousePosition); // ray from where the cursor points
if (Physics.Raycast(ray.origin, ray.direction, out hit, 100f)) { /* clicked-on object */ }
Overlaps — "what's here?"¶
Collider[] near = Physics.OverlapSphere(transform.position, 5f); // everything within 5 m
for (int i = 0; i < near.Length; i++)
{
string pid = Player.IdOf(near[i].gameObject);
if (pid != "") { /* a player is in range */ }
}
bool blocked = Physics.CheckSphere(spawnPoint, 1f); // is the spot occupied?
Also Physics.OverlapBox(center, halfExtents) and Physics.OverlapCapsule(p0, p1, radius).
Queries are capped at 32 results
For performance, overlap and RaycastAll queries return at most 32 colliders. If you expect dense crowds, query a tighter radius or filter with a layer mask rather than relying on getting everything back.
Layer masks¶
Restrict a cast to specific layers:
int mask = LayerMask.GetMask("Interactable", "Enemy");
if (Physics.Raycast(origin, dir, out hit, 30f, mask)) { /* only those layers */ }
LayerMask.NameToLayer(name) → layer index; LayerMask.LayerToName(index) → name.
Rigidbodies — push things around¶
Get the Rigidbody off an object, then apply force, set velocity, or move it. Forces belong in FixedUpdate.
Rigidbody rb = ball.GetComponent<Rigidbody>();
protected override void FixedUpdate()
{
rb.AddForce(Vector3.up * 10f); // continuous push (ForceMode.Force)
rb.AddForce(transform.forward * 5f, 1); // ForceMode.Impulse (instant) — see below
}
rb.velocity = new Vector3(0, jump, 0); // set velocity directly (also: angularVelocity)
rb.AddTorque(Vector3.up * 2f); // spin it
rb.MovePosition(target); // kinematic move that still collides
AddForce/AddTorque take an optional ForceMode as an int: 0 Force, 1 Impulse, 2 VelocityChange, 3 Acceleration. Tunables: mass, drag, angularDrag, useGravity, isKinematic, position, rotation.
CharacterController — move avatars/NPCs without physics jitter¶
For controller‑style movement (no bouncing, climbs steps, slides along walls):
CharacterController cc = GetComponent<CharacterController>();
cc.Move(new Vector3(x, fall, z) * Time.deltaTime); // collide-and-slide
bool grounded = cc.isGrounded;
cc.SimpleMove(dir * speed); // applies gravity for you
height, radius, center, slopeLimit, stepOffset, skinWidth, velocity, isGrounded, collisionFlags.
Collision & trigger callbacks¶
Define these by name (no override) — the runtime calls them when this object's collider touches another. Use triggers for "passed through" zones, collisions for solid impacts.
void OnTriggerEnter(Collider other) // a collider entered our trigger volume
{
string pid = Player.IdOf(other.gameObject);
if (pid != "") Debug.Log(Player.GetUsername(pid) + " entered the zone");
}
void OnTriggerExit(Collider other) { }
void OnTriggerStay(Collider other) { } // every frame while overlapping
void OnCollisionEnter(Collision c) // solid hit
{
Vector3 force = c.impulse;
Vector3 vel = c.relativeVelocity;
ContactPoint p = c.GetContact(0); // p.point, p.normal
if (vel.magnitude > 8f) { /* hard hit — break / sfx */ }
}
| On the argument | Read |
|---|---|
Collider other |
other.gameObject · other.transform · other.bounds · other.isTrigger · other.attachedRigidbody · other.CompareTag(t) |
Collision c |
c.collider · c.gameObject · c.relativeVelocity · c.impulse · c.contactCount · c.GetContact(i) |
A trigger needs a trigger collider
OnTrigger* fires only if one of the two colliders has Is Trigger checked (set it in the editor or col.isTrigger = true), and at least one of the objects has a Rigidbody. OnCollision* fires for solid (non‑trigger) colliders.
2D physics¶
The common 2D calls exist too: Physics2D.Raycast, and Rigidbody2D (AddForce, AddTorque, MovePosition, velocity, gravityScale, …), Collider2D (enabled, isTrigger). Use them only in 2D worlds.
Global gravity is read‑only
Physics.gravity can be read but not set from a creator script (it's a world‑wide setting that would affect everyone unpredictably). To make one object float or fall differently, set rb.useGravity = false and apply your own force, or use Player.SetGravityStrength for a player.
Quick reference¶
| Group | Calls |
|---|---|
| Casts | Physics.Raycast · RaycastAll · SphereCast · BoxCast · CapsuleCast · Linecast |
| Overlaps | Physics.OverlapSphere · OverlapBox · OverlapCapsule · CheckSphere · IgnoreCollision |
| Hit info | RaycastHit.point / normal / distance / collider / transform / rigidbody |
| Layers | LayerMask.GetMask · NameToLayer · LayerToName |
| Rigidbody | AddForce · AddTorque · velocity · angularVelocity · MovePosition · MoveRotation · mass · useGravity · isKinematic |
| Controller | CharacterController.Move · SimpleMove · isGrounded · velocity |
| Callbacks | OnTriggerEnter/Stay/Exit(Collider) · OnCollisionEnter/Stay/Exit(Collision) |
→ See the Look‑to‑interact recipe for a complete look‑and‑use script. Next: Player API for server‑authoritative spatial reads.