Skip to content

Player API

Find players, read where they are and what they're doing, and (carefully) move them. These APIs work from any NexusScript — the object does not have to be an NPC.

Everything keys off a player id (a string). Get one, then pass it to the other calls.

Getting a player id

Enumerate everyone:

string[] ids = Networking.GetPlayers();        // every player in the world
for (int i = 0; i < ids.Length; i++) { string id = ids[i]; /* ... */ }
Also: Networking.GetPlayerCount(), Networking.GetPlayer(index), Networking.GetPlayerById(id).

Resolve one from an object or collider (e.g. in a trigger):

string id = Player.IdOf(other.gameObject);     // "" if it isn't a player

React to join / leave (these methods are called automatically — no wiring):

public void OnPlayerJoined(string playerId) { /* greet, add a UI row, assign a team... */ }
public void OnPlayerLeft(string playerId)   { /* remove their row, free a slot... */ }

These fire on the server, on every script that defines them

OnPlayerJoined/OnPlayerLeft are invoked server‑side only — the server calls them by name on every NexusScript that implements them, once per join/leave (they are not owner‑scoped and do not run per‑client). A client‑authoritative script — one that took ownership with RequestOwnership — runs its per‑frame logic on the client, so it can't react to a leave from its own client instance. Do the reaction server‑side: from OnPlayerLeft, write authoritative World.Set* / [Synced] state or send a ClientRpc. The leaving player's client is already disconnected, so any self‑cleanup must happen on the server.

Capture identity at join, not leave

OnPlayerLeft fires after the player is despawned, so GetUsername/GetUserId/GetPosition return blank there — only the playerId argument is reliable. If you need a leaving player's name or account id, read it in OnPlayerJoined and remember it (e.g. in Storage).

Identity

Players carry two ids, and the difference matters:

  • Session idPlayer.IdOf(obj) and Networking.GetPlayers() give you a per‑session id. It's the playerId you pass to every other Player.* call, but it's tied to the player's current connection and changes if they reconnect — use it to target or read a player this session, never as a durable key.
  • Account idPlayer.GetUserId(id) turns a session id into the player's stable account id (the same across sessions). This is the value to store, compare, and key durable data by — save files, scores, and allow‑lists (e.g. grant a set of accounts access to a VIP door).

For a no‑code allow‑list, use World Triggers / Interactables ▸ Conditions ▸ Player GUID — it matches the player's stable account GUID (copy it from the SocialScape website).

Call What it gives you Use for
Player.IdOf(obj) the player's session id (per‑connection; changes on reconnect) passing to Player.*; targeting them this session
Player.GetUserId(id) their stable account id durable keys, saved data, allow‑lists
Player.GetUsername(id) their account username when you want the name shown/spoken
Player.GetName(id) the name an NPC knows them by (NPC scripts only) NPC dialogue
string id      = Player.IdOf(other.gameObject);  // session id — target/read them this session
string account = Player.GetUserId(id);           // stable — safe to store / allow-list
string name    = Player.GetUsername(id);         // safe to show/speak

An id is not a name: the GUID/username split is what lets an NPC greet generically while still knowing who you are — see World Events.

Who's talking to the NPC right now

Inside an NPC action or chat callback, you often need the id of whoever is asking — without it you can't look up "my score" or "my save". Two zero‑arg calls give you the current speaker:

Call What it is
Player.GetSpeakerId() the current speaker's session id ("" if the NPC isn't handling a message) — pass to Player.GetUserId, or use Player.GetSpeakerUserId(), for the stable account id
Player.GetSpeakerUserId() their account id — same as GetUserId(GetSpeakerId())
// Custom action "look up the player's score"
void OnGetScore()
{
    string uid = Player.GetSpeakerUserId();          // who asked — server-verified
    if (uid == "") return;
    // key the lookup to THEM (see the HTTP / Supabase page)
    HTTP.GetVia("scores-db", "/rest/v1/scores?select=score&user_id=eq." + uid);
}

The speaker id is set by the server from the validated login — the AI never supplies it, so a player can't say "tell me Bob's score" and have the NPC fetch someone else's row. See Custom actions and HTTP & backends.

Reading a player

Vector3 pos  = Player.GetPosition(id);
Vector3 head = Player.GetHeadPosition(id);
bool grounded = Player.IsGrounded(id);
Vector3 vel  = Player.GetVelocity(id);
Also GetRotation, GetHeadRotation, GetBonePosition/GetBoneRotation, GetAvatarHeight, IsValid, IsLocal.

