Unity2D游戏开发-坐标系与辅助线绘制

坐标系

概要

世界坐标

transform.position 游戏中获取的就是世界坐标。

局部/本地坐标

transform.localPosition

本地坐标系的特点是:

  • 原点位于游戏对象的position属性指定的位置。
  • 使用游戏对象自身的transform组件定义的Orientation(方向)。
  • 本地坐标系是相对于父对象坐标系的,如果没有父对象,则相对于世界坐标系。
  • 当对象位置或旋转时,本地坐标系也会跟着改变。
  • 对象的scale属性也会缩放本地坐标系。

本地坐标系经常用于:

  • 指定对象相对自身的偏移量,如使用LocalPosition。
  • 作为对象内部组件的相对坐标系,如相机、碰撞体等组件。
  • 用作计算对象本身的运算坐标系。
  • 表示对象空间下的点、矢量等。

总之,Unity中的本地坐标系是相对游戏对象自身的坐标系统,用于表示对象内部的相对关系,是一个非常重要的概念

屏幕坐标系

屏幕坐标就是相对于游戏的屏幕来说的,最大和游戏分辨率相等。

获取鼠标的位置是屏幕坐标

image-20230803003708334

视口坐标系

视口坐标系和屏幕坐标系完全对等,不过范围是0-1。

image-20230803003553714

GUI坐标系

和屏幕坐标系最大值一样,只不过y轴的方向相反,是从上到下。

image-20230803003408399

别名

  • 世界坐标(全局坐标、左手坐标、绝对坐标)
  • 本地坐标(局部坐标、自身坐标、物体坐标、相对坐标)
  • 屏幕坐标(像素坐标)
  • 视口坐标(视窗坐标)
  • GUI坐标(UI坐标)

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
private void OnGUI()
{
Camera mainCamera = Camera.main;
GUI.contentColor = Color.black;
GUI.skin.label.fontSize = 20;
GUI.Label
(
new Rect
(
10,
10,
400,
30
),
"世界坐标:" + transform.position
);

GUI.Label
(
new Rect
(
10,
40,
400,
30
),
"局部/本地坐标:" + transform.localPosition
);

GUI.Label
(
new Rect
(
10,
70,
400,
30
),
"屏幕宽高:" + Screen.width + "x" + Screen.height
);

GUI.Label
(
new Rect
(
10,
100,
400,
30
),
"屏幕坐标-鼠标位置:" + Input.mousePosition
);

GUI.Label
(
new Rect
(
10,
130,
400,
30
),
"视口坐标-鼠标位置:" + mainCamera.ScreenToViewportPoint(Input.mousePosition)
);
}

坐标转换

屏幕坐标和视口坐标

Input.mousePosition鼠标的位置是屏幕坐标

屏幕坐标=>视口坐标

1
mainCamera.ScreenToViewportPoint(Input.mousePosition)

视口坐标=>屏幕坐标

1
2
3
4
5
6
7
8
9
Vector3 screenPoint = mainCamera.ViewportToScreenPoint
(
new Vector3
(
0.5,
0.5,
0
)
);

屏幕坐标和世界坐标

世界坐标=>屏幕坐标

1
Vector3 wPos = mCamera.WorldToScreenPoint(pPos);

屏幕坐标=>世界坐标

直接调用相机的屏幕转世界是不行的,因为我们获取到的屏幕坐标是没有z值的,所以计算的都是错的。

对于2D可以采用下面的方法,因为z是不变的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/// <summary>
/// 屏幕转世界
/// </summary>
/// <param name="sPos">世界坐标</param>
/// <param name="mCamera">相机</param>
/// <param name="pPos">世界坐标下任意对象的位置,主要获取z值</param>
/// <returns></returns>
private Vector3 ScreenToWord
(
Vector3 sPos,
Camera mCamera,
Vector3 pPos
)
{
//因为屏幕坐标没有z值,所以我们线获取世界转屏幕时的z值
Vector3 wsPos = mCamera.WorldToScreenPoint(pPos);
sPos.z = wsPos.z;
//设置z值后就能使用屏幕转世界了。
return mCamera.ScreenToWorldPoint(sPos);
}

GUI/Gizmos/Debug/Handles绘制及对比

GUI绘制

GUI绘制使用的是GUI坐标。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void OnGUI()
{
Camera mainCamera = Camera.main;
GUI.contentColor = Color.black;
GUI.skin.label.fontSize = 20;
GUI.Label
(
new Rect
(
10,
10,
400,
30
),
"世界坐标:" + transform.position
);
}

Gizmos绘制

OnDrawGizmosSelected和OnDrawGizmos使用其一即可。

  • OnDrawGizmosSelected 元素选中时才显示
  • OnDrawGizmos 都显示

但是无论哪种方式都要在开始调试后,让右上角的Gizmos处于激活状态才能显示。

并且只能放在这两个方法内,不能在OnGUI中。

1
2
3
4
5
6
7
8
9
private void OnDrawGizmosSelected()
{
}

private void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, transform.position + Vector3.right);
}

在 Unity 中,可以通过 Gizmos 类在 Scene 视图中绘制各种调试图形,常见的有:

  • 线条:Gizmos.DrawLine
  • 方框:Gizmos.DrawWireCube
  • 方形:Gizmos.DrawCube
  • 圆环:Gizmos.DrawWireSphere
  • 球体:Gizmos.DrawSphere
  • 射线:Gizmos.DrawRay
  • 网格:Gizmos.DrawMesh
  • 扇形:Gizmos.DrawFrustum
  • 相机视锥:Gizmos.DrawIcon

