Unity 世界坐标、屏幕坐标、UGUI 坐标 相互转换

news2024/11/26 11:33:42

Unity 世界坐标、屏幕坐标、UGUI 坐标 相互转换
坐标转换是游戏开发过程中必不可少的环节
看下图 世界坐标、屏幕坐标、UI 坐标 三种坐标系的转换过程,此文章中的 UI 坐标特指 UGUI 坐标

从上图可以看到,世界坐标 和 UI 坐标 需要通过 屏幕坐标作为中间转换媒介
世界坐标 -> 屏幕坐标 -> UI 坐标
UI 坐标 -> 屏幕坐标 -> 世界坐标

屏幕坐标为 从屏幕左下角开始 坐标为 Vector2 (0, 0),
右上角结束坐标为 Vector2(Screen.width, Screen.height)

关于屏幕的坐标还有一个视口坐标 (Viewport)
视口坐标为 从屏幕左下角开始 坐标为 Vector2 (0, 0),
右上角结束坐标为 Vector2(1, 1)
本篇省略 视口坐标的转换

代码如下

首先提供一个获取 UI 摄像机的方法
UGUI 中 Canvas 的 renderMode 分为
RenderMode.ScreenSpaceOverlay、
RenderMode.ScreenSpaceCamera、
RenderMode.WorldSpace

其中 RenderMode.ScreenSpaceOverlay 不需要摄像机,其他两种需要摄像机,所以 UIManager 类需要根据 renderMode 类型处理 UICamera 的获取

public class UIManager : MonoBehaviour
{
    public static UIManager Instance;
    public Camera UICamera;
 
    // Start is called before the first frame update
    void Start()
    {
        Instance = this;
        Canvas canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
        if (   canvas.renderMode == RenderMode.ScreenSpaceCamera
            || canvas.renderMode == RenderMode.WorldSpace)
        {
            UICamera = canvas.worldCamera;
        }
        else if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
        {
            UICamera = null;
        }
    }
 
    public static UIManager GetInstance()
    {
        return Instance;
    }
}
using UnityEngine;
 
public class PositionConvert
{
 
    /// <summary>
    /// 世界坐标转换为屏幕坐标
    /// </summary>
    /// <param name="worldPoint">屏幕坐标</param>
    /// <returns></returns>
    public static Vector2 WorldPointToScreenPoint(Vector3 worldPoint)
    {
        // Camera.main 世界摄像机
        Vector2 screenPoint = Camera.main.WorldToScreenPoint(worldPoint);
        return screenPoint;
    }
 
    /// <summary>
    /// 屏幕坐标转换为世界坐标
    /// </summary>
    /// <param name="screenPoint">屏幕坐标</param>
    /// <param name="planeZ">距离摄像机 Z 平面的距离</param>
    /// <returns></returns>
    public static Vector3 ScreenPointToWorldPoint(Vector2 screenPoint, float planeZ)
    {
        // Camera.main 世界摄像机
        Vector3 position = new Vector3(screenPoint.x, screenPoint.y, planeZ);
        Vector3 worldPoint = Camera.main.ScreenToWorldPoint(position);
        return worldPoint;
    }
 
    / 
    // RectTransformUtility.WorldToScreenPoint
    // RectTransformUtility.ScreenPointToWorldPointInRectangle
    // RectTransformUtility.ScreenPointToLocalPointInRectangle
    // 上面三个坐标转换的方法使用 Camera 的地方
    // 当 Canvas renderMode 为 RenderMode.ScreenSpaceCamera、RenderMode.WorldSpace 时 传递参数 canvas.worldCamera
    // 当 Canvas renderMode 为 RenderMode.ScreenSpaceOverlay 时 传递参数 null
    
    // UI 坐标转换为屏幕坐标
    public static Vector2 UIPointToScreenPoint(Vector3 worldPoint)
    {
        // RectTransform:target
        // worldPoint = target.position;
        Camera uiCamera = UIManager.GetInstance().UICamera;
 
        Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(uiCamera, worldPoint);
        return screenPoint;
    }
 
    // 屏幕坐标转换为 UGUI 坐标
    public static Vector3 ScreenPointToUIPoint(RectTransform rt, Vector2 screenPoint)
    {
        Vector3 globalMousePos;
        //UI屏幕坐标转换为世界坐标
        Camera uiCamera = UIManager.GetInstance().UICamera;
 
        // 当 Canvas renderMode 为 RenderMode.ScreenSpaceCamera、RenderMode.WorldSpace 时 uiCamera 不能为空
        // 当 Canvas renderMode 为 RenderMode.ScreenSpaceOverlay 时 uiCamera 可以为空
        RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, screenPoint, uiCamera, out globalMousePos);
        // 转换后的 globalMousePos 使用下面方法赋值
        // target 为需要使用的 UI RectTransform
        // rt 可以是 target.GetComponent<RectTransform>(), 也可以是 target.parent.GetComponent<RectTransform>()
        // target.transform.position = globalMousePos;
        return globalMousePos;
    }
 
    // 屏幕坐标转换为 UGUI RectTransform 的 anchoredPosition
    public static Vector2 ScreenPointToUILocalPoint(RectTransform parentRT, Vector2 screenPoint)
    {
        Vector2 localPos;
        Camera uiCamera = UIManager.GetInstance().UICamera;
 
        RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRT, screenPoint, uiCamera, out localPos);
        // 转换后的 localPos 使用下面方法赋值
        // target 为需要使用的 UI RectTransform
        // parentRT 是 target.parent.GetComponent<RectTransform>()
        // 最后赋值 target.anchoredPosition = localPos;
        return localPos;
    }
 
}
 