Check IsValid() before you use an id

Networking.LocalPlayer is a struct!= null does not compile. And with no player, GetPlayerId() returns an all-zeros GUID, not null or "", so a "is the id empty?" guard never fires and a bogus id flows into every later lookup. Gate on IsValid() once and keep the answer. See Gotchas.

Spatial helpers:

float d   = Player.GetDistanceTo(a, b);     // between two players
bool near = Player.IsInRange(a, b, 5f);
bool front = Player.IsInFrontOf(viewer, target);   // is target in viewer's view?
bool seen  = Player.IsVisible(a, b);               // clear line of sight?

Server‑side reads are best‑effort

Reads come from the synced transform on the server. Position, facing (IsInFrontOf/IsBehind), and line‑of‑sight (IsVisible) are accurate; velocity and head/avatar height are approximate (the server doesn't simulate the full avatar rig).

VR & tracking

Every player's VR tracking is networked, so you can read another player's head, hands, gaze, gestures, and full‑body pose — not just your own. It's all read‑only (information for your scripts; nothing here moves the player).

Is this player in VR?

bool vr   = Player.IsVR(id);            // true = VR headset; false = desktop
int mode  = Player.GetTrackingMode(id); // 0 = Desktop, 1 = VR

Head & hands (world space). GetHeadPosition/GetHeadRotation return the real headset pose for a VR player (and a nominal eye position for desktop). Hands are the VR‑tracked hand/controller pose — Vector3.zero for a desktop player (who has no tracked hands; use GetBonePosition(id, "LeftHand") for the animated avatar bone instead):

Vector3 head  = Player.GetHeadPosition(id);
Vector3 lHand = Player.GetHandPosition(id, "left");    // hand: "left"/"right" or 0/1
Vector3 rHand = Player.GetHandPosition(id, 1);
Quaternion rHandRot = Player.GetHandRotation(id, "right");
Vector3 gaze  = Player.GetGazeDirection(id);           // world gaze direction (Vector3.zero if not published)

Hands are VR‑only — desktop returns zero/identity

GetHandPosition returns Vector3.zero and GetHandRotation returns Quaternion.identity for a desktop (non‑VR) player — they have no tracked hands. For desktop aim, use GetHeadPosition/GetHeadRotation (a head‑forward ray) or GetBonePosition(id, "LeftHand")/GetBonePosition(id, "RightHand") for the animated avatar bone. Likewise the native two‑handed / sliding grip (e.g. a pool cue) is VR‑only and does not run in the offline tester — verify it in a real VR session.

Hands, gestures & fingers:

int gesture   = Player.GetGesture(id, "right");        // controller gesture id (0 = open hand)
bool tracked  = Player.IsHandTracked(id, "right");     // true = hand‑tracking, false = holding a controller
float indexCurl = Player.GetFingerCurl(id, "right", 1); // 0..1 curl; finger 0 thumb,1 index,2 middle,3 ring,4 little

GetFingerCurl is meaningful only when that hand IsHandTracked; on a controller the fingers follow the GetGesture id.

Full‑body tracking (hips + feet, when the player has trackers):

bool fbt = Player.HasFullBodyTracking(id);
int mask = Player.GetTrackerMask(id);                  // bitmask: 1 hips, 2 left foot, 4 right foot
Vector3 hip    = Player.GetHipPosition(id);
Quaternion hipRot = Player.GetHipRotation(id);
Vector3 lFoot  = Player.GetFootPosition(id, "left");
Quaternion lFootRot = Player.GetFootRotation(id, "left");
float lift     = Player.GetRootLift(id);               // how far the VR body is lifted above its bind pose (crouch/stand)

Your own devices — VR.*

Where the Player.* reads work for any player, the VR.* group describes the local player's own hardware (it can only see this machine's devices):

bool present = VR.IsPresent();              // is the local player in an active VR session
string hmd   = VR.GetHeadsetName();         // headset device name ("" on desktop)
bool hands   = VR.IsHandTrackingActive();   // controller‑free hand tracking on right now
int trackers = VR.GetTrackerCount();        // connected body trackers (waist/feet/…)

Raw controllers. Read the local player's controller buttons, triggers and thumbsticks directly — hand is 0 (left) or 1 (right), and the names are cross‑controller logical names (the same name is the same physical control on Quest/Index):

bool grab    = VR.GetButton(1, "grip");       // right grip pressed?
float pull   = VR.GetAxis(1, "trigger");      // 0..1 analog trigger
Vector2 move = VR.GetStick(0);                // left thumbstick (x/y each −1..1)

