Unity中的曲线绘制(一)——直线

发表于2016-11-02
评论2 2.7w浏览
直线
  让我们从创建一条简单的直线开始。两点成线,我们需要一个起始点p0,以及终点p2;


  现在我们可以创建一个游戏对象,为它绑定Line脚本,并且设置两个点的坐标。目前我们在scene视图中还看不见这些点。当我们选中Line对象时,希望能够像选中别的游戏对象一样,在scene视图中显示Line的形状。这点可以通过创建自定义的inspector来实现。
  与Editor有关的脚本必须放在Editor文件夹下,在Assets目录下创建一个Editor文件夹,并且在它里面新建一个LineInspector脚本。
  自定义的inspector必须继承UnityEditor.Editor。并且要为它添加UnityEditor.CustomEditor属性。这样Unity才知道选中Line的时候要使用我们自定义的editor。
?
1
2
3
4
5
6
using UnityEditor;
using UnityEngine;
 
[CustomEditor(typeof(Line))]
public class LineInspector : Editor {
}
  要使editor起作用,必须为它添加OnSceneGUI方法。我们需要在这个方法里相关的代码来在scene视图中绘制元素。
Editor类有一个target变量,这个变量代表的是鼠标选中的对象(Hieraychy或者scene中)。我们把它的类型转换为Line,然后使用Handles工具类来在指定点之间绘制直线。
?
1
2
3
4
5
6
private void OnSceneGUI () {
    Line line = target as Line;
 
    Handles.color = Color.white;
    Handles.DrawLine(line.p0, line.p1);
}


  现在我们可以在scene视图中看见一条直线了,但是它是固定的,移动、旋转、缩放Line对象,这条直线是完全不会变的。这是由于Handles是在世界坐标系下进行操作,而我们的两个点的坐标位于相对于Line对象的局部坐标系。因此我们需要将p0和p1的坐标转换为世界坐标后再使用Handles工具类来绘制。
?
1
2
3
4
5
6
7
8
9
private void OnSceneGUI () {
    Line line = target as Line;
    Transform handleTransform = line.transform;
    Vector3 p0 = handleTransform.TransformPoint(line.p0);
    Vector3 p1 = handleTransform.TransformPoint(line.p1);
 
    Handles.color = Color.white;
    Handles.DrawLine(p0, p1);
}

(左图为转换前,右图为转换后)

  除了显示直线,我们还可以为直线上的两个点提供坐标轴示意(红蓝绿轴)。要实现这一点,我们还需要获取transform的旋转。
?
1
2
3
4
5
6
7
8
9
10
11
12
private void OnSceneGUI () {
    Line line = target as Line;
    Transform handleTransform = line.transform;
    Quaternion handleRotation = handleTransform.rotation;
    Vector3 p0 = handleTransform.TransformPoint(line.p0);
    Vector3 p1 = handleTransform.TransformPoint(line.p1);
 
    Handles.color = Color.white;
    Handles.DrawLine(p0, p1);
    Handles.DoPositionHandle(p0, handleRotation);
    Handles.DoPositionHandle(p1, handleRotation);
}
  尽管现在已经可以看到点的坐标轴,但是目前并不能支持Unity的pivot rotation 模式。需要使用Tools.pivotRotation来判断目前的模式,对旋转进行转换。
?
1
2
Quaternion handleRotation = Tools.pivotRotation == PivotRotation.Local ?
    handleTransform.rotation : Quaternion.identity;


  平时使用Unity时,在scene视图中移动对象的坐标轴,对象的transform值就会发生改变。在这里,我们希望实现,移动两点的坐标轴,Line脚本中它们的值也会发生改变。同样地,由于坐标轴的坐标是在世界坐标系下,我们需要用InverseTransformPoint方法把坐标转换为Line脚本绑定对象的局部坐标系。为了实时反馈点位置的改变,我们需要使用EditorGUI.BeginChangeCheck方法和EditorGUI.EndChangeCheck方法。当p0对应坐标轴被我们拖动改变后,EditorGUI.EndChangeCheck()方法会返回true,此时就可以将其坐标进行转换,修改脚本中p0的值。
?
1
2
3
4
5
6
7
8
9
10
EditorGUI.BeginChangeCheck();
p0 = Handles.DoPositionHandle(p0, handleRotation);
if (EditorGUI.EndChangeCheck()) {
    line.p0 = handleTransform.InverseTransformPoint(p0);
}
EditorGUI.BeginChangeCheck();
p1 = Handles.DoPositionHandle(p1, handleRotation);
if (EditorGUI.EndChangeCheck()) {
    line.p1 = handleTransform.InverseTransformPoint(p1);
}
  现在我们可以在scene视图中拖动我们的直线和点了。
  但是仍然有两个小问题。
1、不能撤销点的拖动(windows下为ctrl+z)。
2、Unity不会记录改变,退出的时候不会提示保存信息。
  第一个问题解决方法是,在修改点的位置之前,调用Undo.RecordObject方法;第二个问题通过调用EditorUtility.SetDirty解决。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
EditorGUI.BeginChangeCheck();
p0 = Handles.DoPositionHandle(p0, handleRotation);
if (EditorGUI.EndChangeCheck()) {
    Undo.RecordObject(line, "Move Point");
    EditorUtility.SetDirty(line);
    line.p0 = handleTransform.InverseTransformPoint(p0);
}
EditorGUI.BeginChangeCheck();
p1 = Handles.DoPositionHandle(p1, handleRotation);
if (EditorGUI.EndChangeCheck()) {
    Undo.RecordObject(line, "Move Point");
    EditorUtility.SetDirty(line);
    line.p1 = handleTransform.InverseTransformPoint(p1);
}

腾讯GAD游戏程序交流群:484290331

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引