Merge pull request 'feature/chat_01' (#349) from feature/chat_01 into develop
Reviewed-on: https://git.pthub.vn/Unity/perfect-world-unity/pulls/349
This commit is contained in:
@@ -1019,6 +1019,10 @@ public class CECNPC : CECObject
|
||||
var npcVisual = GetComponent<NPCVisual>();
|
||||
npcVisual?.InitNPCEventDoneHandler(m_NPCInfo);
|
||||
|
||||
// UINPC.Start can run before async model instantiate; refresh anchor once SMR hierarchy exists.
|
||||
// UINPC.Start可能在异步模型实例化之前执行;SMR层次就绪后刷新名牌锚点。
|
||||
m_npcUI?.RefreshWorldNameplatePosition();
|
||||
|
||||
//QueueECModelForLoad(MTL_ECM_NPC, GetNPCInfo().nid, GetBornStamp(), GetServerPos(), szModelFile, tid);
|
||||
}
|
||||
public ROLEBASICPROP GetBasicProps() { return m_BasicProps; }
|
||||
|
||||
@@ -211,4 +211,5 @@ public class NPCVisual : MonoBehaviour
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,11 +61,19 @@ namespace BrewMonster
|
||||
/// </summary>
|
||||
public bool TryGetWorldPosition(out Vector3 worldPos)
|
||||
{
|
||||
return TryGetWorldPosition(out worldPos, out _, logNpcRootFallback: true);
|
||||
}
|
||||
|
||||
/// <param name="npcUsedSkinnedMeshMergedBounds">True when NPC anchor came from merged SMR bounds (not root fallback). NPC以外恒为false。</param>
|
||||
/// <param name="logNpcRootFallback">When false, NPC root fallback does not LogError (for retries before model load). 为假时NPC根回退不打LogError(模型未加载前的重试)。</param>
|
||||
public bool TryGetWorldPosition(out Vector3 worldPos, out bool npcUsedSkinnedMeshMergedBounds, bool logNpcRootFallback = true)
|
||||
{
|
||||
npcUsedSkinnedMeshMergedBounds = false;
|
||||
CacheRefs();
|
||||
if (hostPlayer != null)
|
||||
return TryGetForPlayer(out worldPos);
|
||||
if (hostNpc != null)
|
||||
return TryGetForNpc(out worldPos);
|
||||
return TryGetForNpc(out worldPos, out npcUsedSkinnedMeshMergedBounds, logNpcRootFallback);
|
||||
worldPos = default;
|
||||
return false;
|
||||
}
|
||||
@@ -104,9 +112,10 @@ namespace BrewMonster
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryGetForNpc(out Vector3 worldPos)
|
||||
private bool TryGetForNpc(out Vector3 worldPos, out bool usedSkinnedMeshMergedBounds, bool logNpcRootFallback)
|
||||
{
|
||||
worldPos = default;
|
||||
usedSkinnedMeshMergedBounds = false;
|
||||
if (hostNpc == null)
|
||||
return false;
|
||||
|
||||
@@ -114,11 +123,25 @@ namespace BrewMonster
|
||||
&& npcVisual != null
|
||||
&& npcVisual.TryGetNamePlateAnchorWorld(out worldPos))
|
||||
{
|
||||
usedSkinnedMeshMergedBounds = true;
|
||||
worldPos.y += extraWorldYOffset;
|
||||
return true;
|
||||
}
|
||||
|
||||
worldPos = hostNpc.transform.position + Vector3.up * (fallbackHeightAboveRoot + extraWorldYOffset);
|
||||
if (logNpcRootFallback)
|
||||
{
|
||||
string reason = !preferVisualBoundsFallback
|
||||
? "preferVisualBoundsFallback is false (bounds path skipped)"
|
||||
: npcVisual == null
|
||||
? "npcVisual is null"
|
||||
: "TryGetNamePlateAnchorWorld failed (no usable SkinnedMeshRenderer/sharedMesh)";
|
||||
BMLogger.LogError(
|
||||
"[Cuong] [NameplateWorldAnchor] NPC: cannot use merged SMR bounds; using root fallback. " +
|
||||
$"Reason: {reason}. hostNpc={hostNpc.name} fallbackHeightAboveRoot={fallbackHeightAboveRoot} extraWorldYOffset={extraWorldYOffset}. " +
|
||||
"Formula: worldPos = hostNpc.transform.position + Vector3.up * (fallbackHeightAboveRoot + extraWorldYOffset). " +
|
||||
"When bounds OK: worldPos.x/z = combinedBounds.center.x/z, worldPos.y = combinedBounds.max.y + extraWorldYOffset.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
@@ -41,6 +42,7 @@ namespace BrewMonster
|
||||
|
||||
private NameplateWorldAnchor _nameplateAnchor;
|
||||
private Image _cachedIconImage;
|
||||
private Coroutine _initialNameplateRoutine;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -58,23 +60,84 @@ namespace BrewMonster
|
||||
|
||||
private void Start()
|
||||
{
|
||||
ApplyInitialCanvasRootPosition();
|
||||
_initialNameplateRoutine = StartCoroutine(CoApplyInitialNameplateDeferred(maxFrames: 120));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// World position for the nameplate root is applied once at startup (no per-frame follow).
|
||||
/// 名牌根节点世界坐标仅在启动时设置一次(不做每帧跟随)。
|
||||
/// Call after NPC model async load (e.g. <see cref="CECNPC.QueueLoadNPCModel"/>) so SMR bounds exist before anchoring.
|
||||
/// 在NPC模型异步加载完成后调用(如 <see cref="CECNPC.QueueLoadNPCModel"/>),以便SMR包围盒已存在再定位名牌。
|
||||
/// </summary>
|
||||
private void ApplyInitialCanvasRootPosition()
|
||||
public void RefreshWorldNameplatePosition()
|
||||
{
|
||||
if (_initialNameplateRoutine != null)
|
||||
{
|
||||
StopCoroutine(_initialNameplateRoutine);
|
||||
_initialNameplateRoutine = null;
|
||||
}
|
||||
_initialNameplateRoutine = StartCoroutine(CoApplyInitialNameplateDeferred(maxFrames: 45));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// World position for the nameplate root is applied once SMR bounds exist, or after timeout with root fallback (then may log).
|
||||
/// <see cref="NameplateWorldAnchor.TryGetWorldPosition"/> with <c>logNpcRootFallback: false</c> while retrying so early frames do not spam errors.
|
||||
/// 名牌根节点世界坐标:优先等合并SMR包围盒就绪后再设一次;超时则用根回退(此时才打LogError)。
|
||||
/// </summary>
|
||||
private IEnumerator CoApplyInitialNameplateDeferred(int maxFrames)
|
||||
{
|
||||
if (_canvasRoot == null)
|
||||
{
|
||||
return;
|
||||
CacheRefs();
|
||||
}
|
||||
if (_nameplateAnchor != null && _nameplateAnchor.TryGetWorldPosition(out var worldPos))
|
||||
if (_canvasRoot == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
if (_nameplateAnchor == null)
|
||||
{
|
||||
BMLogger.LogError(
|
||||
"[Cuong] [UINPC] ApplyInitialCanvasRootPosition: NameplateWorldAnchor missing. " +
|
||||
"Expected formula: _canvasRoot.position = anchor.TryGetWorldPosition(out worldPos) ? worldPos : unchanged.");
|
||||
_initialNameplateRoutine = null;
|
||||
yield break;
|
||||
}
|
||||
|
||||
for (var i = 0; i < maxFrames; i++)
|
||||
{
|
||||
if (_nameplateAnchor.TryGetWorldPosition(out var worldPos, out var fromSmr, logNpcRootFallback: false)
|
||||
&& fromSmr)
|
||||
{
|
||||
ApplyCanvasRootLocalPosition(worldPos);
|
||||
_initialNameplateRoutine = null;
|
||||
yield break;
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (_nameplateAnchor.TryGetWorldPosition(out var finalPos, out _, logNpcRootFallback: true))
|
||||
{
|
||||
ApplyCanvasRootLocalPosition(finalPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogError(
|
||||
"[Cuong] [UINPC] ApplyInitialCanvasRootPosition: TryGetWorldPosition returned false (no CECPlayer/CECNPC parent?). " +
|
||||
"Formula when OK: _canvasRoot.position = worldPos. " +
|
||||
"NPC with SMR bounds: worldPos = (combinedBounds.center.x, combinedBounds.max.y, combinedBounds.center.z) + Vector3.up * extraWorldYOffset. " +
|
||||
"NPC fallback: worldPos = hostNpc.transform.position + Vector3.up * (fallbackHeightAboveRoot + extraWorldYOffset).");
|
||||
}
|
||||
_initialNameplateRoutine = null;
|
||||
}
|
||||
|
||||
private void ApplyCanvasRootLocalPosition(Vector3 worldPos)
|
||||
{
|
||||
var parent = _canvasRoot.parent;
|
||||
if (parent == null)
|
||||
{
|
||||
_canvasRoot.position = worldPos;
|
||||
return;
|
||||
}
|
||||
|
||||
_canvasRoot.localPosition = parent.InverseTransformPoint(worldPos);
|
||||
}
|
||||
|
||||
public void SetName(string name)
|
||||
|
||||
@@ -502,7 +502,7 @@ MonoBehaviour:
|
||||
_healthImage: {fileID: 3715353156977051930}
|
||||
_iconTaskMain: {fileID: 0}
|
||||
_listIconTask: []
|
||||
_canvasRoot: {fileID: 0}
|
||||
_canvasRoot: {fileID: 7528353842011023148}
|
||||
--- !u!114 &9001000111222333445
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -147,7 +147,7 @@ RectTransform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3297168817873124018}
|
||||
m_LocalRotation: {x: -3.7702125e-16, y: 0.97228837, z: 0.23378475, w: 1.5679955e-15}
|
||||
m_LocalRotation: {x: 0.0260765, y: 0.57263446, z: -0.018228054, w: 0.8191932}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
@@ -272,7 +272,7 @@ RectTransform:
|
||||
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: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0.5}
|
||||
m_SizeDelta: {x: 0.5, y: 0.5}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &397635185778217656
|
||||
@@ -378,7 +378,7 @@ MonoBehaviour:
|
||||
- {fileID: 536497386, guid: 75fb3bbb7c9421e4d8bf0728cf2b59b1, type: 3}
|
||||
- {fileID: -440769636, guid: 75fb3bbb7c9421e4d8bf0728cf2b59b1, type: 3}
|
||||
- {fileID: 844877792, guid: 75fb3bbb7c9421e4d8bf0728cf2b59b1, type: 3}
|
||||
_canvasRoot: {fileID: 8006159455096186264}
|
||||
_canvasRoot: {fileID: 8745273338113588215}
|
||||
--- !u!114 &9001000111222333446
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
Reference in New Issue
Block a user