发布时间:2026/8/4 2:57:57
FairyGUI与Unity坐标转换全解析:从原理到实战避坑指南 1. 项目概述为什么FairyGUI与Unity的坐标转换如此重要如果你正在用FairyGUI给Unity项目做UI并且遇到过UI元素位置飘忽不定、点击区域对不上、或者在不同分辨率下表现诡异的问题那么你大概率是踩进了坐标转换的“坑”里。这几乎是每个从零开始整合FairyGUI的Unity开发者都会遇到的经典难题。我接手过不少项目发现UI Bug里至少有三成跟坐标没算明白有关。FairyGUI作为一个强大的UI编辑器它有自己的坐标系和渲染逻辑而Unity的UGUI或自己的渲染管线又是另一套体系。当这两套系统需要无缝协作时坐标转换就成了那个关键的“翻译官”。简单来说FairyGUI的世界是一个以左上角为原点(0,0)X轴向右Y轴向下的二维平面。而Unity的世界对于UI来说通常是Canvas下的屏幕空间或摄像机空间原点可能在中心Y轴向上。更复杂的是涉及到鼠标点击、拖拽、UI跟随3D物体比如血条、或者做自适应布局时你需要在这几个坐标系之间来回穿梭FairyGUI本地坐标、FairyGUI全局坐标、Unity屏幕像素坐标、Unity世界坐标、以及归一化的视口坐标。任何一个环节转换出错你的按钮就可能“隐身”在屏幕外或者滑动列表永远对不准。网上有很多代码片段但往往只解决了某一特定情况缺乏系统性的原理梳理和全场景覆盖。2024年随着Unity版本和FairyGUI插件的迭代一些细节也发生了变化。这篇文章我就结合最新的实践把这套转换逻辑从头到尾拆解清楚从最底层的数学原理到最常见的应用场景代码实现让你不仅能“抄作业”更能彻底理解背后的“为什么”。无论你是刚接触FairyGUI的新手还是被坐标问题困扰已久的老手这份全解析都能帮你建立起清晰、稳固的知识框架。2. 核心原理拆解两套坐标系是如何定义的要搞定转换必须先彻底理解双方坐标系的规则。这里不能停留在表面得深入到它们的数据结构和渲染流程。2.1 FairyGUI坐标系一个基于“显示对象”的树形结构FairyGUI的整个UI界面是由一棵“显示对象”树构成的。根节点是GRoot你可以把它理解为一个全屏的容器。每个组件GComponent、图片GImage、按钮GButton都是一个显示对象DisplayObject。1. 本地坐标 (Local Position)这是最基础的概念。一个显示对象的本地坐标是相对于其父容器的坐标原点而言的。例如一个按钮放在一个面板里按钮的x和y属性就是它相对于面板左上角的偏移量。在代码中通常通过displayObject.x和displayObject.y直接访问和设置。2. 全局坐标 (Global Position)这是一个显示对象在整个GRoot坐标系下的绝对位置。计算它需要考虑从该对象一直到GRoot的整个父链上所有的本地坐标、缩放、旋转以及轴心点变换。FairyGUI提供了核心的转换方法displayObject.LocalToGlobal(Vector2 localPoint, Vector2? globalPoint)将本地坐标点转换为全局坐标。displayObject.GlobalToLocal(Vector2 globalPoint, Vector2? localPoint)将全局坐标点转换回某个显示对象的本地坐标。关键理解FairyGUI的全局坐标其原点(0,0)始终对应GRoot的左上角。无论你的GRoot如何适配屏幕这个关系是不变的。GRoot会自动处理与屏幕的对齐比如居中、拉伸但它的内部坐标系原点在左上角。3. 舞台坐标 (Stage Position) - 一个常见的混淆点在早期版本或某些语境下“舞台”指的就是GRoot。所以“舞台坐标”通常等价于“全局坐标”。但在更精确的表述中尤其是在涉及与Unity交互时我们需要一个更“物理”的坐标——那就是相对于Unity屏幕的像素坐标。2.2 Unity UI坐标系从Screen Space到Local SpaceUnity的UI系统UGUI主要工作在几种Canvas Render Mode下最常用的是“Screen Space - Overlay”和“Screen Space - Camera”。1. 屏幕坐标 (Screen Space)这是最直接的坐标系统。原点(0,0)在屏幕的左下角X轴向右Y轴向上。屏幕的右上角坐标是(Screen.width, Screen.height)。鼠标输入Input.mousePosition返回的就是这个坐标系下的值。这是与FairyGUI全局坐标进行互转的关键桥梁之一也是差异最大的一点一个左上角原点一个左下角原点。2. 视口坐标 (Viewport Point)这是一个归一化的坐标范围在(0,0)到(1,1)之间原点同样在左下角。它不依赖于屏幕分辨率常用于跨分辨率计算。通过Camera.ScreenToViewportPoint和Camera.ViewportToScreenPoint与屏幕坐标转换。3. 世界坐标 (World Space)对于“Screen Space - Camera”模式的Canvas其下的UI元素具有世界坐标。对于3D物体其Transform.position就是世界坐标。当我们需要UI如FairyGUI组件跟随3D物体移动时就需要将3D世界坐标转换为FairyGUI可用的坐标。4. 本地坐标 (RectTransform Local Position)在UGUI中一个UI元素的RectTransform.anchoredPosition是其相对于锚点Anchor的本地坐标。虽然FairyGUI不直接使用RectTransform但在做深度集成或自定义渲染时理解这个坐标有助于排查问题。核心差异总结FairyGUI全局坐标原点在左上角Y轴向下Unity屏幕坐标原点在左下角Y轴向上。这是所有转换问题中最根本的矛盾后续的所有公式都源于对此差异的修正。3. 核心转换公式与通用工具类实现理解了原理我们就可以动手编写一个万能的坐标转换工具类。这个类应该静态化方便在任何地方调用。我将分场景给出代码和详细解释。3.1 基础转换FairyGUI全局坐标 ↔ Unity屏幕坐标这是最频繁、最核心的转换。using UnityEngine; using FairyGUI; public static class CoordinateTransformer { /// summary /// 将FairyGUI的全局坐标转换为Unity屏幕坐标左下角原点 /// /summary /// param namefairyGlobalPosFairyGUI全局坐标左上角原点/param /// returnsUnity屏幕坐标左下角原点/returns public static Vector2 FairyGlobalToScreen(Vector2 fairyGlobalPos) { // GRoot的高度代表了FairyGUI舞台的像素高度 float stageHeight GRoot.inst.height; // 转换Y轴fairyGlobalPos.y是距离FairyGUI顶部的高度。 // Screen.height - stageHeight 计算了屏幕高度与FairyGUI舞台高度的差值可能因适配方式产生黑边。 // 但更通用的方法是屏幕Y 屏幕高度 - FairyGUI全局Y。 // 因为FairyGUI的(0,0)对应屏幕左上角其Y值向下增长。 // 屏幕左下角原点的Y值 屏幕总高度 - 左上角原点下的Y值。 // 注意这里假设GRoot与屏幕像素完美对应即ScaleModeScaleMode.ScaleWithScreenSize且适配模式为适配高度或全屏拉伸。 // 对于有黑边或不同适配模式的情况需要引入GRoot的缩放和偏移见3.2节。 float screenY Screen.height - fairyGlobalPos.y; return new Vector2(fairyGlobalPos.x, screenY); } /// summary /// 将Unity屏幕坐标转换为FairyGUI的全局坐标 /// /summary /// param namescreenPosUnity屏幕坐标左下角原点/param /// returnsFairyGUI全局坐标左上角原点/returns public static Vector2 ScreenToFairyGlobal(Vector2 screenPos) { float stageHeight GRoot.inst.height; float fairyY Screen.height - screenPos.y; return new Vector2(screenPos.x, fairyY); } }为什么这样转换核心就是Y轴的反转。FairyGUI的全局Y表示“从顶部向下的距离”而Unity屏幕Y表示“从底部向上的距离”。所以fairyY 屏幕总高 - screenY。X轴方向相同所以直接传递。注意事项GRoot.inst.height的重要性这个值不总是等于Screen.height。当FairyGUI采用“固定大小”或“随屏幕缩放”但带有黑边时GRoot.inst有一个自己的逻辑分辨率。上述基础公式假设了GRoot与屏幕像素一一对应即无缩放、无黑边。在更复杂的情况下必须使用GRoot自带的转换方法。3.2 处理GRoot缩放与适配使用官方API进行精确转换FairyGUI的GRoot提供了最权威的转换方法它能自动处理所有缩放、适配模式如固定宽度、固定高度、缩放填充等以及可能出现的黑边。/// summary /// 推荐使用GRoot的API将Unity屏幕坐标转换为FairyGUI全局坐标自动处理所有缩放和适配 /// /summary public static Vector2 ScreenToFairyGlobal_Official(Vector2 screenPos) { // GRoot.inst.GlobalToLocal 是将“屏幕坐标”转到“GRoot本地坐标”。 // 但这里我们需要的是相对于GRoot左上角的全局坐标。 // 实际上GRoot提供了一个更直接的方法GRoot.inst.GetTouchPosition。 // 但GetTouchPosition是针对触摸的。对于任意屏幕点我们可以 // 1. 将屏幕坐标转换为一个位于“屏幕空间”的虚拟世界点。 // 2. 使用GRoot自带的摄像机如果存在或主摄像机进行转换。 // 更通用的方法是使用Stage.inst.Camera这个摄像机是FairyGUI用于渲染的。 Camera fairyCamera Stage.inst.camera; if (fairyCamera null) { Debug.LogError(FairyGUI Stage camera is not found!); return screenPos; } // 将屏幕坐标转换为FairyGUI摄像机下的世界坐标。 // 注意FairyGUI摄像机通常是正交投影且其大小和位置与GRoot的设置匹配。 Vector3 worldPoint fairyCamera.ScreenToWorldPoint(new Vector3(screenPos.x, screenPos.y, 0)); // 将这个世界坐标转换到GRoot的本地坐标系下。 // 因为GRoot是FairyGUI世界的根其本地坐标就是FairyGUI全局坐标。 Vector2 fairyGlobalPos GRoot.inst.GlobalToLocal(worldPoint); return fairyGlobalPos; } /// summary /// 推荐将FairyGUI全局坐标转换为Unity屏幕坐标自动处理所有缩放和适配 /// /summary public static Vector2 FairyGlobalToScreen_Official(Vector2 fairyGlobalPos) { Camera fairyCamera Stage.inst.camera; if (fairyCamera null) { Debug.LogError(FairyGUI Stage camera is not found!); return fairyGlobalPos; } // 先将FairyGUI全局坐标转换为FairyGUI摄像机下的世界坐标。 // LocalToGlobal是将显示对象本地坐标转全局这里我们已经有全局坐标需要反求世界坐标。 // 更直接的方法是GRoot的本地坐标原点(0,0)对应其左上角在世界空间的位置。 // 我们可以构造一个相对于GRoot原点的向量。 // 实际上由于GRoot的变换缩放、位置已由FairyGUI管理更稳妥的方式是 // 1. 假设一个位于FairyGUI全局坐标点的虚拟显示对象。 // 2. 但更简单且准确的是利用摄像机的WorldToScreenPoint但需要知道该点在摄像机空间中的深度Z值。 // 对于正交摄像机所有点的深度不影响其在屏幕上的XY投影。我们可以使用GRoot的一个角点作为参考。 // 方法获取GRoot左上角在世界空间的位置然后加上fairyGlobalPos向量但需要考虑GRoot的缩放。 // 这有点复杂。实际上对于点击、触摸等交互FairyGUI内部已经处理好了。 // 如果你需要将一个FairyGUI坐标转换为屏幕坐标去设置其他Unity对象比如一个粒子特效 // 最实用的方法是在FairyGUI中创建一个空组件定位到该全局坐标然后获取其屏幕位置。 // 简化方案对于标准设置我们可以认为GRoot的变换是均匀缩放且无旋转的。 // 计算缩放比例 Vector2 scale new Vector2(GRoot.inst.width / Screen.width, GRoot.inst.height / Screen.height); // 计算屏幕坐标这是一个近似在复杂适配下可能不准 Vector2 screenPos new Vector2(fairyGlobalPos.x / scale.x, Screen.height - (fairyGlobalPos.y / scale.y)); return screenPos; }实操心得在大多数情况下如果你需要响应鼠标/触摸事件根本不需要手动转换。FairyGUI的事件系统如onTouchBegin、onClick提供的EventContext中的inputEvent.x和inputEvent.y已经是正确转换后的FairyGUI全局坐标。手动转换的需求通常发生在与非FairyGUI对象的交互上比如用FairyGUI组件跟随一个3D模型。3.3 进阶转换与Unity世界坐标3D空间互转这是实现“3D物体头顶血条”、“世界空间UI”等功能的关键。场景一个3D怪物Enemy的世界坐标是(x, y, z)我们需要在FairyGUI中创建一个HPBar组件并让它始终悬浮在怪物头顶的屏幕位置。using UnityEngine; using FairyGUI; public class WorldSpaceUIFollower : MonoBehaviour { public GComponent uiComponent; // 在FairyGUI中创建的组件如血条 public Transform worldTarget; // 需要跟随的3D物体Transform public Vector3 worldOffset new Vector3(0, 2f, 0); // 在世界空间中的偏移比如头顶2米 private Camera _mainCamera; private GRoot _groot; void Start() { _mainCamera Camera.main; // 用于渲染3D场景的主摄像机 _groot GRoot.inst; if (uiComponent null) { Debug.LogError(UI Component is not assigned!); enabled false; } } void Update() { if (worldTarget null || _mainCamera null) return; // 1. 计算3D目标点在世界空间中的位置加上偏移 Vector3 worldPosition worldTarget.position worldOffset; // 2. 将世界坐标转换为Unity屏幕坐标 Vector3 screenPos _mainCamera.WorldToScreenPoint(worldPosition); // 3. 重要检查目标是否在摄像机前方 if (screenPos.z 0) { // 物体在摄像机后面可以隐藏UI uiComponent.visible false; return; } uiComponent.visible true; // 4. 将Unity屏幕坐标转换为FairyGUI全局坐标使用3.2节的官方API方法 Vector2 fairyGlobalPos CoordinateTransformer.ScreenToFairyGlobal_Official(new Vector2(screenPos.x, screenPos.y)); // 5. 将FairyGUI全局坐标设置为UI组件的坐标。 // 注意uiComponent的x,y是相对于其父容器的本地坐标。 // 如果uiComponent直接放在GRoot下那么设置其xy即可。 // 如果它在一个复杂的嵌套结构里我们需要将全局坐标转换到其父容器的本地坐标。 if (uiComponent.parent ! null) { Vector2 localPos uiComponent.parent.GlobalToLocal(fairyGlobalPos); uiComponent.SetXY(localPos.x, localPos.y); } else { // 直接是GRoot的子物体 uiComponent.SetXY(fairyGlobalPos.x, fairyGlobalPos.y); } } }关键点解析WorldToScreenPoint这是Unity的核心API负责3D到2D的投影。返回值的z分量代表了深度小于0表示在摄像机背后这是一个常用的裁剪判断。屏幕坐标到FairyGUI坐标的转换这里我们调用之前封装好的工具方法确保适配模式被正确处理。GlobalToLocal的再次使用即使我们得到了FairyGUI的全局坐标最终设置组件位置时需要的是相对于其父容器的本地坐标。uiComponent.parent.GlobalToLocal()完成了这最后一步转换。反向转换较少用如果希望通过点击FairyGUI的某个区域来触发3D世界的操作如点击地面移动则需要将FairyGUI坐标-屏幕坐标-世界射线检测。public void OnClickFairyGUI() { Vector2 fairyClickPos ...; // 从事件中获取的FairyGUI全局坐标 Vector2 screenPos CoordinateTransformer.FairyGlobalToScreen_Official(fairyClickPos); Ray ray _mainCamera.ScreenPointToRay(screenPos); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { Vector3 worldPoint hit.point; // 让角色移动到worldPoint } }4. 实战场景与避坑指南理论结合实践下面我列出几个高频场景和其中隐藏的“坑”。4.1 场景一自定义拖拽与边界限制你需要实现一个可以拖动的FairyGUI窗口并且限制它不能拖出屏幕。public class DraggableWindow : MonoBehaviour { private GComponent _window; private Vector2 _dragStartPos; // 开始拖拽时鼠标在屏幕上的位置 private Vector2 _windowStartPos; // 开始拖拽时窗口的FairyGUI全局坐标 void Start() { _window GetComponentUIPanel().ui; // 假设通过UIPanel组件关联 _window.onTouchBegin.Add(OnTouchBegin); } void OnTouchBegin(EventContext context) { InputEvent evt context.inputEvent; // evt.x, evt.y 已经是FairyGUI全局坐标 _dragStartPos new Vector2(evt.x, evt.y); _windowStartPos new Vector2(_window.x, _window.y); // 监听移动和结束事件 _window.onTouchMove.Add(OnTouchMove); _window.onTouchEnd.Add(OnTouchEnd); context.CaptureTouch(); // 重要确保移动事件能被接收 } void OnTouchMove(EventContext context) { InputEvent evt context.inputEvent; Vector2 currentFairyPos new Vector2(evt.x, evt.y); // 计算偏移量在FairyGUI全局坐标系下 Vector2 delta currentFairyPos - _dragStartPos; // 计算窗口的新位置 Vector2 newPos _windowStartPos delta; // --- 关键边界限制计算 --- // 我们需要在FairyGUI全局坐标系下计算边界。 // 屏幕左边界对应x0右边界对应xGRoot.inst.width - _window.width // 屏幕上边界对应y0下边界对应yGRoot.inst.height - _window.height float minX 0; float maxX GRoot.inst.width - _window.width; float minY 0; float maxY GRoot.inst.height - _window.height; newPos.x Mathf.Clamp(newPos.x, minX, maxX); newPos.y Mathf.Clamp(newPos.y, minY, maxY); // --- 边界计算结束 --- _window.SetXY(newPos.x, newPos.y); } void OnTouchEnd(EventContext context) { _window.onTouchMove.Remove(OnTouchMove); _window.onTouchEnd.Remove(OnTouchEnd); } }避坑提示context.CaptureTouch()在onTouchBegin中调用此方法至关重要它能确保即使触摸点移出了当前组件范围后续的onTouchMove和onTouchEnd事件依然能被该组件接收。没有这个拖拽很容易中途失效。边界计算基准所有计算鼠标偏移、窗口位置、边界都应在同一个坐标系这里是FairyGUI全局坐标下进行避免混合坐标系导致错乱。GRoot.inst.width/height是舞台的逻辑尺寸用于边界判断非常合适。4.2 场景二UI组件跟随鼠标如自定义光标、工具提示实现一个始终在鼠标位置的FairyGUI精灵。public class MouseFollower : MonoBehaviour { public GObject follower; // 要跟随的FairyGUI对象 void Update() { if (follower null || Stage.isTouchOnUI) // 如果触摸点在UI上可以忽略 return; // 获取Unity屏幕坐标的鼠标位置 Vector3 mouseScreenPos Input.mousePosition; // 转换为FairyGUI全局坐标使用官方API以处理适配 Vector2 fairyGlobalPos CoordinateTransformer.ScreenToFairyGlobal_Official(new Vector2(mouseScreenPos.x, mouseScreenPos.y)); // 设置跟随对象的位置。通常需要减去对象宽高的一半让对象中心对准鼠标。 if (follower.parent ! null) { Vector2 localPos follower.parent.GlobalToLocal(fairyGlobalPos); follower.SetXY(localPos.x - follower.width / 2, localPos.y - follower.height / 2); } } }注意事项Stage.isTouchOnUI这是一个非常有用的属性当鼠标或触摸点正在点击一个FairyGUI对象时它为true。这可以避免你的鼠标跟随器干扰正常的UI点击操作。中心点对齐直接设置位置会让对象的左上角对准鼠标。通过减去宽高的一半可以实现中心点对齐视觉效果更自然。4.3 场景三跨分辨率与多适配模式下的坐标稳定性这是问题最多的领域。你的游戏可能需要在1920x1080、2340x1080、iPad等多种分辨率下运行FairyGUI的GRoot也设置了不同的ScaleMode如ScaleMode.ScaleWithScreenSizeScaleMode.ConstantPhysicalSize等。核心原则永远以GRoot.inst的逻辑尺寸为基准进行UI布局和坐标计算而不是Screen.width/height。错误示例// 假设设计分辨率是1920x1080 float screenX Input.mousePosition.x; float designX screenX * (1920f / Screen.width); // 这种计算在非设计分辨率下且GRoot有缩放时会出错。正确做法对于UI内部布局使用FairyGUI的关联系统如AddRelation、锚点pivot和百分比布局。这是最稳定、最推荐的方式。对于需要手动计算的位置如上述的拖拽边界、世界空间UI边界值使用GRoot.inst.width和GRoot.inst.height。坐标转换优先使用Stage.inst.camera和GRoot.inst.GlobalToLocal/LocalToGlobal这一套官方流程。如果必须用屏幕坐标通过GRoot.inst的LocalToGlobal(Vector3.zero)先获取GRoot左上角对应的屏幕坐标再结合GRoot的缩放因子进行计算。一个通用缩放因子获取方法// 获取从FairyGUI逻辑坐标到实际屏幕像素的缩放比例 public Vector2 GetFairyToScreenScale() { // 方法1通过GRoot的实际渲染尺寸与逻辑尺寸之比 // 注意GRoot.inst.size 是逻辑尺寸Stage.inst.size 是舞台实际像素尺寸 // 更准确的是FairyGUI内部有一个缩放因子。我们可以通过一个已知点来推算。 // 方法2实用取一个逻辑点看它转换到屏幕后的变化。 Vector2 fairyOrigin Vector2.zero; Vector2 screenOrigin CoordinateTransformer.FairyGlobalToScreen_Official(fairyOrigin); Vector2 fairyPoint new Vector2(100, 100); Vector2 screenPoint CoordinateTransformer.FairyGlobalToScreen_Official(fairyPoint); Vector2 scale (screenPoint - screenOrigin) / 100f; return scale; }5. 常见问题排查与调试技巧即使理解了原理实际开发中还是会遇到各种诡异的问题。这里我列一个排查清单和调试方法。5.1 问题速查表现象可能原因排查步骤与解决方案UI点击无响应或位置偏移1. 坐标转换错误点击位置未正确映射到UI。2. 触摸被其他UI组件阻挡。3.hitTest区域设置不当。1. 在onClick事件中打印inputEvent.x/y看是否是预期的FairyGUI坐标。2. 检查是否有全屏透明的GComponent盖在上面或touchable属性为false。3. 对于不规则图形检查自定义hitTest委托是否正确返回。拖拽过程中UI“卡顿”或“跳动”1. 在Update和事件回调中重复设置位置产生竞争。2. 坐标转换基准不统一混合了屏幕坐标和本地坐标。3. 帧率波动导致位置计算不跟手。1. 确保拖拽逻辑只在onTouchMove事件中执行。2. 检查计算偏移量时起点和当前点是否在同一坐标系推荐全程使用FairyGUI全局坐标。3. 使用Time.deltaTime进行平滑插值Lerp但注意这会降低跟手性。不同分辨率下UI位置错乱1. 硬编码了屏幕像素值如Screen.width。2. 未考虑GRoot的适配模式如FixedWidth/FixedHeight。3. UI组件锚点或关联关系设置错误。1. 将所有与位置相关的计算改为基于GRoot.inst.width/height。2. 在Unity编辑器中切换不同的分辨率进行测试使用FairyGUI的“测试分辨率”功能。3. 复查FairyGUI编辑器中组件与父容器的关联关系。3D物体头顶血条位置不准1.WorldToScreenPoint的Z值判断错误。2. 未将屏幕坐标正确转换为FairyGUI父容器的本地坐标。3. 主摄像机与FairyGUI摄像机混淆。1. 打印WorldToScreenPoint返回的screenPos.z确保物体在摄像机前方0。2. 在Update中打印转换后的各阶段坐标世界-屏幕-Fairy全局-UI本地定位出错环节。3. 确认用于转换的摄像机是渲染3D场景的主摄像机而不是FairyGUI的Stage.inst.camera。UI在屏幕旋转后错位1. 坐标转换逻辑未在屏幕尺寸变化时更新。2. FairyGUI的GRoot适配模式未正确响应屏幕旋转。1. 监听Screen.orientation或Screen.resolution变化事件或在Update中判断Screen.width/height是否改变然后重新计算位置。2. 检查FairyGUI发布设置中的“适配模式”对于需要横竖屏切换的游戏通常选择“ScaleWithScreenSize”并勾选“适配宽度和高度”。5.2 调试技巧可视化坐标轴在复杂问题定位时光靠打印日志不够直观。我习惯在场景中创建几个临时的调试用FairyGUI图形来标记坐标。public class CoordinateDebugger : MonoBehaviour { public GComponent debugRoot; private GGraph _crosshair; // 一个十字标记 void Start() { // 创建一个红色的十字线用于标记鼠标位置 _crosshair new GGraph(); _crosshair.DrawRect(20, 20, Color.red, Color.clear); _crosshair.pivot new Vector2(0.5f, 0.5f); // 中心点对齐 debugRoot.AddChild(_crosshair); _crosshair.visible false; // 监听全局触摸移动 Stage.inst.onTouchMove.Add(OnStageTouchMove); } void OnStageTouchMove(EventContext context) { InputEvent evt context.inputEvent; Vector2 fairyPos new Vector2(evt.x, evt.y); // 将十字标记移动到鼠标的FairyGUI全局坐标处 Vector2 localPos debugRoot.GlobalToLocal(fairyPos); _crosshair.SetXY(localPos.x, localPos.y); _crosshair.visible true; // 同时在Unity中画一个Debug射线世界空间 Vector2 screenPos CoordinateTransformer.FairyGlobalToScreen_Official(fairyPos); Ray ray Camera.main.ScreenPointToRay(screenPos); Debug.DrawRay(ray.origin, ray.direction * 100, Color.green, 0.1f); // 打印坐标信息 Debug.Log($FairyGUI全局: {fairyPos}, Unity屏幕: {screenPos}); } }通过这样一个简单的调试器你可以清晰地看到鼠标位置在FairyGUI坐标系和Unity世界中的对应关系对于验证转换公式的正确性有奇效。坐标转换的本质是理解不同空间之间的映射关系。FairyGUI与Unity的协作核心在于抓住GRoot这个枢纽。在绝大多数UI交互中相信并利用好FairyGUI内置的事件坐标它已经帮你做好了转换。只有在需要与Unity原生3D世界或其他非FairyGUI系统交互时才需要手动进行我们上面详细拆解的这些转换步骤。记住保持所有计算在同一个坐标系内进行并优先使用GRoot和Stage.camera提供的官方转换方法是避免诡异Bug的最有效法则。

相关新闻

2026/8/4 2:57:57

华为CANN PYASC Python算子接口开发与性能优化指南

1. CANN PYASC Python算子接口概述在AI和高性能计算领域,华为推出的CANN(Compute Architecture for Neural Networks)作为昇腾AI处理器的软件栈核心,近期推出的PYASC(Python Accelerated Scientific Computing&#xf…

2026/8/4 2:57:57

Unity动态SDF字体生成技术与性能优化

1. Unity TMP_SDF 数据源深度解析在Unity文本渲染管线中,TextMeshPro的SDF(Signed Distance Field)字体渲染技术已经成为UI文字显示的事实标准。作为TMP核心子系统之一,SDF数据源的生成与处理直接影响最终渲染效果的质量和性能。本…

2026/8/4 4:08:01

复小波变换时频脊线提取技术及其MATLAB实现

1. 复小波变换时频脊线提取技术概述复小波变换时频脊线提取是一种用于非平稳信号频率特征分析的重要方法。我在处理振动信号和声学信号时,发现传统傅里叶变换在时频局部化分析上的局限性,而小波脊线技术完美解决了这个问题。这项技术的核心价值在于&…

2026/8/4 4:08:01

设计模式 04 · 抽象工厂模式

上一篇的工厂方法,解决的是"一个产品有多种实现,该造哪一个"。但现实里有一类更麻烦的情况:你要造的不是一个产品,而是一整套互相搭配、必须配套使用的产品。 比如做一笔线上订单,你需要的不只是一个 Order,还有配套的电子发票 Invoice、以及虚拟发货单 Shipment;而换…

2026/8/4 4:08:01

Java单例模式:线程安全实现与最佳实践

1. 单例模式的核心价值与应用场景单例模式可能是设计模式中最简单却又最容易被误用的一个。我在十多年的Java开发经历中,见过太多错误实现单例的案例——有的导致性能问题,有的甚至根本不能保证单例。这个看似简单的模式,实际上蕴含着线程安全…

2026/8/4 4:03:01

Java项目转SpringBoot实战:依赖管理与配置优化

1. 从零开始:普通Java项目转SpringBoot的完整指南去年接手一个遗留的Java Web项目时,我面临着一个典型困境:这个使用传统SSH(StrutsSpringHibernate)架构的项目,配置文件散落在各处,启动需要依赖…

2026/8/3 21:14:30

如何用免费工具突破游戏窗口限制:SRWE完整使用指南

如何用免费工具突破游戏窗口限制:SRWE完整使用指南 【免费下载链接】SRWE Simple Runtime Window Editor 项目地址: https://gitcode.com/gh_mirrors/sr/SRWE 你是否遇到过这样的困扰?想为心爱的游戏截图,却发现游戏不支持自定义分辨率…

2026/8/4 0:02:01

dealsea是什么?跨境卖家必知的美国deal站入门指南

说实话,第一次听说美国这个老牌折扣网站的跨境卖家,十个有八个会问同一个问题:这个平台到底是干嘛的?我见过一个做家居出口的朋友,他在亚马逊上月销二十万美金,却从来没用过它。我给他看了首页——一屏一屏…

2026/8/3 22:40:58

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/3 13:26:41

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/3 16:43:13

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…