Initial commit
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace XRMultiplayer
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is used to represent the Avatar IK system in both the <see cref="XRINetworkPlayer"/> and the Offline Player"/>.
|
||||
/// </summary>
|
||||
public class XRAvatarIK : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Transform for the Network Player Head.
|
||||
/// </summary>
|
||||
[SerializeField, Tooltip("Transform for the Network Player Head.")] Transform m_HeadTransform;
|
||||
|
||||
/// <summary>
|
||||
/// Torso Parent Transform.
|
||||
/// </summary>
|
||||
[SerializeField, Tooltip("Torso Parent Transform.")] Transform m_TorsoParentTransform;
|
||||
|
||||
/// <summary>
|
||||
/// Root of the Head Visuals.
|
||||
/// </summary>
|
||||
[SerializeField, Tooltip("Root of the Head Visuals.")] Transform m_HeadVisualsRoot;
|
||||
|
||||
/// <summary>
|
||||
/// Neck Transform.
|
||||
/// </summary>
|
||||
[SerializeField, Tooltip("Neck Transform.")] Transform m_Neck;
|
||||
|
||||
/// <summary>
|
||||
/// Offset to be applied to the head height.
|
||||
/// </summary>
|
||||
[SerializeField, Tooltip("Offset to be applied to the head height.")] float m_HeadHeightOffset = .3f;
|
||||
|
||||
/// <summary>
|
||||
/// Theshold to where body rotation appoximation is applied.
|
||||
/// </summary>
|
||||
[Range(0, 360.0f)]
|
||||
[SerializeField, Tooltip("Theshold to where body rotation appoximation is applied.")] float m_RotateThreshold = 25.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Speed at which the body rotates.
|
||||
/// </summary>
|
||||
[SerializeField, Tooltip("Speed at which the body rotates.")] float m_RotateSpeed = 3.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Transform associated with this script.
|
||||
/// </summary>
|
||||
Transform m_Transform;
|
||||
|
||||
/// <summary>
|
||||
/// Rotation destination for the Y euler value.
|
||||
/// </summary>
|
||||
float m_DestinationY;
|
||||
|
||||
/// <inheritdoc/>
|
||||
private void Start()
|
||||
{
|
||||
m_Transform = GetComponent<Transform>();
|
||||
m_DestinationY = m_HeadTransform.transform.eulerAngles.y;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
private void Update()
|
||||
{
|
||||
// Update Head.
|
||||
m_HeadVisualsRoot.position = m_HeadTransform.position;
|
||||
m_HeadVisualsRoot.position -= m_HeadTransform.up * m_HeadHeightOffset;
|
||||
m_Neck.rotation = m_HeadTransform.rotation;
|
||||
|
||||
// Update Body.
|
||||
m_Transform.position = m_HeadTransform.position;
|
||||
m_TorsoParentTransform.rotation = Quaternion.Slerp(m_TorsoParentTransform.rotation, Quaternion.Euler(new Vector3(0, m_DestinationY, 0)), Time.deltaTime * m_RotateSpeed);
|
||||
|
||||
// Rotate Body if past threshold.
|
||||
if (Mathf.Abs(m_TorsoParentTransform.eulerAngles.y - m_HeadTransform.eulerAngles.y) >= m_RotateThreshold)
|
||||
{
|
||||
m_DestinationY = m_HeadTransform.transform.eulerAngles.y;
|
||||
}
|
||||
|
||||
// Update scale.
|
||||
m_HeadVisualsRoot.localScale = m_HeadTransform.localScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa392b809b089674e82ce792ca5f7dbb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XRMultiplayer
|
||||
{
|
||||
[RequireComponent(typeof(XRINetworkPlayer))]
|
||||
public class XRAvatarVisuals : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Head Renderers to change rendering mode for local players.
|
||||
/// </summary>
|
||||
[Header("Renderer References"), SerializeField, Tooltip("Head Renderers to change rendering mode for local players.")]
|
||||
protected Renderer[] m_HeadRends;
|
||||
|
||||
/// <summary>
|
||||
/// Head Renderer to control the blend shape for mouth movement. Also updates shirt color based on <see cref="playerColor"/>.
|
||||
/// </summary>
|
||||
[SerializeField, Tooltip("Head Renderer to drive mouth movement blendshape and player shirt color.")]
|
||||
protected SkinnedMeshRenderer m_headRend;
|
||||
|
||||
/// <summary>
|
||||
/// GameObject to enable to show what player is the Room Host.
|
||||
/// </summary>
|
||||
[Header("Host Visuals"), SerializeField, Tooltip("GameObject that gets enabled for the Host only.")]
|
||||
protected GameObject m_HostVisuals;
|
||||
|
||||
/// <summary>
|
||||
/// GameObject to enable to show what player is the Room Host.
|
||||
/// </summary>
|
||||
[SerializeField, Tooltip("Show Host Visuals.")]
|
||||
protected bool m_ShowHostVisuals = true;
|
||||
|
||||
/// <summary>
|
||||
/// Materials to swap for the local player.
|
||||
/// </summary>
|
||||
[Header("Local Player Material Swap"), SerializeField]
|
||||
protected LocalPlayerMaterialSwap m_LocalPlayerMaterialSwap;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the attached XRINetworkPlayerAvatar component.
|
||||
/// </summary>
|
||||
protected XRINetworkPlayer m_NetworkPlayerAvatar;
|
||||
|
||||
public virtual void Awake()
|
||||
{
|
||||
if (!TryGetComponent(out m_NetworkPlayerAvatar))
|
||||
{
|
||||
Utils.LogError("XRAvatarVisuals requires a XRINetworkPlayerAvatar component to be attached to the same GameObject. Disabling this component now.");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_NetworkPlayerAvatar.onSpawnedLocal += PlayerSpawnedLocal;
|
||||
m_NetworkPlayerAvatar.onSpawnedAll += PlayerSpawnedAll;
|
||||
m_NetworkPlayerAvatar.onColorUpdated += SetPlayerColor;
|
||||
}
|
||||
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
m_NetworkPlayerAvatar.onSpawnedLocal -= PlayerSpawnedLocal;
|
||||
m_NetworkPlayerAvatar.onSpawnedAll -= PlayerSpawnedAll;
|
||||
m_NetworkPlayerAvatar.onColorUpdated -= SetPlayerColor;
|
||||
}
|
||||
|
||||
public virtual void Update()
|
||||
{
|
||||
UpdateMouth();
|
||||
}
|
||||
|
||||
public virtual void UpdateMouth()
|
||||
{
|
||||
if (m_headRend != null)
|
||||
m_headRend.SetBlendShapeWeight(0, 100 - (m_NetworkPlayerAvatar.playerVoiceAmp * 100));
|
||||
}
|
||||
|
||||
public virtual void PlayerSpawnedLocal()
|
||||
{
|
||||
m_LocalPlayerMaterialSwap.SwapMaterials();
|
||||
int layer = LayerMask.NameToLayer("Mirror");
|
||||
foreach (var r in m_HeadRends)
|
||||
{
|
||||
r.gameObject.layer = layer;
|
||||
r.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void PlayerSpawnedAll()
|
||||
{
|
||||
m_HostVisuals.SetActive(m_ShowHostVisuals && m_NetworkPlayerAvatar.IsOwnedByServer);
|
||||
}
|
||||
|
||||
public virtual void SetPlayerColor(Color newColor)
|
||||
{
|
||||
m_headRend.materials[2].SetColor("_BaseColor", newColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for swapping the local player to standard materials from the dithering materials.
|
||||
/// </summary>
|
||||
public class LocalPlayerMaterialSwap
|
||||
{
|
||||
public Renderer headRend;
|
||||
public Renderer hmdRend;
|
||||
public Renderer hostRend;
|
||||
public Renderer[] hands;
|
||||
public Material[] headMaterials;
|
||||
public Material[] hmdMaterials;
|
||||
public Material hostMaterial;
|
||||
public Material handMaterial;
|
||||
|
||||
|
||||
public void SwapMaterials()
|
||||
{
|
||||
for (int i = 0; i < hands.Length; i++)
|
||||
{
|
||||
hands[i].material = handMaterial;
|
||||
}
|
||||
|
||||
hmdRend.materials = hmdMaterials;
|
||||
headRend.materials = headMaterials;
|
||||
hostRend.material = hostMaterial;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 054a6b4527d98b446aa238d86cd24270
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,592 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using Unity.XR.CoreUtils;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.XR.Hands;
|
||||
using UnityEngine.XR.Interaction.Toolkit.Inputs;
|
||||
|
||||
namespace XRMultiplayer
|
||||
{
|
||||
/// <summary>
|
||||
/// This class will synchronize the hand poses over the network.
|
||||
/// It will also allow for the user to control the fidelity of the hand poses and how much data is being sent over the network.
|
||||
/// This class is going to have major changes in the near future based on the Hand Pose work being done.
|
||||
/// </summary>
|
||||
public class XRHandPoseReplicator : NetworkBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Controls the level of fidelity and how much data is being sent over the network.
|
||||
/// 0 is highest level of fidelity and the most bandwidth, 2 is the lowest and the least bandwidth.
|
||||
/// </summary>
|
||||
[Header("Hands and Fingers"), Tooltip("0 is highest, 2 is lowest")]
|
||||
[Range(0, 2), SerializeField] int m_FidelityLevel;
|
||||
[SerializeField, Tooltip("Determines minimum value threshold for updating finger rotations")] float m_MinUpdateDelta = .1f;
|
||||
[SerializeField] JointBasedHand[] m_HandCurler;
|
||||
|
||||
[SerializeField] float m_FingerLerpSpeed = 20.0f;
|
||||
[SerializeField] bool m_UpdateHandsLocally;
|
||||
|
||||
[Header("Controller Inputs")]
|
||||
[SerializeField] InputActionProperty[] m_GripInputProperties;
|
||||
[SerializeField] InputActionProperty[] m_TriggerInputProperties;
|
||||
[SerializeField] InputActionProperty[] m_ThumbTouchProperties;
|
||||
|
||||
NetworkList<Vector3> m_FingerRotationsLeft;
|
||||
NetworkList<Vector3> m_FingerRotationsRight;
|
||||
|
||||
NetworkList<float> m_FingerCurlLeft;
|
||||
NetworkList<float> m_FingerCurlRight;
|
||||
|
||||
NetworkVariable<bool> m_IsInitialized = new NetworkVariable<bool>(false, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
Pose[] m_handTrackedStartPose = new Pose[2];
|
||||
|
||||
[Header("Offsets")]
|
||||
[SerializeField] Vector3[] m_HandControllerOffsets;
|
||||
[SerializeField] Vector3[] m_HandControllerEulerOffsets;
|
||||
HandFidelityOption[] m_LocalHandFidelityOptions;
|
||||
|
||||
public XRInputModalityManager.InputMode trackingType { get => m_TrackingType.Value; }
|
||||
readonly NetworkVariable<XRInputModalityManager.InputMode> m_TrackingType = new(XRInputModalityManager.InputMode.None, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
XRInputModalityManager m_XRModalityManager;
|
||||
XROrigin m_XROrigin;
|
||||
|
||||
Transform m_LeftHandTransformReference;
|
||||
Transform m_RightHandTransformReference;
|
||||
|
||||
Transform m_LeftControllerTransformReference;
|
||||
Transform m_RightControllerTransformReference;
|
||||
|
||||
/// <summary>
|
||||
/// Internal references to the Local Player Transforms.
|
||||
/// </summary>
|
||||
protected Transform m_LeftHandOrigin, m_RightHandOrigin;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
m_FingerRotationsLeft = new NetworkList<Vector3>(new List<Vector3>(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
m_FingerRotationsRight = new NetworkList<Vector3>(new List<Vector3>(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
m_FingerCurlLeft = new NetworkList<float>(new List<float>(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
m_FingerCurlRight = new NetworkList<float>(new List<float>(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
for (int i = 0; i < m_HandCurler.Length; i++)
|
||||
{
|
||||
m_handTrackedStartPose[i].position = m_HandCurler[i].transform.localPosition;
|
||||
m_handTrackedStartPose[i].rotation = m_HandCurler[i].transform.localRotation;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_TrackingType.OnValueChanged += UpdateTrackingType;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
m_TrackingType.OnValueChanged -= UpdateTrackingType;
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
if (IsOwner)
|
||||
{
|
||||
m_XRModalityManager.trackedHandModeStarted.RemoveListener(SwapToHands);
|
||||
m_XRModalityManager.motionControllerModeStarted.RemoveListener(SwapToControllers);
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!m_IsInitialized.Value || !NetworkManager.IsConnectedClient || NetworkManager.ShutdownInProgress) return;
|
||||
|
||||
if (trackingType == XRInputModalityManager.InputMode.TrackedHand)
|
||||
{
|
||||
switch (m_FidelityLevel)
|
||||
{
|
||||
case 0:
|
||||
SyncAllFingerData();
|
||||
break;
|
||||
case 1:
|
||||
SyncFingerCurl();
|
||||
break;
|
||||
case 2:
|
||||
SyncFingerCurlLimited();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SyncControllerTracking();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (IsOwner)
|
||||
{
|
||||
m_XROrigin = FindFirstObjectByType<XROrigin>();
|
||||
if (m_XROrigin.TryGetComponent(out m_XRModalityManager))
|
||||
{
|
||||
SetupLocalHands();
|
||||
}
|
||||
|
||||
SetupLocalFingerReferences();
|
||||
}
|
||||
else
|
||||
{
|
||||
ChangeControllerType(m_TrackingType.Value);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateTrackingType(XRInputModalityManager.InputMode old, XRInputModalityManager.InputMode current)
|
||||
{
|
||||
ChangeControllerType(current);
|
||||
}
|
||||
|
||||
void SetupLocalHands()
|
||||
{
|
||||
m_LeftControllerTransformReference = m_XRModalityManager.leftController.transform;
|
||||
m_RightControllerTransformReference = m_XRModalityManager.rightController.transform;
|
||||
|
||||
if (m_XRModalityManager.leftHand == null) //Rig doesn't have hands setup
|
||||
{
|
||||
m_LeftHandTransformReference = m_XRModalityManager.leftController.transform;
|
||||
m_RightHandTransformReference = m_XRModalityManager.rightController.transform;
|
||||
SetTrackingType(XRInputModalityManager.InputMode.MotionController);
|
||||
}
|
||||
else //Setup Hands and modality change listeners
|
||||
{
|
||||
m_LeftHandTransformReference = m_XRModalityManager.leftHand.GetComponentInChildren<XRHandSkeletonDriver>().rootTransform;
|
||||
m_RightHandTransformReference = m_XRModalityManager.rightHand.GetComponentInChildren<XRHandSkeletonDriver>().rootTransform;
|
||||
|
||||
SetTrackingType(XRInputModalityManager.currentInputMode.Value);
|
||||
|
||||
m_XRModalityManager.trackedHandModeStarted.AddListener(SwapToHands);
|
||||
m_XRModalityManager.motionControllerModeStarted.AddListener(SwapToControllers);
|
||||
}
|
||||
}
|
||||
|
||||
void SetupLocalFingerReferences()
|
||||
{
|
||||
XRInputModalityManager modalityMangager = FindFirstObjectByType<XRInputModalityManager>();
|
||||
|
||||
// Early out if hands are not setup
|
||||
if (modalityMangager.leftHand == null)
|
||||
{
|
||||
// Set Default values for finger rotations
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
m_FingerCurlLeft.Add(0.0f);
|
||||
m_FingerCurlRight.Add(0.0f);
|
||||
}
|
||||
|
||||
m_IsInitialized.Value = true;
|
||||
ChangeControllerType(XRInputModalityManager.InputMode.MotionController);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
XRHandSkeletonDriver localLeftHandSkeleton = modalityMangager.leftHand.GetComponentInChildren<XRHandSkeletonDriver>();
|
||||
XRHandSkeletonDriver localRightHandSkeleton = modalityMangager.rightHand.GetComponentInChildren<XRHandSkeletonDriver>();
|
||||
|
||||
|
||||
m_LocalHandFidelityOptions = new HandFidelityOption[2];
|
||||
for (int i = 0; i < m_LocalHandFidelityOptions.Length; i++)
|
||||
{
|
||||
m_LocalHandFidelityOptions[i].fingerJoints = new FingerJoints[5];
|
||||
|
||||
// Loop through each finger and setup name and joints
|
||||
for (int j = 0; j < m_LocalHandFidelityOptions[i].fingerJoints.Length; j++)
|
||||
{
|
||||
m_LocalHandFidelityOptions[i].fingerJoints[j].fingerName = m_HandCurler[i].handFidelityOptions[0].fingerJoints[j].fingerName;
|
||||
m_LocalHandFidelityOptions[i].fingerJoints[j].jointTransformReferences = new List<JointToTransformReference>();
|
||||
|
||||
// Loop through each joint in the finger and setup the joint references
|
||||
for (int k = 0; k < m_HandCurler[i].handFidelityOptions[0].fingerJoints[j].jointTransformReferences.Count; k++)
|
||||
{
|
||||
XRHandSkeletonDriver currentHandSkeletonDriver = i % 2 == 0 ? localLeftHandSkeleton : localRightHandSkeleton;
|
||||
// Loop through each local hand and look up the joint reference
|
||||
foreach (var localJoint in currentHandSkeletonDriver.jointTransformReferences)
|
||||
{
|
||||
if (m_HandCurler[i].handFidelityOptions[0].fingerJoints[j].jointTransformReferences[k].xrHandJointID == localJoint.xrHandJointID)
|
||||
{
|
||||
m_LocalHandFidelityOptions[i].fingerJoints[j].jointTransformReferences.Add(localJoint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var fingerSync in m_LocalHandFidelityOptions[0].fingerJoints)
|
||||
{
|
||||
foreach (var joint in fingerSync.jointTransformReferences)
|
||||
{
|
||||
m_FingerRotationsLeft.Add(joint.jointTransform.eulerAngles);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var fingerSync in m_LocalHandFidelityOptions[1].fingerJoints)
|
||||
{
|
||||
foreach (var joint in fingerSync.jointTransformReferences)
|
||||
{
|
||||
m_FingerRotationsRight.Add(joint.jointTransform.eulerAngles);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set Default values for finger curl
|
||||
foreach (var fingerSync in m_LocalHandFidelityOptions[0].fingerJoints)
|
||||
{
|
||||
m_FingerCurlLeft.Add(0.0f);
|
||||
}
|
||||
|
||||
foreach (var fingerSync in m_LocalHandFidelityOptions[1].fingerJoints)
|
||||
{
|
||||
m_FingerCurlRight.Add(0.0f);
|
||||
}
|
||||
|
||||
|
||||
m_IsInitialized.Value = true;
|
||||
|
||||
SetFidelity(m_FidelityLevel);
|
||||
}
|
||||
|
||||
public void ChangeControllerType(XRInputModalityManager.InputMode inputMode)
|
||||
{
|
||||
if (inputMode == XRInputModalityManager.InputMode.MotionController)
|
||||
{
|
||||
SetFidelity(2);
|
||||
SetHandsToControllerOffset();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFidelity(m_FidelityLevel);
|
||||
ResetHandsToStart();
|
||||
}
|
||||
}
|
||||
|
||||
void SetFidelity(int fidelity)
|
||||
{
|
||||
fidelity = Mathf.Clamp(fidelity, 0, 2);
|
||||
|
||||
m_HandCurler[0].fidelityLevel = fidelity;
|
||||
m_HandCurler[1].fidelityLevel = fidelity;
|
||||
|
||||
m_HandCurler[0].useCurl = fidelity > 0;
|
||||
m_HandCurler[1].useCurl = fidelity > 0;
|
||||
}
|
||||
|
||||
void SyncAllFingerData()
|
||||
{
|
||||
if (IsOwner)
|
||||
{
|
||||
SetNetworkFingerRotations();
|
||||
|
||||
if (m_UpdateHandsLocally)
|
||||
{
|
||||
GetNetworkFingerRotations();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNetworkFingerRotations();
|
||||
}
|
||||
}
|
||||
|
||||
void SetNetworkFingerRotations()
|
||||
{
|
||||
int currentIdx = 0;
|
||||
for (int i = 0; i < m_LocalHandFidelityOptions[0].fingerJoints.Length; i++)
|
||||
{
|
||||
for (int j = 0; j < m_LocalHandFidelityOptions[0].fingerJoints[i].jointTransformReferences.Count; j++)
|
||||
{
|
||||
m_FingerRotationsLeft[currentIdx++] =
|
||||
m_LocalHandFidelityOptions[0].fingerJoints[i].jointTransformReferences[j].jointTransform.eulerAngles;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
currentIdx = 0;
|
||||
for (int i = 0; i < m_LocalHandFidelityOptions[1].fingerJoints.Length; i++)
|
||||
{
|
||||
for (int j = 0; j < m_LocalHandFidelityOptions[1].fingerJoints[i].jointTransformReferences.Count; j++)
|
||||
{
|
||||
m_FingerRotationsRight[currentIdx++] =
|
||||
m_LocalHandFidelityOptions[1].fingerJoints[i].jointTransformReferences[j].jointTransform.eulerAngles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetNetworkFingerRotations()
|
||||
{
|
||||
int currentIdx = 0;
|
||||
int hand = 0;
|
||||
for (int i = 0; i < m_HandCurler[hand].handFidelityOptions[0].fingerJoints.Length; i++)
|
||||
{
|
||||
for (int j = 0; j < m_HandCurler[hand].handFidelityOptions[0].fingerJoints[i].jointTransformReferences.Count; j++)
|
||||
{
|
||||
m_HandCurler[hand].handFidelityOptions[0].fingerJoints[i].jointTransformReferences[j].jointTransform.rotation =
|
||||
Quaternion.Slerp(m_HandCurler[hand].handFidelityOptions[0].fingerJoints[i].jointTransformReferences[j].jointTransform.rotation,
|
||||
Quaternion.Euler(m_FingerRotationsLeft[currentIdx++]),
|
||||
Time.deltaTime * m_FingerLerpSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
currentIdx = 0;
|
||||
hand = 1;
|
||||
for (int i = 0; i < m_HandCurler[hand].handFidelityOptions[0].fingerJoints.Length; i++)
|
||||
{
|
||||
for (int j = 0; j < m_HandCurler[hand].handFidelityOptions[0].fingerJoints[i].jointTransformReferences.Count; j++)
|
||||
{
|
||||
m_HandCurler[hand].handFidelityOptions[0].fingerJoints[i].jointTransformReferences[j].jointTransform.rotation =
|
||||
Quaternion.Slerp(m_HandCurler[hand].handFidelityOptions[0].fingerJoints[i].jointTransformReferences[j].jointTransform.rotation,
|
||||
Quaternion.Euler(m_FingerRotationsRight[currentIdx++]),
|
||||
Time.deltaTime * m_FingerLerpSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SyncFingerCurl()
|
||||
{
|
||||
if (IsOwner)
|
||||
{
|
||||
SetNetworkCurl();
|
||||
|
||||
if (m_UpdateHandsLocally)
|
||||
{
|
||||
GetNetworkCurl();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNetworkCurl();
|
||||
}
|
||||
}
|
||||
|
||||
void SetNetworkCurl()
|
||||
{
|
||||
for (int i = 0; i < m_LocalHandFidelityOptions[0].fingerJoints.Length; i++)
|
||||
{
|
||||
SetLocalNetworkFingerCurl(0, i, GetAverageX(0, i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_LocalHandFidelityOptions[1].fingerJoints.Length; i++)
|
||||
{
|
||||
SetLocalNetworkFingerCurl(1, i, GetAverageX(1, i));
|
||||
}
|
||||
}
|
||||
|
||||
void GetNetworkCurl()
|
||||
{
|
||||
for (int i = 0; i < m_HandCurler[0].handFidelityOptions[0].fingerJoints.Length; i++)
|
||||
{
|
||||
m_HandCurler[0].SetCurl(i, m_FingerCurlLeft[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_HandCurler[1].handFidelityOptions[0].fingerJoints.Length; i++)
|
||||
{
|
||||
m_HandCurler[1].SetCurl(i, m_FingerCurlRight[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float GetAverageX(int hand, int finger)
|
||||
{
|
||||
float x = 0;
|
||||
int digitCount = 4;
|
||||
|
||||
if (finger == 0) //Thumbs have 1 less joint
|
||||
{
|
||||
digitCount--;
|
||||
}
|
||||
|
||||
for (int i = 1; i < digitCount; i++)
|
||||
{
|
||||
float currentX = m_LocalHandFidelityOptions[hand].fingerJoints[finger].jointTransformReferences[i].jointTransform.localEulerAngles.x;
|
||||
if (currentX < 0 || currentX > 180)
|
||||
{
|
||||
currentX = 0;
|
||||
}
|
||||
x += currentX;
|
||||
}
|
||||
|
||||
float avg = Mathf.Clamp(x / (digitCount - 1), 0, 100);
|
||||
|
||||
return avg / 100;
|
||||
}
|
||||
|
||||
void SyncFingerCurlLimited()
|
||||
{
|
||||
if (IsOwner)
|
||||
{
|
||||
SetNetworkCurlLimited();
|
||||
|
||||
if (m_UpdateHandsLocally)
|
||||
{
|
||||
GetNetworkCurlLimited();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNetworkCurlLimited();
|
||||
}
|
||||
}
|
||||
|
||||
void SetNetworkCurlLimited()
|
||||
{
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
SetLocalNetworkFingerCurl(0, i, GetAverageX(0, i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
SetLocalNetworkFingerCurl(1, i, GetAverageX(1, i));
|
||||
}
|
||||
|
||||
SetLocalNetworkFingerCurl(0, 2, GetAverageXCombined(0));
|
||||
SetLocalNetworkFingerCurl(1, 2, GetAverageXCombined(1));
|
||||
}
|
||||
|
||||
void GetNetworkCurlLimited()
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
m_HandCurler[0].SetCurl(i, m_FingerCurlLeft[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
m_HandCurler[1].SetCurl(i, m_FingerCurlRight[i]);
|
||||
}
|
||||
}
|
||||
|
||||
float GetAverageXCombined(int hand)
|
||||
{
|
||||
float x = 0;
|
||||
|
||||
int digitCount = 4;
|
||||
int startFinger = 2;
|
||||
int endFinger = 5;
|
||||
|
||||
int count = 0;
|
||||
for (int i = startFinger; i < endFinger; i++)
|
||||
{
|
||||
for (int j = 1; j < digitCount; j++)
|
||||
{
|
||||
float currentX = m_LocalHandFidelityOptions[hand].fingerJoints[i].jointTransformReferences[j].jointTransform.localEulerAngles.x;
|
||||
if (currentX < 0 || currentX > 180)
|
||||
{
|
||||
currentX = 0;
|
||||
}
|
||||
x += currentX;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
float avg = Mathf.Clamp(x / count, 0, 100);
|
||||
|
||||
return avg / 100;
|
||||
}
|
||||
|
||||
void SyncControllerTracking()
|
||||
{
|
||||
//TODO: Sync Controller Input and map to hand poses
|
||||
if (IsOwner)
|
||||
{
|
||||
SetNetworkControllerFingerSync();
|
||||
if (m_UpdateHandsLocally)
|
||||
{
|
||||
GetNetworkedControllerFingerSync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNetworkedControllerFingerSync();
|
||||
}
|
||||
}
|
||||
|
||||
void SetNetworkControllerFingerSync()
|
||||
{
|
||||
SetLocalNetworkFingerCurl(0, 0, m_ThumbTouchProperties[0].action?.ReadValue<float>() ?? 0.0f);
|
||||
SetLocalNetworkFingerCurl(0, 1, m_TriggerInputProperties[0].action?.ReadValue<float>() ?? 0.0f);
|
||||
SetLocalNetworkFingerCurl(0, 2, m_GripInputProperties[0].action?.ReadValue<float>() ?? 0.0f);
|
||||
|
||||
SetLocalNetworkFingerCurl(1, 0, m_ThumbTouchProperties[1].action?.ReadValue<float>() ?? 0.0f);
|
||||
SetLocalNetworkFingerCurl(1, 1, m_TriggerInputProperties[1].action?.ReadValue<float>() ?? 0.0f);
|
||||
SetLocalNetworkFingerCurl(1, 2, m_GripInputProperties[1].action?.ReadValue<float>() ?? 0.0f);
|
||||
}
|
||||
|
||||
void SetLocalNetworkFingerCurl(int hand, int finger, float value)
|
||||
{
|
||||
if (hand == 0)
|
||||
{
|
||||
if (Mathf.Abs(m_FingerCurlLeft[finger] - value) > m_MinUpdateDelta)
|
||||
m_FingerCurlLeft[finger] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Mathf.Abs(m_FingerCurlRight[finger] - value) > m_MinUpdateDelta)
|
||||
m_FingerCurlRight[finger] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void GetNetworkedControllerFingerSync()
|
||||
{
|
||||
GetNetworkCurlLimited();
|
||||
}
|
||||
|
||||
void SwapToHands()
|
||||
{
|
||||
SetTrackingType(XRInputModalityManager.InputMode.TrackedHand);
|
||||
}
|
||||
|
||||
void SwapToControllers()
|
||||
{
|
||||
SetTrackingType(XRInputModalityManager.InputMode.MotionController);
|
||||
}
|
||||
|
||||
public void SetTrackingType(XRInputModalityManager.InputMode trackingType)
|
||||
{
|
||||
m_TrackingType.Value = trackingType;
|
||||
if (trackingType == XRInputModalityManager.InputMode.MotionController)
|
||||
{
|
||||
m_LeftHandOrigin = m_LeftControllerTransformReference;
|
||||
m_RightHandOrigin = m_RightControllerTransformReference;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_LeftHandOrigin = m_LeftHandTransformReference;
|
||||
m_RightHandOrigin = m_RightHandTransformReference;
|
||||
}
|
||||
|
||||
XRINetworkPlayer.LocalPlayer.SetHandOrigins(m_LeftHandOrigin, m_RightHandOrigin);
|
||||
}
|
||||
|
||||
void ResetHandsToStart()
|
||||
{
|
||||
for (int i = 0; i < m_HandCurler.Length; i++)
|
||||
{
|
||||
m_HandCurler[i].transform.localPosition = m_handTrackedStartPose[i].position;
|
||||
m_HandCurler[i].transform.localRotation = m_handTrackedStartPose[i].rotation;
|
||||
}
|
||||
}
|
||||
|
||||
void SetHandsToControllerOffset()
|
||||
{
|
||||
for (int i = 0; i < m_HandCurler.Length; i++)
|
||||
{
|
||||
m_HandCurler[i].transform.localPosition = m_HandControllerOffsets[i];
|
||||
m_HandCurler[i].transform.localRotation = Quaternion.Euler(m_HandControllerEulerOffsets[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79af78f95e61cdd458d2f24b1a8356fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,449 @@
|
||||
using UnityEngine;
|
||||
using Unity.Netcode;
|
||||
using Unity.XR.CoreUtils;
|
||||
using Unity.Collections;
|
||||
using System;
|
||||
using Unity.Services.Vivox;
|
||||
using Unity.XR.CoreUtils.Bindings.Variables;
|
||||
|
||||
namespace XRMultiplayer
|
||||
{
|
||||
/// <summary>
|
||||
/// XRINetworkPlayer class used for simple interactions.
|
||||
/// </summary>
|
||||
public class XRINetworkPlayer : NetworkBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Speed at which voice amplitude changes.
|
||||
/// </summary>
|
||||
const float k_VoiceAmplitudeSpeed = 15.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton Reference for the Local Player.
|
||||
/// </summary>
|
||||
public static XRINetworkPlayer LocalPlayer;
|
||||
|
||||
[Header("Avatar Transform References"), Tooltip("Assign to local avatar transform.")]
|
||||
/// <summary>
|
||||
/// Non-Local player transforms.
|
||||
/// </summary>
|
||||
public Transform head;
|
||||
|
||||
/// <summary>
|
||||
/// Non-Local player transforms.
|
||||
/// </summary>
|
||||
public Transform leftHand;
|
||||
|
||||
/// <summary>
|
||||
/// Non-Local player transforms.
|
||||
/// </summary>
|
||||
public Transform rightHand;
|
||||
|
||||
/// <summary>
|
||||
/// Action called when the player name is updated.
|
||||
/// </summary>
|
||||
public Action<string> onNameUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Action called when the player color is updated.
|
||||
/// </summary>
|
||||
public Action<Color> onColorUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Action called when the Local Player is finished spawning in.
|
||||
/// </summary>
|
||||
public Action onSpawnedLocal;
|
||||
|
||||
/// <summary>
|
||||
/// Action called when the Local Player is finished spawning in.
|
||||
/// </summary>
|
||||
public Action onSpawnedAll;
|
||||
|
||||
/// <summary>
|
||||
/// Action called when the player color is updated.
|
||||
/// </summary>
|
||||
public Action<XRINetworkPlayer> onDisconnected;
|
||||
|
||||
/// <summary>
|
||||
/// Bindable Variable used for other clients to mute this user locally.
|
||||
/// </summary>
|
||||
public BindableVariable<bool> squelched = new BindableVariable<bool>(false);
|
||||
|
||||
/// <summary>
|
||||
/// Current Voice Amplitude driven from Vivox.
|
||||
/// </summary>
|
||||
public float playerVoiceAmp { get => m_VoiceAmplitudeCurrent; }
|
||||
float m_VoiceAmplitudeCurrent;
|
||||
|
||||
/// <summary>
|
||||
/// Player Voice Id string that reads from the internal NetworkVariable for the Player Voice Id.
|
||||
/// </summary>
|
||||
public string playerVoiceId { get => m_PlayerVoiceId.Value.ToString(); }
|
||||
readonly NetworkVariable<FixedString128Bytes> m_PlayerVoiceId = new("", NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
/// <summary>
|
||||
/// Player Name string that reads from the internal NetworkVariable for the Player Name.
|
||||
/// </summary>
|
||||
public string playerName { get => m_PlayerName.Value.ToString(); }
|
||||
readonly NetworkVariable<FixedString128Bytes> m_PlayerName = new("", NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
/// <summary>
|
||||
/// Player Color that reads from the internal NetworkVariable for the Player Color.
|
||||
/// </summary>
|
||||
public Color playerColor { get => m_PlayerColor.Value; }
|
||||
readonly NetworkVariable<Color> m_PlayerColor = new(Color.white, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
[HideInInspector] public readonly NetworkVariable<bool> selfMuted = new(false, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Player Name Tag.
|
||||
/// </summary>
|
||||
[Header("Player Name Tag"), SerializeField, Tooltip("Player Name Tag.")] protected bool m_UpdateObjectName = true;
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// /// Head Renderers to change rendering mode for local players.
|
||||
// /// </summary>
|
||||
// [SerializeField, Tooltip("Head Renderers to change rendering mode for local players.")] protected Renderer[] m_HeadRends;
|
||||
|
||||
/// <summary>
|
||||
/// Hand Objects to be disabled for the local player.
|
||||
/// </summary>
|
||||
[Header("Networked Hands"), SerializeField, Tooltip("Hand Objects to be disabled for the local player.")] protected GameObject[] m_handsObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Player Name Tag.
|
||||
/// </summary>
|
||||
[Header("Player Name Tag"), SerializeField, Tooltip("Player Name Tag.")] protected PlayerNameTag m_PlayerNameTag;
|
||||
|
||||
/// <summary>
|
||||
/// Internal references to the Local Player Transforms.
|
||||
/// </summary>
|
||||
protected Transform m_LeftHandOrigin, m_RightHandOrigin, m_HeadOrigin;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the local player XR Origin
|
||||
/// </summary>
|
||||
protected XROrigin m_XROrigin;
|
||||
|
||||
/// <summary>
|
||||
/// If the player has been connected to the the game.
|
||||
/// </summary>
|
||||
protected bool m_InitialConnected = false;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the VoiceChatManager.
|
||||
/// </summary>
|
||||
protected VoiceChatManager m_VoiceChat;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the VivoxParticipant.
|
||||
/// </summary>
|
||||
protected VivoxParticipant m_VivoxParticipant;
|
||||
|
||||
/// <summary>
|
||||
/// Time to update the voice position.
|
||||
/// </summary>
|
||||
protected float m_VoicePositionUpdateTime = .1f, m_VoiceUpdatePosotionDelta = .05f;
|
||||
|
||||
/// <summary>
|
||||
/// Destination for the voice amplitude.
|
||||
/// </summary>
|
||||
protected float m_VoiceAmplitudeDestination;
|
||||
|
||||
/// <summary>
|
||||
/// Timer to check the voice position.
|
||||
/// </summary>
|
||||
protected float m_VoicePositionCheckTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Previous position of the head.
|
||||
/// </summary>
|
||||
protected Vector3 m_PrevHeadPos;
|
||||
|
||||
protected void Awake()
|
||||
{
|
||||
m_VoiceChat = FindFirstObjectByType<VoiceChatManager>();
|
||||
m_VoicePositionCheckTimer = m_VoicePositionUpdateTime;
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
m_PlayerName.OnValueChanged += UpdatePlayerName;
|
||||
m_PlayerColor.OnValueChanged += UpdatePlayerColor;
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
m_PlayerName.OnValueChanged -= UpdatePlayerName;
|
||||
m_PlayerColor.OnValueChanged -= UpdatePlayerColor;
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (IsOwner && XRINetworkGameManager.Instance.positionalVoiceChat)
|
||||
{
|
||||
if (Time.time > m_VoicePositionCheckTimer)
|
||||
{
|
||||
m_VoicePositionCheckTimer += m_VoicePositionUpdateTime;
|
||||
|
||||
if (Vector3.Distance(m_PrevHeadPos, m_HeadOrigin.position) > m_VoiceUpdatePosotionDelta)
|
||||
{
|
||||
m_PrevHeadPos = m_HeadOrigin.position;
|
||||
if (XRINetworkGameManager.Instance.positionalVoiceChat)
|
||||
{
|
||||
m_VoiceChat.Set3DAudio(m_HeadOrigin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_VoiceAmplitudeCurrent = Mathf.Lerp(m_VoiceAmplitudeCurrent, m_VoiceAmplitudeDestination, Time.deltaTime * k_VoiceAmplitudeSpeed);
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
protected virtual void LateUpdate()
|
||||
{
|
||||
if (!IsOwner) return;
|
||||
|
||||
// Set transforms to be replicated with ClientNetworkTransforms
|
||||
leftHand.SetPositionAndRotation(m_LeftHandOrigin.position, m_LeftHandOrigin.rotation);
|
||||
rightHand.SetPositionAndRotation(m_RightHandOrigin.position, m_RightHandOrigin.rotation);
|
||||
head.SetPositionAndRotation(m_HeadOrigin.position, m_HeadOrigin.rotation);
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (IsOwner)
|
||||
{
|
||||
// Local Name unsubscribe.
|
||||
XRINetworkGameManager.LocalPlayerName.Unsubscribe(UpdateLocalPlayerName);
|
||||
XRINetworkGameManager.LocalPlayerColor.Unsubscribe(UpdateLocalPlayerColor);
|
||||
m_VoiceChat.selfMuted.Unsubscribe(SelfMutedChanged);
|
||||
}
|
||||
else if (NetworkManager.Singleton != null && NetworkManager.Singleton.IsConnectedClient)
|
||||
{
|
||||
// Inform Network Manager that player left current session.
|
||||
XRINetworkGameManager.Instance.PlayerLeft(NetworkObject.OwnerClientId);
|
||||
}
|
||||
|
||||
// Unsubscribe from color updating.
|
||||
m_PlayerColor.OnValueChanged -= UpdatePlayerColor;
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
if (IsOwner)
|
||||
{
|
||||
// Set Local Player.
|
||||
LocalPlayer = this;
|
||||
XRINetworkGameManager.Instance.LocalPlayerConnected(NetworkObject.OwnerClientId);
|
||||
|
||||
// Get Origin and set head.
|
||||
m_XROrigin = FindFirstObjectByType<XROrigin>();
|
||||
if (m_XROrigin != null)
|
||||
{
|
||||
m_HeadOrigin = m_XROrigin.Camera.transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.Log("No XR Rig Available", 1);
|
||||
}
|
||||
|
||||
SetupLocalPlayer();
|
||||
}
|
||||
CompleteSetup();
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
base.OnNetworkDespawn();
|
||||
PlayerHudNotification.Instance.ShowText($"<b>{m_PlayerName.Value}</b> left");
|
||||
onDisconnected?.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from <see cref="XRHandPoseReplicator"/> when swapping between hand tracking and controllers.
|
||||
/// </summary>
|
||||
/// <param name="left">Transform for Left Hand.</param>
|
||||
/// <param name="right">Transform for Right Hand.</param>
|
||||
public void SetHandOrigins(Transform left, Transform right)
|
||||
{
|
||||
m_LeftHandOrigin = left;
|
||||
m_RightHandOrigin = right;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides and disables Renderers and GameObjects on the Local Player.
|
||||
/// Also sets the initial values for <see cref="m_PlayerColor"/> and <see cref="m_PlayerName"/>.
|
||||
/// Finally we subscribe to any updates for Color and Name.
|
||||
/// </summary>
|
||||
/// <remarks>Only called on the Local Player.</remarks>
|
||||
protected virtual void SetupLocalPlayer()
|
||||
{
|
||||
foreach (var hand in m_handsObjects)
|
||||
{
|
||||
hand.SetActive(false);
|
||||
}
|
||||
|
||||
m_PlayerColor.Value = XRINetworkGameManager.LocalPlayerColor.Value;
|
||||
m_PlayerName.Value = new FixedString128Bytes(XRINetworkGameManager.LocalPlayerName.Value);
|
||||
XRINetworkGameManager.LocalPlayerColor.Subscribe(UpdateLocalPlayerColor);
|
||||
XRINetworkGameManager.LocalPlayerName.Subscribe(UpdateLocalPlayerName);
|
||||
m_VoiceChat.selfMuted.Subscribe(SelfMutedChanged);
|
||||
m_VoiceChat.ToggleSelfMute(true, true);
|
||||
|
||||
onSpawnedLocal?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from the local player only
|
||||
/// </summary>
|
||||
/// <param name="muted"></param>
|
||||
void SelfMutedChanged(bool muted)
|
||||
{
|
||||
selfMuted.Value = muted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback for the bindable variable <see cref="XRINetworkGameManager.LocalPlayerColor"/>.
|
||||
/// </summary>
|
||||
/// <param name="color">New Color for player.</param>
|
||||
/// <remarks>Only called on Local Player.</remarks>
|
||||
protected virtual void UpdateLocalPlayerColor(Color color)
|
||||
{
|
||||
m_PlayerColor.Value = XRINetworkGameManager.LocalPlayerColor.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback for the bindable variable <see cref="XRINetworkGameManager.LocalPlayerName"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">New Name for player.</param>
|
||||
/// <remarks>Only called on Local Player.</remarks>
|
||||
protected virtual void UpdateLocalPlayerName(string name)
|
||||
{
|
||||
m_PlayerName.Value = new FixedString128Bytes(XRINetworkGameManager.LocalPlayerName.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the player object is finished being setup.
|
||||
/// </summary>
|
||||
void CompleteSetup()
|
||||
{
|
||||
// Add player to XRINetworkManager.
|
||||
XRINetworkGameManager.Instance.PlayerJoined(NetworkObject.OwnerClientId);
|
||||
|
||||
// Update Color and Name.
|
||||
UpdatePlayerColor(Color.white, m_PlayerColor.Value);
|
||||
UpdatePlayerName(new FixedString128Bytes(""), m_PlayerName.Value);
|
||||
|
||||
// Check if WorldCanvas exists
|
||||
WorldCanvas worldCanvas = FindFirstObjectByType<WorldCanvas>();
|
||||
if (worldCanvas != null)
|
||||
{
|
||||
// If we are using a World Canvas, reparent name tag and destroy local canvas.
|
||||
Canvas localCanvas = m_PlayerNameTag.GetComponentInParent<Canvas>();
|
||||
worldCanvas.SetupPlayerNameTag(this, m_PlayerNameTag);
|
||||
Destroy(localCanvas.gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we are not using a World Canvas, setup the name tag for local use.
|
||||
m_PlayerNameTag.SetupNameTag(this);
|
||||
}
|
||||
|
||||
onSpawnedAll?.Invoke();
|
||||
}
|
||||
/// <summary>
|
||||
/// Callback anytime the local player sets <see cref="m_PlayerName"/>.
|
||||
/// </summary><remarks>Invokes the callback <see cref="onNameUpdated"/>.</remarks>
|
||||
void UpdatePlayerName(FixedString128Bytes oldName, FixedString128Bytes currentName)
|
||||
{
|
||||
onNameUpdated?.Invoke(currentName.ToString());
|
||||
|
||||
if (!m_InitialConnected & !string.IsNullOrEmpty(currentName.ToString()))
|
||||
{
|
||||
m_InitialConnected = true;
|
||||
if (!IsLocalPlayer)
|
||||
PlayerHudNotification.Instance.ShowText($"<b>{playerName}</b> joined");
|
||||
}
|
||||
|
||||
if (m_UpdateObjectName)
|
||||
gameObject.name = currentName.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when the local player sets <see cref="m_PlayerColor"/>.
|
||||
/// </summary><remarks>Invokes the callback <see cref="onColorUpdated"/>.</remarks>
|
||||
void UpdatePlayerColor(Color oldColor, Color newColor)
|
||||
{
|
||||
onColorUpdated?.Invoke(newColor);
|
||||
}
|
||||
|
||||
void UpdatePlayerVoiceEnergy(float current)
|
||||
{
|
||||
m_VoiceAmplitudeDestination = Mathf.Clamp01(current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when new players connect to the game and set their initial <see cref="m_PlayerVoiceId"/>
|
||||
/// and when <see cref="VoiceChatManager.OnParticipantAdded(VivoxParticipant)"/> is called for existing players.
|
||||
/// </summary>
|
||||
public void SetupVoicePlayer()
|
||||
{
|
||||
m_VivoxParticipant = m_VoiceChat.GetVivoxParticipantById(playerVoiceId);
|
||||
if (m_VivoxParticipant != null)
|
||||
{
|
||||
m_VivoxParticipant.ParticipantAudioEnergyChanged += ParticipantAudioEnergyChanged;
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.Log($"No Participant with id: {playerVoiceId}", 1);
|
||||
}
|
||||
|
||||
if (!VoiceChatManager.m_PlayersDictionary.ContainsKey(playerVoiceId))
|
||||
{
|
||||
VoiceChatManager.AddNewVivoxPlayer(playerVoiceId, this);
|
||||
}
|
||||
}
|
||||
private void ParticipantAudioEnergyChanged()
|
||||
{
|
||||
UpdatePlayerVoiceEnergy((float)m_VivoxParticipant.AudioEnergy);
|
||||
}
|
||||
|
||||
public void SetVoiceId(string voiceId)
|
||||
{
|
||||
if (!IsOwner) return;
|
||||
m_PlayerVoiceId.Value = new FixedString128Bytes(voiceId);
|
||||
SetupVoicePlayer();
|
||||
if (XRINetworkGameManager.Instance.positionalVoiceChat)
|
||||
{
|
||||
m_VoiceChat.Set3DAudio(m_HeadOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from clients to mute this player locally for that client.
|
||||
/// </summary>
|
||||
public void ToggleSquelch()
|
||||
{
|
||||
if (m_VivoxParticipant != null)
|
||||
{
|
||||
squelched.Value = !squelched.Value;
|
||||
if (squelched.Value)
|
||||
m_VivoxParticipant.MutePlayerLocally();
|
||||
else
|
||||
m_VivoxParticipant.UnmutePlayerLocally();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c156e0102a8d0514b91acc63faadec62
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user