网易首页 > 网易游戏 > 正文

《星际争霸II》休闲地图示例教程:扫雷

2011-06-10 22:23:11 来源: 网易游戏频道 举报
0
分享到:
T + -

本文由麦德三世为网易星际争霸II地图大赛独家编写,转载请注明。

休闲地图示例教程:扫雷

休闲地图示例教程——扫雷

在本教程中,你可以通过跟随教程制作一个简单的扫雷游戏来了解一些用编辑器来制作休闲游戏的入门知识。

注意,本教程假设你已经看过以下二篇暴雪官方教程,或已具备同等的基础知识:

地形模块 触发模块

以上二篇教程介绍了星际争霸II编辑器中绘制地形和使用触发器的最基本知识。了解它们是学习本教程的先决条件。

>>点击下载本教程示例地图MineSweeper

开始之前

先别忙着进入正题,在开始之前,先让我们观赏一段轻松的恶搞视频来活跃一下气氛——传说中的“扫雷真人版”:

好了,为啥要贴这部视频呢,因为我们今天要做的就是“真人版”扫雷。虽然这里面的“人”实际上还是游戏里的角色就是了。

休闲地图示例教程:扫雷

虽然严格来说,扫雷是一个2D平面游戏,因此要完全模仿扫雷的话,实际上应该使用自定义界面,直接绘制扫雷的2D界面,并在此基础上进行制作(就像官方《宝石》地图的界面那样)。但是自定义界面在SC2编辑器中是一个相当偏向中高级的部分,具有一定难度。而本教程又仅仅是一个入门教程,因此我会尽量避开编辑器中的困难部分,用尽可能简单的方式来教大家实现扫雷这个游戏。所以在这个教程中,我们要制作的是一个3D版本的扫雷,玩家操作着扫雷工到处移动并挖雷插旗,地图中仅仅对自定义界面部分作一些最最基本的接触——比如图中的“挖掘”和“插旗”按钮,以及右上角显示的“尚需挖开的格子数”。

准备地形和主角

由于本教程并不是地形教程,所以关于如何绘制地形的部分,大家可以参考官方教程《地形模块》。但是这里我也会对关键部分做一些说明。下面给出我用来演示本教程的地图所采用的地形图片。

注意 :如果你想要完全按照本篇教程的步骤来进行每一步操作,请务必在创建地图时选择搭载“战役”Mod。具体搭载战役Mod的方法,在官方教程《地形模块》中已有说明

休闲地图示例教程:扫雷

本教程的扫雷网格数设计为固定的9x9格方阵。因此我在地图上用Protoss能量线(一种装饰物)分割出了9x9个方格。网格外围采用完全不同的地形纹理,以明确边界。这里请大家注意一点,我想大部分同学的视力还不能达到可以手动摆放这些能量线并使得所有方格边长完全相同的程度(显然我不行)。所以我们需要打开地形模块自带的网格辅助系统来帮助我们定位。

具体来说,首先我们在地形模块中进入地形层,然后在菜单中,选择“查看->显示栅格”,然后在子菜单中选择“大型”,这样我们就能在地形模块中看到密密麻麻的辅助网格了(如果你还是看不到,请按一次“V”键以退出游戏视图)。在这些网格当中,每两条平行的红线间距为8,每两条白线间距为4。本教程的地图直接沿着白线来绘制扫雷的格子,也就是说每个雷格边长为4。当然这并不是必须的,大家也可以自行决定别的边长和总格子数。

不过,在摆放边界线的时候,还有一个注意事项:大家必须先去菜单中,把“工具->对齐到栅格”勾上。这样,我们在摆放物件时,编辑器会自动帮我们把物件“吸附”到栅格线上。这样才能做出分毫不差的间距来。

休闲地图示例教程:扫雷

上面就是完成图,左边那个大标识牌是我为了应景起见放的,英文版的SC2中,标识牌上头有字:“钻头是男人的浪漫”云云。

休闲地图示例教程:扫雷

接下来我们决定扫雷的主角,这里我选的是矿工大叔,也就是标识牌上的那位同学,大家也可以选择别的单位作为主角。

休闲地图示例教程:扫雷

#!!#

放置印花(信标)

在正式开始写代码之前,我们还有一件事情要做:在每个格子里放上“印花”。看过地形模块的同学应该知道印花是什么,它是一种装饰物,而且还有着不止一个模型,其中我们可以在地形模块中通过[<]键和[>]键来改变它们具体显示的模型。

在本演示地图中,我们将使用印花来显示扫雷中表示周围雷数的那些数字。因为印花的多种模型中,就存在0-9这几个数字。当然,在实际应用中我们只需要1-8,因为扫雷中每个格子周围最多也就只能有8个地雷。而0个地雷是不会显示的,格子会直接留空。

一般来说,这些数字应该是在地图运行的时候动态创建的,因为我们不可能在制作阶段就知道地雷的布局,因为扫雷这个游戏,地雷若不是随机布下的话,那就毫无意义了。

不过,若想要动态创建数字,势必就要对创建位置进行一些坐标运算,以确保数字会创建在每个格子的中心,不过本教程旨在入门,所以打算绕过这些复杂的坐标运算。所以这里采用了一种特殊的方式:我们一开就在每个格子正中都摆上印花,然后在正式挖掘的时候,改变那些印花的模型,使得它们成为正确的数字,这样就不需要等到运行时才创建它们,省去了坐标运算的麻烦。而我们事先把每个格子都摆上印花,还有一个额外作用:我们可以用它们来表示尚未挖掘过的格子。

为了方便我们将印花精确摆放到每个格子的正中,我们需要进一步细化栅格。选择“查看->显示栅格->小型”我们就可以看到地形模块的栅格进一步细分了。除了原有的红白线之外,又出现了黑线,每条黑线之间的间距是1。

这样,我们就可以对着黑线,把所有的格子中心都放上印花了。本演示地图使用外面包一个三角形的感叹号形状来表示尚未被挖掘的格子,它和数字0-9一样。

不过,这里有一点要注意,本地图要摆放的印花,其实并非原装的印花,原本的印花是一种装饰物,而我们这次要摆放的东西却是单位。一种使用着印花模型的单位。为什么必须是单位呢?这个问题的答案将在后面揭晓,这里先卖个关子。

编辑器中并不存在使用印花模型的单位,我们得自己把它改出来。由于从头开始完全新建一个单位难度略高,并不是编辑器入门级别的要求,所以我们这里就直接修改现成的单位的模型好了。由于我们要求我们的“印花”无法被选择,而且没有碰撞,不会移动,别的单位可以直接踩上去,因此直接使用信标这种单位,然后修改它们的模型最为方便。

在菜单中,点击模块->数据就可以打开数据模块。我们在数据类型那一栏中选择“单位”就可以打开单位数据页。我们在这里使用搜索功能来搜索“信标”这个关键字,可以找到很多名字里带信标的单位,可以随便挑一个来改,本演示地图中改的是Protoss的信标,即信标 (星灵 大型)这个单位。

休闲地图示例教程:扫雷

我们选中信标 (星灵 大型),然后在左下角的对象浏览器中选择Beacon_Protoss(未命名),这个东西控制着信标 (星灵 大型)的模型。我们选中它后就可以看到右边的窗格中列出了Beacon_Protoss(未命名) 所具有的属性。我们首先要改的是美术-模型这个属性。我们将其改为印花。这样,信标 (星灵 大型)的模型从此就变为印花的模型了。

之后我们需要修改美术-比例这个属性为3,代表将模型放大3倍,这是为了使它可以适合目前我们地图的格子的尺寸。

注意:如果你看到的数据模块右侧窗格的数据并不是以列表模式显示的,请将右侧窗格上方的5个按钮中的前四个全部按下

休闲地图示例教程:扫雷

之后我们就可以在地面上放置信标 (星灵 大型)了,它看上去就和装饰物的印花一模一样。摆放时记得使用之前说过的网格对齐法,而且我们也同样可以用<和>按钮来改变它的外观。我们这里把81个格子都放上信标。然后把它们的外观设为外包一个三角形的感叹号,用来表示这个格子还未被挖掘过。

特别注意的是,如果你先放下81个信标,然后逐一修改它们的外观,那是会累死人的。所幸我们可以用复制功能来解决问题,我们先放下一个信标,调整好它的外观,然后选中它,按Ctrl+C和Ctrl+V就可以对它进行复制了,用复制功能复制出来的信标会直接使用和原信标相同的外观,无需再进一步修改。更重要的是,复制功能不止可以复制单个对象。比方说我们放慢一排信标以后,可以全部选中它们,这时候按Ctrl+C和Ctrl+V复制的就是这一整排信标,我们可以将其复制到下一行,以此类推的话,81个信标看似繁多,但其实三五下就能弄完的。

休闲地图示例教程:扫雷

不过,大家可以在这里发现,这些“印花”在我原本采用的沙地纹理上,显示的效果非常不好。下图是我用这个地形一路将该地图做到后期的样子,我们可以很明显的发现,这些数字印花在这种颜色的地表下根本看不清楚。

休闲地图示例教程:扫雷

因此后来我把雷区的地形纹理换成了巧克力色的黑土,这样就印花显示起来就清晰多了:

休闲地图示例教程:扫雷

#!!#

挖雷和埋雷的操作算法构思

现在我们来构思一下这个游戏的运行机制,首先我们必须解决随机布雷和挖掘操作这两个最基本的问题。说到扫雷,不可避免的就要涉及到算法。首先,埋雷的时候,如何随机选出N个不重复的格子布下地雷?而且,扫雷还有一个额外要求,玩家所挖的第一个格子必须不是地雷。其次,扫雷中挖地雷的时候,如果挖到周围8格都没有地雷的格子(也就是数字为0的格子),那么它就会自动翻开周围八格,如果周围八格中同样有数字为0的格子,同样必须自动翻开它周围八格,如此重复。也就就是我们在扫雷的时候按到空位时周围一片格子都被翻开的现象。

通常来说,不管你使用什么编程语言,要实现这些都必须使用一些较为复杂的算法。我们可以在网上搜索到扫雷编程常见的算法,基本都要用到数组和递归等等复杂的东西。不过前面说了,这是个入门教程,不可能说你必须会编程算法才能入门这个编辑器,那这门槛可就太高了。所以在本教程中,我尽量采用各种取巧的方式利用SC2本身的一些特性来大大简化这个扫雷游戏的算法,让入门读者使用非常简单技术也能达到一些高级算法才能实现的效果。

一、挖掘操作的傻瓜算法

