Unity的实时绘制与坐标转换总结

2015年11月03日 10:52 0 点赞 0 评论 更新于 2025-11-21 19:19

最近在处理有关Unity的交互和线路绘制问题,在此进行总结。

问题概述

我们希望在屏幕上通过鼠标拖动,根据动作动态绘制线路,线路可以是直线、点或者抛物线等。主要面临两个问题:一是采用何种方法进行绘制,二是使用什么坐标来绘制。

绘制方式及对应坐标

绘制方式

绘制一根线(或点集)可采用以下方式:

  1. LineRenderer:Unity自带的用于绘制线条的组件。
  2. NGUI绘制:借助成熟的NGUI插件进行绘制。
  3. 使用Unity内置基本几何图形:如使用Cube、Sphere等作为点集合。
  4. 使用GL底层库:Unity的底层图形库。

Unity中的坐标类型

Unity本身有以下几种坐标:

  1. 世界坐标:用于普通三维世界,原点在屏幕中间,例如 XXobject.transform.position
  2. 屏幕坐标:鼠标(在移动平台为手指触控)使用,与屏幕分辨率有关,原点在屏幕左下角,如 Input.mousePosition
  3. 投影坐标:与摄像机投影变换有关,用户使用较少。
  4. GUI坐标:Unity内置GUI使用,单位是像素,与屏幕坐标类似,但原点在左上角。

具体使用方式

使用LineRenderer

若选择使用LineRenderer绘制线路,由于其使用世界坐标,若要实现在屏幕上点击一下就在该点放置小球,步骤大致如下:

  1. 捕获鼠标点击时的屏幕坐标:使用 Vector3 pos = Input.mousePosition 获取鼠标的屏幕坐标。
  2. 将屏幕坐标转换为世界坐标:使用 Camera.main.ScreenToWorldPoint(pos) 进行转换。但需注意,鼠标的屏幕坐标是2D的,其z轴存在问题,因为屏幕上的一点对应空间中的一条射线(包括透视投影和正交投影)。直接转换得到的世界坐标z轴不准确,所以在转换前需要给z轴赋予一个有意义的值。例如,可以简单地将 pos.z = 1。我的做法是:先获取起点的世界坐标(如 Vector3 B = _vecStart),将其转换为屏幕坐标 Vector3 C = Camera.main.WorldToScreenPoint(B),然后将C的z轴值赋给要处理的坐标(pos.z = C.z),这样转换后的z轴和起点一致。
  3. 将世界坐标赋值给小球:完成坐标转换后,将转换后的世界坐标赋值给小球的位置。

LineRenderer的具体使用方法中,还可以在Inspector面板可视化指定材质、点的个数等。若将材质设为颜色,视觉效果可能不佳;设置贴图时,对于较短的线效果尚可,但点数较多时,贴图会被拉伸。若要绘制长线,只能使用多个LineRenderer首尾连接,操作较为麻烦。

使用NGUI

采用NGUI这个成熟的插件,图元制作会变得非常简单,但主要问题在于坐标转换。NGUI有自己独有的一套坐标系统,因此需要进行一次转换。Unity和NGUI的对接点是屏幕坐标,因为NGUI的 ScreenToWorldPoint(vecTempScreen) 方法与Unity的坐标转换方法类似,转换后的世界坐标是NGUI的世界坐标,转换前的屏幕坐标是Unity的屏幕坐标,所以可以将屏幕坐标作为中介。

具体做法如下:若要将Unity场景中一个物体(如Cube)的世界坐标转换为NGUI坐标,可以先获取该物体的世界坐标,将其转换为屏幕坐标,再通过NGUI的 ScreenToWorldPoint 方法转换为NGUI的世界坐标。

有时Unity会提示 UICamera.currentCamera 取不到实例,这可能是因为场景中有多台摄像机。为避免这种情况,可以自定义一个变量 public Camera NguiCurrentCamera,并在Inspector界面中将UIRoot中的摄像机拖给它。然后使用 NguiCurrentCamera.ScreenToWorldPoint(posScreen) 进行坐标转换,最后将转换后的坐标赋值给NGUI的图元,如 NGUIGameObject.transform.position = posNgui

如果直接获取屏幕坐标(如鼠标点击)并转换为NGUI世界坐标,同样要注意屏幕坐标的z轴问题,应先给z轴赋予正确的值(如1),再进行转换。另外,由于世界坐标相对屏幕坐标值较小,计算时要注意误差问题。

使用GL

GL是Unity的底层图形库,实际上是GL立即绘制函数,仅使用当前材质的设置。除非显式指定材质,否则材质可以是任意的,并且GL可能会改变材质。

GL是立即执行的,如果在 Update() 里调用,它们将在相机渲染前执行,而相机渲染会清空屏幕,导致GL效果无法看到。通常的做法是在相机上挂载脚本,并在 OnPostRender() 里执行GL绘制。

使用GL时需要注意以下两点:

  1. GL的线等基本图元没有uv,因此没有贴图纹理映射,Shader里仅进行单色计算或对之前的影像进行处理,图形美观度欠佳,若需要炫丽效果不推荐使用。
  2. GL所使用的Shader里必须有 Cull off 指令,否则显示会出现问题。

使用GL的一般步骤可参考官方例子(稍作修改),脚本挂载在摄像机上。GL.LoadOrtho() 方法用于设置正交视角,范围从 (0,0,-1)(1,1,100),主要用于纯2D图元绘制。使用该方法后,GL.Vertex3() 的取值范围从左下角的 (0,0,0) 至右上角的 (1,1,0),需要将世界坐标转换为屏幕坐标,并缩放到0 - 1的范围。若不调用 GL.LoadOrtho(),则使用普通世界坐标。

使用Unity的内置几何体

在绘制点线时,可直接使用Unity的内置几何体,如Cube、Sphere等,其坐标使用普通的世界坐标,有时比LineRenderer更加简单方便。可以使用Sphere制作满意的点,做成预制体,根据需求动态加载进场景,并设置世界坐标。

作者信息

洞悉

洞悉

共发布了 3994 篇文章