使用方法如下

世界坐标 -> 屏幕坐标

    GameObject go = GameObject.Find("GO");
    Vector2 screenPoint = PositionConvert.WorldPointToScreenPoint(go.transform.position);

屏幕坐标-> 世界坐标

    Vector2 screenPoint = new Vector2(100, 100);
    // 距离摄像机 Z 轴方向距离为 10
    float planeZ = 10; 
    Vector3 worldPoint = PositionConvert.ScreenPointToWorldPoint(screenPoint, planeZ);

UI 坐标 -> 屏幕坐标

    // 获取 UGUI 组件 transform
    Transform dst = GameObject.Find("Canvas/Panel/Image").transform;
    // 将 UI transform.position 坐标转换为屏幕坐标
    Vector2 screenPoint = PositionConvert.UIPointToScreenPoint(dst.position);

屏幕坐标 -> UI 坐标 方法一

    GameObject target = GameObject.Find("Canvas/Panel1/target");
    RectTransform rt = target.GetComponent<RectTransform>();
 
    // 将屏幕坐标转换为 UI transform.position
    Vector3 uiPoint = PositionConvert.ScreenPointToUIPoint(rt, screenPoint);
    target.transform.position = uiPoint;

屏幕坐标 -> UI 坐标 方法二

    GameObject target = GameObject.Find("Canvas/Panel1/target");
    RectTransform targetRt = target.GetComponent<RectTransform>();
 
    // target Parent
    Transform parent = target.transform.parent;
    RectTransform parentRt = parent.GetComponent<RectTransform>();
 
    // 将屏幕坐标转换为 UI transform.position
    Vector3 uiPoint = PositionConvert.ScreenPointToUILocalPoint(parentRt, screenPoint);
    targetRt.anchoredPosition = uiPoint;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1580104.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从“危”到“机”:HubSpot如何助企业转化出海营销CRM风险?

在全球化的大背景下&#xff0c;越来越多的企业选择出海拓展业务&#xff0c;以寻求更大的发展空间。然而&#xff0c;随着市场的扩大&#xff0c;企业在出海营销过程中也面临着各种风险。为了有效规避这些风险&#xff0c;许多企业选择借助HubSpot这样的专业营销软件。今天运营…

A Study of Network Forensic Investgation in Docker Environments文章翻译

A Study of Network Forensic Investgation in Docker Environments Docker环境下的网络取证研究 摘要 网络罪犯利用越来越多的技术(如虚拟机或基于容器的基础设施)进行恶意活动。 这些虚拟环境的固有动态简化了恶意服务的快速创建,并隐藏了所涉及的系统,这是以前没有的技…

C语言面试题之化栈为队

化栈为队 实例要求 C语言实现实现一个MyQueue类&#xff0c;该类用两个栈来实现一个队列&#xff1b;示例&#xff1a; MyQueue queue new MyQueue();queue.push(1); queue.push(2); queue.peek(); // 返回 1 queue.pop(); // 返回 1 queue.empty(); // 返回 false说明&…

关于51单片机TMOD定时器的安全配置

定时器介绍&#xff1a; -------------------------------------------------------------------------------------------------------------------------- 首先配置的是控制寄存器 TCON 说直白点&#xff0c;这个寄存器就是用来计数的&#xff0c;打开计时器&#xff0c;关…

UEditor 任意文件上传漏洞

前言 前段时间在做某政府单位的项目的时候发现存在该漏洞&#xff0c;虽然是一个老洞&#xff0c;但这也是容易被忽视&#xff0c;且能快速拿到shell的漏洞&#xff0c;在利用方式上有一些不一样的心得&#xff0c;希望能帮助到一些还不太了解的小伙伴&#xff0c;故此写了此篇…

AI爆款文案 巧用AI大模型让文案变现插上翅膀

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

kali基础渗透学习,永恒之蓝,木马实战

简介 kali的学习本质是在linux上对一些攻击软件的使用&#xff0c;只是学习的初期 先在终端切换到root用户&#xff0c;以便于有些工具对权限的要求 下载链接 镜像源kali 攻击流程 公网信息搜集 寻找漏洞&#xff0c;突破口&#xff0c;以进入内网 进入内网&#xff0c…