先让我们来想想,在挖掘这个操作里我们需要做什么?首先,得到我们的扫雷者所在的格子,得到格子里中间放着的“印花”,看看这个格子里是否有地雷。然后决定是爆炸还是显示数字,如果是显示数字,那么就算出自己周围8格内的地雷总数,把印花的外观变为相应的数字。特别的,如果数字是0,那么还得把印花隐藏掉,因为0不会显示,而且还得自动翻开周围八格。

好的,首先,如何得到扫雷者所在格子中的印花?按照一般的思路,我们需要获取扫雷者(也就是我们的矿工大叔)所处的坐标,然后按照其XY轴的位置来判断它所在的格。这就需要麻烦的坐标运算和取余数运算,之后得到所在格后,你还是得通过坐标去找格子里的印花单位,这实在太麻烦了。所以这里说一下第一个取巧的办法:

在《触发模块》这篇教程中,我们应该都对区域这个东西稍有了解了,我们可以用单位进入和离开区域事件来制作触发,也可以获得区域中符合条件的所有单位。然后,其实星际争霸II编辑器有一个有趣之处,可以把一个区域附着到一个单位身上。这样单位移动时,区域也会跟着移动。这样,我们只要事先做一个比一个方格稍微小一点点的区域,把它套在英雄身上,这样它走进一个方格时。就能通过捕捉区域内的印花单位,来获得当前格子里的印花了,因为英雄身上的区域比一个格子稍微小一些,故而边长小于4,无论英雄怎么走,它身上的区域里最多就只能有一个印花,而这个印花必定是英雄当前所处的格子中的那个印花,这样我们就把第一个问题简单解决了。之前我之所以要求印花必须是一种单位,这便是原因之一。

更进一步的,由于每个格子都里都有且只有一个印花单位,而格子这个东西,本身并非单位也非装饰物,它其实是人类分割开来的地形而已,并不是一个单独的对象,所以,我们在今后的触发编写中可以进一步使用每个格子里的印花来代表其所处的格子。这样就可以把格子变成确定的对象了,而非只能用坐标来计算的地形位置。

下一个问题,看格子里是否有地雷。在这里,这个问题可以通过和获取印花完全相同的方式来解决。我们只要把地雷做成一种单位,然后只需要捕捉英雄身上那个一个格子大小的区域内的所有地雷组成的单位组,然后计算组中单位数量即可,反正0个就代表当前格子里没有地雷,1个就代表有,超方便的。

单位组:单位组即单位的集合。我们可以用触发器来建立很多的单位组。我们可以对单位组进行的操作有:把单位放入、踢出单位组依次列出单位组中所有单位,并对它们逐一进行操作计算单位组中的单位个数获得单位组中指定序号的单位获得单位组中的一个随机单位等等。注意,单位组可以是空的,也就是说这个集合里一个单位都没有。在《触发模块》这篇教程里我们已经使用过单位组了,不过在本教程中我们会用到它的更多功能。

第三个问题,如果当前格子没有地雷,就得计算周围八格里的地雷数这可怎么做呢?一般考虑的话,我们还是得通过坐标运算来获取左上、上、右上、左、右、左下、右下八个格子的坐标,然后再判断里面是否有地雷,最后将他们相加。更麻烦的是,如果计算的是边界上的格子,那么它们周围的格子数不到8个,还得记得处理,免得坐标越出边界。怎么想都很麻烦哪。嗳,其实,为什么不能举一反三呢?我们能计算1个格子里的地雷数,为什么就不能计算8个格子里的呢?其实,最简单的方法莫过于此:事先准备好一个3格x3格大小的区域,当要计算某个格子周围有几个地雷时,把这个区域套到该格中心的那个印花上就好。这样,通过获取区域中所有的地雷单位这个单位组,然后计算单位组中单位数量,就可以得到格子周围的地雷数了。

第四个问题,如果我们挖掘到的格子,数字是0,那么就得隐藏这个数字,然后掘开周围8个格子。这个怎么弄呢?隐藏这步很简单,我们有现成的触发器动作可以隐藏单位。而掘开周围8个格子这个,我们可以这样做,上面不是已经列出了挖掘这个操作要做些什么了么?而当挖出数字0时,再挖周围8个格子时,挖每个格子的操作其实和挖中间那个完全一样,所不同的只是挖掘目标而已。因此我们可以把整个挖掘操作做成一个带参数的自定义触发器动作,把想要挖的那个格子当作参数。前面说了,我们可以直接用格子中的印花来代表那个格子,而周围8格的印花是可以用上面说的方法直接获取的,所以整个过程就变的很简单了:如果我们挖到数字0,就对周围对每一个印花重新执行一次我们自定义好的挖掘操作本身。这样就能解决了。要获得周围那些印花,我们可以故技重施,用一个3格x3格的区域来获取周围所有印花组成的单位组,然后我们就可以用“挑选单位组中的每一个单位”这条动作来对该组内所有单位进行挖掘操作了。要注意的是,由于用3格x3格区域获得的所有印花单位会把中央的那个印花包括在内,所以对于已经挖掘过的印花,我们要做一个标记,然后让挖掘操作跳过它们。否则中间那个印花连续被挖到就会造成死循环了,因为它被挖掘后就重新执行“挖开周围8格”,这样下去可就没完没了。至于如何在已被挖掘的单位上做标记,我们将在下面正式操作的段落中说明。

二、随机埋雷的傻瓜算法

扫雷里面很重要的一点是,地雷的埋布必须是随机的。所以我们不可能像印花一样事先把地雷摆放在地面上。故而这就产生了两个问题。第一个问题,假设我们要埋下N个地雷,首先就得从所有的格子当中随机选出不重复的N个格子?第二个问题,我们在创建地雷的时候,如何获得我们想要放雷的位置的坐标?然后,埋雷的问题不止这两个。还有第三个问题,扫雷要求我们第一个挖下去的格子必须不能是地雷(正规的扫雷游戏都有这个规定)。我么在随机创建地雷的时候如何保证这一点?以下便讨论这三个问题:

对于问题一,如何选出N个不重复的格子:在编程界通常是用一个2维数组来表示所有的格子,用随机数来取格子。但数组,尤其是多维数组,我们不能要求刚入门的同学掌握。而且就算使用2维数组,如何取得不重复的N个随机元素,也还是需要一些算法的。所以在这里,我们再一次利用星际争霸II自带的机制来大大简化这个问题:

我前面放了一个小方框,里头对单位组这个东西做了一些简单介绍。说是我们可以把单位丢进单位组这个集合,也可以丢出去,还可以随机从里面取一个单位。而我前面也说了,我们可以直接用印花来代表它们所处的格子。那么从所有格子中获得N个不重复的格子的问题,就转化为了从所有印花中获得N个不重复的印花的问题。而印花是单位,所以我们可以利用单位组啊。先用所有的印花来构成一个单位组。然后随机取出一个印花,再将其踢出这个单位组;再从单位组中随机取一个,再踢出,如此重复N次,不就可以拿出N个不重复的印花了么?问题解决。

对于问题二,如何取得放雷位置的坐标:由于地雷必须是动态创建的,你在创造它时少不得要给它指定坐标,这个坐标就是你所随机到的那个格子的中心坐标。那我们岂不是还是没法绕开很麻烦坐标运算了?并非如此,要放地雷的坐标在格子中心,但别忘记这个坐标偏偏就是代表这个格子的印花的坐标。在星际争霸II编辑器中,我们可以动态获得地图上一个单位的坐标。所以我们只要直接获得在上一步中随机出来的N个印花的坐标,然后把地雷创建在印花的位置即可,完全不需要任何坐标计算。

至于问题三,如何保证第一个挖的位置肯定不是地雷呢?这个问题看起来很玄乎,其实说穿了是很简单的事情,我们不要在游戏一开始就随机创造好地雷,而是等到玩家挖开第一格后再开始创造地雷就好了,这时候玩家挖了哪个格子是已知的,这样,我们只需要在创建时避开那一格,问题就解决了。至于说,我们如何避开那一格呢?那就更简单了,上面不是说了,我们在所有印花中随机取出N个的不重复印花的方法就是不断将随机到的印花踢出单位组么。所以,只要我们在开始取第一个印花之前,先把被挖掉的那个印花踢出单位组去,这样就永远都不可能取到那个格子了。轻松解决。

这样,我们仅仅是把印花做成了一种单位,便籍此解决了大量的算法难题。至此,实现这个扫雷游戏的技术方面的难题已经全部解决了。接下去我们差不多该进入实际操作阶段了。

地形模块的最后处理

在正式进入触发模块之前,我们在地形模块还有一些未尽的事物:

一、镜头

对于本地图而言,由于默认的游戏镜头无法将整个雷区尽收眼底,会造成玩家操作上的不便,所以我们先得调整一下游戏镜头。我们先在地形编辑器里放下一个我们认为合适的游戏用镜头。

休闲地图示例教程:扫雷

这个镜头必须能把整个雷区尽收眼底,但同时又不会离开太远,使雷区变得太小。下面是这个镜头的观察效果:

休闲地图示例教程:扫雷

我们给这个镜头起个名字,比如叫“Camera Input”。方便触发器来调用它。

二、区域

回顾一下我们上面所讨论的算法,会发现我们大致上需要2个区域:1]略小于1格的方形区域一个,用于附着到角色的身上,用来判断角色所在的格子。2]3格x3格大小的方形区域一个,用来计算周围八格的雷数。

在后面的操作中,我们还需要用到第三个区域:覆盖整个雷区的一个区域,用来判断角色是否在工作区内。

所以我们就在这里提前画好3个区域即可。注意第一个区域的变长要比雷区的格子稍微小一点点。

我们分别给三个区域起名,这里称第一个区域为Engage Zone,第二个区域为Count Zone,第三个区域为Work Zone。

以下是三大区域的样例:

休闲地图示例教程:扫雷

然后让我们来考虑一下触发器。

#!!#

触发器框架构思

别被本章标题中“框架”什么的给唬住了。其实简单地说就是动手之前先考虑一下我们要用触发器做些什么事情。通常我们会把每件事做成一个或多个相关触发器,这样我们在开始阶段就会对本地图大致会做几个触发器有些初步印象。

当然,有构思不代表在实际操作中就完全按照构思来做,而且实际问题总是比想象中的略为复杂一些,所以一般来说地图实际完成时的数量总是会比构思阶段 的多一些。尽管如此,构思阶段还是必要的,让我能明确自己需要做哪些事情,写哪些关键的触发器。这样我们在编写触发器时就会有明确的目标,不至于误入歧 途,比方说写了半天触发器结果尽是些和游戏内容无关的东西,或者写到中途突然对着触发模块干瞪眼,不知道自己接下去该干什么。