绘制线

1
2
3
Gizmos.color = Color.red;
//绘制线
Gizmos.DrawLine(transform.position, transform.position + Vector3.right);

绘制框

1
2
3
4
5
6
7
8
9
10
11
12
Gizmos.color = Color.red;    
//绘制框
Gizmos.DrawWireCube
(
transform.position,
new Vector3
(
2,
2,
2
)
);

绘制圆环(中空)

1
2
3
4
5
6
7
Gizmos.color = Color.red;
//绘制圆
Gizmos.DrawWireSphere
(
transform.position,
10f
);

绘制球(实心)

1
2
3
4
5
6
7
Gizmos.color = Color.red;
//绘制圆
Gizmos.DrawSphere
(
transform.position,
10f
);

绘制射线

1
Gizmos.DrawRay(transform.position, Vector3.right * 6);

从源码中我们可以看到本质还是画线

1
public static void DrawRay(Vector3 from, Vector3 direction) => Gizmos.DrawLine(from, from + direction);

Debug绘制

Debug绘制使用的世界坐标

右上角的Gizmos处于激活状态才能显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void DrawLine()
{
float moveX = Input.GetAxisRaw("Horizontal");
float moveY = Input.GetAxisRaw("Vertical");
Vector2 pos = transform.position;

Vector2 v = new Vector2(moveX, moveY).normalized;
Debug.DrawLine
(
pos,
pos + v * PlayerStats.shootDistance,
Color.red
);
}

或者

这个和上面的效果是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void DrawLine()
{
float moveX = Input.GetAxisRaw("Horizontal");
float moveY = Input.GetAxisRaw("Vertical");
Vector2 pos = transform.position;

Vector2 v = new Vector2(moveX, moveY).normalized;
Debug.DrawRay
(
pos,
v * PlayerStats.shootDistance,
Color.red
);
}

Handles绘制

Handles使用的世界坐标,但是注意绘制图形的时候需要设置相机,绘制Label的时候不需要设置相机。

绘制Label

比如在我们的人物上显示名称

1
2
3
4
private void OnGUI()
{
Handles.Label(transform.position, "小明");
}

绘制圆

1
2
3
4
5
6
7
8
9
10
11
12
private void OnGUI()
{
Camera mainCamera = Camera.main;
Handles.color = Color.red;
Handles.SetCamera(mainCamera);
Handles.RadiusHandle
(
Quaternion.identity,
transform.position,
2
);
}

绘制线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void OnGUI()
{
Camera mainCamera = Camera.main;
Handles.color = Color.red;
Handles.SetCamera(mainCamera);
Handles.lighting = true;
Vector3 startPoint = transform.position;
Vector3 endPoint = startPoint + transform.right * 2;

Handles.DrawLine
(
startPoint,
endPoint
);
}

使用场景

概要

GUI、Gizmos 和 Handles 在 Unity 中的主要使用场景如下:

GUI - 用于在游戏界面中创建UI元素,如按钮、文本等。适用于显示游戏信息,提供交互。

使用GUI坐标系。
示例:

1
2
3
void OnGUI() { 
GUI.Button(new Rect(100,100,200,50), "Start Game");
}

Gizmos - 用于在场景编辑器中可视化辅助信息,如球体、线条等。

使用世界坐标系。
示例:

1
2
3
void OnDrawGizmos() {
Gizmos.DrawWireSphere(transform.position, 10);
}

Handles - 用于在场景编辑器中可视化交互编辑信息,如移动、旋转控制器。

使用世界坐标系。
示例:

1
2
3
void OnSceneGUI() {
Handles.DoPositionHandle(transform.position, transform.rotation);
}

Debug.DrawLine 和 Gizmos.DrawLine

Debug.DrawLine 和 Gizmos.DrawLine 都是用于在 Unity 编辑器中绘制线条的方法,它们有以下几点主要区别:

  1. 绘制时机
    Debug.DrawLine 是在运行时绘制,也就是在游戏运行时绘制,可以用来在场景中临时绘制一些辅助线条。
    Gizmos.DrawLine 是在设计时绘制,在场景编辑模式下绘制,主要用于可视化辅助。
  2. 持续时间
    Debug.DrawLine 绘制的线条只会持续一个帧。
    Gizmos.DrawLine 绘制的线条会持续显示直到再次编译代码或进入播放模式。
  3. 可用性
    Debug.DrawLine 可以在任何环境下使用。
    Gizmos.DrawLine 只能在编辑器中使用。

主要的使用时机如下:

  • 如果需要在游戏运行时绘制临时辅助线条,用于Debug,选择 Debug.DrawLine。
  • 如果需要在编辑器模式下持续显示的辅助线条,用于场景可视化,选择 Gizmos.DrawLine。
  • 如果需要可在编辑器和运行时都使用的线条绘制功能,可以封装一下,根据情况调用上面两种方法。

总结

使用GUI坐标系的

  • GUI用于游戏运行时界面

使用世界坐标系的

  • Debug.DrawLine用于游戏运行时界面

  • Gizmos用于场景编辑的可视化信息

  • Handles用于场景编辑的交互控制

明确每一个的适用场景,可以更好地在Unity中使用它们实现各自的功能。