Merge remote-tracking branch 'origin/develop' into feature/dlg_setting

This commit is contained in:
Tungdv
2026-05-18 16:26:37 +07:00
11 changed files with 20128 additions and 20665 deletions
@@ -190,7 +190,7 @@ Material:
- _DissolveColor: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _FnlColor: {r: 1, g: 1, b: 1, a: 1}
- _MainColor: {r: 4, g: 4, b: 4, a: 1}
- _MainColor: {r: 1, g: 1, b: 1, a: 1}
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -190,7 +190,7 @@ Material:
- _DissolveColor: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _FnlColor: {r: 1, g: 1, b: 1, a: 1}
- _MainColor: {r: 4, g: 4, b: 4, a: 1}
- _MainColor: {r: 1, g: 1, b: 1, a: 1}
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -190,7 +190,7 @@ Material:
- _DissolveColor: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _FnlColor: {r: 1, g: 1, b: 1, a: 1}
- _MainColor: {r: 4, g: 4, b: 4, a: 1}
- _MainColor: {r: 0.5471698, g: 0.5471698, b: 0.5471698, a: 1}
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: d2fe87cdd491ffb47bd5f3fc0b2d2381
guid: fbd2271b63f2e8f4090db5f589cb44d7
PrefabImporter:
externalObjects: {}
userData:
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:20826e4631ccd8df95267f53f85e8dd91287f8a8b5c61eeeb700e7f859c7b35e
size 304738
oid sha256:118edff57ce13d46edc50acb1387bca1d79d66795958be304ef1e9d017cff634
size 309966
@@ -15,7 +15,6 @@ using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using BrewMonster.Scripts.UI.Inventory;
using CSNetwork.Protocols.RPCData;
namespace BrewMonster.Scripts.Managers
@@ -168,9 +167,26 @@ namespace BrewMonster.Scripts.Managers
ShowDetailPanel(false);
ShowSplitPanel(false);
RefreshAll();
BeginInventoryCharacterPreview();
RefreshCharacterModelPreview();
}
private void OnDisable()
{
PlayerModelPreview.Instance?.EndInventoryPreview();
}
private void BeginInventoryCharacterPreview()
{
var preview = PlayerModelPreview.Instance;
if (preview == null)
return;
RoleInfo roleInfo = UnityGameSession.Instance != null ? UnityGameSession.Instance.GetRoleInfo() : null;
if (roleInfo != null)
preview.BeginInventoryPreview(roleInfo.roleid);
}
private void WireSplitUI()
{
if (splitOpenButton != null)
@@ -1626,20 +1642,12 @@ namespace BrewMonster.Scripts.Managers
public void RefreshCharacterModelPreview()
{
var preview = PlayerModelPreview.Instance;
if (preview != null)
{
RoleInfo roleInfo = BuildCurrentPreviewRoleInfo();
if (roleInfo != null)
{
preview.ReloadRoleModel(roleInfo);
}
}
if (preview == null)
return;
var inventoryPreview = FindFirstObjectByType<InventoryCharacterPreview>();
if (inventoryPreview != null)
{
inventoryPreview.QueueRefresh();
}
RoleInfo roleInfo = BuildCurrentPreviewRoleInfo();
if (roleInfo != null)
preview.ReloadRoleModel(roleInfo);
}
private RoleInfo BuildCurrentPreviewRoleInfo()
@@ -10,6 +10,7 @@ namespace BrewMonster.Scripts
public class PlayerModelPreview : MonoSingleton<PlayerModelPreview>
{
[SerializeField] private Transform modelRoot;
[SerializeField] private bool enablePreviewCameraWithInventory = true;
public List<GameObject> playerModels = new();
public List<int> playerModelIds = new();
@@ -26,10 +27,21 @@ namespace BrewMonster.Scripts
private bool _hasRequestedPlayerModelId;
private int _requestedPlayerModelId;
private bool _inventoryPreviewActive;
private bool _suppressHierarchyWatch;
private bool _refreshQueued;
private RoleInfo _lastPreviewRoleInfo;
private Transform _lastOriginModelRoot;
private int _lastOriginModelChildCount;
private Camera _previewCamera;
private void Awake()
{
if (modelRoot == null)
modelRoot = transform;
_previewCamera = GetComponent<Camera>();
}
private void OnDestroy()
@@ -37,6 +49,64 @@ namespace BrewMonster.Scripts
ClearModelsImmediate();
}
private void LateUpdate()
{
if (!_inventoryPreviewActive || _suppressHierarchyWatch)
return;
Transform activeRoot = GetActivePreviewModelTransform();
if (activeRoot == null)
{
if (_refreshQueued)
{
_refreshQueued = false;
TryRefreshFromCachedRole();
}
return;
}
int currentChildCount = CountAllChildren(activeRoot);
if (_lastOriginModelRoot != activeRoot || _lastOriginModelChildCount != currentChildCount)
{
bool hierarchyChanged = _lastOriginModelRoot != null;
_lastOriginModelRoot = activeRoot;
_lastOriginModelChildCount = currentChildCount;
if (hierarchyChanged && _lastPreviewRoleInfo != null)
_refreshQueued = true;
}
if (_refreshQueued)
{
_refreshQueued = false;
TryRefreshFromCachedRole();
}
}
/// <summary>Called when Inventory UI opens — shows the host preview model on shared Bootstrap camera.</summary>
public void BeginInventoryPreview(int roleId)
{
_inventoryPreviewActive = true;
ResetHierarchyWatch();
if (enablePreviewCameraWithInventory && _previewCamera != null)
_previewCamera.enabled = true;
ShowPlayerModel(roleId);
}
/// <summary>Called when Inventory UI closes — hides preview models. Camera stays enabled for login/character-select reuse.</summary>
public void EndInventoryPreview()
{
_inventoryPreviewActive = false;
_refreshQueued = false;
_suppressHierarchyWatch = false;
ResetHierarchyWatch();
HideAllPlayerModels();
}
/// <summary>
/// Loads one preview instance per role (via <see cref="NPCManager.GetModelPlayer"/>).
/// All instances stay hidden until <see cref="ShowPlayerModel"/> is called.
@@ -50,6 +120,7 @@ namespace BrewMonster.Scripts
if (roleInfos == null || roleInfos.Count == 0 || NPCManager.Instance == null)
{
ClearModels();
_suppressHierarchyWatch = false;
return;
}
@@ -82,13 +153,17 @@ namespace BrewMonster.Scripts
ApplyRequestedModelVisibility();
}
for(int i=0; i < oldModels.Count; i++)
for (int i = 0; i < oldModels.Count; i++)
{
if (oldModels[i] != null)
{
Destroy(oldModels[i]);
}
}
_suppressHierarchyWatch = false;
if (_inventoryPreviewActive)
SyncHierarchyBaseline();
}
/// <summary>
@@ -96,14 +171,6 @@ namespace BrewMonster.Scripts
/// </summary>
public void ShowPlayerModel(int playerModelId)
{
//int n = Mathf.Min(playerModels.Count, playerModelIds.Count);
//for (int i = 0; i < n; i++)
//{
// GameObject go = playerModels[i];
// if (go == null)
// continue;
// go.SetActive(playerModelIds[i] == playerModelId);
//}
_hasRequestedPlayerModelId = true;
_requestedPlayerModelId = playerModelId;
@@ -128,13 +195,15 @@ namespace BrewMonster.Scripts
continue;
bool shouldBeActive = playerModelIds[i] == _requestedPlayerModelId;
bool wasActive = go.activeSelf;
bool wasActive = go.activeSelf;
go.SetActive(shouldBeActive);
// Fire once when this model first becomes visible
if (shouldBeActive && !wasActive)
OnModelReady?.Invoke(playerModelIds[i]);
}
if (_inventoryPreviewActive)
SyncHierarchyBaseline();
}
public void HideAllPlayerModels()
@@ -151,14 +220,6 @@ namespace BrewMonster.Scripts
public void ClearModels()
{
//for (int i = 0; i < playerModels.Count; i++)
//{
// if (playerModels[i] != null)
// {
// Destroy(playerModels[i]);
// }
//}
for (int i = 0; i < playerModels.Count; i++)
{
if (playerModels[i] != null)
@@ -177,9 +238,9 @@ namespace BrewMonster.Scripts
{
if (modelRoot != null)
{
if(playerModels[i] != null)
if (playerModels[i] != null)
{
if(Application.isPlaying)
if (Application.isPlaying)
Destroy(playerModels[i]);
else
DestroyImmediate(playerModels[i]);
@@ -193,9 +254,9 @@ namespace BrewMonster.Scripts
private async UniTask<GameObject> LoadPlayerModel(RoleInfo role)
{
var elemendataman = BrewMonster.ElementDataManProvider.GetElementDataMan();
var elemendataman = ElementDataManProvider.GetElementDataMan();
GameObject model = await NPCManager.Instance.GetModelPlayer(role.occupation, role.gender);
if(model == null)
if (model == null)
return null;
model.SetActive(false);
@@ -209,6 +270,7 @@ namespace BrewMonster.Scripts
{
return null;
}
DATA_TYPE DataType = default;
bool useDefaultUpper = true;
bool useDefaultLower = true;
@@ -217,7 +279,7 @@ namespace BrewMonster.Scripts
GRoleInventory equipment;
for(int i = 0; i < role.equipment.Count; i++)
for (int i = 0; i < role.equipment.Count; i++)
{
equipment = role.equipment[i];
@@ -265,7 +327,6 @@ namespace BrewMonster.Scripts
if (equipData == null) break;
var pArmor = (ARMOR_ESSENCE)equipData;
var nLocation = pArmor.equip_location;
// BMLogger.Log($"ShowEquipments():: Armor Essence: {pArmor.RealName}");
var armorSkinPath = CECPlayer._GenEquipmentSkinPath(role.occupation, role.gender, pArmor.RealName);
if (!armorSkinPath.EndsWith(".ecm"))
@@ -281,7 +342,6 @@ namespace BrewMonster.Scripts
armorObject.transform.localRotation = Quaternion.identity;
armorObject.transform.localScale = Vector3.one;
var skinnedMeshRenderer = armorObject.GetComponent<SkinnedMeshRenderer>();
var skinnedMeshRenderereFromDataList = armorObject.GetComponentsInChildren<SkinnedMeshRenderFromData>();
foreach (var skinnedMeshRenderereFromData in skinnedMeshRenderereFromDataList)
{
@@ -292,7 +352,6 @@ namespace BrewMonster.Scripts
}
}
// disable/enable the default equipment
switch (nLocation)
{
case (uint)CECPlayer.SkinIndex.SKIN_UPPER_BODY_INDEX:
@@ -310,49 +369,22 @@ namespace BrewMonster.Scripts
}
}
break;
//TODO: Handle Wings later
// case DATA_TYPE.DT_WINGMANWING_ESSENCE:
// m_wingType = enumWingType.WINGTYPE_WING;
// //ChangeWing(pResult, static_cast<const WINGMANWING_ESSENCE*>(pEquip)->file_model);
// var pWingData = (WINGMANWING_ESSENCE)equipData;
// //string path1 = "models/players/通用装备/翅膀/天鹅之翼/天鹅之翼_Test.ecm";
// //var pWingPrefab = await AddressableManager.Instance.LoadPrefabAsync(path1);
// var pWingPrefab = await AddressableManager.Instance.LoadPrefabAsync(pWingData.FileModel.ToLower().Replace('\\', '/'));
// //Transform parentWing = FindChildRecursive(_pPlayerModel.transform, "HH_chibang");
// if(pWingPrefab != null)
// {
// var pflySwordObject = Instantiate(pWingPrefab).transform;
// pflySwordObject.parent = m_pPlayerCECModel.m_skeletonBuilder.GetHook("HH_chibang").transform;
// pflySwordObject.localPosition = Vector3.zero;
// pflySwordObject.localRotation = Quaternion.identity;
// pflySwordObject.localScale = Vector3.one;
}
// m_Wing = pflySwordObject.transform;
// m_Wing.gameObject.SetActive(false);
// }
// BMLogger.Log($"ShowEquipments():: Wingman Wing Essence: {pWingData.id} {pWingData.Name} -- {pWingData.FileModel}");
// break;
default:
switch (equipment.pos)
{
case InventoryConst.EQUIPIVTR_BODY:
useDefaultUpper = false;
break;
case InventoryConst.EQUIPIVTR_LEG:
useDefaultLower = false;
break;
case InventoryConst.EQUIPIVTR_WRIST:
useDefaultWrist = false;
break;
case InventoryConst.EQUIPIVTR_FOOT:
useDefaultFoot = false;
break;
switch (equipment.pos)
{
case InventoryConst.EQUIPIVTR_BODY:
useDefaultUpper = false;
break;
case InventoryConst.EQUIPIVTR_LEG:
useDefaultLower = false;
break;
case InventoryConst.EQUIPIVTR_WRIST:
useDefaultWrist = false;
break;
case InventoryConst.EQUIPIVTR_FOOT:
useDefaultFoot = false;
break;
}
}
}
@@ -371,12 +403,14 @@ namespace BrewMonster.Scripts
{
return child.gameObject;
}
var childObject = FindChildObjectRecursive(child, name);
if (childObject != null)
{
return childObject;
}
}
return null;
}
@@ -386,12 +420,8 @@ namespace BrewMonster.Scripts
{
return null;
}
SkeletonBuilder skeletonBuilder = null;
if (skeletonBuilder == null)
{
skeletonBuilder = characterModel.GetComponentInChildren<SkeletonBuilder>();
}
return skeletonBuilder;
return characterModel.GetComponentInChildren<SkeletonBuilder>();
}
public void ReloadRoleModel(RoleInfo roleInfo)
@@ -399,8 +429,71 @@ namespace BrewMonster.Scripts
if (roleInfo == null)
return;
_lastPreviewRoleInfo = roleInfo;
_suppressHierarchyWatch = true;
ShowAllPlayerModels(new List<RoleInfo> { roleInfo });
ShowPlayerModel(roleInfo.roleid);
}
private Transform GetActivePreviewModelTransform()
{
if (!_hasRequestedPlayerModelId)
return null;
int n = Mathf.Min(playerModels.Count, playerModelIds.Count);
for (int i = 0; i < n; i++)
{
GameObject model = playerModels[i];
if (model != null && model.activeSelf && playerModelIds[i] == _requestedPlayerModelId)
return model.transform;
}
for (int i = 0; i < n; i++)
{
GameObject model = playerModels[i];
if (model != null && playerModelIds[i] == _requestedPlayerModelId)
return model.transform;
}
return null;
}
private void TryRefreshFromCachedRole()
{
if (_lastPreviewRoleInfo == null || !_inventoryPreviewActive || _suppressHierarchyWatch)
return;
ReloadRoleModel(_lastPreviewRoleInfo);
}
private void SyncHierarchyBaseline()
{
Transform activeRoot = GetActivePreviewModelTransform();
if (activeRoot == null)
return;
_lastOriginModelRoot = activeRoot;
_lastOriginModelChildCount = CountAllChildren(activeRoot);
}
private void ResetHierarchyWatch()
{
_lastOriginModelRoot = null;
_lastOriginModelChildCount = 0;
}
private static int CountAllChildren(Transform root)
{
if (root == null)
return 0;
int count = root.childCount;
for (int i = 0; i < root.childCount; i++)
{
count += CountAllChildren(root.GetChild(i));
}
return count;
}
}
}
@@ -1,687 +0,0 @@
using Animancer;
using BrewMonster;
using UnityEngine;
using UnityEngine.UI;
namespace BrewMonster.Scripts.UI.Inventory
{
public enum PreviewDistanceScaleMode
{
AspectRatio,
ViewportHeight
}
/// <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;
[Header("Camera Output")]
[SerializeField] private RawImage previewFrame;
[SerializeField] private RenderTexture previewRenderTexture;
[SerializeField] private bool autoFocusCamera = true;
[SerializeField] private Vector3 cameraFocusOffset = new Vector3(0f, 0f, 0f);
[Header("Camera Framing")]
[SerializeField] private float previewCameraDistance = 3.3f;
[SerializeField] private Vector3 previewCameraOffset = new Vector3(0.15f, 0.8f, 0f);
[SerializeField] private bool overrideFieldOfView = true;
[SerializeField][Range(10f, 60f)] private float previewFieldOfView = 33f;
[SerializeField] private bool autoFrameByBounds = true;
[SerializeField][Range(1f, 2f)] private float autoFramePadding = 1.15f;
[SerializeField] private float minAutoCameraDistance = 1.25f;
[SerializeField] private float maxAutoCameraDistance = 8f;
[Header("Device Framing")]
[Tooltip("Design-time screen width (1920x1080 baseline); previewCameraDistance/offset tuned for this.")]
[SerializeField] private float referenceScreenWidth = 1920f;
[Tooltip("Design-time screen height (1920x1080 baseline); previewCameraDistance/offset tuned for this.")]
[SerializeField] private float referenceScreenHeight = 1080f;
[Tooltip("Use previewFrame pixel size when available; otherwise Screen.width/height.")]
[SerializeField] private bool useRawImageViewport = true;
[SerializeField] private PreviewDistanceScaleMode distanceScaleMode = PreviewDistanceScaleMode.AspectRatio;
[Tooltip("Scale previewCameraOffset.y by viewport height vs 1080p reference (1.0 at 1920x1080).")]
[SerializeField] private bool scaleCameraOffsetYByViewport = true;
private bool _cameraStateCached;
private Vector3 _cachedCameraPosition;
private Quaternion _cachedCameraRotation;
private float _cachedCameraFov;
//[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 int _lastOriginModelChildCount;
private Transform _lastOriginModelRoot;
private Camera _previewCamera;
private PlayerModelPreview _playerModelPreview;
private RenderTexture _cachedTargetTexture;
private void Awake()
{
if (previewRoot == null)
{
previewRoot = transform;
}
TryBindPreviewSystem();
TryBindHostPlayer();
EnsureCameraBindings();
}
private void OnEnable()
{
TryBindPreviewSystem();
EnsureCameraBindings();
QueueRefresh();
}
private void OnDisable()
{
RestoreCameraTargetTexture();
RestoreSharedCameraState();
ReleasePreviewReference();
}
private void LateUpdate()
{
TryBindPreviewSystem();
TryBindHostPlayer();
// Check if origin model structure has changed (equipment models are child objects)
// This detects equipment changes in realtime since all equipment is attached as children
Transform currentOriginRoot = ResolveSourceModelRoot();
if (currentOriginRoot == null)
{
if (_previewInstance == null)
{
QueueRefresh();
}
if(_refreshQueued)
{
_refreshQueued = false;
BuildPreviewModel();
}
return;
}
int currentChildCount = CountAllChildren(currentOriginRoot);
if (_lastOriginModelRoot != currentOriginRoot || _lastOriginModelChildCount != currentChildCount)
{
_lastOriginModelRoot = currentOriginRoot;
_lastOriginModelChildCount = currentChildCount;
QueueRefresh();
}
if (_refreshQueued)
{
_refreshQueued = false;
BuildPreviewModel();
}
if(_previewInstance != null)
{
EnsureCameraFocus(_previewInstance.transform);
}
}
/// <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 TryBindPreviewSystem()
{
if (_playerModelPreview == null)
{
_playerModelPreview = PlayerModelPreview.Instance;
}
if (_playerModelPreview == null)
{
return;
}
if (_previewCamera == null)
{
_previewCamera = _playerModelPreview.GetComponent<Camera>();
}
}
private void TryBindHostPlayer()
{
if (!autoBindHostPlayer || hostPlayer != null)
{
return;
}
hostPlayer = FindFirstObjectByType<CECHostPlayer>();
if (hostPlayer != null)
{
QueueRefresh();
}
}
private void EnsureCameraBindings()
{
if (_previewCamera == null)
{
TryBindPreviewSystem();
}
if (_previewCamera == null || previewFrame == null)
{
return;
}
if (_cachedTargetTexture == null)
{
_cachedTargetTexture = _previewCamera.targetTexture;
}
if (previewRenderTexture != null)
{
_previewCamera.targetTexture = previewRenderTexture;
}
if(previewFrame != null)
previewFrame.texture = _previewCamera.targetTexture;
}
private void RestoreCameraTargetTexture()
{
if (_previewCamera == null)
{
return;
}
if (_cachedTargetTexture != null)
{
_previewCamera.targetTexture = _cachedTargetTexture;
}
}
private void BuildPreviewModel()
{
if (previewRoot == null)
{
return;
}
Transform sourceRoot = ResolveSourceModelRoot();
if (sourceRoot == null)
{
_previewInstance = null;
_lastOriginModelRoot = null;
_lastOriginModelChildCount = 0;
QueueRefresh();
return;
}
if (_previewInstance != null && _previewInstance != sourceRoot.gameObject)
{
DestroyPreviewInstance();
}
if (!sourceRoot.gameObject.activeSelf)
{
sourceRoot.gameObject.SetActive(true);
}
// Use resolved source root, not sourceModelRoot field directly.
_previewInstance = sourceRoot.gameObject;
Transform instanceTransform = _previewInstance.transform;
if (instanceTransform.parent != previewRoot)
{
instanceTransform.SetParent(previewRoot, false);
}
instanceTransform.localPosition = previewLocalPosition + Vector3.up;
instanceTransform.localRotation = Quaternion.Euler(previewLocalEuler);
instanceTransform.localScale = previewLocalScale;
ApplyLayerRecursive(instanceTransform, previewRoot.gameObject.layer);
EnsureCameraFocus(instanceTransform);
EnsureCameraBindings();
}
private void DestroyPreviewInstance()
{
if (_previewInstance != null)
{
Destroy(_previewInstance);
_previewInstance = null;
}
}
private Transform ResolveSourceModelRoot()
{
if (sourceModelRoot != null)
{
return sourceModelRoot;
}
if (_playerModelPreview == null)
{
return null;
}
// Prefer active model.
for (int i = 0; i < _playerModelPreview.playerModels.Count; i++)
{
GameObject model = _playerModelPreview.playerModels[i];
if (model != null && model.activeSelf)
{
return model.transform;
}
}
// Fallback to first available model.
for (int i = 0; i < _playerModelPreview.playerModels.Count; i++)
{
GameObject model = _playerModelPreview.playerModels[i];
if (model != null)
{
return model.transform;
}
}
return null;
}
private void ReleasePreviewReference()
{
_previewInstance = null;
}
//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 (_previewCamera == null)
{
return;
}
if (!_cameraStateCached)
{
_cachedCameraPosition = _previewCamera.transform.position;
_cachedCameraRotation = _previewCamera.transform.rotation;
_cachedCameraFov = _previewCamera.fieldOfView;
_cameraStateCached = true;
}
// Apply FOV override independently from auto focus.
if (overrideFieldOfView)
{
_previewCamera.fieldOfView = previewFieldOfView;
}
if (!autoFocusCamera || modelRoot == null)
{
return;
}
Vector3 effectiveOffset = ComputeEffectiveCameraOffset();
Vector3 focusPoint = modelRoot.TransformPoint(cameraFocusOffset) + effectiveOffset;
Vector3 viewDirection = modelRoot.forward;
viewDirection.y = 0f;
if (viewDirection.sqrMagnitude < 0.0001f)
{
viewDirection = Vector3.back;
}
viewDirection.Normalize();
float effectiveDistance = ComputeEffectiveCameraDistance(modelRoot, focusPoint, viewDirection);
_previewCamera.transform.position = focusPoint + viewDirection * effectiveDistance;
_previewCamera.transform.LookAt(focusPoint);
}
private bool TryGetViewportSize(out float width, out float height)
{
if (useRawImageViewport && previewFrame != null)
{
RectTransform rt = previewFrame.rectTransform;
Canvas canvas = previewFrame.canvas;
float scale = canvas != null ? canvas.scaleFactor : 1f;
width = rt.rect.width * scale;
height = rt.rect.height * scale;
if (width > 1f && height > 1f)
{
return true;
}
}
width = Screen.width;
height = Screen.height;
return width > 0f && height > 0f;
}
private float ComputeAspectScaledDistance()
{
float distance = previewCameraDistance * ComputeViewportFramingScale();
return Mathf.Clamp(distance, minAutoCameraDistance, maxAutoCameraDistance);
}
private float ComputeViewportFramingScale()
{
if (!TryGetViewportSize(out float viewportWidth, out float viewportHeight))
{
return 1f;
}
float refWidth = Mathf.Max(1f, referenceScreenWidth);
float refHeight = Mathf.Max(1f, referenceScreenHeight);
switch (distanceScaleMode)
{
case PreviewDistanceScaleMode.ViewportHeight:
return viewportHeight / refHeight;
default:
float refAspect = refWidth / refHeight;
float viewAspect = viewportWidth / viewportHeight;
return viewAspect / refAspect;
}
}
private float ComputeViewportHeightScale()
{
if (!TryGetViewportSize(out _, out float viewportHeight))
{
return 1f;
}
float refHeight = Mathf.Max(1f, referenceScreenHeight);
return viewportHeight / refHeight;
}
private Vector3 ComputeEffectiveCameraOffset()
{
if (!scaleCameraOffsetYByViewport)
{
return previewCameraOffset;
}
// Tuned at 1920x1080: scale is 1.0 so previewCameraOffset.y is unchanged on reference resolution.
float offsetYScale = ComputeViewportHeightScale();
return new Vector3(
previewCameraOffset.x,
previewCameraOffset.y * offsetYScale,
previewCameraOffset.z);
}
private float ComputeBoundsFitDistance(Transform modelRoot, Vector3 focusPoint, Vector3 viewDirection)
{
if (modelRoot == null)
{
return previewCameraDistance;
}
var renderers = modelRoot.GetComponentsInChildren<Renderer>(true);
if (renderers == null || renderers.Length == 0)
{
return previewCameraDistance;
}
Bounds bounds = renderers[0].bounds;
for (int i = 1; i < renderers.Length; i++)
{
if (renderers[i] != null)
{
bounds.Encapsulate(renderers[i].bounds);
}
}
if (bounds.size.sqrMagnitude < 0.0001f)
{
return previewCameraDistance;
}
if (!TryGetViewportSize(out float viewportWidth, out float viewportHeight))
{
return previewCameraDistance;
}
Vector3 cameraForward = -viewDirection;
Vector3 cameraRight = Vector3.Cross(cameraForward, Vector3.up);
if (cameraRight.sqrMagnitude < 0.0001f)
{
cameraRight = Vector3.Cross(cameraForward, Vector3.forward);
}
cameraRight.Normalize();
Vector3 cameraUp = Vector3.Cross(cameraRight, cameraForward).normalized;
Vector3 boundsMin = bounds.min;
Vector3 boundsMax = bounds.max;
float maxHorizontal = 0f;
float maxVertical = 0f;
for (int x = 0; x <= 1; x++)
{
for (int y = 0; y <= 1; y++)
{
for (int z = 0; z <= 1; z++)
{
Vector3 corner = new Vector3(
x == 0 ? boundsMin.x : boundsMax.x,
y == 0 ? boundsMin.y : boundsMax.y,
z == 0 ? boundsMin.z : boundsMax.z);
Vector3 offset = corner - focusPoint;
maxHorizontal = Mathf.Max(maxHorizontal, Mathf.Abs(Vector3.Dot(offset, cameraRight)));
maxVertical = Mathf.Max(maxVertical, Mathf.Abs(Vector3.Dot(offset, cameraUp)));
}
}
}
float viewAspect = viewportWidth / viewportHeight;
float verticalFovRad = previewFieldOfView * Mathf.Deg2Rad;
float halfVerticalTan = Mathf.Tan(verticalFovRad * 0.5f);
if (halfVerticalTan < 0.0001f)
{
return previewCameraDistance;
}
float halfHorizontalTan = halfVerticalTan * viewAspect;
float distanceForHeight = maxVertical / halfVerticalTan;
float distanceForWidth = maxHorizontal / Mathf.Max(halfHorizontalTan, 0.0001f);
float boundsDistance = Mathf.Max(distanceForHeight, distanceForWidth);
return boundsDistance * autoFramePadding;
}
private float ComputeEffectiveCameraDistance(Transform modelRoot, Vector3 focusPoint, Vector3 viewDirection)
{
float aspectScaledDistance = ComputeAspectScaledDistance();
float effectiveDistance = aspectScaledDistance;
if (autoFrameByBounds)
{
float boundsDistance = ComputeBoundsFitDistance(modelRoot, focusPoint, viewDirection);
effectiveDistance = Mathf.Max(aspectScaledDistance, boundsDistance);
}
return Mathf.Clamp(effectiveDistance, minAutoCameraDistance, maxAutoCameraDistance);
}
//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 RestoreSharedCameraState()
{
if (!_cameraStateCached || _previewCamera == null)
{
return;
}
_previewCamera.transform.position = _cachedCameraPosition;
_previewCamera.transform.rotation = _cachedCameraRotation;
_previewCamera.fieldOfView = _cachedCameraFov;
_cameraStateCached = false;
}
//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;
// }
//}
/// <summary>
/// Counts all children recursively in the transform hierarchy.
/// Used to detect when equipment (child objects) are added or removed from the origin model.
/// </summary>
private int CountAllChildren(Transform root)
{
if (root == null)
return 0;
int count = root.childCount;
for (int i = 0; i < root.childCount; i++)
{
count += CountAllChildren(root.GetChild(i));
}
return count;
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 693e85b6242cc8f4ea4c161aa42dfb1f
+94 -43
View File
@@ -6208,6 +6208,78 @@ MonoBehaviour:
m_FillOrigin: 2
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &2704868524884608902
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1881134003886110467}
- component: {fileID: 402641304906908385}
- component: {fileID: 7868692572267099838}
m_Layer: 5
m_Name: RawImage
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1881134003886110467
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2704868524884608902}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.65, y: 0.65, z: 0.65}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 6778274724352405780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 1024, y: 1024}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &402641304906908385
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2704868524884608902}
m_CullTransparentMesh: 1
--- !u!114 &7868692572267099838
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2704868524884608902}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Texture: {fileID: 8400000, guid: 7b35d3c1ebbb32d47a5ed61606928730, type: 2}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
--- !u!1 &2741252180928514397
GameObject:
m_ObjectHideFlags: 0
@@ -11013,9 +11085,9 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 6778274724352405780}
- component: {fileID: 5765354275366674162}
- component: {fileID: 6707240765686952970}
- component: {fileID: 2036361547023377128}
- component: {fileID: 1379908071248080112}
- component: {fileID: 1721909722328673308}
- component: {fileID: 3977902385666770241}
m_Layer: 5
m_Name: character_preview
m_TagString: Untagged
@@ -11034,7 +11106,8 @@ RectTransform:
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Children:
- {fileID: 1881134003886110467}
m_Father: {fileID: 3289674559629147232}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -11042,7 +11115,7 @@ RectTransform:
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 304.4946, y: 608.9891}
m_Pivot: {x: 1, y: 1}
--- !u!222 &5765354275366674162
--- !u!222 &1379908071248080112
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@@ -11050,7 +11123,7 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5468137454250289500}
m_CullTransparentMesh: 1
--- !u!114 &6707240765686952970
--- !u!114 &1721909722328673308
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@@ -11059,25 +11132,28 @@ MonoBehaviour:
m_GameObject: {fileID: 5468137454250289500}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastTarget: 0
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Texture: {fileID: 8400000, guid: 42c3c43cd0c3b704cb6cf0dd1051d9ff, type: 2}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
--- !u!114 &2036361547023377128
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &3977902385666770241
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@@ -11086,35 +11162,10 @@ MonoBehaviour:
m_GameObject: {fileID: 5468137454250289500}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 693e85b6242cc8f4ea4c161aa42dfb1f, type: 3}
m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
m_Name:
m_EditorClassIdentifier:
hostPlayer: {fileID: 0}
autoBindHostPlayer: 1
sourceModelRoot: {fileID: 0}
cloneWholeHostHierarchy: 1
stripRuntimeComponents: 1
previewRoot: {fileID: 6778274724352405780}
previewLocalPosition: {x: 0, y: 0, z: 100}
previewLocalEuler: {x: 0, y: 0, z: 0}
previewLocalScale: {x: 1, y: 1, z: 1}
previewFrame: {fileID: 6707240765686952970}
previewRenderTexture: {fileID: 8400000, guid: 42c3c43cd0c3b704cb6cf0dd1051d9ff, type: 2}
autoFocusCamera: 1
cameraFocusOffset: {x: 0, y: 0, z: 0}
previewCameraDistance: 3.3
previewCameraOffset: {x: 0.06, y: 2.04, z: 0}
overrideFieldOfView: 1
previewFieldOfView: 44
autoFrameByBounds: 1
autoFramePadding: 1.15
minAutoCameraDistance: 1.25
maxAutoCameraDistance: 8
referenceScreenWidth: 1920
referenceScreenHeight: 1080
useRawImageViewport: 1
distanceScaleMode: 0
scaleCameraOffsetYByViewport: 1
m_ShowMaskGraphic: 0
--- !u!1 &5473020210238587200
GameObject:
m_ObjectHideFlags: 0