对于本地图来说,我们这个扫雷游戏也可以简单地定立一个框架,我们要做的触发器大致可分为四个部分:

地图初始化部分、操作实现部分、界面部分、胜利\失败部分。

而这四部分里我们大致需要做哪些事情呢?

一、地图初始化部分

理论上,几乎所有自定义地图都需要这个部分。所谓地图初始化,就是表示我们要让触发器在地图刚载入完毕后,为游戏做哪些准备工作。以本地图为例的话,就是指消除战争迷雾,调整游戏镜头等等的工作。

在本地图中,我们计划用一个名为Init的触发器来进行初始化操作。

二、操作实现部分

操作部分是整个扫雷游戏的核心,整个扫雷游戏中,玩家就2个关键操作:挖掘和插旗。所以我们在操作部分中要做的两件事情也就是实现挖掘和插旗操作。

在本地图中,我们计划用一个名为Dig的触发器来实现挖掘操作。并用一个名为Mark的触发器来实现插旗操作。

三、界面部分

本地图对界面部分触及不多,所需做的就是显示“挖掘”和“插旗”两个按钮。然后再显示一个提示信息,来告诉玩家还有多少个格子要挖。

在本地图中,我们计划用一个名为Init UI来触发器来显示这些界面。

四、胜利\失败部分

几乎所有地图都会需要胜利\失败触发器。在有些地图中,胜利失败触发器可以是完全孤立的模块,而在另一些游戏中,胜败条件是融入在游戏过程中的。我们这个游戏就属于后者,胜利失败条件是在必须挖掘操作的过程中进行判定的(挖到雷或者把没有地雷的格子全部挖完)。

但是我们依然可以分出一个部分来做胜利和失败的触发器,决定我们在游戏胜利\失败时分别要做些什么。然后让别的触发器在进行胜败判定后分别调用它们即可。

在本地图中,我们计划用一个名为Victory的触发器来决定游戏胜利时要做的事。并用一个名为Defeat的触发器来决定失败时要做的事。

以上就是我们这张地图的触发器大致框架。在后面的实际编写过程中,大家会发现我们写出来的触发器数量比我们这里计划的要多一些。这是很正常的现象。 因为在这个阶段我们仅仅是构思一个大致框架而已,实际内容总是会比框架要丰富一些。要说在构思阶段就想好了没一个细节,最终写出的触发器和构思阶段分毫不 差,那不现实,也没有任何意义。构思阶段只要让我们能把握好“我们需要做哪些事情”就好了。

接下来我们就要实际地开始编写触发器了。

#!!#

触发器的编写

一、地图初始化

我们要做的第一件事就是实现地图初始化部分,也就是告诉触发器在地图刚载入完毕后,要为游戏做哪些准备工作。大致上,在这个触发器里,我们要清除战争迷雾,并调整好游戏镜头,去掉游戏的默认界面,以及其它一些零散操作。

首先,我们要用一个全局变量来把我们操作的矿工单位大叔记录下来。所以我们新建一个单位类型的全局变量,变量名设为Hero(这个可以随便起,中文变量名也可以):

休闲地图示例教程:扫雷

然后将其初始值设为我们的矿工大叔:

休闲地图示例教程:扫雷

然后开始编写第一个初始化触发器。

我们新建一个触发器,名字为Init(名字可以随便起,下同,不重复说明了)。事件设为地图初始化。意思是在地图刚载入完毕时执行这个触发器。然后我们给它设定动作:

前二句,我们使用启用或禁用可见性这条动作,分别将黑色遮罩和战争迷雾禁用掉。这样,我们在游戏里就看不到战争迷雾了,相当于打了地图全开秘技。因为战争迷雾在这个游戏里根本毫无意义。

第三句,我们使用设置画面模式这条动作,参数中指定的玩家设为所有玩家,模式设为全屏,持续时间设为默认。这样,我们就隐藏掉了玩家的单位控制面板,因为单位控制面板在这个游戏里同样毫无意义。

第四句,我们使用显示或隐藏UI框体这条动作,隐藏的框体设为资源列表。这样,屏幕上就连资源列表也一并被隐藏掉了。就剩下左上角的游戏菜单而已。

第五句,我们将为玩家1应用我们刚才制作的镜头“Camera Input”。

第六句,我们使用锁定镜头输入,来锁定玩家1的镜头输入。由于我们在应用镜头以后,玩家依然可以通过鼠标滚轮来让镜头变回默认的镜头。因此这句动作的作用既是,锁住玩家1的镜头,无论玩家1怎么做,都无法改变他的镜头了。

第七句,我们使用将区域附着到单位动作,将那个最小的区域附着到矿工大叔身上。这里将区域参数设为Engage Zone,单位参数设为Hero即可。偏移量使用默认的(0,0),所谓偏移量是指区域附着到单位身上后,区域中心相对单位位置的偏移,显然我们这里用不着任何偏移。

第八第九句存粹是为了方便操作的,用选择单位动作让玩家1在游戏一开始就自动选中矿工大叔,然后锁住玩家的选择,免得玩家选到别的什么东西上去(虽然这个地图上也没有别的什么东西可以选的,第八句动作完全是为了以防万一。)

最后写出的Init触发器便如下图右侧所示:

休闲地图示例教程:扫雷

#!!#

二、界面初始化

如果大家在这个阶段就测试地图,会发现地图的视角正常,而我们也可以通过鼠标来操作矿工大叔移动,但是现在出现了一个问题:界面太过清爽了。我们至少需要两个按钮来进行挖雷赫插旗操作吧?目前这样子,仅仅就能移动而已了。而且,至少还得有个提示信息吧?得知道我还得再挖开几块砖才算赢。

休闲地图示例教程:扫雷

所以这一节就教大家编写触发器来实现两个按钮和一个屏幕提示信息。其实这些界面元素同样是地图刚读取完毕时创建的,所以其实可以和尚一个触发器写在一起,但是为了便于规划和分类,我们把它写在一个单独的触发器里了。

这里特别说一下本游戏的提示信息:在通常的扫雷游戏里,提示信息有2项,一项是告诉你过去了多少时间。另一项是告诉你还有几块砖要挖才能胜利(或者告诉你还剩几个旗子可以插,其实效果一样)。但是由于本游戏是采用“真人”挖雷,我们的矿工大叔在雷区移动是需要时间的,所以还拿时间来限制就没什么意思了。所以本演示地图不带计时器。不过如果同学们想要在自己的地图里面加也可以。

在编写界面初始化触发器之前,我们同样得准备全局变量。我们需要挖开的砖块总数永远是总格子数减去地雷总数。所以我们要先用一个全局变量来保存我们打算在本游戏里创建的地雷的数量。这里我们用一个名为MineCount的整数类型全局变量来做这个事情。这里我们将地雷数定为10。这个值可以随便决定,只要小于80即可。因为这里总共才81个格子,第一个格子不能是地雷。

休闲地图示例教程:扫雷

此外,由于扫雷这个游戏,胜利条件本质上是“翻开所有不是地雷的格子”。在本教程中,为方便玩家操作起见,会把“剩下的不是地雷的格子”总数直接显示给玩家。所以我们需要一个对话框项来显示这个数字,因为这个数字是经常变动的,所以我们需要用一个全局变量记下这个对话框项,以方便我们在游戏过程中修改它显示的值。

所以这里我们再创建一个新的全局变量,类型为对话框项。起名为HintText。

对话框项:又叫对话框控件。星际争霸II中,触发器是通过对话框来实现动态界面的。最常见的对话框就是当电脑对手打出GG时,玩家可以选择是否接受投降的那种窗口。对话框上的元素,比如按钮、输入框、文字标签、列表框等等,就称为对话框项。

全局变量准备完毕后,我们就开始编写界面初始化触发器:

我们先新建一个触发器文件夹,名为UI,然后新建一个触发器Init UI,代表这个触发器是用来初始化界面的。

我们将事件设置为地图初始化。代表这个触发器同样是在地图载入完毕之后立刻发动。

下面开始编写动作:

我们将用前六条动作来构建地图右上角的提示信息:

休闲地图示例教程:扫雷

首先,我们用创建对话框动作来创建一个对话框。虽然我们实际需要的只是两段文字:“还需挖开的格子数:”和具体的数字。但这些文字是用对话框项显示的,而对话框项必须依附于一个对话框,所以我们必须先创建一个对话框。以下是我们需要的参数:

偏移量X: 0

偏移量Y: 0

锚点: 右上

宽度: 400

高度: 200

模态: 非形式的

锚点参数是用来定位对话框的基准位置,由于我们希望把提示信息显示在右上角,所以这里选择右上。偏移量X和Y是用来决定对话框位置相对于基准位置的偏差的,这里我们并不需要特别决定偏差位置,故而填写0。宽度和高度参数用来决定对话框的大小,这里我们只要设为足够大,能容量两段文字即可。因为这里我们在实际使用中是不打算把对话框的背景和外框显示出来的,只需要显示上面两个对话框项即可,所以无须严格决定外框的大小,这里设为400和200就差不多了。模态这个参数目前没有用,无须顾及(其实“模态”是个翻译错误,不过这里无所谓了)。

创建完对话框,我们的第二条动作就是创建用来显示“还需挖开的格子数:”这句话的对话框项了。由于用来显示的对话框项的种类是标签。因此这里我们使用创建对话框项 (标签)这条动作。以下为其参数:

对话框: 上一次创建的对话框

宽度: 280

高度: 50

锚点: 左

偏移 X: 50

偏移 Y: 50

文本: "还需挖开的格子数:"

颜色: 白色

文本写出: true

文本写出持续时间: 2.0

对话框这个参数用来指定承载我们这个对话框项的对话框,这里使用“上一次创建的对话框”这个函数表示我们要把这个对话框项创建在刚刚创建的对话框里,至于锚点和偏移这些参数的含义基本上和上面的创建对话框动作的参数一致,只不过位置变成了对话框上的位置而已,而“文本”参数我们要填的就是我们想要它显示的文本。颜色是指文字颜色。特别一提的是“文本写出”和“文本写出持续时间”这两个参数,如果我们把文本写出设为true,那么标签上的文字会一个个地浮现出来,而不是一下子显示出来,控制它完全浮现所需时间的参数就是“文本写出持续时间”。这里实际上完全用不着这个花哨的功能,只是顺便一提而已。

