修改锚点窗口对entry类的依赖,现在可以自行通过泛型扩展。
This commit is contained in:
@@ -27,6 +27,13 @@ namespace XericUI.VisualForm
|
||||
[SerializeField, Tooltip("预分配的中间类对象池容量")]
|
||||
private int m_StatePoolPreallocate = 16;
|
||||
|
||||
[Header("安全区")]
|
||||
[SerializeField, Tooltip("安全区矩形(Canvas本地空间),窗口超出此区域视为超出安全区")]
|
||||
private Rect m_SafeZone = new Rect(-800f, -450f, 1600f, 900f);
|
||||
|
||||
[SerializeField, Tooltip("安全区检测模式")]
|
||||
private SafeZoneMode m_SafeZoneMode = SafeZoneMode.Rect;
|
||||
|
||||
#region 静态单例
|
||||
|
||||
private static AnchorDock s_Main;
|
||||
@@ -243,11 +250,12 @@ namespace XericUI.VisualForm
|
||||
state.CheckPositionDirty(m_PositionThreshold);
|
||||
}
|
||||
|
||||
// 4. 遍历所有中间类,根据脏标记更新坐标
|
||||
// 4. 遍历所有中间类,根据脏标记更新坐标(跳过无启用条目的状态)
|
||||
foreach (var kvp in _activeStates)
|
||||
{
|
||||
var state = kvp.Value;
|
||||
if (state == null || !state.IsEnabled) continue;
|
||||
if (!state.HasAnyEnabled) continue;
|
||||
|
||||
bool needUpdate = _cameraDirty || state.CoordinateDirty;
|
||||
if (needUpdate)
|
||||
@@ -436,7 +444,8 @@ namespace XericUI.VisualForm
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新指定中间类中所有窗口条目的屏幕坐标(基础位置 + 条目偏移量)
|
||||
/// 更新指定中间类中所有窗口条目的屏幕坐标。
|
||||
/// 流程:计算基础坐标 → 检测溢出 → 钳制需要安全区约束的条目 → 逐个设置位置。
|
||||
/// </summary>
|
||||
private void UpdateStatePosition(WindowState state)
|
||||
{
|
||||
@@ -448,14 +457,44 @@ namespace XericUI.VisualForm
|
||||
if (!TryWorldToScreenPoint(worldPos, _worldCamera, _uiCamera, _canvas, RectTransform, out Vector2 basePoint))
|
||||
return;
|
||||
|
||||
// 为每个窗口条目设置位置:基础坐标 + 条目自身偏移量
|
||||
foreach (var entry in state.Entries)
|
||||
Rect canvasLocalRect = ((RectTransform)_canvas.transform).rect;
|
||||
bool hasClamp = state.HasAnyClamp;
|
||||
var anchorWindow = state.Anchor as IAnchorWindowEntry;
|
||||
|
||||
// 计算基础坐标的溢出状态
|
||||
OverflowState baseOverflow = CheckOverflow(basePoint, m_SafeZone, m_SafeZoneMode, canvasLocalRect);
|
||||
state.Overflow = baseOverflow;
|
||||
|
||||
// 为基础坐标计算钳制位置(仅在需要钳制的条目上使用)
|
||||
Vector2 clampedBase = hasClamp ? ClampToSafeZone(basePoint, m_SafeZone, m_SafeZoneMode) : basePoint;
|
||||
|
||||
for (int i = 0; i < state.Entries.Count; i++)
|
||||
{
|
||||
var entry = state.Entries[i];
|
||||
if (entry.Instance == null) continue;
|
||||
if (!entry.Enabled) continue;
|
||||
|
||||
Vector2 rawPoint = basePoint + entry.ScreenOffset;
|
||||
Vector2 finalPoint;
|
||||
|
||||
if (entry.ClampToSafeZone && baseOverflow != OverflowState.InRange)
|
||||
{
|
||||
finalPoint = clampedBase + entry.ScreenOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
finalPoint = rawPoint;
|
||||
}
|
||||
|
||||
// 检测逐条目溢出状态变化,触发生命周期事件
|
||||
OverflowState entryOverflow = CheckOverflow(finalPoint, m_SafeZone, m_SafeZoneMode, canvasLocalRect);
|
||||
if (entryOverflow != entry.CurrentOverflow)
|
||||
{
|
||||
entry.CurrentOverflow = entryOverflow;
|
||||
anchorWindow?.OnWindowLevelChange(i, entryOverflow);
|
||||
}
|
||||
|
||||
Vector2 finalPoint = basePoint + entry.ScreenOffset;
|
||||
var target = entry.Instance.transform;
|
||||
|
||||
if (target is RectTransform rectTarget)
|
||||
rectTarget.anchoredPosition = finalPoint;
|
||||
else
|
||||
@@ -607,6 +646,95 @@ namespace XericUI.VisualForm
|
||||
return Camera.main;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查本地坐标是否超出安全区。
|
||||
/// 矩形模式:检查点是否在Rect范围内。
|
||||
/// 椭圆模式:检查点到中心的归一化距离是否超过各轴半径。
|
||||
/// </summary>
|
||||
/// <param name="localPoint">Canvas本地坐标</param>
|
||||
/// <param name="safeZone">安全区矩形</param>
|
||||
/// <param name="mode">检测模式</param>
|
||||
/// <param name="canvasLocalRect">Canvas本地空间矩形(用于判断是否超出屏幕)</param>
|
||||
/// <returns>溢出状态(Flags可叠加)</returns>
|
||||
public static OverflowState CheckOverflow(Vector2 localPoint, Rect safeZone, SafeZoneMode mode, Rect canvasLocalRect)
|
||||
{
|
||||
OverflowState result = OverflowState.InRange;
|
||||
|
||||
// 检查是否超出安全区
|
||||
bool outOfSafeZone;
|
||||
if (mode == SafeZoneMode.Ellipse)
|
||||
{
|
||||
// 椭圆:(px/rx)² + (py/ry)² > 1
|
||||
Vector2 center = safeZone.center;
|
||||
float rx = safeZone.width * 0.5f;
|
||||
float ry = safeZone.height * 0.5f;
|
||||
if (rx <= 0f || ry <= 0f)
|
||||
{
|
||||
outOfSafeZone = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
float nx = (localPoint.x - center.x) / rx;
|
||||
float ny = (localPoint.y - center.y) / ry;
|
||||
outOfSafeZone = (nx * nx + ny * ny) > 1f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outOfSafeZone = !safeZone.Contains(localPoint);
|
||||
}
|
||||
|
||||
if (outOfSafeZone)
|
||||
result |= OverflowState.OutOfSafeZone;
|
||||
|
||||
// 检查是否超出屏幕
|
||||
if (!canvasLocalRect.Contains(localPoint))
|
||||
result |= OverflowState.OutOfScreen;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将本地坐标钳制到安全区边界内。
|
||||
/// 矩形模式:clamp x到[minX, maxX],y到[minY, maxY]。
|
||||
/// 椭圆模式:沿中心方向投影到椭圆边界上。
|
||||
/// </summary>
|
||||
/// <param name="localPoint">Canvas本地坐标</param>
|
||||
/// <param name="safeZone">安全区矩形</param>
|
||||
/// <param name="mode">检测模式</param>
|
||||
/// <returns>钳制后的坐标</returns>
|
||||
public static Vector2 ClampToSafeZone(Vector2 localPoint, Rect safeZone, SafeZoneMode mode)
|
||||
{
|
||||
if (mode == SafeZoneMode.Ellipse)
|
||||
{
|
||||
Vector2 center = safeZone.center;
|
||||
float rx = safeZone.width * 0.5f;
|
||||
float ry = safeZone.height * 0.5f;
|
||||
|
||||
if (rx <= 0f || ry <= 0f)
|
||||
return center;
|
||||
|
||||
Vector2 delta = localPoint - center;
|
||||
float nx = delta.x / rx;
|
||||
float ny = delta.y / ry;
|
||||
float dist = Mathf.Sqrt(nx * nx + ny * ny);
|
||||
|
||||
if (dist <= 1f)
|
||||
return localPoint; // 已在椭圆内
|
||||
|
||||
// 投影到椭圆边界
|
||||
return center + delta / dist;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 矩形钳制
|
||||
return new Vector2(
|
||||
Mathf.Clamp(localPoint.x, safeZone.xMin, safeZone.xMax),
|
||||
Mathf.Clamp(localPoint.y, safeZone.yMin, safeZone.yMax)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericUI.VisualForm
|
||||
{
|
||||
/// <summary>
|
||||
/// 锚点窗口
|
||||
/// </summary>
|
||||
[AddComponentMenu("Xeric UI Vessel/AnchorWindow/AnchorWindow")]
|
||||
public class AnchorWindowByDefaultEntry : AnchorWindow<WindowEntry> { }
|
||||
|
||||
/// <summary>
|
||||
/// 锚点窗口 - WorldAnchor的子类,持有一组UI窗口条目的引用。
|
||||
/// 每个条目包含窗口对象、屏幕偏移量和激活状态,支持一个锚点同时生成多个定位窗口。
|
||||
/// 窗口对象可以是实际的场景对象,也可以是预制体。
|
||||
/// </summary>
|
||||
[AddComponentMenu("Xeric UI Vessel/AnchorWindow/AnchorWindow")]
|
||||
public class AnchorWindow : WorldAnchor
|
||||
public abstract class AnchorWindow<T> : WorldAnchor, IAnchorWindowEntry<T>
|
||||
where T : WindowEntry
|
||||
{
|
||||
[Header("窗口条目")]
|
||||
[SerializeField, Tooltip("窗口对象集合,每个条目可独立配置偏移和启用状态")]
|
||||
private List<WindowEntry> m_WindowEntries = new List<WindowEntry>();
|
||||
private List<T> m_WindowEntries = new List<T>();
|
||||
|
||||
/// <summary>
|
||||
/// 窗口条目列表
|
||||
/// </summary>
|
||||
public List<WindowEntry> WindowEntries => m_WindowEntries;
|
||||
public IList<T> WindowEntries => m_WindowEntries;
|
||||
|
||||
public IEnumerable<WindowEntry> DefaultTypeWindowEntries => m_WindowEntries;
|
||||
|
||||
public int DefaultTypeWindowEntriesCount => m_WindowEntries.Count;
|
||||
|
||||
/// <summary>
|
||||
/// 当前生效的窗口条目数(过滤掉WindowObject为null的条目)
|
||||
@@ -36,8 +45,9 @@ namespace XericUI.VisualForm
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取第一个有效窗口对象的Transform(兼容旧API)
|
||||
/// 获取第一个有效窗口对象的Transform
|
||||
/// </summary>
|
||||
[Obsolete("旧api")]
|
||||
public Transform GetWindowTransform()
|
||||
{
|
||||
foreach (var entry in m_WindowEntries)
|
||||
@@ -50,6 +60,50 @@ namespace XericUI.VisualForm
|
||||
|
||||
#region 工具方法
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定索引条目的启用状态。写入后自动检查所有条目,
|
||||
/// 若全部禁用则同步标记到 WindowState(坞将跳过该窗口的位置更新)。
|
||||
/// </summary>
|
||||
/// <param name="index">条目索引</param>
|
||||
/// <param name="enabled">是否启用</param>
|
||||
public void SetEntryEnabled(int index, bool enabled)
|
||||
{
|
||||
if (index < 0 || index >= m_WindowEntries.Count) return;
|
||||
|
||||
var entry = m_WindowEntries[index];
|
||||
if (entry == null) return;
|
||||
|
||||
entry.SetEnabledInternal(enabled);
|
||||
|
||||
// 同步到WindowState
|
||||
GetWindowState()?.RefreshFlags();
|
||||
}
|
||||
|
||||
public void SetAllEntryEnabled(bool enabled)
|
||||
{
|
||||
for (int i = 0; i < m_WindowEntries.Count; i++)
|
||||
m_WindowEntries[i].SetEnabledInternal(enabled);
|
||||
GetWindowState()?.RefreshFlags();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定索引条目的钳制到安全区标记。写入后自动同步 WindowState 的 HasAnyClamp。
|
||||
/// </summary>
|
||||
/// <param name="index">条目索引</param>
|
||||
/// <param name="clamp">是否钳制</param>
|
||||
public void SetEntryClampToSafeZone(int index, bool clamp)
|
||||
{
|
||||
if (index < 0 || index >= m_WindowEntries.Count) return;
|
||||
|
||||
var entry = m_WindowEntries[index];
|
||||
if (entry == null) return;
|
||||
|
||||
entry.SetClampToSafeZoneInternal(clamp);
|
||||
|
||||
// 同步到WindowState
|
||||
GetWindowState()?.RefreshFlags();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从所属锚点坞获取自己的中间窗口状态类
|
||||
/// </summary>
|
||||
@@ -118,5 +172,17 @@ namespace XericUI.VisualForm
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期事件
|
||||
|
||||
/// <summary>
|
||||
/// 窗口层级变更事件 — 当某个条目的溢出状态发生变化时触发。
|
||||
/// 继承类可重写此方法,根据溢出状态切换不同窗口显示(如溢出后切换为指向示意器)。
|
||||
/// </summary>
|
||||
/// <param name="entryIndex">发生变化的条目索引</param>
|
||||
/// <param name="state">新的溢出状态</param>
|
||||
public virtual void OnWindowLevelChange(int entryIndex, OverflowState state) { }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace XericUI.VisualForm
|
||||
{
|
||||
public interface IAnchorWindowEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认窗口条目列表
|
||||
/// </summary>
|
||||
public IEnumerable<WindowEntry> DefaultTypeWindowEntries { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认窗口条目数量
|
||||
/// </summary>
|
||||
public int DefaultTypeWindowEntriesCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 窗口层级变更事件 — 当某个条目的溢出状态发生变化时触发。
|
||||
/// </summary>
|
||||
/// <param name="entryIndex">发生变化的条目索引</param>
|
||||
/// <param name="state">新的溢出状态</param>
|
||||
public void OnWindowLevelChange(int entryIndex, OverflowState state);
|
||||
}
|
||||
|
||||
public interface IAnchorWindowEntry<T> : IAnchorWindowEntry
|
||||
where T : WindowEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 窗口条目列表
|
||||
/// </summary>
|
||||
public IList<T> WindowEntries { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36a31785515e4fa98a926f175813ebbc
|
||||
timeCreated: 1781485721
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace XericUI.VisualForm
|
||||
{
|
||||
/// <summary>
|
||||
/// 安全区检测模式
|
||||
/// </summary>
|
||||
public enum SafeZoneMode
|
||||
{
|
||||
/// <summary>矩形安全区</summary>
|
||||
Rect,
|
||||
/// <summary>椭圆形安全区</summary>
|
||||
Ellipse
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 窗口溢出状态(Flags枚举,支持位运算叠加)
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum OverflowState
|
||||
{
|
||||
/// <summary>在安全区范围内</summary>
|
||||
InRange = 0,
|
||||
/// <summary>超出安全区</summary>
|
||||
OutOfSafeZone = 1 << 0,
|
||||
/// <summary>超出屏幕</summary>
|
||||
OutOfScreen = 1 << 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3402a5cfbf1cca740b9374783abb4c6b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -6,6 +6,7 @@ namespace XericUI.VisualForm
|
||||
/// 窗口条目数据 — 锚点窗口上每个窗口对象的配置集合。
|
||||
/// 包含窗口对象引用、屏幕二维偏移量和激活状态。
|
||||
/// 支持一个锚点同时生成多个定位窗口。
|
||||
/// 运行时修改通过 AnchorWindow 的 SetEntryXxx 方法,自动同步到 WindowState。
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class WindowEntry
|
||||
@@ -19,6 +20,9 @@ namespace XericUI.VisualForm
|
||||
[SerializeField, Tooltip("是否启用此窗口")]
|
||||
private bool m_Enabled = true;
|
||||
|
||||
[SerializeField, Tooltip("超出安全区后是否钳制在安全区内")]
|
||||
private bool m_ClampToSafeZone;
|
||||
|
||||
/// <summary>
|
||||
/// 窗口对象引用(实例或预制体)
|
||||
/// </summary>
|
||||
@@ -38,13 +42,14 @@ namespace XericUI.VisualForm
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用此窗口
|
||||
/// 是否启用此窗口。代码修改请通过 AnchorWindow.SetEntryEnabled()
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get => m_Enabled;
|
||||
set => m_Enabled = value;
|
||||
}
|
||||
public bool Enabled => m_Enabled;
|
||||
|
||||
/// <summary>
|
||||
/// 超出安全区后是否钳制在安全区内。代码修改请通过 AnchorWindow.SetEntryClampToSafeZone()
|
||||
/// </summary>
|
||||
public bool ClampToSafeZone => m_ClampToSafeZone;
|
||||
|
||||
// --- 运行时状态(非序列化) ---
|
||||
|
||||
@@ -59,5 +64,16 @@ namespace XericUI.VisualForm
|
||||
/// </summary>
|
||||
[System.NonSerialized]
|
||||
public bool IsPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// 当前帧的溢出状态(逐条目跟踪,用于检测变化触发事件)
|
||||
/// </summary>
|
||||
[System.NonSerialized]
|
||||
public OverflowState CurrentOverflow;
|
||||
|
||||
// --- 内部方法(供 AnchorWindow 调用) ---
|
||||
|
||||
internal void SetEnabledInternal(bool value) => m_Enabled = value;
|
||||
internal void SetClampToSafeZoneInternal(bool value) => m_ClampToSafeZone = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,21 @@ namespace XericUI.VisualForm
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前窗口溢出状态(由坞每帧更新)
|
||||
/// </summary>
|
||||
public OverflowState Overflow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否存在至少一个需要钳制在安全区内的条目
|
||||
/// </summary>
|
||||
public bool HasAnyClamp { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否存在至少一个启用的条目
|
||||
/// </summary>
|
||||
public bool HasAnyEnabled { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 坐标脏标记 - 当锚点世界坐标发生变化时标记
|
||||
/// </summary>
|
||||
@@ -80,13 +95,12 @@ namespace XericUI.VisualForm
|
||||
{
|
||||
if (Dock == null || Anchor == null) return;
|
||||
|
||||
var anchorWindow = Anchor as AnchorWindow;
|
||||
if (anchorWindow == null || anchorWindow.WindowEntries.Count == 0)
|
||||
if (Anchor is not IAnchorWindowEntry { DefaultTypeWindowEntriesCount: > 0 } anchorWindow)
|
||||
return;
|
||||
|
||||
Entries.Clear();
|
||||
|
||||
foreach (var srcEntry in anchorWindow.WindowEntries)
|
||||
foreach (var srcEntry in anchorWindow.DefaultTypeWindowEntries)
|
||||
{
|
||||
if (srcEntry == null || srcEntry.WindowObject == null)
|
||||
continue;
|
||||
@@ -109,6 +123,8 @@ namespace XericUI.VisualForm
|
||||
|
||||
Entries.Add(entry);
|
||||
}
|
||||
|
||||
RefreshFlags();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -178,10 +194,32 @@ namespace XericUI.VisualForm
|
||||
Anchor = null;
|
||||
Dock = null;
|
||||
IsEnabled = false;
|
||||
Overflow = OverflowState.InRange;
|
||||
HasAnyClamp = false;
|
||||
HasAnyEnabled = false;
|
||||
CoordinateDirty = false;
|
||||
_lastPosition = Vector3.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新聚合标记:遍历所有条目,更新 HasAnyClamp 和 HasAnyEnabled。
|
||||
/// 由 AnchorWindow 的 SetEntryXxx 方法或 ResolveWindowEntries 调用。
|
||||
/// </summary>
|
||||
public void RefreshFlags()
|
||||
{
|
||||
bool anyClamp = false;
|
||||
bool anyEnabled = false;
|
||||
|
||||
foreach (var entry in Entries)
|
||||
{
|
||||
if (entry.ClampToSafeZone) anyClamp = true;
|
||||
if (entry.Enabled) anyEnabled = true;
|
||||
}
|
||||
|
||||
HasAnyClamp = anyClamp;
|
||||
HasAnyEnabled = anyEnabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查锚点坐标是否发生变化,超过阈值则标记脏
|
||||
/// 由锚点坞在每帧调用
|
||||
@@ -224,5 +262,49 @@ namespace XericUI.VisualForm
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 静态工具方法 — 指向示意旋转计算
|
||||
|
||||
/// <summary>
|
||||
/// 计算从钳制位置指向屏幕中心的方向矢量。
|
||||
/// </summary>
|
||||
/// <param name="clampedPosition">钳制后的屏幕坐标</param>
|
||||
/// <param name="screenCenter">屏幕中心坐标</param>
|
||||
/// <returns>归一化方向矢量(从clampedPosition指向screenCenter)</returns>
|
||||
public static Vector2 GetLookAtDirection(Vector2 clampedPosition, Vector2 screenCenter)
|
||||
{
|
||||
Vector2 dir = screenCenter - clampedPosition;
|
||||
return dir.sqrMagnitude > 0.0001f ? dir.normalized : Vector2.up;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算Z轴旋转角度,使初始指向矢量旋转后对准从钳制位置到屏幕中心的方向。
|
||||
/// 典型用法:传入 Vector2.up 作为初始指向,得到 transform.eulerAngles.z 的值。
|
||||
/// </summary>
|
||||
/// <param name="clampedPosition">钳制后的屏幕坐标</param>
|
||||
/// <param name="screenCenter">屏幕中心坐标</param>
|
||||
/// <param name="initialDirection">初始指向矢量(如 Vector2.up 表示默认向上)</param>
|
||||
/// <returns>Z轴旋转角度(度),可直接赋给 transform.eulerAngles.z</returns>
|
||||
public static float GetLookAtAngle(Vector2 clampedPosition, Vector2 screenCenter, Vector2 initialDirection)
|
||||
{
|
||||
Vector2 dir = GetLookAtDirection(clampedPosition, screenCenter);
|
||||
return Vector2.SignedAngle(initialDirection, dir);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算旋转四元数,使初始指向矢量旋转后对准从钳制位置到屏幕中心的方向。
|
||||
/// 典型用法:传入 Vector2.up 作为初始指向,得到 transform.rotation 的值。
|
||||
/// </summary>
|
||||
/// <param name="clampedPosition">钳制后的屏幕坐标</param>
|
||||
/// <param name="screenCenter">屏幕中心坐标</param>
|
||||
/// <param name="initialDirection">初始指向矢量(如 Vector2.up 表示默认向上)</param>
|
||||
/// <returns>Z轴旋转四元数,可直接赋给 transform.rotation</returns>
|
||||
public static Quaternion GetLookAtRotation(Vector2 clampedPosition, Vector2 screenCenter, Vector2 initialDirection)
|
||||
{
|
||||
float angle = GetLookAtAngle(clampedPosition, screenCenter, initialDirection);
|
||||
return Quaternion.Euler(0f, 0f, angle);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace XericUI.VisualForm
|
||||
/// 世界对象锚点 - 挂载在场景对象上,将世界空间位置传递给锚点坞进行屏幕空间定位
|
||||
/// </summary>
|
||||
[AddComponentMenu("Xeric UI Vessel/AnchorWindow/WorldAnchor")]
|
||||
public class WorldAnchor : XVFormBase
|
||||
public class WorldAnchor : XvFormBase
|
||||
{
|
||||
[SerializeField, Tooltip("目标锚点坞,不填则自动在父级中查找")]
|
||||
private AnchorDock m_TargetDock;
|
||||
|
||||
@@ -5,8 +5,8 @@ namespace XericUI.VisualForm
|
||||
/// <summary>
|
||||
/// 虚拟窗体基类,提供基础的窗体标记功能
|
||||
/// </summary>
|
||||
[AddComponentMenu("Xeric UI Vessel/AnchorWindow/XVFormBase")]
|
||||
public class XVFormBase : MonoBehaviour
|
||||
[AddComponentMenu("Xeric UI Vessel/AnchorWindow/XvFormBase")]
|
||||
public class XvFormBase : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private string m_FormID;
|
||||
Reference in New Issue
Block a user