手把手教你做3D扫雷:准备篇
发表于2017-01-06
益智类游戏一般是酱紫的:网格,特定行为的格子还有规则。在本系列中,我将带领大家撸出一个简单的经典的扫雷游戏,这可谓是益智类游戏的代表。
对于想做小游戏的朋友们,不管你想做简单的记忆游戏还是复杂的策略游戏,实现扫雷的基础模块对初学者可谓受益匪浅。教程的第一部分,我们对我们的游戏做一些基础的准备。我们需要准备的东西呢,就是,还有你啦。
扫雷,顾名思义,就是在一块区域内把所有非地雷的格子揭开即胜利;踩到地雷格子就算失败。区域的大小随难度的大小决定,有9X9(简单)到16X30(困难)之分,另外还有自定义的。
通过点击格子来打开它,如果里面有雷,你就输了;没有雷,出现一个数字n,表示在与其相邻的8个格子内存在着n个雷。如果相邻的格子没有雷,那么这个格子也会被发现。
从上面的规则中,我们可以推断出扫雷简单版需要的几个要素:
· 布满格子的网格
· 格子能够填充一个雷的大小
· 格子能够随着鼠标的点击响应事件
· 当某个各自响应鼠标点击事件时,它能够计算出周围的雷的数量。
创建一个基本的格子
新建一个Unity项目,再创建一个cube命名为Tile。将它拖到项目文件中作为一个预制物体。我们会用这个没有脚本的格子作为玩的范围,而后我们会添加脚本给他。
新建一个空的物体命名为Grid,同样把它作为预制物体。它将作为游戏区域和所有格子的生成器。再新建一个Js脚本,命名为Grid,将其添加到Grid物体上面。Grid脚本主要创建了一个域,如下:
[C#] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 | public var tilePrefab: GameObject; public var numberOfTiles: int = 10; public var distanceBetweenTiles: float = 1.0; function Start() { CreateTiles(); } function CreateTiles() { } |
然后,指定Grid物体上面的Tile Prefab公有变量,将预制物体Tile赋值给它。numberOfTiles变量表示允许你创建的格子数,DistanceBetweenTiles声明了调整格子间距的大小。
![](http://img.manew.com/data/attachment/forum/201612/27/094047iofaxplpgml2bmjp.png.thumb.jpg)
现在,网格生成器还没有具体功能,在CreateTile()函数中添加以下代码:
[C#] 纯文本查看 复制代码
1 2 3 4 5 6 | var xOffset: float = 0.0; for (var tilesCreated: int = 0; tilesCreated < numberOfTiles; tilesCreated += 1) { xOffset += distanceBetweenTiles; Instantiate(tilePrefab, Vector3(transform.position.x + xOffset, transform.position.y, transform.position.z), transform.rotation); } |
如果你执行当前的场景,就会创建一行格子,就像这样:
![](http://img.manew.com/data/attachment/forum/201612/27/094045dvwlhhvht1ltpl17.png.thumb.jpg)
这个函数复制了我们指定数量的格子,然后我们将他们放置一行,通过distanceBetweenTiles控制他们之间的距离。调整距离以达到适当的距离。
对于扫雷游戏,我们需要的是让格子摆成一个网格,而不是一条线。为了实现这种效果,我们还需要在Grid代码的开始添加以下代码:
[C#] 纯文本查看 复制代码
public var tilesPerRow: int = 4; |
调整CreateTile()函数,就像这样:
[C#] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | function CreateTiles() { var xOffset: float = 0.0; var zOffset: float = 0.0; for (var tilesCreated: int = 0; tilesCreated < numberOfTiles; tilesCreated += 1) { xOffset += distanceBetweenTiles; if (tilesCreated % tilesPerRow == 0) { zOffset += distanceBetweenTiles; xOffset = 0; } Instantiate(tilePrefab, Vector3(transform.position.x + xOffset, transform.position.y, transform.position.z + zOffset), transform.rotation); } } |
![](http://img.manew.com/data/attachment/forum/201612/27/094046vulffsubmsjby7ti.png.thumb.jpg)
如果你设置的tilesPerRow变量合适,生成器便会创建一个很棒的规则区域。如果你编程能力很强的话,你还可以尝试更好的优化方案。
既然我们已经创建了基座扫雷格子,那么我们就可以给它添加“炸弹”了。新建一个Js脚本,命名为Tile,并将它添到Tile预制物体上,然后声明一个bool变量。
[C#] 纯文本查看 复制代码
public var isMined: boolean = false ; |
这个变量表示在格子中是否存在炸弹。接着我们得将“炸弹”放进网格中。为了实现这样的效果,我们需要改变Tile预制物体的游戏物体类型,使其转变为我们刚刚创建的Tile类,如下:
[C#] 纯文本查看 复制代码
public var tilePrefab: Tile; |
然后把Tile物体再次赋值给这个变量。现在我们就可以开始访问这些变量了。赋值炸弹还是有些技巧的,我们应该在网格生成器中赋值。
首先,在Grid代码中用三个数组来表示一个格子的数据:
[C#] 纯文本查看 复制代码
1 2 | static var tilesAll: Tile[]; static var tilesMined: Array; static var tilesUnmined: Array; |
这里,我们需要初始化数组。初始化操作放在CreateTiles函数中进行,如下:
[C#] 纯文本查看 复制代码
1 2 | tilesAll = new Tile[numberOfTiles]; tilesMined = new Array(); tilesUnmined = new Array(); |
然后,还是在CreateTiles函数中改变实例化操作的参数,如下:
[C#] 纯文本查看 复制代码
1 | var newTile = Instantiate(tilePrefab, Vector3(transform.position.x + xOffset, transform.position.y, transform.position.z + zOffset), transform.rotation); tilesAll[tilesCreated] = newTile; |
在CreateTiles函数最后添加AssighMines函数:
[C#] 纯文本查看 复制代码
AssignMines(); |
CreateTiles函数整体如下:
[C#] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | function CreateTiles() { tilesAll = new Tile[numberOfTiles]; tilesMined = new Array(); tilesUnmined = new Array(); var xOffset: float = 0.0; var zOffset: float = 0.0; for (var tilesCreated: int = 0; tilesCreated < numberOfTiles; tilesCreated += 1) { xOffset += distanceBetweenTiles; if (tilesCreated % tilesPerRow == 0) { zOffset += distanceBetweenTiles; xOffset = 0; } var newTile = Instantiate(tilePrefab, Vector3(transform.position.x + xOffset, transform.position.y, transform.position.z + zOffset), transform.rotation); tilesAll[tilesCreated] = newTile; } AssignMines(); } |
然后,实现AssighMines函数:
[C#] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 | function AssignMines() { tilesUnmined = tilesAll; for (var minesAssigned: int = 0; minesAssigned < numberOfMines; minesAssigned += 1) { var currentTile: Tile = tilesUnmined[Random.Range(0, tilesUnmined.length)]; tilesMined.Push(currentTile); tilesUnmined.Remove(currentTile); currentTile.GetComponent(Tile).isMined = true ; } } |
代码梳理:当一个格子Tile在CreateTiles函数中被创建的时候,这个格子便会被添加到tileAll数组中,所有的格子还会被复制到tileUnmined数组中。在这个数组中,我们随机选择格子给其添加炸弹。添加炸弹的操作就是设置变量isMined为true,即表示本格子内存在炸弹。
![](http://img.manew.com/data/attachment/forum/201612/27/094047zsvu5hhuhqdhnhu3.png.thumb.jpg)
现在,格子就矗立在Unity立方体上,让我们把他们变成真的“格子”。在原文件中,你能够找到一个叫做“puzzleObjects.fbx”的3D文件。把复制到的资源文件中,我们就能用它了。我们还要确保导入的文件它的比例为1,因为我们之前使用的设置就是这个比例。
文件导入设置如下:
![](http://img.manew.com/data/attachment/forum/201612/27/094046u10gti9wfwptri18.png.thumb.jpg)
然后设置一下预制物体格子,用tileImproved替换掉立方体原有的mesh。
![](http://img.manew.com/data/attachment/forum/201612/27/094046vk9uf06b69nq0c67.png.thumb.jpg)
当我们进行到这一步,重置格子的BoxCollider组件。这么做会让碰撞体更加契合格子。
![](http://img.manew.com/data/attachment/forum/201612/27/094046rof464uag1tsihuh.png.thumb.jpg)
最后给格子重新赋值一个新的材质,不然格子纯白色的,不好看。
![](http://img.manew.com/data/attachment/forum/201612/27/094046y6t9k8tl99z43kke.png.thumb.jpg)
注意:改变预制物体后记得要apply一下,这样我们改动的东西才能应用到之后创建的预制物体上。之后创建的格子都会照着新的格子去复制。
添加一个数字展示在格子上
我们需要一个数字来显示本格子周围有多少地雷。实现这个功能的一个简单方法就是使用Unity的3D文字。通过点击GameObject ->CreateOther ->3DText创建,然后把它添加到格子上,就像这样:
![](http://img.manew.com/data/attachment/forum/201612/27/094047azjr2k4pd11mjfmp.png.thumb.jpg)
让我们再改进一下:旋转text让它面朝上。先给它赋个0值,这样好让我们能够知道字体大小是否合适。还要调整字体和文本的大小,让字体显得清晰就行。
![](http://img.manew.com/data/attachment/forum/201612/27/094047r510195pocceo00e.png.thumb.jpg)
![](http://img.manew.com/data/attachment/forum/201612/27/094047jqcaqpaophzy9zjj.png.thumb.jpg)
好了,现在我们还要在代码中能够访问3D文本,添加以下代码:
[C#] 纯文本查看 复制代码
public var displayText: TextMesh; |
把3D文本拖到对应的变量上面:
![](http://img.manew.com/data/attachment/forum/201612/27/094047i7z1lagpwfp7mlv7.png.thumb.jpg)
再次提醒,改变预制物体需要应用一下!
总结
我们已经实现了扫雷游戏的基础函数,目前来说还不能运行,在下一个部分会添加更多的内容,敬请期待。