接下去我们编写第三条动作,依然使用创建对话框项 (标签)创建用来显示实际数字的对话框项。我们之所以把这个对话框项和前一个分开来创建,是因为前一个对话框项的内容在游戏内是无须变化的,而这个数字需要随时改动,为了方便起见,我们将它们分割开来。以下是第三条动作的参数:

对话框: 上一次创建的对话框

宽度: 50

高度: 50

锚点: 右

偏移 X: 50

偏移 Y: 55

文本: 将整数转换成文本

值: 算法(整数)

值1: 81

运算符: -

值2: MineCount

颜色: 橙色

文本写出: false

文本写出持续时间: 2.0

你可能注意到了这里“文本”参数和上面的一大不同之处。在地图初始化时,尚需翻开的格子数自然等于所有没有地雷的格子的总数,也就是总格数减去地雷总数。总格数是我们在画地形时定死的9x9=81个,而我们又用MineCount这个数字来决定了地雷总数(在本地图里,我们将MineCount设为了10)。因此所有没有地雷的格子的总数自然就成了81- MineCount。但是实际上,由于计算结果是个整数类型,我们还需要将这个整数转换为我们能用对话框项来显示的“文本”。所以我们在修改文本这个参数的时候,应该先选择将整数转换成文本这个函数:

休闲地图示例教程:扫雷

然后再修改将整数转换成文本函数的“值”参数,将其设为算法(整数)算法(整数)这个函数可以对两个值进行四则运算。因此我们将值1设为81,值2就选择变量MineCount,运算符就选择“-”,代表我们想让它们想减。

在创建完对话框项后,我们还需要把刚刚创建的这个对话框项保存起来,便于以后修改。因此我们下一步就使用设置变量动作。来设置HintText = (上一次创建的对话框项)

到这里,右上角的提示信息对话框基本上设置完了,不过我们还需要一个收尾工作。把对话框的外框隐藏掉,让上面两段文字看上去就是直接显示在屏幕上的。因此我们使用显示/隐藏对话框背景这个动作。

可见: 隐藏

对话框: 上一次创建的对话框

这样,右上角的界面工作完成。我们还需要创建两个按钮,来显示右下角的两个按钮:挖掘插旗

休闲地图示例教程:扫雷

我们想要让这两个按钮按下后执行相应的触发器。所以我们在继续编写Init UI触发器之前,先在左边的触发器列表里创建两个空的触发器,Dig和Mark。分别用来表示挖掘和插旗的触发器。目前我们还不需要为它们编写内容,所以我们回到Init UI触发器,来继续创建按钮。

我们可以用显示屏幕按钮动作来直接创建按钮,所以我们只需要两条显示屏幕按钮动作动作就可以显示挖掘和插旗按钮。

因此Init UI触发器第七条动作动作为显示屏幕按钮,具体参数如下:

锚点: 右下

偏移量X: 300

偏移量Y: 70

回调: Dig

屏幕按钮ID: 1

宽度: 200

高度: 60

文本: "挖掘(D)"

注意“回调”这个参数这个参数决定了我们按下按钮时执行什么触发器,挖掘按钮要执行的当然是Dig触发器了。所以这里选Dig。而屏幕按钮ID这个参数非常重要,这里可以给你的按钮指定编号,我们以后就可以用这个编号来找到我们创建的这个按钮,不需要和上面的标签对话框项一样使用全局变量。值得注意的是“文本”这个参数。为了让“挖掘”后面的D字看起来像个真正的快捷键,我们需要在D字上加一点特殊的标记(编辑器中称为文字样式):

具体来说就是将"挖掘(D)"写成“挖掘(< s val="HotkeyDialogBindButton">D)”,这样,D字就会应用上游戏中对话框快捷键的文本样式,变得看起来像个快捷键了:

休闲地图示例教程:扫雷

但要注意的是<HotkeyDialogBindButton>这一样式仅仅只是能让D看起来像是个快捷键而已,如果我们真正想要D键按下就相当于按下这个按钮,还需要在Dig触发器里进一步设置。不过目前我们先继续编写Init UI触发器。

Init UI触发器的最后一条动作,显示“插旗”按钮:其实我们只需要把上一条动作复制一下,然后修改一下文本、偏移、屏幕按钮ID这几个参数而已:

锚点: 右下

偏移量X: 50

偏移量Y: 70

回调: Mark

屏幕按钮ID: 2

宽度: 200

高度: 60

文本: "插旗(F)"

注意:屏幕按钮ID必须不能和上一条动作相同,否则总共只会创建一个按钮,而不是二个。这里我们分别把挖掘和插旗按钮的ID设为1和2。

整个Init UI触发器的完整结果如下图右侧所示:

休闲地图示例教程:扫雷

#!!#

三、启用和禁用屏幕按钮

在很多时候,我们会希望把挖掘和插旗按钮禁用掉,以免玩家混淆。比如矿工大叔死亡后,或者矿工大叔离开雷区后。如果矿工大叔离开雷区后,玩家依然可以按“挖掘”按钮,那么玩家会完全混淆,搞不清楚这游戏的规则。所以在某些时候禁用按钮是必要的。当然,当矿工大叔回来后,我们依然需要重新启用屏幕按钮。

所以我们在UI触发器目录下创建两个新的触发器Enable Buttons和Disable Buttons,分别用来启用和禁用我们的屏幕按钮。

Enable Buttons的内容:

  • Enable Buttons
  • 事件
  • 单位 -Hero进入Work Zone
  • 局部变量
  • 条件
  • 动作
  • 对话框 -为(所有玩家)启用(屏幕按钮1)
  • 对话框 -为(所有玩家)启用(屏幕按钮2)

-事件

我们使用单位进入或离开区域来做这个触发器的事件。

单位: Hero

状态: 进入

地区: Work Zone

由于我们是想在矿工大叔进入雷区(Work Zone)后启用两个按钮。所以状态参数选择进入,而地区选择Work Zone。单位当然是Hero,也就是我们的矿工阿叔。这样,当Hero进入Work Zone时,我们这个触发器就会执行了。

-动作

那么我们要执行的动作是什么呢?我们只需要两条启用或禁用对话框项动作就能解决问题,比方说我们要

启用屏幕按钮1(挖掘):

启用或禁用对话框项

玩家: 所有玩家

启用选项: 启用

对话框项: 屏幕按钮

屏幕按钮ID: 1

唯一要注意的是,对话框项这个参数,我们必须在里面填写屏幕按钮这个函数。然后在屏幕按钮函数的屏幕按钮ID参数中写1。表示我们要把屏幕按钮1启动。

第二条动作则如法炮制:

启用或禁用对话框项

玩家: 所有玩家

启用选项: 启用

对话框项: 屏幕按钮

屏幕按钮ID: 2

如此,在Hero进入Work Zone的时候,我们两个按钮就会被启用。

Disable Buttons触发器的内容:

  • Disable Buttons
  • 事件
  • 单位 -Hero离开Work Zone
  • 单位 -Hero死亡
  • 局部变量
  • 条件
  • 动作
  • 对话框 -为(所有玩家)禁用(屏幕按钮1)
  • 对话框 -为(所有玩家)禁用(屏幕按钮2)

基本上只需要复制Enable Buttons的内容,然后把进入改为离开,启用改为禁用。这样,Hero离开Work Zone的时候,按钮就会被禁用。不过我们还需要再加一个事件:单位死亡,并将其中的“单位”参数设为Hero,这样,Hero死亡后,插旗和挖掘同样会被禁用。

完成了启用和禁用按钮后,我们先按Ctrl+F9来测试一下地图吧。我们会发现,当矿工大叔走入雷区时,按钮会变亮:

休闲地图示例教程:扫雷

而离开雷区后就会变暗:

休闲地图示例教程:扫雷

#!!#

四、插旗触发器

接下来就要进入这个地图核心部分触发器了。这个地图的核心操作主要有2个:挖掘和插旗。挖掘是最复杂的部分,我们留待后面。而插旗相对简单些,所以我们由浅入深,先从插旗触发器开始制作:

在扫雷这个游戏中,插旗这件事情其实并不是必要的。扫雷的胜利规则是把所有的没有地雷的格子都翻开来。所以插旗更多是方便玩家用的,让玩家标记下自己认为有雷的格子,免得自己什么时候忘记了或者误点到了。

在本地图的设定里,插旗按钮并不仅仅是在指定的格子里放下一面旗子而已。其实插旗按钮隐含插旗和拔旗两个操作。如果玩家在一个已经放下旗子的格子里再按插旗按钮,那么就视为玩家认为这个旗子放错了,所以我们得反过来把旗子拔掉。而我们进一步规定旗子不能放在已经挖过的格子里,因为这样没有意义。而且规定如果一个格子有旗子,那么玩家想要挖开这个格子就必须先把旗子先拔掉,这都是一般扫雷游戏里的常见规则。

“有旗子的格子不能挖”这一判断我们放到挖掘操作里去做好了。所以插旗触发器里面要做的事情就是:1]如果格子被挖过,那么什么都不做。2]如果没挖过,而且该格没有旗子,就放下旗子。3]有旗子就拔掉旗子。

那么,如何知道“格子有没有被挖过”呢?别忘记我们前面说的,我们直接用每个格子里的印花来代表它所在的格子。所以我们只要在挖掘操作中给挖过的印花单位做个记号,然后我们直接判断这个记号就行了。

那么用什么来做记号呢?方法很多,我们这里使用单位自定义值这东西。

单位自定义值:简单来说,可以认为是单位的额外属性。在星际争霸II中,每个单位可以拥有64个额外属性,分别用0-63来编号。它们不会对单位造成实际影像,但是我们可以用触发器来读取和设置它们。每个自定义属性都是实数,也就是说。我们可以在每个单位身上保存64个实数。

我们直接用0号自定义属性来标记印花是否被挖掘过。由于所有自定义属性默认值都是0。所以我们指定0代表没有挖过,而其余值则代表挖过。然后我们要记得在之后的挖掘触发器中把我们所挖掘印花的自定义值设为不是0的值,比如100。

然后,我们这里用克哈 旗帜 - 可摧毁物这个单位来充当旗子单位,因为它本身就是个旗子。不过要把它用在这个游戏里,还是需要一些修改的,因为克哈 旗帜 - 可摧毁物本身是有体积的,放下后会挡住矿工大叔去路,这样据没法扫雷了。所以我们通过菜单的模块->数据来打开数据编辑器。

数据类型选择单位,打开单位页面。找到克哈 旗帜 - 可摧毁物,然后修改它的移动 - 半径属性为0移动 - 足印路径控制属性为(无),这样旗子就不会占据任何空间了。

休闲地图示例教程:扫雷

