手把手教你做3D扫雷:准备篇

发表于2017-01-06
评论0 4.4k浏览
       益智类游戏一般是酱紫的:网格,特定行为的格子还有规则。在本系列中,我将带领大家撸出一个简单的经典的扫雷游戏,这可谓是益智类游戏的代表。
      对于想做小游戏的朋友们,不管你想做简单的记忆游戏还是复杂的策略游戏,实现扫雷的基础模块对初学者可谓受益匪浅。教程的第一部分,我们对我们的游戏做一些基础的准备。我们需要准备的东西呢,就是,还有你啦。

扫雷的规则
      扫雷,顾名思义,就是在一块区域内把所有非地雷的格子揭开即胜利;踩到地雷格子就算失败。区域的大小随难度的大小决定,有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声明了调整格子间距的大小。


  现在,网格生成器还没有具体功能,在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);
}
  如果你执行当前的场景,就会创建一行格子,就像这样:

 

  这个函数复制了我们指定数量的格子,然后我们将他们放置一行,通过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);
    }
}
  如果运行程序,应该就像这样:


  如果你设置的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,即表示本格子内存在炸弹。

 

让格子看起来更有趣
  现在,格子就矗立在Unity立方体上,让我们把他们变成真的“格子”。在原文件中,你能够找到一个叫做“puzzleObjects.fbx”的3D文件。把复制到的资源文件中,我们就能用它了。我们还要确保导入的文件它的比例为1,因为我们之前使用的设置就是这个比例。
文件导入设置如下:

 

  然后设置一下预制物体格子,用tileImproved替换掉立方体原有的mesh。

 

  当我们进行到这一步,重置格子的BoxCollider组件。这么做会让碰撞体更加契合格子。

 

  最后给格子重新赋值一个新的材质,不然格子纯白色的,不好看。

 

  注意:改变预制物体后记得要apply一下,这样我们改动的东西才能应用到之后创建的预制物体上。之后创建的格子都会照着新的格子去复制。

添加一个数字展示在格子上
  我们需要一个数字来显示本格子周围有多少地雷。实现这个功能的一个简单方法就是使用Unity的3D文字。通过点击GameObject ->CreateOther ->3DText创建,然后把它添加到格子上,就像这样:

 

  让我们再改进一下:旋转text让它面朝上。先给它赋个0值,这样好让我们能够知道字体大小是否合适。还要调整字体和文本的大小,让字体显得清晰就行。

 

 

  好了,现在我们还要在代码中能够访问3D文本,添加以下代码:
[C#] 纯文本查看 复制代码
 
public var displayText: TextMesh;
把3D文本拖到对应的变量上面:

 

再次提醒,改变预制物体需要应用一下!

总结
  我们已经实现了扫雷游戏的基础函数,目前来说还不能运行,在下一个部分会添加更多的内容,敬请期待。

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