Files
test/Assets/PerfectWorld/Scripts/UI/Inventory/InventoryCharacterPreview.cs
T
2025-11-24 16:41:54 +07:00

323 lines
9.8 KiB
C#

using Animancer;
using BrewMonster;
using UnityEngine;
using UnityEngine.UI;
namespace BrewMonster.Scripts.UI.Inventory
{
/// <summary>
/// Clones the current host-player visual model into a dedicated preview rig for the inventory UI.
/// </summary>
[DisallowMultipleComponent]
public class InventoryCharacterPreview : MonoBehaviour
{
[Header("Player Binding")]
[SerializeField] private CECHostPlayer hostPlayer;
[Tooltip("Auto locate the active host player if none is assigned.")]
[SerializeField] private bool autoBindHostPlayer = true;
[Tooltip("Optional manual override for the visual root. Leave empty to auto detect the PlayerVisual/host root.")]
[SerializeField] private Transform sourceModelRoot;
[Tooltip("Clone the entire CECHostPlayer hierarchy instead of just the PlayerVisual child.")]
[SerializeField] private bool cloneWholeHostHierarchy = true;
[Tooltip("Remove gameplay scripts/colliders from the preview clone when duplicating the full hierarchy.")]
[SerializeField] private bool stripRuntimeComponents = true;
[Header("Preview Rig")]
[SerializeField] private Transform previewRoot;
[SerializeField] private Vector3 previewLocalPosition = Vector3.zero;
[SerializeField] private Vector3 previewLocalEuler;
[SerializeField] private Vector3 previewLocalScale = Vector3.one;
[Tooltip("Copy the preview root layer onto the cloned hierarchy so the preview camera can isolate it.")]
[SerializeField] private bool inheritPreviewLayer = true;
[Header("Camera Output")]
[SerializeField] private Camera previewCamera;
[SerializeField] private RawImage previewFrame;
[SerializeField] private RenderTexture previewRenderTexture;
[SerializeField] private bool autoFocusCamera = true;
[SerializeField] private Vector3 cameraFocusOffset = new Vector3(0f, 1.5f, 0f);
[Header("Behaviour")]
[Tooltip("Disable PlayerVisual/Animancer components on the clone so it stays in a frozen idle pose.")]
[SerializeField] private bool freezeAnimation = true;
private GameObject _previewInstance;
private bool _refreshQueued;
private void Awake()
{
if (previewRoot == null)
{
previewRoot = transform;
}
TryBindHostPlayer();
EnsureCameraBindings();
}
/*private void OnEnable()
{
EventBus.Subscribe<PlayerEquipmentChangedEvent>(OnEquipmentChanged);
QueueRefresh();
}*/
/*private void OnDisable()
{
EventBus.Unsubscribe<PlayerEquipmentChangedEvent>(OnEquipmentChanged);
DestroyPreviewInstance();
}*/
private void LateUpdate()
{
TryBindHostPlayer();
if (_refreshQueued)
{
_refreshQueued = false;
BuildPreviewModel();
}
}
/// <summary>Allows manual binding from external UI scripts.</summary>
public void BindPlayer(CECHostPlayer player)
{
if (player == null || player == hostPlayer)
{
return;
}
hostPlayer = player;
QueueRefresh();
}
/// <summary>Forces an immediate rebuild of the preview model.</summary>
public void ForceRefreshNow()
{
_refreshQueued = false;
BuildPreviewModel();
}
/// <summary>Marks the clone dirty so it gets recreated next LateUpdate.</summary>
public void QueueRefresh()
{
_refreshQueued = true;
}
/*private void OnEquipmentChanged(PlayerEquipmentChangedEvent evt)
{
if (hostPlayer != null && evt.Player == hostPlayer)
{
QueueRefresh();
}
}*/
private void TryBindHostPlayer()
{
if (!autoBindHostPlayer || hostPlayer != null)
{
return;
}
hostPlayer = FindFirstObjectByType<CECHostPlayer>();
if (hostPlayer != null)
{
QueueRefresh();
}
}
private void EnsureCameraBindings()
{
if (previewCamera != null && previewRenderTexture != null)
{
previewCamera.targetTexture = previewRenderTexture;
}
if (previewFrame != null && previewCamera != null)
{
previewFrame.texture = previewCamera.targetTexture;
}
}
private void BuildPreviewModel()
{
if (previewRoot == null)
{
return;
}
var sourceRoot = ResolveSourceModelRoot();
if (sourceRoot == null)
{
return;
}
DestroyPreviewInstance();
_previewInstance = Instantiate(sourceRoot.gameObject, previewRoot, false);
_previewInstance.name = $"{sourceRoot.name}_Preview";
var instanceTransform = _previewInstance.transform;
instanceTransform.localPosition = previewLocalPosition;
instanceTransform.localRotation = Quaternion.Euler(previewLocalEuler);
instanceTransform.localScale = previewLocalScale;
if (inheritPreviewLayer)
{
ApplyLayerRecursive(instanceTransform, previewRoot.gameObject.layer);
}
if (freezeAnimation)
{
DisableComponentInChildren<PlayerVisual>(_previewInstance);
DisableComponentInChildren<AnimancerComponent>(_previewInstance);
DisableComponentInChildren<Animator>(_previewInstance);
FreezeAnimators(_previewInstance);
}
if (cloneWholeHostHierarchy && stripRuntimeComponents)
{
StripRuntimeScripts(_previewInstance);
}
EnsureCameraFocus(instanceTransform);
}
private Transform ResolveSourceModelRoot()
{
if (sourceModelRoot != null)
{
return sourceModelRoot;
}
if (hostPlayer == null)
{
return null;
}
if (cloneWholeHostHierarchy)
{
sourceModelRoot = hostPlayer.transform;
return sourceModelRoot;
}
var playerVisual = hostPlayer.GetComponentInChildren<PlayerVisual>(true);
sourceModelRoot = playerVisual != null ? playerVisual.transform : hostPlayer.transform;
return sourceModelRoot;
}
private void DestroyPreviewInstance()
{
if (_previewInstance != null)
{
Destroy(_previewInstance);
_previewInstance = null;
}
}
private static void DisableComponentInChildren<T>(GameObject root) where T : Behaviour
{
if (root == null)
{
return;
}
var components = root.GetComponentsInChildren<T>(true);
for (int i = 0; i < components.Length; i++)
{
var component = components[i];
if (component == null)
continue;
component.enabled = false;
if (component is PlayerVisual)
{
Destroy(component);
}
}
}
private void ApplyLayerRecursive(Transform root, int layer)
{
if (root == null)
{
return;
}
root.gameObject.layer = layer;
for (int i = 0; i < root.childCount; i++)
{
ApplyLayerRecursive(root.GetChild(i), layer);
}
}
private void EnsureCameraFocus(Transform modelRoot)
{
if (!autoFocusCamera || previewCamera == null || modelRoot == null)
{
return;
}
var focusPoint = modelRoot.TransformPoint(cameraFocusOffset);
}
private void StripRuntimeScripts(GameObject cloneRoot)
{
if (cloneRoot == null)
{
return;
}
var monoBehaviours = cloneRoot.GetComponentsInChildren<MonoBehaviour>(true);
for (int i = 0; i < monoBehaviours.Length; i++)
{
var behaviour = monoBehaviours[i];
if (behaviour == null)
continue;
Destroy(behaviour);
}
var colliders = cloneRoot.GetComponentsInChildren<Collider>(true);
for (int i = 0; i < colliders.Length; i++)
{
var collider = colliders[i];
if (collider == null)
continue;
Destroy(collider);
}
var rigidbodies = cloneRoot.GetComponentsInChildren<Rigidbody>(true);
for (int i = 0; i < rigidbodies.Length; i++)
{
var body = rigidbodies[i];
if (body == null)
continue;
Destroy(body);
}
}
private void FreezeAnimators(GameObject cloneRoot)
{
if (cloneRoot == null)
{
return;
}
var animators = cloneRoot.GetComponentsInChildren<Animator>(true);
for (int i = 0; i < animators.Length; i++)
{
var animator = animators[i];
if (animator == null)
continue;
animator.speed = 0f;
}
}
}
}