我们开始编辑触发器,首先我们新建一个触发器文件夹,随便起名,这里叫Operation。我们将把所有的按钮操作触发器都放到这个文件夹里,方便分类。前面,我们已经创建了两个空触发器:Dig和Mark。所以我们直接把它们移动进来即可。然后本节中我们要修改的是插旗触发器,也就是Mark触发器。

Mark触发器的最终内容:

  • Mark
  • 事件
  • UI -玩家 任意玩家在shift键允许, control键 允许,alt键 允许的情况下,将F键朝下按
  • 局部变量
  • Target = (单位1 取自 (在Engage Zone内的属于玩家任意玩家的,并且匹配不包括: 发射物, 死亡的, 隐藏的的信标 (星灵 大型)单位,最多有任意数量个)) <单位>
  • TargetFlag = (单位1 取自 (在Engage Zone内的属于玩家任意玩家的,并且匹配不包括: 发射物, 死亡的, 隐藏的的克哈 旗帜 - 可摧毁物单位,最多有任意数量个)) <单位>
  • 条件
  • Target!=无单位
  • (Target的自定义值0)==0.0
  • 动作
  • 综合 -If (条件) then do (动作) else do (动作)
  • If
  • TargetFlag==无单位
  • Then
  • 单位 -为玩家1在(Target的位置点)创建1个使用默认朝向(无选项)的克哈旗帜 - 可摧毁物
  • Else
  • 单位 -从游戏中移除TargetFlag

-事件

由于我们之前已经把插旗按钮关联到了这个触发器,所以我们无需再编写“按下插旗按钮”这样的事件。但是我们同时又想让玩家按下键盘上“F”按钮也能达到和按下“插旗”按钮一样的效果,所以这里我们需要加一条键被按下事件,来模拟插旗按钮的快捷键。相关参数如下:

键被按下

玩家: 任意玩家

Shift: 允许

Control: 允许

Alt: 允许

键: F

下: 下

这些参数代表当任意玩家按下F键时执行此触发。而且只关注F键,无论此时Shift键、Ctrl键、Alt键是什么状态都无所谓。

-局部变量

本触发器需要用到局部变量,记录矿工目前所挖的格子(印花),以及格子里的旗子(如果有的话)。

局部变量:局部变量即只能在本触发器或者本函数内使用的局部变量,与全局变量相对。局部变量在其作用域外等于不存在。所以这里定义的局部变量Target和TargetFlag对其余触发器来说是不存在的,只能在本触发器里使用。另外局部变量在每个进程内都是独立的,这一点可以简单地这样理解:如果某个触发器执行了多次,那么每一次执行时,里面的局部变量都互相没关系。

我们右键点击Mark触发器的局部变量这一行,然后选择“新建元素”就能给这个触发器新建局部变量了。

我们新建两个单位类型的局部变量,一个叫Target,一个叫TargetFlag。

休闲地图示例教程:扫雷

我们要给这两个变量设定初始值。首先Target变量用来保存我们正在挖的印花(信标)。我们该如何抓到目前正在挖的印花呢?如果你忘记了,请回过头去复习 挖雷和埋雷的操作算法构思 那一章。我们可以通过区域内匹配条件的单位组函数来获取Engage Zone(也就是附在矿工大叔身上那个区域)中所有符合条件的单位。所以我们只要获取其中所有的信标 (星灵 大型)就可以构成一个单位组。由于这个区域里最多也就一个信标,也就是矿工大叔目前所踩的这格里的信标。所以我们可以用取自单位组的单位这个函数来从这个单位组里取出第一个单位,这个单位必定是我们要找的信标(印花),要么这个单位组里干脆就一个单位都没有(矿工大叔根本就没站在雷区里或者站到了边线上的情况)。

所以我们就按上图所示来设置Target变量的初始值。

而TargetFlag变量的初始值则如下图所示:

休闲地图示例教程:扫雷

TargetFlag的初始值和Target的唯一区别就是,单位类型由信标 (星灵 大型)变成了克哈 旗帜 - 可摧毁物也就是说一个捕捉的是印花,另一个捕捉的是旗子。注意,如果什么单位都没捕捉到,这些变量的值会变成空,也就是说变成没有单位。这一点我们将在下面的条件判断中加以利用。

-条件

我们要放旗子或者拔旗子,至少需要满足2个条件,1]Target不等于没有单位,否则肯定是矿工大叔根本就没站在雷区里或者站到了边线上的情况。2]Target没有被挖过,也就是说0号自定义值为0。

所以本触发器的条件很容易编写:

条件

Target!=无单位

(Target的自定义值0)==0.0

注:!=在星际争霸II编辑器里就是不等于的意思,而==则是代表等于。

-动作

由于具体是拔旗还是插旗视情况而定,所以我们这里用一个分支判断动作If Then Else来进行进一步的条件判断。我们要判断的是,眼前的格子里有没有旗子。这个很简单。上面已经用TargetFlag捕捉了旗子,如果TargetFlag等于没有单位,那么表示格子里没有旗子,而否则就是有旗子。所以插旗触发器的动作就是:

动作

综合 -If (条件) then do (动作) else do (动作)

If

TargetFlag==无单位

Then

单位 -为玩家1在(Target的位置点)创建1个使用默认朝向(无选项)的克哈 旗帜 - 可摧毁物

Else

单位 -从游戏中移除TargetFlag

Then的部分,代表没有旗时要执行的动作。所以我们要创建旗子,这里我们用使用默认朝向创建单位动作来创建旗子,参数如下:

使用默认朝向创建单位

玩家: 1

点: 单位位置点

单位: Target

计数: 1

标旗: 无选项

类型: 克哈 旗帜 - 可摧毁物

“点”参数指定了我们要把旗子创建在哪里,因为我们的旗子自然是创建在印花(Target)的位置,所以我们这里使用单位位置点函数,来获取Target的坐标。并将我们的旗子创建于此。

Else的部分,代表有旗子的时候要执行的动作,所以我们直接删除旗子。使用移除单位动作直接删除TargetFlag即可。

完成之后,我们可以按Ctrl+F9测试一下插\拔旗子触发器是否已经正常运作。

休闲地图示例教程:扫雷

#!!#

五、挖掘触发器

由于挖掘操作的触发器过于复杂,所以这里把挖掘操作拆成三份:翻开格子前的准备工作做成一份。挖第一个格子时创建地雷的工作做成一份。实际翻开格子的工作做成一份。以方便大家理清思路。至于为什么这么分呢。首先,因为创建地雷的工作相对独立,每盘游戏只会执行一次,因此这里我们把它做成一个独立的自定义动作。而翻开格子的工作,由于我们之前所讨论的算法的缘故,本身需要做成能自己调用自己的形式,故而我们同样将至做成一个自定义动作。

自定义动作。自定义动作是指由地图作者或者Mod作者自定义的触发器动作。自定义动作其实是由一大堆触发器动作打包而成,可以像普通的触发器动作一样被触发器执行,也可以接受参数。自定义动作实际上是一种特殊的自定义函数。

虽然本节还不需要编写两个自定义动作的具体内容,但是为了方便编写挖掘触发器,我们先在触发模块左边的触发器列表的Operation文件夹上点击右键,然后选择新建->新建动作定义,然后重复一次,用这个方法在Operation文件夹下创建两个空的自定义动作:Init Mines和Unfold。

休闲地图示例教程:扫雷

注:我们也可以用快捷键:Ctrl+Alt+R来创建新的动作定义。

Init Mines这个动作将用来初始化地雷。由于我们之前说过,创建地雷必须是在知道第一次挖掘的是哪个格子之后,所以我们在调用这个动作时必须告诉它我们第一次挖的是哪个印花,这样这个动作在创造地雷的时候避开那个印花。

故而,我们得先给Init Mines动作加个参数,用来传递需要避开的那个印花。所以我们在Init Mines动作的参数那一行,点击右键,新建元素来新建一个参数。名称为Exclude,类型为单位。

休闲地图示例教程:扫雷

我们暂时不需要编写Init Mines的具体内容。所以我们再切换到Unfold动作。

Unfold动作将用来翻开印花(即将印花的外观变成数字),查看本格是数字还是地雷,以及在遇到数字0时翻开周围8格。所以这个动作同样需要一个参数,要知道自己该翻开那一格。所以如法炮制,新建一个名为Target的参数,类型为单位。

休闲地图示例教程:扫雷

我们同样暂时不需要编写Unfold的具体内容,因为本节的主角是Dig触发器。

Dig触发器中需要做的事情很简单,抓到矿工目前所要挖的格子(印花),然后看上面有没有旗子,要是没有,就做个挖掘的特效,然后如果是第一次挖,那么就调用Init Mines动作来创建地雷。最后再用Unfold动作来翻开格子。

不过要判断是否是第一次挖,仍然需要一个全局变量。

所以我们创建一个布尔类型的全局变量,名为MineCreated。初始为false。

休闲地图示例教程:扫雷

我们指定这个变量为false时,代表地雷尚未创建,为true时代表已创建。

我们想要在挖掘的时候来点特效,让玩家清楚地了解到“哦,矿工开始探雷了”。所以在矿工的头上创建一个Terran轨道指挥部的雷达扫描效果可以起到不少直观的作用。

休闲地图示例教程:扫雷

不过雷达原本的持续时间太长了,我们不需要这么长,而且我们也不希望这个雷达真的把地图上周围隐形的地雷扫描出来了(那这游戏就不用玩了)。所以我们需要去数据编辑器里改上一改:

休闲地图示例教程:扫雷

我们进入数据模块,数据类型选择效果。然后找到扫描器扫描这个效果。将其搜索 - 显示标旗属性里面的钩子全部去掉。这样扫描时就不会真的把隐藏单位显示出来了。再将效果 - 结束延迟设为3,这样,扫描效果就将在3秒后结束,不会留在原地很久,有碍观瞻。

然后我们就来正式编写Dig触发器的内容了,下面是Dig触发器最终的样子:

  • Dig
  • 事件
  • UI -玩家 任意玩家在shift键允许, control键 允许,alt键 允许的情况下,将D键朝下按
  • 局部变量
  • Target = (单位1 取自 (在Engage Zone内的属于玩家任意玩家的,并且匹配不包括: 发射物, 死亡的, 隐藏的的信标 (星灵 大型)单位,最多有任意数量个)) <单位>
  • TargetFlag = (单位1 取自 (在Engage Zone内的属于玩家任意玩家的,并且匹配不包括: 发射物, 死亡的, 隐藏的的克哈 旗帜 - 可摧毁物单位,最多有任意数量个)) <单位>
  • 条件
  • Target!=无单位
  • TargetFlag==无单位
  • 动作
  • 触发 -将 (当前触发器) 调为 关闭
  • 环境 -在Hero身上创建由Hero所施放的扫描器扫描
  • 综合 -If (条件) then do (动作) else do (动作)
  • If
  • MineCreated==false
  • Then
  • Init Mines(Target)
  • 变量 -设置MineCreated = true
  • Else
  • Unfold(Target)
  • 触发 -将 (当前触发器) 调为 开启

