Merge pull request 'feature/CheckSkill_Cuong' (#442) from feature/CheckSkill_Cuong into develop

Reviewed-on: https://git.pthub.vn/Unity/perfect-world-unity/pulls/442
This commit is contained in:
cuongnv
2026-05-18 07:26:39 +00:00
2 changed files with 223 additions and 27 deletions
@@ -5,6 +5,12 @@ 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>
@@ -45,6 +51,17 @@ namespace BrewMonster.Scripts.UI.Inventory
[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;
@@ -396,7 +413,8 @@ namespace BrewMonster.Scripts.UI.Inventory
return;
}
Vector3 focusPoint = modelRoot.TransformPoint(cameraFocusOffset) + previewCameraOffset;
Vector3 effectiveOffset = ComputeEffectiveCameraOffset();
Vector3 focusPoint = modelRoot.TransformPoint(cameraFocusOffset) + effectiveOffset;
Vector3 viewDirection = modelRoot.forward;
viewDirection.y = 0f;
@@ -407,10 +425,178 @@ namespace BrewMonster.Scripts.UI.Inventory
viewDirection.Normalize();
_previewCamera.transform.position = focusPoint + viewDirection * previewCameraDistance;
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)
+35 -25
View File
@@ -500,10 +500,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 588.35, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &9112205963990496432
CanvasRenderer:
@@ -907,10 +907,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 479.18, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &3997873020355048166
CanvasRenderer:
@@ -1238,10 +1238,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 370.01, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4479228720555407959
CanvasRenderer:
@@ -4929,10 +4929,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 42.5, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &7052320096474271168
CanvasRenderer:
@@ -11103,9 +11103,18 @@ MonoBehaviour:
autoFocusCamera: 1
cameraFocusOffset: {x: 0, y: 0, z: 0}
previewCameraDistance: 3.3
previewCameraOffset: {x: 0.06, y: 1.07, z: 0}
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
--- !u!1 &5473020210238587200
GameObject:
m_ObjectHideFlags: 0
@@ -12366,6 +12375,7 @@ MonoBehaviour:
m_EditorClassIdentifier:
skillNameText: {fileID: 0}
imageProgress: {fileID: 0}
btnCancel: {fileID: 0}
inventoryPackButtons:
- {fileID: 8631960679953241981}
- {fileID: 7087549554151292240}
@@ -14070,10 +14080,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 151.67, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4452551064742165538
CanvasRenderer:
@@ -19517,10 +19527,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 260.84, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5155025384962724770
CanvasRenderer: