您的位置:首页 > 移动开发 > Unity3D

【unity 5学习记录】 可编辑地形 网格 原理讲解 17.8.8

2017-08-08 14:38 711 查看


本篇为上一篇的原理讲解,我觉得我的表达能力很差,看上一篇的源代码以及边上的注释可能理解到会容易一些

先说ray.cs
射线探测 鼠标按键的探测也写在这里面

0是左键 1右键 2中间滚轮键 px,py,pz是射线所碰到的方块面提供的坐标

C# Code
1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

if (Input.GetMouseButtonDown(0))

{

terraManager.BuildBlock(px, py - 1, pz, 0);

}

else if (Input.GetMouseButtonDown(1))

{

terraManager.BuildBlock(px, py, pz, 1);

}

else if (Input.GetMouseButtonDown(2))

{

terraManager.SetTerrain(10, 10, 10, 5);

}

else

{

//没有碰撞时

}

TerrainManager里改写的比较多因为要建立的是三维的。之前的面片是二维的

思路是获取鼠标指针射线碰撞的坐标,转化成要操作的方块坐标。

目前是操作一个区块里的方块。

private Vector3 chunkSize;//区块的长宽高

private int groundHeight;//地面高度

private ushort[] blockData;//方块数组

方块数组初始化对象的时候元素数([]里面要表示的)就是 区块 长*宽*高,代码如下

C# Code
1

blockData = new ushort[(int)(chunkSize.x * chunkSize.y * chunkSize.z) + 1];
这是根据方块坐标 计算方块编号的函数 方块坐标从0,0,0开始到(chunksize.x-1,chunksize.y-1,chunksize.z-1)

C# Code
1

2

3

4

private int getBlockIndex(int x, int y, int z)

{

return (z + y * ((int)chunkSize.z) + (x * (int)(chunkSize.y) * (int)(chunkSize.z)));

}

方块数组的值设置为0就是空 1就是有方块,然后接下来在顶点以及三角形遍历的时候,就是读取方块数据来判断是否需要绘制该顶点/三角形

先来说顶点绘制。由于顶点比方块多 我们for循环的范围需要+1。比方说2*2*2的方块需要遍历的顶点数就是3*3*3,首先我们要通过函数bool ifIsSide(int x, int y, int z,int VorT)来判断当前遍历是不是周围方块。如果是的话先暂时避开。因为他们并不是上下左右前后6个面都能获取到方块数据。这个函数还有一个参数来区分是顶点的避开还是三角形面的避开,如果是顶点遍历那VorT取0
如果是三角形遍历那 VorT取1

下面附上该函数代码

C# Code
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

private bool ifIsSide(int x, int y, int z, int VorT) //v =0 t=1

{

//检测是否在边界

if (VorT == 0)

{

if (x == 0 || y == 0 || z == 0 || x >= (int)chunkSize.x || y >= (int)chunkSize.y || z >= (int)chunkSize.z)

{

return true;

}

else

{

return false;

}

}

else

{

if (x == 0 || y == 0 || z == 0 || x >= (int)chunkSize.x - 1 || y >= (int)chunkSize.y - 1 || z >= (int)chunkSize.z - 1)

{

return true;

}

else

{

return false;

}

}

}

然后在接下来就是判断包含该顶点位置的四周的方块是不是都是空或都是实心。如果不是。那这个顶点就要被加入进去。函数是bool ifDifFromAround(int x, int y, int z) 参数为当前遍历的顶点坐标

C# Code
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

private bool ifDifFromAround(int x, int y, int z)

{

//检测是否和周围的方块不同 存在或不存在

if (blockData[getBlockIndex(x, y, z)] == 0)

{

if (blockData[getBlockIndex(x - 1, y, z)] > 0 ||

blockData[getBlockIndex(x, y - 1, z)] > 0 ||

blockData[getBlockIndex(x, y, z - 1)] > 0 ||

blockData[getBlockIndex(x - 1, y - 1, z)] > 0 ||

blockData[getBlockIndex(x, y - 1, z - 1)] > 0 ||

blockData[getBlockIndex(x - 1, y, z - 1)] > 0 ||

blockData[getBlockIndex(x - 1, y - 1, z - 1)] > 0)

{

return true;

}

else

{

return false;

}

}

else

{

if (blockData[getBlockIndex(x - 1, y, z)] == 0 ||

blockData[getBlockIndex(x, y - 1, z)] == 0 ||

blockData[getBlockIndex(x, y, z - 1)] == 0 ||

blockData[getBlockIndex(x - 1, y - 1, z)] == 0 ||

blockData[getBlockIndex(x, y - 1, z - 1)] == 0 ||

blockData[getBlockIndex(x - 1, y, z - 1)] == 0 ||

blockData[getBlockIndex(x - 1, y - 1, z - 1)] == 0)

{

return true;

}

else

{

return false;

}

}

return false;

}

整个顶点遍历的代码如下

C# Code
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

private Vector3[] GetVertives()

{

int sum;

int index = 0;

//GetUV();

vertives = new Vector3[(int)((chunkSize.x + 1) * (chunkSize.y + 1) * (chunkSize.z + 1) + 1)];

for (int x = 0; x < chunkSize.x + 1; x++)

{

for (int y = 0; y < chunkSize.y + 1; y++)

{

for (int z = 0; z < chunkSize.z + 1; z++)

{

if (y == 0)

{

vertives[index] = new Vector3(x, y, z);

}

else

{

if (!ifIsSide(x, y, z, 0))

{

if (ifDifFromAround(x, y, z))

{

vertives[index] = new Vector3(x, y, z);

}

}

}

index++;

}

}

}

GetTriangles();

return vertives;

}

然后就是遍历三角形,其实是遍历方块坐标,先筛选出是实心的方块,然后分别判断这个方块坐标
上下左右前后的方块是不是空的。如果是空的。就需要吧这两个方块之间的面通过添加三角形的方式绘制出来。以下是代码

C# Code
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

134

135

136

137

138

139

140

141

142

143

144

private int[] GetTriangles()

{

int sum = Mathf.FloorToInt(chunkSize.x * chunkSize.y * chunkSize.z * 6);

triangles = new int[sum];

uint index = 0;

for (int x = 0; x < chunkSize.x; x++)

{

for (int y = 0; y < chunkSize.y; y++)

{

for (int z = 0; z < chunkSize.z; z++)

{

if (y == 0)

{

int self = z + y * ((int)chunkSize.z + 1) + (x * (int)(chunkSize.y + 1) * (int)(chunkSize.z + 1));

int next = z + (y * (int)(chunkSize.z + 1) + ((x + 1) * (int)((chunkSize.y + 1) * (chunkSize.z + 1))));

triangles[index] = self;

triangles[index + 1] = self + 1;

triangles[index + 2] = next + 1;

triangles[index + 3] = self;

triangles[index + 4] = next + 1;

triangles[index + 5] = next;

index += 6;

}

else

{

if (!ifIsSide(x, y, z, 1))

{

if (blockData[getBlockIndex(x, y, z)] == 0)

{

//check up and draw triangle

if (blockData[getBlockIndex(x, y + 1, z)] != 0)

{

int self = z + (y + 1) * ((int)chunkSize.z + 1) + (x * (int)(chunkSize.y + 1) * (int)(chunkSize.z + 1));

int next = z + ((y + 1) * (int)(chunkSize.z + 1) + ((x + 1) * (int)((chunkSize.y + 1) * (chunkSize.z + 1))));

<
233e4
wbr> triangles[index] = self;

triangles[index + 1] = next + 1;

triangles[index + 2] = self + 1;

triangles[index + 3] = self;

triangles[index + 4] = next;

triangles[index + 5] = next + 1;

index += 6;

}

//check doawn and draw triangle

if (blockData[getBlockIndex(x, y - 1, z)] != 0)

{

int self = z + (y) * ((int)chunkSize.z + 1) + (x * (int)(chunkSize.y + 1) * (int)(chunkSize.z + 1));

int next = z + ((y) * (int)(chunkSize.z + 1) + ((x + 1) * (int)((chunkSize.y + 1) * (chunkSize.z + 1))));

triangles[index] = self;

triangles[index + 1] = self + 1;

triangles[index + 2] = next + 1;

triangles[index + 3] = self;

triangles[index + 4] = next + 1;

triangles[index + 5] = next;

index += 6;

}

//side

if (blockData[getBlockIndex(x, y , z - 1)] != 0)

{

int self = z + (y) * ((int)chunkSize.z + 1) + (x * (int)(chunkSize.y + 1) * (int)(chunkSize.z + 1));

int next = z + ((y) * (int)(chunkSize.z + 1) + ((x + 1) * (int)((chunkSize.y + 1) * (chunkSize.z + 1))));

int sup = self + (int)chunkSize.z + 1;

int nup = next + (int)chunkSize.z + 1;

triangles[index] = self;

triangles[index + 1] = nup;

triangles[index + 2] = sup;

triangles[index + 3] = self;

triangles[index + 4] = next;

triangles[index + 5] = nup;

index += 6;

}

if (blockData[getBlockIndex(x, y , z + 1)] != 0)

{

int self = z + 1 + (y) * ((int)chunkSize.z + 1) + (x * (int)(chunkSize.y + 1) * (int)(chunkSize.z + 1));

int next = z + 1 + ((y) * (int)(chunkSize.z + 1) + ((x + 1) * (int)((chunkSize.y + 1) * (chunkSize.z + 1))));

int sup = self + (int)chunkSize.z + 1;

int nup = next + (int)chunkSize.z + 1;

triangles[index] = self;

triangles[index + 1] = sup;

triangles[index + 2] = nup;

triangles[index + 3] = self;

triangles[index + 4] = nup;

triangles[index + 5] = next;

index += 6;

}

if (blockData[getBlockIndex(x - 1, y, z)] != 0)

{

int self = z + (y) * ((int)chunkSize.z + 1) + (x * (int)(chunkSize.y + 1) * (int)(chunkSize.z + 1));

int next = z + ((y) * (int)(chunkSize.z + 1) + ((x + 1) * (int)((chunkSize.y + 1) * (chunkSize.z + 1))));

int sup = self + (int)chunkSize.z + 1;

int nup = next + (int)chunkSize.z + 1;

triangles[index] = self;

triangles[index + 1] = sup;

triangles[index + 2] = sup + 1;

triangles[index + 3] = self;

triangles[index + 4] = sup + 1;

triangles[index + 5] = self + 1;

index += 6;

}

if (blockData[getBlockIndex(x + 1, y , z)] != 0)

{

int self = z + (y) * ((int)chunkSize.z + 1) + ((x + 1) * (int)(chunkSize.y + 1) * (int)(chunkSize.z + 1));

int next = z + ((y) * (int)(chunkSize.z + 1) + ((x + 2) * (int)((chunkSize.y + 1) * (chunkSize.z + 1))));

int sup = self + (int)chunkSize.z + 1;

int nup = next + (int)chunkSize.z + 1;

triangles[index] = self;

triangles[index + 1] = sup + 1;

triangles[index + 2] = sup;

triangles[index + 3] = self;

triangles[index + 4] = self + 1;

triangles[index + 5] = sup + 1;

index += 6;

}

//vertives[index] = new Vector3(x, y, z);

}

//check dawn and draw

}

//index++;

}

}

}

}

return triangles;

}

到这里为止我们就已经实现了改变方块数据。以及根据方块数据绘制面所需要调用的函数了

接下来是执行方块的放置和破坏的函数。表面上是放置和破坏。实际上是把整个地形重新绘制一遍,所以我们要引入区块的思想。不然绘制的太多。会导致运算量过大

C# Code
1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public void BuildBlock(float px, float py, float pz, int blockID)

{

blockData[(int)((pz + py * ((int)chunkSize.z) + (px * (int)(chunkSize.y) * (int)(chunkSize.z))))] = (ushort)blockID;

GetVertives();

//给mesh 赋值

mesh.Clear();

mesh.vertices = vertives;//,pos);

mesh.uv = uvs;

mesh.triangles = triangles;

//重置法线

mesh.RecalculateNormals();

//重置范围

mesh.RecalculateBounds();

terrain.GetComponent<MeshCollider>().sharedMesh = mesh;

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息