-事件

事件部分,就如插旗触发器(Mark)一般如法炮制,只是将F键换成了D键。

-局部变量

局部变量也和Mark触发器一样如法炮制,获得格子里的印花和妻子。

-条件

我们要求Target不能没有单位。因为没有单位意味着矿工大叔没有站对位置(原理同前)。而TargetFlag必须没有单位,也就是说目前这个格子里必须没有旗子才能挖。

-动作

我们用开启或关闭触发器来编写第一条动作。为的是在挖掘进行过程中,这个触发器不会重复被触发。有时候一些挖掘工作,比如连续翻开周围8格等等工作是无法瞬间完成的,所以我们有必要在前一次挖掘工作完成后才能继续下一次工作,第一条动作参数如下:

开启或关闭触发器

触发: 当前触发

状态: 关闭

这样写就能临时性地关闭自己这个触发器。

在关闭触发器后,我们就开始创建雷达扫描特效。这个特效在这里没有任何实际作用,但是可以直观地告诉玩家,我们挖开了这个格子。我们使用在单位上创建效果(源自单位的)这个动作来创建雷达扫描的效果:

在单位上创建效果(源自单位的)

目标: Hero

施法者: Hero

效果: 扫描器扫描

在这里,目标和施法者代表了这个由触发器所创建的效果的施法者和目标,由于我们要创建在矿工头上,所以两个都写Hero。而效果则是我们想要创建的效果:扫描器扫描。

此后我们将进行一次判断,看看目前是不是第一次挖掘(即目前还有没有创建地雷)。所以我们再一次使用If Then Else动作来进行条件判断:

综合 -If (条件) then do (动作) else do (动作)

If

MineCreated==false

Then

Init Mines(Target)

变量 -设置MineCreated = true

Else

我们判断MineCreated变量,如果它为假,那么就调用Init Mines动作,并把要跳过的那个格子(Target)传递给它。完了以后把MineCreated设为真。

当以上这些都执行完毕后,我们就开始翻开Target这个格子,所以我们使用Unfold这个自定义动作,把要挖的格子作为参数传给它:

Unfold(Target)

翻开完毕最后,把我们用开启或关闭触发器来重新启用这个触发器。让玩家可以继续挖下一个格子:

开启或关闭触发器

触发: 当前触发

状态: 开启

#!!#

六、初始化地雷(Init Mine)动作

我们之前创建了空的自定义动作Init Mine,但是还没给它填进任何内容,本节就来编写Init Mine。

Init Mine是用来在雷区的所有格子里随机选出N个并进行布雷。所以按照之前所考虑的算法,我们势必需要一个单位组全局变量来保存雷区所有的格子。

所以我们创建一个名为Slots的单位组,内容是地图上所有的信标 (星灵 大型)。我们依然是用区域内匹配条件的单位来做。

休闲地图示例教程:扫雷

我们另外需要一个单位组,来存放目前尚未挖开的那些没有埋雷的格子,毕竟这是胜利条件。

所以我们新建一个空单位组,名为RemainingSlots。

休闲地图示例教程:扫雷

然后我们就开始着手编写Init Mine动作。

首先贴出Init Mine的最终形态:

  • Init Mines
  • 选项:动作
  • 返回类型:(无)
  • 参数
  • Exclude = 无单位 <单位>
  • 语法文本: Init Mines(Exclude)
  • 提示文本: (无)
  • 自定义脚本代码
  • 局部变量
  • tmpUG = (Slots的复件) <单位组>
  • tmpUnit = 无单位 <单位>
  • i = 0 <整数>
  • 动作
  • 单位组 -从tmpUG中移除Exclude
  • 综合 -为从1到MineCount的每个包含增量1的整数i,执行(动作)
  • 动作
  • 变量 -设置tmpUnit = (取自tmpUG的随机活体单位)
  • 单位 -为玩家MinePlayer在(tmpUnit的位置点)创建1个使用默认朝向(无选项)的秃鹰 - 蜘蛛雷 (潜地的)
  • 单位组 -从tmpUG中移除tmpUnit
  • 变量 -设置RemainingSlots = tmpUG

参数我们之前已经设定过了,我们从局部变量开始。

-局部变量

我们这里需要三个局部变量:一个临时单位组tmpUG、一个临时单位变量tmpUnit、一个用来进行循环操作的整数i。只有tmpUG需要赋初始值:

休闲地图示例教程:扫雷

我们将其初始值设为单位组Slots的复件,意思是把Slots的内容复制了一份来过来。这样,tmpUG中就拥有所有的印花单位,可以从中进行随机挑选了。

-动作

第一条动作就是把Exclude从单位组中踢出去。因为Exclude正是我们创建地雷时要跳过的那个格子(注意,使用把将单位移出单位组动作并不会让单位真正消失,使用移除单位这个动作才真正会删除单位。):

将单位移出单位组

单位组: tmpUG

单位: Exclude

然后第二条动作开始,我们建立一个循环,按照之前算法章节中所说的方案,“随机出印花->创建地雷->把印花踢出单位组”如此重复N次来随机买下N个位置不重复的地雷。

由于要执行N次,N=地雷的总数,也就是MineCount变量。所以我们用为每个整数(这个翻译的真糟糕,简单地理解为这个动作是用来进行For循环的就好了)这个动作来做:

为每个整数

开始: 1

结束: MineCount

增量: 1

变量: i

参数中,开始1,结束MineCount。就代表这个循环会执行MineCount次。在本游戏中,MineCount被设为10。

这样只是设置了循环表达式而已,我们还需要在它的动作那一行中添加三个动作来完成循环体:

综合 -为从1到MineCount的每个包含增量1的整数i,执行(动作)

动作

变量 -设置tmpUnit = (取自tmpUG的随机活体单位)

单位 -为玩家0在(tmpUnit的位置点)创建1个使用默认朝向(无选项)的秃鹰 - 蜘蛛雷 (潜地的)

单位组 -从tmpUG中移除tmpUnit

这三个动作会被执行10次。循环体内的第一条动作,用设置变量语句给tmpUnit赋值:

设置变量

变量: tmpUnit

值: 取自单位组的随机单位

组: tmpUG

类型: 活体

赋值时用取自单位组的随机单位来获得tmpUG中的一个随机印花。活体这个翻译不甚恰当,其实它的意思就是只在tmpUG里头活着的单位中进行随机选择而已。

循环体第二条动作,用使用默认朝向创建单位在随机出来的印花位置创建蜘蛛雷。这里我们用蜘蛛雷代表地雷,不过其实反正用什么都无所谓,只要是隐身的让玩家看不到就行。

使用默认朝向创建单位

玩家: 0

点: 单位位置点

单位: tmpUnit

计数: 1

标旗: 无选项

类型: 秃鹰 - 蜘蛛雷 (潜地的)

这里有2个参数要注意:1]类型参数:如果我们决定用蜘蛛雷来代表地雷,那么在选择单位类型时必须是秃鹰 - 蜘蛛雷 (潜地的),非潜地的蜘蛛雷在创建时会有一个明显的下潜动作,就把地雷的位置暴露给玩家了。2]玩家参数:我们固然不能把蜘蛛雷创建成玩家1的单位,因为矿工大叔自己是玩家1。这样蜘蛛雷直接就给我们看到了。但是我们也不能把它们创建成和玩家1敌对的玩家2。为啥呢?因为我们也不希望矿工大叔走到地雷格子上还没安挖掘就直接给炸死了。所以我们直接创建成中立单位玩家0,又看不见,又炸不到。当然,把蜘蛛雷的自爆技能去掉也行。反正一样。

循环体第三条动作,再把tmpUnit从tmpUG中踢出去。为什么这么做?请回顾前面算法章。

自此,循环结束。所有地雷都埋下了。tmpUG就变成了目前距离达到胜利还需要挖开的所有印花的集合。我们把tmpUG单位组赋给RemainingSlots。就完成了地雷初始化动作。

#!!#

七、翻开(Unfold)动作

好了,我们终于进入到了最复杂的一个项目的编写阶段。翻开动作基本上就是扫雷的核心部分。在这个动作中,我们要做的就是把Dig触发器传过来的格子“翻开来”。具体怎么定义“翻开来”呢?也就是说,如果该格存在地雷就爆炸,如果没有地雷,就把该格的印花变成代表周围8格中地雷总数的数字,如果数字是0,就把周围8格按照同样规则翻开。

由于翻开动作直接涉及到了胜败检测,所以我们先创建创建两个空触发器Victory和Defeat,分别用来决定胜利和失败时执行的动作,方便Unfold动作来调用它们。

我们新建一个触发器文件夹Victory&Defeat,然后新建两个空触发器即可,这两个触发器的内容我们之后再编写。

休闲地图示例教程:扫雷

然后我们开始编写Unfold动作,以下是Unfold自定义动作的最终形态:

  • Unfold
  • 选项:动作
  • 返回类型:(无)
  • 参数
  • Target = 无单位 <单位>
  • 语法文本: Unfold(Target)
  • 提示文本: (无)
  • 自定义脚本代码
  • 局部变量
  • tmliUnit = 无单位 <单位>
  • TargetMine = (单位1 取自 (在Engage Zone内的属于玩家任意玩家的,并且匹配不包括: 发射物, 死亡的, 隐藏的的秃鹰 - 蜘蛛雷 (潜地的)单位,最多有任意数量个)) <单位>
  • TargetFlags = (空的单位组) <单位组>
  • Count = 0 <整数>
  • Var = 0 <整数>
  • NearBySlots = (空的单位组) <单位组>
  • RemainingSlotsCount = 0 <整数>
  • 动作
  • 综合 -If (条件) then do (动作) else do (动作)
  • If
  • (Target的自定义值0)==0.0
  • Then
  • 单位 -将Target的自定义值0设为100.0
  • 单位组 -从RemainingSlots中移除Target
  • 变量 -设置RemainingSlotsCount = (在RemainingSlots内的活体单位数)
  • 对话框 -为(所有玩家)将HintText的文本设置为(文本(RemainingSlotsCount))
  • 综合 -If (条件) then do (动作) else do (动作)
  • If
  • RemainingSlotsCount==0
  • Then
  • 触发 -运行Victory(核对条件,在其结束之前不要等待)
  • Else
  • 综合 -If (条件) then do (动作) else do (动作)
  • If
  • TargetMine!=无单位
  • Then
  • 单位 -将单位 Target 模型按照变化 14 以及纹理 "" 设置成 印花
  • 镜头 -为玩家1应用(默认游戏镜头),持续0.0秒,使用现有速度%初始速度,10%减速度,并且包括目标
  • 镜头 -为玩家1将镜头平移到(Hero的位置点),持续0.0秒,使用现有速度%初始速度,10%减速度,并且不执行使用智能平移
  • 环境 -在(TargetMine的位置点)处创建由TargetMine所施放的HeroNukeDamage(未命名)
  • 触发 -运行Defeat(核对条件,在其结束之前不要等待)
  • Else
  • 区域 -将Count Zone附着到Target,并偏移(点(0.0,0.0))
  • 综合 -等待0.0625游戏时间秒
  • 变量 -设置Count = (在(在Count Zone内的属于玩家任意玩家的,并且匹配不包括: 发射物, 死亡的, 隐藏的的秃鹰 - 蜘蛛雷 (潜地的)单位,最多有任意数量个)内的活体单位数)
  • 综合 -If (条件) then do (动作) else do (动作)
  • If
  • Count==0
  • Then
  • 单位 -隐藏Target
  • 变量 -设置TargetFlags = (在Count Zone内的属于玩家任意玩家的,并且匹配不包括: 发射物, 死亡的, 隐藏的的克哈 旗帜 - 可摧毁物单位,最多有任意数量个)
  • 单位组 -为TargetFlags中的每一个单位tmliUnit执行(动作)
  • 动作
  • 单位 -从游戏中移除tmliUnit
  • 变量 -设置NearBySlots = (在Count Zone内的属于玩家任意玩家的,并且匹配不包括: 发射物, 死亡的, 隐藏的的信标 (星灵大型)单位,最多有任意数量个)
  • 单位组 -为NearBySlots中的每一个单位tmliUnit执行(动作)
  • 动作
  • Unfold(tmliUnit)
  • Else
  • 变量 -设置Var = (Count+4)
  • 单位 -将单位 Target 模型按照变化 Var 以及纹理 "" 设置成 印花
  • Else

-局部变量

本动作需要7个局部变量。

tmpUnit,单位类型,用来保存临时单位。

TargetFlags,单位组类型,用来捕捉所有我们要移除的旗子,如果在动作中有遇到需要翻开周围8格的情况,而周围八格中又有存在旗子的话,显然是玩家放错了旗子。我们需要帮玩家把这些旗子移除。

TargetMine,单位类型,用来捕捉矿工同学所挖的那个格中的地雷(如果有的话)。初始值可以完全依照前面捕捉当前格印花和当前格旗子的方法,只是所捕捉的单位类型变成了潜地的蜘蛛雷

Count,整数类型,用来计算周围地雷总数。

Var,整数类型。用来计算我们要把当前印花变成的外观的编号。简单而言,就是我们想要让印花显示的数字+4。比如如果我们想显示1,那Var就必须是5。因为印花的模型中,数字1这个外观的编号就是5。

NearBySlots,单位组类型。在需要翻开周围八格时,用来获取周围八格的印花。

RemainingSlotsCount,整数类型,用来保存要获得胜利尚需翻开的格子数。

-动作

由于这里不是触发器,而是一个自定义动作,不能直接填条件,所以我们就用If Then Else来做一个总的条件判断了。由于前面提到的算法,我们要求只对没有翻开过的格子进行翻开操作。因此我们用If Then Else来做个总判断,只有在我们需要操作的那个格子没有翻开过的情况下(0号自定义值等于0),才继续执行接下去一切操作:

休闲地图示例教程:扫雷

If中的条件的参数详情:

比较

值 1: 单位的自定义值

单位: Target

索引: 0

运算符: ==

值 2: 0.0

之后才是我们正式的动作内容:

第一条正式动作,将Target的0号自定义值设为100。我们使用设置单位自定义值动作来实现当然

其实也不是非要是100,随便一个不是0的值都可以。

设置单位自定义值

单位: Target

索引: 0

值: 100.0

第二条动作,从RemainingSlots中移除Target,意思是把目前翻开的这个格子从“为了达到胜利尚需翻开的那些格子”单位组中拿出去。注意如果Target本来就不存在于RemainingSlots中,这个动作将毫无影响。所以只有当Target这个格子没有翻开过,而且没有地雷的情况下,RemainingSlots里的印花个数才会减少,参数详情:

将单位移出单位组

单位组: RemainingSlots

单位: Target

第三条动作,计算RemainingSlots中元素的个数,将它赋给局部变量RemainingSlotsCount。

设置变量

变量: RemainingSlotsCount

值: 单位组中的单位数量

单位组: RemainingSlots

类型: 活体

第四条动作,把算得的RemainingSlotsCount显示到HintText对话框项中。这个对话框项是我们前面创建的,就是为了对玩家显示尚需翻开的格子数,以下是参数详情:

设置对话框项文本

玩家: 所有玩家

对话框项: HintText

文本: 将整数转换成文本

值: RemainingSlotsCount

设置对话框项文本这个动作,正是用来动态修改对话框项显示的文字的,我们这里要把对话框项HintText的文字修改为我们计算出的值,所以文本参数要用将整数转换成文本函数才能将RemainingSlotsCount这个整数转化为可以显示出来的文本。

这样,每次我们翻开一个正确的格子,右上角的剩余格子数提示就会减少了。

接下去我们立刻进行一次胜利条件判断。因为扫雷的胜利条件就是在不触雷的情况下把所有没有地雷的格子翻开。所以我们要在RemainingSlots里的单位数量变化的时候判断一下,如果里头的单位数量变成0了。那么就表示玩家胜利了。也就是说,如果RemainingSlotsCount=0。那么就执行事先准备好的胜利触发器,让玩家胜利:

综合 -If (条件) then do (动作) else do (动作)

If

RemainingSlotsCount==0

Then

触发 -运行Victory(核对条件,在其结束之前不要等待)

Else

接下去就该进行有没有地雷的判断了。

判断有没有地雷自然是看TargetMine这个变量是否等于没有单位。

休闲地图示例教程:扫雷

以下的动作就都基于这个判断了。基于这个判断,接下去的动作分成两种可能。如果TargetMine有单位(Then部分),那么爆炸吧,失败吧。如果没有单位(Else部分),那么开始计算周围的地雷数。

我们先看Then,部分,也就是有地雷。我们进入Bad End。Bad End要做那几件事情呢?当然就是爆炸,然后失败。为了美观起见,在爆炸的同时,把我们撞地雷的这格印花的图案外观改成14号,也就是核弹图标。

休闲地图示例教程:扫雷

我前面说了那么多都还一直没说如何修改印花的外观,印花的模型有许多外观变种,我们可以通过编号来指定它们,比如14号就是这格核辐射图标,而5号是数字图案1,12号是数字图案8。

触发器里有设置单位变化这个动作,可以给单位更改模型,也可以指定外观变化的编号。我们这里就用它来改变印花单位的外观。实际上要修改外观变种,编辑器里有着更正式的方法,但是那涉及比较高级的内容,限于本文是入门教程,这里我们暂时就不讨论了。我们直接使用设置单位变化即可:

#!!#

设置单位变化

单位: Target

变化: 14

纹理: ""

模型: 印花

其中单位这个属性指定我们要改模型的目标。当然是Target。模型这个属性,指定我们需要改成什么模型,但是实际上我们并不需要更改模型,所以依然填写印花这个模型,我们要的只是印花这个模型的一个变种外观(就跟对战里有多种外观的黑暗圣殿武士差不多)。所以我们把变化填成14(核辐射图标)即可。纹理这个参数可以改变纹理,但是本地图内用不到这个。所以留空便是。

如此便能在触雷时将印花的外观变成如上图所示的核辐射图标。

接下去两个动作涉及镜头,由于我们这个游戏全程采用的是拉的非常高的镜头,为了能看到雷区全貌。所以在触雷爆炸的时候,为了给我们的爆炸来给特写,我们特地把镜头瞬间拉回默认视角,让玩家能欣赏可怜的矿工大叔被和谐地炸死的特写镜头.

我们使用应用镜头对象动作来将玩家1的镜头设回默认:

应用镜头对象

玩家: 1

镜头对象: 默认游戏镜头

持续时间: 0.0

初始速度: 现有速度

减速: 10

使用目标: 包括目标

直接用拉回默认镜头,我们却不见得能看见主角,所以我们还需要再加一句,平移镜头。把镜头对准我们的主角:

平移镜头

玩家: 1

点: 单位位置点

单位: Hero

持续时间: 0.0

初始速度: 现有速度

减速: 10

智能: 不执行

然后重头戏来了,让我们来创造爆炸吧!有些同学可能觉得,我们干脆让蜘蛛雷直接爆炸岂不是得了。但是蜘蛛雷的爆炸特效实在太没意思了。好不容易来次爆炸特写,不来个大的怎么成呢?

我们干脆来爆颗核弹。

休闲地图示例教程:扫雷

其实我们也不用让个幽灵来丢。就和前面我们可以直接创建轨道指挥部的扫描效果一样,我们同样可以直接创造核弹的爆炸效果。我们同样使用在点处创建效果(源自单位的)动作:

在点处创建效果(源自单位的)

点: 单位位置点

单位: TargetMine

施法者: TargetMine

效果: HeroNukeDamage(未命名)

HeroNukeDamage(未命名)就是一种核弹爆炸效果。用在这里再合适不过了。

好了,爆炸也爆炸过了特写也特写了,我们就进入失败环节吧,用运行触发器动作来运行之前创建好的却还没写内容的失败触发器(Defeat)。

运行触发器

触发器: Defeat

勾选: 核对条件

等待: 不要等待

至此,触雷的情况处理完毕。

接下去就该处理未触雷的情况了。进入TargetMine!=无单位判断的Else环节。当不存在TargetMine时。我们开始计算周围雷数:

于是,按照前面算法所述,直接把3格x3格大小Count Zone区域套到Target身上,然后输里面的地雷数便是。不过这里要特别说明一下。将区域附着到单位这个动作有个特点:它不是瞬间就能把区域完全附着到单位身上的。这个动作从发布开始,至少需要等待一个星际争霸II游戏的标准周期(0.0625游戏秒)才能正式生效。因此在附着区域后,我们要等到0.0625秒再开始计算,否则便会出错。