组装机械狗电子玩具方案

这款机械狗玩具电子方案结合了现代电子技术和人工智能元素&#xff0c;旨在为用户提供一个高科技、互动性强的娱乐体验。通过不断的软件更新和硬件迭代&#xff0c;机械狗的功能将持续扩展。 一、功能特点&#xff1a; 1、自动巡游&#xff1a;机械狗能够自主在房间内巡游&am…

用Wireshark工具对gRPC接口进行本地抓包

前言&#xff1a; 本人一名敲代码的程序员&#xff0c;突然领导安排研究gRPC接口&#xff0c;并且抓包分析&#xff0c; 抓包工具试了Charles、mitmproxy都不行&#xff0c;浪费很多时间&#xff0c;最后使用Wireshark工具对本地启动的gRPC接口成功抓包&#xff0c;关于安装W…

modelsim 仿真bmp图片实现RGB_YCrCb

用modelsim_se软件仿真bmp图片&#xff0c;可在modesim中实现一些图片处理算法和查看效果 本文以最简单的仿真一副bmp图像为例&#xff0c;实现RGB_YCrCb的modelsim仿真,带源工程 1、先在本地建立文件夹 2、首先打开moselsim 3、新建库和新建项目&#xff0c;保存到建立的文件…

逐行讲解python实现A*路径规划

目录 搜索步骤关键点开集合和闭集合复杂度优化 代价父节点替换 距离地图设置 完整代码备注 搜索步骤 A*路径规划是一种广度优先搜索算法&#xff0c;需要在栅格地图上进行搜索。其主要搜索步骤如下&#xff1a; 得到栅格地图&#xff0c;确定起点和终点位置&#xff1b;计算起…

Vue项目打包配置生产环境去掉console.log语句的方法

一、Vue2项目 使用webpack内置的 terser 工具&#xff0c;在vue.config.js文件加上相应的配置即可。 二、Vue3项目 同样是使用 terser 工具&#xff0c;不过vite没有内置terser&#xff0c;需要手动安装依赖 安装完后在vite.config.js文件加上相应的配置即可。 2024-4-9

深挖抖快近2000个品类,我们发现了10万亿“她经济”的新商机!

在这个数字化时代&#xff0c;女性消费力量正以前所未有的速度崛起。根据埃森哲数据显示&#xff0c;我国现有近4亿20岁-60岁的女性消费者&#xff0c;其每年所掌握的消费支出高达10万亿元&#xff01; 面对庞大的“她经济”市场&#xff0c;专属于女性的三八妇女节&#xff0c…

【Python系列】读取 Excel 第一列数据并赋值到指定列

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

米多论文方便吗 #经验分享#经验分享

米多论文是一款专业的论文写作、查重和降重工具&#xff0c;被广泛认可为高效、靠谱、方便的软件。无论是学生、科研人员还是教师&#xff0c;都可以从中受益匪浅。 首先&#xff0c;米多论文拥有强大的查重功能&#xff0c;可以帮助用户快速检测论文中的抄袭内容&#xff0c;提…

openGauss 5.0 单点企业版部署_Centos7_x86(上)

背景 通过openGauss提供的脚本安装时&#xff0c;只允许在单台物理机部署一个数据库系统。如果您需要在单台物理机部署多个数据库系统&#xff0c;建议您通过命令行安装&#xff0c;不需要通过openGauss提供的安装脚本执行安装。 本文档环境&#xff1a;CentOS7.9 x86_64 4G1…

人社大赛算法赛题解题思路分享+季军+三马一曹团队

团队成员介绍: 梅鵾 上海交通大学 众安科技 算法工程师 吴栋梁 复旦大学 众安科技 算法工程师 李玉娇 复旦大学 众安科技 算法工程师 一、赛题背景分析及理解 本赛题提供了部分地区2016年度的医疗保险就医结…

最新剧透前沿信息GPT-5或将今年发布

GPT2 很糟糕 &#xff0c;GPT3 很糟糕 &#xff0c;GPT4 可以 &#xff0c;但 GPT5 会很好。 PS:GPT2 很糟糕,3 很糟糕,4 可以,5 很可以。 如果想升级GPT4玩玩&#xff0c;地址 今年发布的具有推理功能的 GPT5不断发展&#xff0c;就像 iPhone 一样 Sam Altman 于 17 日&am…

SD-WAN在金融行业的重要性

金融行业的数字化转型已成为当今的主要趋势&#xff0c;而软件定义广域网&#xff08;SD-WAN&#xff09;作为金融机构网络架构的新宠&#xff0c;其地位日益凸显。随着金融业务的日益复杂化和网络连接需求的不断增长&#xff0c;SD-WAN的优势愈发显著。本文将深入探讨SD-WAN在…