using UdonSharp; using UnityEngine; using VRC.SDKBase; [UdonBehaviourSyncMode(BehaviourSyncMode.NoVariableSync)] [DisallowMultipleComponent] [AddComponentMenu("Artyom Scripting System/Bone Look At Player")] public class BoneLookAtPlayer : UdonSharpBehaviour { public GameObject objectToRotate; // The object to rotate public float rotationSpeed = 90.0f; // Maximum rotation speed in degrees per second public Vector3 rotationOffset; // Rotation offset for each axis private Transform objectTransform; private VRCPlayerApi[] players = new VRCPlayerApi[0]; // Reused every frame, grown when more players join void Start() { if (objectToRotate == null) { Debug.LogError("[AUMSS] Bone Look At Player: Object To Rotate isn't assigned, so nothing will turn."); enabled = false; // Stops Update, which would otherwise throw and halt the script return; } objectTransform = objectToRotate.transform; } void Update() { if (objectTransform == null) return; // Not set up (enabled = false only takes effect next frame) VRCPlayerApi nearestPlayer = GetNearestPlayer(); if (nearestPlayer == null) return; Vector3 playerHeadPosition = nearestPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head).position; // Calculate the direction to look Vector3 directionToLook = playerHeadPosition - objectTransform.position; if (directionToLook != Vector3.zero) { Quaternion targetRotation = Quaternion.LookRotation(directionToLook); // Apply the rotation offset targetRotation *= Quaternion.Euler(rotationOffset); objectTransform.rotation = Quaternion.RotateTowards(objectTransform.rotation, targetRotation, rotationSpeed * Time.deltaTime); } } VRCPlayerApi GetNearestPlayer() { int count = VRCPlayerApi.GetPlayerCount(); if (players.Length < count) players = new VRCPlayerApi[count]; VRCPlayerApi.GetPlayers(players); VRCPlayerApi nearestPlayer = null; float closestDistance = float.MaxValue; Vector3 currentPosition = transform.position; for (int i = 0; i < count; i++) { VRCPlayerApi player = players[i]; if (!Utilities.IsValid(player)) continue; // The local player counts too float distance = Vector3.Distance(currentPosition, player.GetPosition()); if (distance < closestDistance) { closestDistance = distance; nearestPlayer = player; } } return nearestPlayer; } }