See Input, time & camera → VR controllers for the full logical‑name catalog (buttons, axes, sticks) and the per‑controller‑family fallbacks.

Desktop players return neutral values

For a desktop player the VR reads return zero/identity/false — always check Player.IsVR(id) (or VR.IsPresent() for yourself) first. VR tracking values are client‑local per player, so like the AudioLink reads they're for local, cosmetic logic — don't feed them into networked/world state (the server reads them as zero).

Controlling a player

You can move players from a script — the command is sent to that player's client and applied there, then synced to everyone:

Player.Teleport(id, new Vector3(0, 1, 10));
Player.SetVelocity(id, new Vector3(0, 8, 0));   // set their velocity outright (here: launch straight up)
Player.SetWalkSpeed(id, 6f);
Player.SetJumpHeight(id, 2.5f);
Player.Immobilize(id);        // freeze input
Player.RestoreMobility(id);
Also AddVelocity, SetRunSpeed, SetJumpImpulse, SetGravityStrength, EnableFlying/DisableFlying.

If your world uses gravity zones (planets/ships), Player.SetGravityReorientSmoothing(seconds) overrides how fast the player's "up" swings when they cross between bodies — 0 snaps instantly, larger is gentler, and a negative value restores each zone's own Reorient Smoothing. It's a per‑player "cope" knob on top of the per‑zone setting (e.g. tighten it up if transitions between close bodies feel off).

Movement control is experimental

Teleport and the movement setters are new and still being hardened in‑world. They're eventually consistent (a GetPosition right after Teleport may read the old spot until the client's update round‑trips), and EnableFlying is currently hover‑only. Test before relying on them in a published world. Reading and identity are solid.

Respawn & checkpoints

Every player has a respawn point (their "checkpoint") that defaults to where they spawned. These calls read and move that checkpoint and send the player back to it:

Player.Respawn();                          // send the local player to their respawn point
Player.SetRespawnPoint(0f, 1f, 10f);       // move the checkpoint here (default = spawn point)
Vector3 home = Player.GetRespawnPoint();   // where they'll return to
Player.SetHeightRespawn(1);                // fall-below-world behavior: 0 off / 1 safe respawn / 2 death

SetHeightRespawn controls what happens when the player falls past the world's respawn height line: 0 leaves it off (nothing happens), 1 is a safe respawn (reset to the checkpoint, no death), and 2 is a death (ragdoll, then respawn).

These act on the local player only

Respawn, SetRespawnPoint, GetRespawnPoint, and SetHeightRespawn all operate on the local player — the one whose client runs the script. Placement is server‑authoritative (the server owns the real checkpoint). For damage, healing, death, and object health, see Combat & Health.

Quick reference

Group Calls
Enumerate Networking.GetPlayers / GetPlayerCount / GetPlayer / GetPlayerById
Resolve Player.IdOf(obj) · OnPlayerJoined(id) / OnPlayerLeft(id)
Identity GetUserId · GetUsername · GetName
Current speaker GetSpeakerId · GetSpeakerUserId (who's talking to the NPC right now)
Read GetPosition / GetRotation / GetVelocity / IsGrounded / GetHeadPosition / GetBonePosition / IsValid / IsLocal
Spatial GetDistanceTo · IsInRange · GetNearby · IsInFrontOf · IsBehind · IsVisible
VR & tracking (read‑only, per‑player) IsVR · GetTrackingMode · GetHeadPosition/Rotation · GetHandPosition/Rotation · GetGazeDirection · GetGesture · IsHandTracked · GetFingerCurl · HasFullBodyTracking · GetTrackerMask · GetHip/FootPosition/Rotation · GetRootLift
VR devices (local) VR.IsPresent · VR.GetHeadsetName · VR.IsHandTrackingActive · VR.GetTrackerCount
VR controllers (local) VR.GetButton(hand, name) · VR.GetAxis(hand, name) · VR.GetStick(hand) — see Input
Control (experimental) Teleport · SetVelocity · AddVelocity · SetWalkSpeed · SetRunSpeed · SetJumpHeight · SetGravityStrength · SetGravityReorientSmoothing · Immobilize · RestoreMobility · EnableFlying · DisableFlying
Respawn (local) Player.Respawn · Player.SetRespawnPoint · Player.GetRespawnPoint · Player.SetHeightRespawn

→ See PlayerRoster.cs for a copy‑pasteable list/read/join‑leave example.