在上回书中说到,Boss给我分配了大富翁的制作,其中有一项地图的基础

其中要求是说需要建立很多很多cube,作为地图的地基

这样可以方便后期进行快速调节

但是问题来了,整个游戏的地图还算是很大的,如果生成100*200个cube的话,我能想到的就是一个一个地添加,然后点线面的方式进行增加

虽然可以一组一组的进行复制,但是还是一项很麻烦的工序

这时候我突然想到,之前在做2048的时候,可以通过代码去自动生成cube

那么我可不可以用代码这样生成呢

这样想之后,我发现了一个问题

一直以来所编写的所有C#Script都是在工程开始运行的时候执行的

也就是说,我的地基是在运行时候才能生成,这是绝对不行的

所以我又去询问了伟大的Google老师

Google老师告诉我,Unity有一个叫做MenuItem的东西

以这种方式可以写出来一个菜单控件,并且可以生成组件

这就符合我的需求了

于是根据下方参考资料中的代码,我写出了符合自己需要的代码

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using Microsoft.SqlServer.Server;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

public class MapCreater : EditorWindow
{
// 水平长度
private int _width;
// 垂直长度长度
private int _height;
// 复制的层数
private int _number;
// 位移增量
private Vector3 _position;

// 缩放增量
private Vector3 _scale;

[MenuItem("rtmacha/创建地图")] // 这里的rtmacha会显示在unity的带单中,创建地图为子菜单选项
public static void ShowWindow()
{
//GetWindow函数的意思是创建一个面板
//类型为BatchingLiteWindow
//第一个参数是面板的标题
EditorWindow.GetWindow(typeof(MapCreater), false, "MapCreater");
}
void OnGUI()
{

//然后使用IntField方法绘制一个int类型的输入框,使用与Vector3相似
//由于复制的数量不能为负数,所以我们要限制一下修改后的数值
_number = Mathf.Max(EditorGUILayout.IntField("复制的层数", _number), 0);
_width = Mathf.Max(EditorGUILayout.IntField("水平长度", _width), 0);
_height = Mathf.Max(EditorGUILayout.IntField("垂直长度长度", _height), 0);

//Space的作用是空一行
EditorGUILayout.Space();

//使用Vector3Field方法绘制 Vector3的输入框,第一个为输入框的标签(显示的名字),第二个参数需要传入需要显示的Vector3值。
//返回值为一个Vector3,当没有修改的时候,这个值为原来的值,当有修改的时候,这个返回值就是修改后的值。
//例如,把返回值赋予给_position,这样,输入框有修改的时候,_position能够拿到最新的值。
_position = EditorGUILayout.Vector3Field("位移增量", _position);

_scale = EditorGUILayout.Vector3Field("Scale", _scale);

EditorGUILayout.Space();

//BeginHorizontal方法和EndHorizontal是成对存在的,然后他们的作用是水平布局,在两个函数内绘制的UI会限制在一个水平位置。
//相似的方法还有BeginVertical和EndVertical,是垂直布局。
EditorGUILayout.BeginHorizontal();

//绘制一个Generate Button,这里使用GUILayout而不使用EditorGUILayout是因为EditorGUILayout没有Button(不知道原因)。
//Button方法第一个参数是button显示的label。
//返回值为Button是否为点击,
if (GUILayout.Button("Generate"))
{
Generate();
}

//这里缓存Cancel按钮的状态,在EndHorizontal之后再调用Cancel方法。
bool isCancel = GUILayout.Button("Cancel");

EditorGUILayout.EndHorizontal();

if (isCancel)
{
Cancel();
}
}
///

/// 生成复制对象
///
private void Generate()
{
//我们将obj1初始化为一个Cube立方体,当然我们也可以初始化为其他的形状
GameObject MapBase = new GameObject("MapBase");
//设置物体的位置Vector3三个参数分别代表x,y,z的坐标数
MapBase.transform.position = new Vector3(0, 0, 0);

GameObject layerObj = new GameObject("Layer 1");
layerObj.transform.SetParent(MapBase.transform);

GameObject obj = new GameObject("cubeGroup 1");
obj.transform.SetParent(layerObj.transform);

for (int j = 0; j < _height; j++)
{
//我们将obj1初始化为一个Cube立方体,当然我们也可以初始化为其他的形状
GameObject obj1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
//设置物体的位置Vector3三个参数分别代表x,y,z的坐标数
obj1.transform.position = new Vector3(0, 0, 0);
obj1.transform.SetParent(obj.transform);
//给这个创建出来的对象起个名字
obj1.name = "cube " + (j + 1);
obj1.transform.localScale = Vector3.Max(_scale, new Vector3(0.1f, 0.1f, 0.1f));
obj1.transform.localPosition = obj.transform.localPosition + Vector3.right * _position.x * j * obj1.transform.localScale.x;
//Undo.RegisterCreatedObjectUndo(obj1, "Batching Create GameObject");
}

for (int i = 0; i < _width; i++)
{
GameObject gameObject = Instantiate(obj);
gameObject.transform.name = "cubeGroup " + (i + 2);
gameObject.transform.SetParent(layerObj.transform);
gameObject.transform.localScale = Vector3.Max(_scale, new Vector3(0.1f, 0.1f, 0.1f));
gameObject.transform.localPosition = obj.transform.localPosition + Vector3.forward * _position.z * i * gameObject.transform.localScale.z;
}

for (int i = 0; i < _number; i++)
{
GameObject gameObject = Instantiate(layerObj);
gameObject.transform.name = "Layer " + (i + 2);
gameObject.transform.SetParent(MapBase.transform);
gameObject.transform.localScale = Vector3.Max(_scale, new Vector3(0.1f, 0.1f, 0.1f));
gameObject.transform.localPosition = layerObj.transform.localPosition + Vector3.up * _position.y * i * gameObject.transform.localScale.y;
}
}

/// 取消操作
/// 同ctrl + z
///
private void Cancel()
{
//PerformUndo作用跟ctrl + z一样
Undo.PerformUndo();
}
}


在我写完这些代码之后,我开始测试代码

最开始是使用双重循环嵌套逐一生成的

比如我需要生成一个 100*200的cube组,我就需要生成2w次

后来改成了分组复制,这样做的好处就是,首先生成100次的一行,然后将这行作为整体进行复制,复制200次即可

这样总体的生成为300次,生成速度会比原来快很多

于是,在写完最后一个功能(生成多层)之后,我不知道为什么脑抽生成了一个100*100*100的cube,也就是说我一下子生成了100w个cube组合成了一个大的cube。。

结果可想而知,稍微动一下地图整个unity就卡死了hhhhhh

仔细看那一大坨绿色的东西,就是那100w个cube组合成的一个大号的cube啦

点了删除之后,unity又开始疯狂处理了

就酱,等着unity全部再说吧

于是我又水了一篇文章hhhhh

成功向独立游戏制作人迈进一步,因为开始自己写Unity组件了

参考资料

https://indienova.com/indie-game-development/unity3d-plugin-development-tutorial-02/