AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > Unity3D

【Unity C#编程】制作星星(三)

51自学网 2014-05-24 http://www.wanshiok.com

WYSIWYG

现在我们的查看器已经很不错了,但是在编辑的时候我们是看不到星星的,现在需要改变它了。

首先需要让Unity知道我们组建在编辑模式下是激活的。可以通过添加ExecuteInEditMode类属性来实现。现在开始,我们的开始函数可以在编辑器中任意一个星星显示的时候调用了。

在开始中,我们创建了一个网,它是在编辑模式下创建的。我们给它赋值了一个MeshFilter,它是持久的,保存在场景中。现在我们不想这样了,因为我们需要动态创建网,可以通过设置合适的HideFlags来防止Unity保存网。

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
using UnityEngine;
 
[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Star : MonoBehaviour {
 
public ColorPoint center;
 
[List(showSize = false, showElementLabels = false)]
public ColorPoint[] points;
 
[Range(1, 20)]
public int frequency = 1;
 
private Mesh mesh;
private Vector3[] vertices;
private Color[] colors;
private int[] triangles;
 
void Start () {
GetComponent<MeshFilter>().mesh = mesh = new Mesh();
mesh.name = "Star Mesh";
mesh.hideFlags = HideFlags.HideAndDontSave;
 
if (frequency < 1) {
frequency = 1;
}
if (points == null) {
points = new ColorPoint[0];
}
int numberOfPoints = frequency * points.Length;
vertices = new Vector3[numberOfPoints + 1];
colors = new Color[numberOfPoints + 1];
triangles = new int[numberOfPoints * 3];
 
if (numberOfPoints >= 3) {
vertices[0] = center.position;
colors[0] = center.color;
float angle = -360f / numberOfPoints;
for(int repetitions = 0, v = 1, t = 1; repetitions < frequency; repetitions++){
for(int p = 0; p < points.Length; p += 1, v += 1, t += 3){
vertices[v] = Quaternion.Euler(0f, 0f, angle * (v - 1)) * points[p].position;
colors[v] = points[p].color;
triangles[t] = v;
triangles[t + 1] = v + 1;
}
}
triangles[triangles.Length - 1] = 1;
}
mesh.vertices = vertices;
mesh.colors = colors;
mesh.triangles = triangles;
}
}

现在当编辑器重绘的时候,可以点击或者保存场景来触发它。这样我们的星星就会在编辑模式下显示了。然而,在我们修改星星组件的时候它是不会改变的。因为开始函数只是在第一次组件激活的时候调用。我们将代码移到它自身的公用函数中,这样就可以任意时候公开调用了。我们同时需要添加一些检查条件来防止在非必须情况下重新创建网和数组。

最后,由于我们重用了网,我们需要在顶点数量赋值新数据之前改变的时候清空它。否则将会产生不匹配。

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
void Start () {
UpdateMesh();
}
 
public void UpdateMesh () {
if (mesh == null) {
GetComponent<MeshFilter>().mesh = mesh = new Mesh();
mesh.name = "Star Mesh";
mesh.hideFlags = HideFlags.HideAndDontSave;
}
 
if (frequency < 1) {
frequency = 1;
}
if (points == null) {
points = new ColorPoint[0];
}
int numberOfPoints = frequency * points.Length;
if (vertices == null || vertices.Length != numberOfPoints + 1) {
vertices = new Vector3[numberOfPoints + 1];
colors = new Color[numberOfPoints + 1];
triangles = new int[numberOfPoints * 3];
mesh.Clear();
}
 
if (numberOfPoints >= 3) {
vertices[0] = center.position;
colors[0] = center.color;
float angle = -360f / numberOfPoints;
for(int repetitions = 0, v = 1, t = 1; repetitions < frequency; repetitions++){
for(int p = 0; p < points.Length; p += 1, v += 1, t += 3){
vertices[v] = Quaternion.Euler(0f, 0f, angle * (v - 1)) * points[p].position;
colors[v] = points[p].color;
triangles[t] = v;
triangles[t + 1] = v + 1;
}
}
triangles[triangles.Length - 1] = 1;
}
mesh.vertices = vertices;
mesh.colors = colors;
mesh.triangles = triangles;
}

在编辑器中,ApplyModifiedProperties函数会在发生任何修改的时候返回。这样的话,我们现在可以调用目标的UpdateStar函数。我们需要明确将其强制转型为Star,因为编辑器可以处理任何类型的对象,它使用通用的UnityEngine.Object类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public override void OnInspectorGUI () {
star.Update();
EditorGUIUtility.LookLikeInspector();
EditorGUILayout.PropertyField(center, true);
EditorGUILayout.PropertyField(points, true);
EditorGUILayout.PropertyField(frequency);
int totalPoints = frequency.intValue * points.arraySize;
EditorGUI.indentLevel -= 1;
if (totalPoints < 3) {
EditorGUILayout.HelpBox("At least three points are needed.", MessageType.Warning);
}
else {
EditorGUILayout.HelpBox(totalPoints + " points in total.", MessageType.Info);
}
EditorGUI.indentLevel += 1;
if (star.ApplyModifiedProperties()) {
(target as Star).UpdateMesh();
}
}

现在我们做出修改后网就会立即更新,这就让编辑整体来说简单多了。当然,它不会对撤销有反应。

不幸的是,没有简单通用安全方式来检查Unity中的撤销时间,但是我们现在已经很接近了。在我们的例子中,我们可以通过检车ValidateCommand时间发送来作为撤销动作的参考。由于此事件必须跟当前选择的对象有关,我们只需要将它看做我们组建发生了修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public override void OnInspectorGUI () {
star.Update();
EditorGUIUtility.LookLikeInspector();
EditorGUILayout.PropertyField(center, true);
EditorGUILayout.PropertyField(points, true);
EditorGUILayout.PropertyField(frequency);
int totalPoints = frequency.intValue * points.arraySize;
EditorGUI.indentLevel -= 1;
if (totalPoints < 3) {
EditorGUILayout.HelpBox("At least three points are needed.", MessageType.Warning);
}
else {
EditorGUILayout.HelpBox(totalPoints + " points in total.", MessageType.Info);
}
EditorGUI.indentLevel += 1;
if (star.ApplyModifiedProperties() ||
(Event.current.type == EventType.ValidateCommand &&
Event.current.commandName == "UndoRedoPerformed")) {
(target as Star).UpdateMesh();
}
}

最后,多么完美的编辑啊!其他的呢?需要重置一个组件吗?在每个组件查看器右上角都有一个齿轮图标选项来重置组件。够了,我们的网在重置星星组件的时候不会发生更新。

我们可以通过在星星组件中添加一个Reset函数来检测组件的重置。这是一个只在编辑器中使用的Unity事件函数。任何时候事件发生,我们需要做的就是更新网。

1
2
3
void Reset () {
UpdateMesh();
}

好了,现在重置完了。我们全做完了吗?prefab怎么样呢?对我们的星星来说使用prefab没多大意义,因为我们的星星都是生成它自身的小网。如果你希望使用大量类似的星星,最后在三维编辑器中创建一个星星模型并导入网。这样星星就可以共享相同的网了。但是假如我们的确希望使用一个prefab,可以之后个人自己初始化类似的星星。

由于我们的prefab不是以实例的形式存在在场景中,我们不希望为他们创建一个网。它们的Unity事件函数永远都不会调用,但是我们还是需要在编辑器中更新它。我们可以使用PrefabUnity.GetPrefabType函数来检测是否我们的查看器对象是一个prefab。如果是,我们就需要更新了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public override void OnInspectorGUI () {
star.Update();
EditorGUIUtility.LookLikeInspector();
EditorGUILayout.PropertyField(center, true);
EditorGUILayout.PropertyField(points, true);
EditorGUILayout.PropertyField(frequency);
int totalPoints = frequency.intValue * points.arraySize;
EditorGUI.indentLevel -= 1;
if (totalPoints < 3) {
EditorGUILayout.HelpBox("At least three points are needed.", MessageType.Warning);
}
else {
EditorGUILayout.HelpBox(totalPoints + " points in total.", MessageType.Info);
}
EditorGUI.indentLevel += 1;
if (star.ApplyModifiedProperties() ||
(Event.current.type == EventType.ValidateCommand &&
Event.current.commandName == "UndoRedoPerformed")) {
if (PrefabUtility.GetPrefabType(target) != PrefabType.Prefab){
(target as Star).UpdateMesh();
}
}
}

星星prefab.

现在我们可以从层级中安全的拖拽出一个星星到项目视图中将它变为一个prefab。

不幸的是,对prefab的修改不会引起prefab实例的网的更新。它会导致每个prefab的修改触发所有实例的onDisable和OnEnable Unity事件函数。我们可以使用这个来更新我们的网。由于OnEnable同事也好在对象激活的时候调用,我们可以用OnEnable来代替我们的开始函数。现在它们的实例会在修改prefabs时候继续。

1
2
3
void OnEnable () {
UpdateMesh();
}

现在结束了吧?如果我们希望在同一时间支持编辑多个星星则还需要修改。只需要赋值我们的星星并尽量选择它们。

现在还不支持编辑多个对象。

现在我们需要支持多对象编辑。幸运的是,这很简单。我们需要添加CanEditMultipleObjects属性来表明我们的编辑器支持此功能。之后我们需要初始化所有模板的SerializedObject,而不仅仅只是初始化一个。我们需要在检测到修改的时候保证我们更新所有的目标。

同时不需要展示点信息,因为在星星有不同点数的时候是没有意义的。但是你可以随时检测它们是否相同的,是否显示。

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
using UnityEditor;
using UnityEngine;
 
[CanEditMultipleObjects, CustomEditor(typeof(Star))]
public class StarInspector : Editor {
 
private SerializedObject star;
private SerializedProperty center, points, frequency;
 
void OnEnable () {
star = new SerializedObject(targets);
center = star.FindProperty("center");
points = star.FindProperty("points");
frequency = star.FindProperty("frequency");
}
 
public override void OnInspectorGUI () {
star.Update();
EditorGUIUtility.LookLikeInspector();
EditorGUILayout.PropertyField(center, true);
EditorGUILayout.PropertyField(points, true);
EditorGUILayout.PropertyField(frequency);
if (targets.Length == 1) {
int totalPoints = frequency.intValue * points.arraySize;
EditorGUI.indentLevel -= 1;
if (totalPoints < 3) {
EditorGUILayout.HelpBox("At least three points are needed.", MessageType.Warning);
}
else {
EditorGUILayout.HelpBox(totalPoints + " points in total.", MessageType.Info);
}
EditorGUI.indentLevel += 1;
}
if (star.ApplyModifiedProperties() ||
(Event.current.type == EventType.ValidateCommand &&
Event.current.commandName == "UndoRedoPerformed")) {
foreach (Star s in targets) {
if (PrefabUtility.GetPrefabType(s) != PrefabType.Prefab){
s.UpdateMesh();
}
}
}
}
}

多对象编辑

原文链接:http://catlikecoding.com/unity/tutorials/editor/star/


建议使用电驴(eMule)下载分享的资源。

说明
:本教程来源互联网或网友分享或出版商宣传分享,仅为学习研究或媒体推广,wanshiok.com不保证资料的完整性。
 
上一篇:【Unity C#编程】制作星星(四)  下一篇:【Unity C#编程】制作星星(二)