Files
BlockSpace-v1.5.0b/Assets/Scripts/Assembly-CSharp/BlockSpace/Voxels/AvatarCustomizerController.cs
T
2026-06-01 17:19:55 +02:00

439 lines
12 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
namespace BlockSpace.Voxels
{
[DisallowMultipleComponent]
public sealed class AvatarCustomizerController : MonoBehaviour
{
private const float AvatarVoxelSizeScale = 0.5f;
private const float ButtonPressCooldown = 0.2f;
private const float ButtonShrinkDuration = 0.08f;
private const float ButtonSettleDuration = 0.12f;
private const float ButtonPressedScale = 0.84f;
private const float ButtonOvershootScale = 1.04f;
private const string ButtonThockResourcePath = "Thock";
private static readonly string[] SuccessClipAssetPaths = new string[3] { "Assets/success.wav", "Assets/success.mp3", "Assets/success.ogg" };
private const string UiRootPath = "UI";
private const string EditHeadPath = "UI/Edit Head";
private const string EditLeftPath = "UI/Edit Left";
private const string EditRightPath = "UI/Edit Right";
private const string StopEditingPath = "UI/Stop Editing";
private const string ApplyAndSavePath = "UI/Apply and Save";
private const string HeadGroupPath = "Origins/Head/Head Group";
private const string LeftGroupPath = "Origins/Left/Left Group";
private const string RightGroupPath = "Origins/Right/Right Group";
[SerializeField]
private VoxelGroupManager groupManager;
[SerializeField]
private VRVoxelBrush voxelBrush;
[SerializeField]
private Collider editHeadButton;
[SerializeField]
private Collider editLeftButton;
[SerializeField]
private Collider editRightButton;
[SerializeField]
private Collider stopEditingButton;
[SerializeField]
private Collider applyAndSaveButton;
[SerializeField]
private Transform headGroupTransform;
[SerializeField]
private Transform leftGroupTransform;
[SerializeField]
private Transform rightGroupTransform;
[SerializeField]
private AudioClip buttonThockClip;
[SerializeField]
private AudioClip applySuccessClip;
private VoxelGroup headEditorGroup;
private VoxelGroup leftEditorGroup;
private VoxelGroup rightEditorGroup;
private VoxelGroup activeEditorGroup;
private readonly Dictionary<Transform, Vector3> buttonBaseScales = new Dictionary<Transform, Vector3>();
private readonly Dictionary<Transform, Coroutine> buttonAnimationCoroutines = new Dictionary<Transform, Coroutine>();
private bool editorGroupsInitialized;
private bool loadedPersistedAvatar;
private bool editHeadInsidePrevious;
private bool editLeftInsidePrevious;
private bool editRightInsidePrevious;
private bool stopEditingInsidePrevious;
private bool applyAndSaveInsidePrevious;
private float nextButtonPressTime;
public void Configure(VoxelGroupManager manager, VRVoxelBrush brush)
{
if (manager != null)
{
groupManager = manager;
}
if (brush != null)
{
voxelBrush = brush;
}
ResolveSceneReferences();
EnsureButtonAudioAssigned();
EnsureApplySuccessAudioAssigned();
TryInitializeEditorGroups();
}
private void Awake()
{
ResolveSceneReferences();
EnsureButtonAudioAssigned();
EnsureApplySuccessAudioAssigned();
}
private void OnDisable()
{
StopAllCoroutines();
StopEditing();
ResetAllButtonScales();
}
private void Update()
{
ResolveSceneReferences();
if (TryInitializeEditorGroups())
{
HandleButton(editHeadButton, ref editHeadInsidePrevious, delegate
{
StartEditing(headEditorGroup);
});
HandleButton(editLeftButton, ref editLeftInsidePrevious, delegate
{
StartEditing(leftEditorGroup);
});
HandleButton(editRightButton, ref editRightInsidePrevious, delegate
{
StartEditing(rightEditorGroup);
});
HandleButton(stopEditingButton, ref stopEditingInsidePrevious, StopEditing);
HandleButton(applyAndSaveButton, ref applyAndSaveInsidePrevious, ApplyAndSave);
}
}
private void ResolveSceneReferences()
{
if (groupManager == null)
{
groupManager = ((VoxelGroupManager.Instance != null) ? VoxelGroupManager.Instance : UnityEngine.Object.FindObjectOfType<VoxelGroupManager>());
}
if (voxelBrush == null)
{
voxelBrush = ((VRVoxelBrush.Instance != null) ? VRVoxelBrush.Instance : UnityEngine.Object.FindObjectOfType<VRVoxelBrush>());
}
if (editHeadButton == null)
{
editHeadButton = FindCollider("UI/Edit Head");
}
if (editLeftButton == null)
{
editLeftButton = FindCollider("UI/Edit Left");
}
if (editRightButton == null)
{
editRightButton = FindCollider("UI/Edit Right");
}
if (stopEditingButton == null)
{
stopEditingButton = FindCollider("UI/Stop Editing");
}
if (applyAndSaveButton == null)
{
applyAndSaveButton = FindCollider("UI/Apply and Save");
}
if (headGroupTransform == null)
{
headGroupTransform = base.transform.Find("Origins/Head/Head Group");
}
if (leftGroupTransform == null)
{
leftGroupTransform = base.transform.Find("Origins/Left/Left Group");
}
if (rightGroupTransform == null)
{
rightGroupTransform = base.transform.Find("Origins/Right/Right Group");
}
}
private bool TryInitializeEditorGroups()
{
if (editorGroupsInitialized)
{
return true;
}
if (groupManager == null || headGroupTransform == null || leftGroupTransform == null || rightGroupTransform == null)
{
return false;
}
headEditorGroup = PrepareEditorGroup(headGroupTransform.gameObject, headEditorGroup, 61000);
leftEditorGroup = PrepareEditorGroup(leftGroupTransform.gameObject, leftEditorGroup, 61001);
rightEditorGroup = PrepareEditorGroup(rightGroupTransform.gameObject, rightEditorGroup, 61002);
if (headEditorGroup == null || leftEditorGroup == null || rightEditorGroup == null)
{
return false;
}
editorGroupsInitialized = true;
if (!loadedPersistedAvatar)
{
AvatarVoxelStorage.ApplyPayload(AvatarVoxelStorage.LoadLocalOrDefault(), headEditorGroup, leftEditorGroup, rightEditorGroup, groupManager, useLocalGridTransform: true);
loadedPersistedAvatar = true;
}
return true;
}
private VoxelGroup PrepareEditorGroup(GameObject groupObject, VoxelGroup existingGroup, ushort groupId)
{
if (groupObject == null || groupManager == null)
{
return null;
}
VoxelGroup voxelGroup = ((existingGroup != null) ? existingGroup : groupObject.GetComponent<VoxelGroup>());
if (voxelGroup == null)
{
voxelGroup = groupObject.AddComponent<VoxelGroup>();
}
AvatarVoxelStorage.ApplyGroupData(voxelGroup, AvatarVoxelStorage.CaptureGroup(voxelGroup), groupManager, useLocalGridTransform: true);
voxelGroup.SetVoxelSizeScale(0.5f);
voxelGroup.id = groupId;
return voxelGroup;
}
private void StartEditing(VoxelGroup group)
{
if (!(group == null))
{
activeEditorGroup = group;
VRHaptics.Pulse(XRNode.RightHand, 0.18f, 0.035f);
VRGroupSelector.Instance?.ClearSelectionForToolUse();
VRGroupSelector.Instance?.SetInteractionSuppressed(suppressed: true);
voxelBrush?.SetAvatarEditorTarget(group);
}
}
private void StopEditing()
{
activeEditorGroup = null;
voxelBrush?.ClearAvatarEditorTarget();
VRGroupSelector.Instance?.SetInteractionSuppressed(suppressed: false);
}
private void ApplyAndSave()
{
if (TryInitializeEditorGroups())
{
voxelBrush?.FinalizeAvatarEditorSessions();
AvatarVoxelPayload payload = AvatarVoxelStorage.CapturePayload(headEditorGroup, leftEditorGroup, rightEditorGroup);
AvatarVoxelStorage.SaveLocal(payload);
PhotonVoxelAvatar localOwnedInstance = PhotonVoxelAvatar.LocalOwnedInstance;
if (localOwnedInstance != null)
{
localOwnedInstance.ApplyAndBroadcastAvatar(payload);
}
VRHaptics.Pulse(XRNode.RightHand, 0.32f, 0.055f);
PlayApplySuccessFeedback();
}
}
public bool TryGetEditorGroups(out VoxelGroup headBody, out VoxelGroup left, out VoxelGroup right)
{
headBody = null;
left = null;
right = null;
if (!TryInitializeEditorGroups())
{
return false;
}
headBody = headEditorGroup;
left = leftEditorGroup;
right = rightEditorGroup;
if (headBody != null && left != null)
{
return right != null;
}
return false;
}
private void HandleButton(Collider button, ref bool insidePrevious, Action action)
{
bool flag = IsHandInside(button, (voxelBrush != null) ? voxelBrush.LeftHandAnchor : null) || IsHandInside(button, (voxelBrush != null) ? voxelBrush.RightHandAnchor : null);
if (flag && !insidePrevious)
{
PlayButtonEnterAnimation(button);
if (Time.unscaledTime >= nextButtonPressTime)
{
nextButtonPressTime = Time.unscaledTime + 0.2f;
PlayButtonClickFeedback(button);
action?.Invoke();
}
}
insidePrevious = flag;
}
private void PlayButtonEnterAnimation(Collider button)
{
if (!(button == null))
{
Transform transform = button.transform;
if (!buttonBaseScales.TryGetValue(transform, out var value))
{
value = transform.localScale;
buttonBaseScales.Add(transform, value);
}
if (buttonAnimationCoroutines.TryGetValue(transform, out var value2) && value2 != null)
{
StopCoroutine(value2);
}
transform.localScale = value;
buttonAnimationCoroutines[transform] = StartCoroutine(AnimateButtonScale(transform, value));
}
}
private void PlayButtonClickFeedback(Collider button)
{
if (!(buttonThockClip == null) && !(button == null))
{
AudioSource.PlayClipAtPoint(buttonThockClip, button.bounds.center, 1f);
}
}
private IEnumerator AnimateButtonScale(Transform buttonTransform, Vector3 baseScale)
{
Vector3 pressedScale = baseScale * 0.84f;
Vector3 overshootScale = baseScale * 1.04f;
float elapsed = 0f;
while (elapsed < 0.08f)
{
if (buttonTransform == null)
{
yield break;
}
elapsed += Time.unscaledDeltaTime;
float t = Mathf.Clamp01(elapsed / 0.08f);
buttonTransform.localScale = Vector3.LerpUnclamped(baseScale, pressedScale, t);
yield return null;
}
elapsed = 0f;
while (elapsed < 0.12f)
{
if (buttonTransform == null)
{
yield break;
}
elapsed += Time.unscaledDeltaTime;
float num = Mathf.Clamp01(elapsed / 0.12f);
float num2 = num * num * (3f - 2f * num);
Vector3 localScale = ((num2 < 0.5f) ? Vector3.LerpUnclamped(pressedScale, overshootScale, num2 / 0.5f) : Vector3.LerpUnclamped(overshootScale, baseScale, (num2 - 0.5f) / 0.5f));
buttonTransform.localScale = localScale;
yield return null;
}
if (buttonTransform != null)
{
buttonTransform.localScale = baseScale;
buttonAnimationCoroutines[buttonTransform] = null;
}
}
private void ResetAllButtonScales()
{
foreach (KeyValuePair<Transform, Vector3> buttonBaseScale in buttonBaseScales)
{
if (buttonBaseScale.Key != null)
{
buttonBaseScale.Key.localScale = buttonBaseScale.Value;
}
}
buttonAnimationCoroutines.Clear();
}
private void EnsureButtonAudioAssigned()
{
if (buttonThockClip == null)
{
buttonThockClip = Resources.Load<AudioClip>("Thock");
}
}
private void EnsureApplySuccessAudioAssigned()
{
_ = applySuccessClip != null;
}
private void PlayApplySuccessFeedback()
{
if (!(applySuccessClip == null))
{
Vector3 position = ((applyAndSaveButton != null) ? applyAndSaveButton.bounds.center : base.transform.position);
AudioSource.PlayClipAtPoint(applySuccessClip, position, 1f);
}
}
private Collider FindCollider(string relativePath)
{
Transform transform = base.transform.Find(relativePath);
if (!(transform != null))
{
return null;
}
return transform.GetComponent<Collider>();
}
private static bool IsHandInside(Collider button, Transform handAnchor)
{
if (button == null || handAnchor == null)
{
return false;
}
Vector3 position = handAnchor.position;
return (button.ClosestPoint(position) - position).sqrMagnitude <= 1E-06f;
}
}
}