于是开始附着区域:

将区域附着到单位

区域: Count Zone

单位: Target

偏移量: 点取自XY

X: 0.0

Y: 0.0

由于上面所说的原因,我们得用一次等待动作:

等待

时间: 0.0625

时间类型: 游戏时间

经过一个标准周期后,我们就可以放心开始计算单位数量了。

然后获取Count Zone中所有的活着的蜘蛛雷这个单位组,并计算它们的数量,然后赋值给Count。

设置变量

变量: Count

值: 单位组中的单位数量

单位组: 区域内匹配条件的单位

区域: Count Zone

玩家: 任意玩家

单位筛选器: 不包括: 发射物, 死亡的, 隐藏的

单位类型: 秃鹰 - 蜘蛛雷 (潜地的)

计数: 任意数量

类型: 活体

在此之后,我们需要进行最后一轮的判断,看看算出来的数字是不是0,也就是判断Count==0,据此来做出不同的反应:

休闲地图示例教程:扫雷

若是0,我们就要把目前挖的那个印花隐藏起来。然后获取周围八格内的旗子,全部逐一删掉。然后获取周围八格的印花,逐一对它们进行Unfold操作(也就是我们现在正在编写的这个动作本身)。

我们使用隐藏或显示单位来把Target隐藏掉:

隐藏或显示单位

显示/隐藏: 隐藏

单位: Target

这样,数字0就像正规扫雷游戏一样不显示出来了。

然后,我们用TargetFlags这个单位组来获取周围八格中所有的旗子。类似事情前面做的太多了。所以这里就不用多解释了。

设置变量

变量: TargetFlags

值: 区域内匹配条件的单位

区域: Count Zone

玩家: 任意玩家

单位筛选器: 不包括: 发射物, 死亡的, 隐藏的

单位类型: 克哈旗帜 - 可摧毁物

计数: 任意数量

然后,下面我们要列举单位组里所有的单位,把它们逐一从游戏里删除。注意,使用把将单位移出单位组动作并不会让单位真正消失,使用移除单位这个动作才真正会删除单位:

单位组 -为TargetFlags中的每一个单位tmpUnit执行(动作)

动作

单位 -从游戏中移除tmpUnit

再来,我们如法炮制,把周围八格中的信标 (星灵 大型)都塞进单位组NearBySlots里。

设置变量

变量: NearBySlots

值: 区域内匹配条件的单位

区域: Count Zone

玩家: 任意玩家

单位筛选器: 不包括: 发射物, 死亡的, 隐藏的

单位类型: 信标 (星灵 大型)

计数: 任意数量

然后对单位组里的每一个印花执行“翻开”操作,也就是直接调用目前我们正在编写的这个动作本身。因为我们想要对每一个格子执行和中央格一样的翻开操作(这其实是一种简单的递归)。

单位组 -为NearBySlots中的每一个单位tmpUnit执行(动作)

动作

Unfold(tmpUnit)

至此,数字为0的分支解决完了。

最终分支,让印花显示数字。由于之前已经说过,印花显示出来数字1-8和这些数字外观对应的外观编号相差4。所以我们只需要把Count加上4,赋给代表外观编号的Var变量即可。

设置变量

变量: Var

值: 算法(整数)

值1: Count

运算符: +

值2: 4

我们使用算法(整数)函数来对Count加上4。

最后一步用设置单位变化动作来改变印花的外观,完事!

设置单位变化

单位: Target

变化: Var

纹理: ""

模型: 印花

至此,这个教程中最最复杂的那部分触发器终于结束了。接下去我们只需要随意的编写胜利\失败触发器即可。

在这个阶段,我们已经可以通过Ctrl+F9对游戏进行完整的测试了。

休闲地图示例教程:扫雷

休闲地图示例教程:扫雷

休闲地图示例教程:扫雷

#!!#

八、胜利、失败触发器

我们之前已经创建好了空的胜利失败触发器,但是并没有给他们编写内容。实际上里面需要写的就是我们在胜利、失败触发器被执行时需要做的事情,大家可以随自己高兴来编写。比如失败后可以重来啊,胜利时给点鼓励语啊什么的。

由于这些不是教程需要教的东西,大家自由发挥即可。我这个演示中的胜利失败触发器几乎什么都没做,单纯就是等待一段时间,然后跳出胜利失败对话框而已:

胜利触发:

  • Victory
  • 事件
  • 局部变量
  • 条件
  • GameOver==false
  • 动作
  • 变量 -设置GameOver = true
  • 触发 -将 (当前触发器) 调为 关闭
  • 触发 -将 Defeat 调为 关闭
  • 综合 -等待2.0游戏时间秒
  • 游戏 -为玩家1以胜利结束游戏(显示对话,隐藏得分画面)
  • Defeat
  • 事件
  • 单位 -Hero死亡
  • 局部变量
  • 条件
  • GameOver==false
  • 动作
  • 变量 -设置GameOver = true
  • 触发 -将 (当前触发器) 调为 关闭
  • 触发 -将 Victory 调为 关闭
  • 综合 -等待15.0游戏时间秒
  • 游戏 -为玩家1以战败结束游戏(显示对话,隐藏得分画面)

大家可能注意到,这里用到了个全局变量GameOver,来判断游戏是否结束。这是因为这里的胜利失败触发在跳出对话框之前设置了等待时间,比如胜利触发器会等待2秒,让玩家能反应过来“哦,我已经扫完了!”但是却有可能出现这种情况:比如玩家挖完了所有格子,却在2秒的胜利等待时间内,又画蛇添足地多挖了一格,结果被炸死了,这样就会导致胜利和失败触发器都被执行,所以保证不管是胜利还是失败都只执行一次是必要的。

另外,也有同学会问,为啥胜利要等待2秒,而失败要等15秒呢?这个原因嘛,很简单啊,因为核弹爆炸的动作就是这么长。总要让玩家看完蘑菇云才跳出失败对话框,你说是吧?

在编写完最后的胜利和失败动作后,同样别忘了测试一下:

休闲地图示例教程:扫雷

休闲地图示例教程:扫雷

通关以后恭喜一下自己吧,你终于把这篇三万五千字的入门教程给看完啦。

#!!#

编后:额外话题

制作扫雷游戏的示例教程已经结束了。本章只是提一下一些零散的额外话题。大家可以自己思考一下。

一、重置雷区

大家在进行游戏的时候也许会发现,无论是胜利还是失败,我们如果想重来一遍,结果都得重新读一遍地图。那不是很浪费时间么?有没有法子不读图,在游戏胜利或者失败后,甚至是游戏中途,可以重置雷区,从头玩起呢?

其实这是很容易的。我们要做的无非就是创造一个新的矿工,把原来的干掉(如果还活着的话)。干掉地图上残存的地雷和旗子。把所有印花的0号自定义值都设为0,并把隐藏掉的印花重新显示出来,再把他们外观都变回最初的三角形包感叹号(22号外观)另外MineCreated要设回false,重新运行一次Init触发器,以重置镜头,并将区域附着到新的矿工身上,重开Defeat和Victory触发器,再把GameOver变量设回false。于是雷区就重置了。

  • Reset
  • 事件
  • 局部变量
  • 条件
  • 动作
  • 单位 -从游戏中移除Hero
  • 单位 -为玩家1在((实用地图区域)的中心)创建1个使用默认朝向(无选项)的矿工 (男性)
  • 变量 -设置Hero = (上一次创建的单位)
  • 单位组 -挑选(在(整张地图)内的属于玩家任意玩家的,并且匹配不包括: 发射物, 死亡的, 隐藏的的秃鹰 - 蜘蛛雷 (潜地的)单位,最多有任意数量个)中的每一个单位并执行(动作)
  • 动作
  • 单位 -从游戏中移除(被挑选的单位)
  • 单位组 -挑选(在(整张地图)内的属于玩家任意玩家的,并且匹配不包括: 发射物, 死亡的, 隐藏的的克哈 旗帜 - 可摧毁物单位,最多有任意数量个)中的每一个单位并执行(动作)
  • 动作
  • 单位 -从游戏中移除(被挑选的单位)
  • 单位组 -挑选Slots中的每一个单位并执行(动作)
  • 动作
  • 单位 -将(被挑选的单位)的自定义值0设为0.0
  • 单位 -显示(被挑选的单位)
  • 单位 -将单位 (被挑选的单位) 模型按照变化 22 以及纹理 "" 设置成 印花
  • 变量 -设置MineCreated = false
  • 触发 -运行Init(核对条件,在其结束之前不要等待)
  • 触发 -将 Defeat 调为 开启
  • 触发 -将 Victory 调为 开启
  • 变量 -设置GameOver = false

上面就是一个重置棋盘的触发器例子。我这个教程所附带的地图中并没有实际使用它。大家可以自己试着创造一些按钮或者对话框来让玩家可以随时重置棋盘。

二、记录最高分

星际争霸II编辑器允许我们在玩家硬盘上保存一些数据。这意味着我们可以利用它老保存玩家的扫雷最高分之类的东西。虽然本教程的扫雷游戏并没有带上计时器。但是大家同样也可以尝试自己来做一个计时器或者计分器。有必要的话,还可以把这些分数保存到玩家硬盘上。由于本文仅仅是一个入门教程,所以并没有涉及这些数据保存方面的知识。有兴趣的同学可以自行摸索或者前往各大星际争霸II地图制作论坛讨论,比如地精研究院,以及地精研究院,或者地精研究院什么的。

三、阻止保存

脑筋转的快的同学可能会发现,我这个扫雷游戏是单人游戏,不是能随时保存的么?这样的话,只要在挖开之前保存一下游戏,用SL大法再怎么样都能破关啊。

没错,实际上这是个基础教程,本身并没有在游戏难度上下什么功夫。何况9x9的扫雷本来就是最简单的那种,用SL大法就更没难度了。所以如果有同学真的打算做扫雷的话呢。倒是可以试着在游戏里加一些限制来使得SL大法不能用。具体的方法很多,也欢迎大家研究讨论。

于是本教程结束(这次是真的。)

坦克 本文来源:网易游戏频道 作者:麦德三世 责任编辑:王晓易_NE0011
分享到:
跟贴0
参与0
发贴
为您推荐
  • 推荐
  • 娱乐
  • 体育
  • 财经
  • 时尚
  • 科技
  • 军事
  • 汽车
+ 加载更多新闻
×

电竞赛事
Gaming

阅读下一篇

返回网易首页 返回游戏首页