EVERYTHING
This commit is contained in:
@@ -0,0 +1,438 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user