Seats
A seat is an FL# script of your world: NodeWorld has no seat component. The script uses three things of Player (players):
Player.sittingPoint = seat: the avatar takes its sitting pose on the transformseat, its root (the ground under its hips) on the point, facing the point's forward, and follows it when it moves. Its head and hands still follow the player.nullstands it up.Player.sittingEyeOffset: where the eyes of the seated avatar are, from the point, in the point's space.Vector3.zerowithout an avatar.Player.Teleport: the script puts the player's view where the avatar's eyes are.
Only the local player sits, on their own game; the others see them seated through their avatar's pose.
A chair
using NodeWorld.Flux;
namespace MyWorld
{
[ClientOnly]
public class Chair : FluxScript
{
public Transform exit; // where the player stands up; empty: the chair itself
private bool _seated;
private float _gravity;
private bool _immobilized;
protected override void Start()
{
Interaction.SetInteractable(gameObject, true);
Interaction.SetText(gameObject, "Sit");
}
protected override void OnInteract()
{
if (_seated) return;
_seated = true;
_gravity = Player.gravityStrength;
_immobilized = Player.immobilized;
Player.gravityStrength = 0f;
Player.immobilized = true;
Player.sittingPoint = transform;
var eyes = Player.sittingEyeOffset;
if (eyes == Vector3.zero) // no avatar: 0.77 of the standing eye height, as the seats of La Guinguette
eyes = new Vector3(0f, 0.77f * (Player.headPosition.y - Player.GetTrackingPosition(TrackingPoint.Origin).y), 0f);
PlaceView(transform.position + transform.rotation * eyes, transform.eulerAngles.y);
}
protected override void Update()
{
if (_seated && Input.GetButtonDown(InputButton.Jump)) StandUp();
}
// Respawn stands the player up and gives them back their own moves.
protected override void OnPlayerRespawn() => _seated = false;
private void StandUp()
{
_seated = false;
Player.sittingPoint = null;
Player.gravityStrength = _gravity;
Player.immobilized = _immobilized;
var place = exit != null ? exit : transform;
Player.Teleport(place.position, place.eulerAngles.y);
}
// Puts the player's eyes at "eyes", facing "yaw": in VR the head is not above the origin of the play space.
private static void PlaceView(Vector3 eyes, float yaw)
{
var origin = Player.GetTrackingPosition(TrackingPoint.Origin);
var turn = Mathf.DeltaAngle(Player.headRotation.eulerAngles.y, yaw);
var head = Quaternion.Euler(0f, turn, 0f) * (Player.headPosition - origin);
Player.Teleport(eyes - head, Player.GetTrackingRotation(TrackingPoint.Origin).eulerAngles.y + turn);
}
}
}Put it on an object on the floor under the seat, facing the front of the chair: the avatar's root goes there, its hips above it. Give it a collider, on it or on a child, for players to point at.
Seats that move
On a vehicle, the seat moves under the player: in LateUpdate, apply its move and turn since the last frame to the player with Player.Teleport(position, rotation). This form tilts the view with the seat when the vehicle pitches and rolls; Player.Teleport(position, yRotationDegrees) and Respawn set it upright again.
What the player keeps
The player can always open the menu and choose Respawn (or press Shift+R): NodeWorld stands them up, gives them back their own moves (not immobilized, gravity 1, their speeds) and brings them to the spawn point. Your script gets OnPlayerRespawn() (what a script cannot block).
