游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

分享用Construct 2制作连线消除游戏的教程(4)

发布时间:2013-08-08 15:50:52 Tags:,,,,,

作者:David Silverman

在之前的教程中,我们将基本的匹配检测系统整合到连线消除游戏中。尽管我们一直在努力创造一款具有游戏性的游戏,但是在真正喊出自己创造出一款“游戏”之前,我们仍需要考虑一些重要的游戏元素。本篇文章便将侧重分析那些被遗漏的细节内容。(请点击此处阅读本系列第12、3篇

最后的游戏演示版本

以下是我们在这系列教程中努力创造出的游戏演示版本:

demo(from tutsplus)

demo(from tutsplus)

1.奖励点

在开始完善匹配系统前我们需要注意到这些要点,因为如果我们已经执行了一个点系统,那么这便能够帮助我们更轻松地看到当前匹配系统中所存在的问题。

在我们开始编辑事件时,我们需要在点显示中添加某些内容,所以让我们先到布局1中执行以下内容:

1.插入一个新的精灵对象。

从图像包中打开图像Game Field Images/ScoreArea.png。

关闭动画编辑器。

将位置设为491,383。

2.插入一个新的文本对象。

将字体设为Calibri,粗体,22

将名字设为ScoreText

将颜色设为白色或255,255,255

将位置设为419,398

将尺寸设为200,50

将文本设为0

布局1将如下:

layout(from tutsplus)

layout(from tutsplus)

现在我们能够告诉玩家点文本代表什么,而文本对象能够告诉给玩家相关分数,我们可以通过移动将点呈现给玩家。来到事件表1中,创造一个新的全局变量。

Global Variable
Name: “Score”
Type = Number
Value = 0

变量应该如下:

variable(from tutsplus)

variable(from tutsplus)

这是我们在向玩家呈现点时需要修改的变量。接下来,我们将创造一个新函数,当它被调用时将能检测到玩家匹配了多少砖块到群组中,从而提供给他们适当的点数。

Event:
Condition: Function> On Function
Name = “GivePoints”
Condition: System>For Each
Object = Block
Condition: Block>Is boolean instance variable set
Instance Variable = IsMatched
Action: System> Add to
Variable = Score
Value = 10
Action: ScoreText>Set text
Text = Score

你的代码应该如下:

code(from tutsplus)

code(from tutsplus)

需要再次重申的是,该事件是着眼于每一个砖块。每次当它发现一个砖块带有设定为true的IsMatched时(游戏邦注:即意味着它已被证实为群组中的一部分),它便会给予玩家该砖块的10个点,并更新分数文本。

如果你在这里测试游戏,这看起来就像是不能运行的函数。因为我们并未在代码中真正调用该函数,所以点数便不会增加,而文本也不会更新。切换到FindMatches函数并在该函数最后的子事件开始处添加一个新的行动。

Action: Function>Call function
Name = “GivePoints”

你的FindMatches函数应该如下:

function(from tutsplus)

function(from tutsplus)

注:确保你已经将这一新行动添加到子事件的开始处。如果你是将它添加到末尾,它便不能有效运行,因为所有的匹配砖块会在GivePoints函数被调用前被摧毁。这便意味着,当它在搜索匹配砖块时,它将找不到任何砖块,所以玩家也不能接收到任何点数。

这时候,你可以再次测试游戏,你将会看到点文本的更新,所以玩家能够收到有关自己创造的每个匹配的正确点数。

2.完善匹配检测

现在我们已经拥有点系统,我希望你能运行游戏,并创造如下的场景。

scenario(from tutsplus)

scenario(from tutsplus)

现在,交换突出的那两个砖块,然后观看你获得了多少点数。

blocks(from tutsplus)

blocks(from tutsplus)

当你完成了这些匹配,你便能看到自己获得了50个点数。这是因为当前的点系统给予每个标记着IsMatched的砖块10个点数,而不是给予每个匹配中的每个砖块10个点数,就像我之前所描述的系统那样。

如果点系统的能够合理运行,它便能带给玩家60个点:30个给予3个垂直砖块群组,另外30个给予3个水平砖块群组。这一问题是源自匹配系统并不能在砖块同时处于水平和垂直状态时进行有效标记,它只知道砖块是否出现匹配。

为了解决这一问题,我们最先在砖块对象中添加了2个新的实例变量,MatchedX和MatchedY。

Instance Variable:
Name = MatchedX
Type = Boolean
Initial Value = false
Instance Variable:
Name = MatchedY
Type = Boolean
Initial Value = false

你的变量应该如下:

variable(from tutsplus)

variable(from tutsplus)

这些变量将用于连接IsMatched去告诉系统何时砖块是水平的,或属于X群组,以及何时砖块是垂直的,或属于Y群组。既然我们现在拥有变量,所以便能够修改CheckMatches函数从而在它将砖块标记为IsMatched(因为它发现一个足够大的群组)时,它也能够根据参数3或参数4是否为1而将砖块标记为X群组或Y群组的组成部分。

来到CheckMatches函数并用以下两个新的子事件去替换最初的NumMatchesFound检查:

Sub-Event:
Condition: System>Compare two values
First value = Function.Param(3)
Comparison = Equal to
Second value = 1
Condition: System>Compare variable
Variable = NumMatchesFound
Comparison = Greater or equal
Value = 3
Action: Block>Set Boolean
Instance variable = IsMatched
Value = True
Action: Block>Set Boolean
Instance variable = MatchedX
Value = True
Sub-Event:
Condition: System>Compare two values
First value = Function.Param(4)
Comparison = Equal to
Second value = 1
Condition: System>Compare variable
Variable = NumMatchesFound
Comparison = Greater or equal
Value = 3
Action: Block>Set Boolean
Instance variable = IsMatched
Value = True
Action: Block>Set Boolean
Instance variable = MatchedY
Value = True

你的CheckMatches函数应该如下:

function(from tutsplus)

function(from tutsplus)

所以CheckMatches函数的新版本与之前的一样,除了它会检查砖块是否会出现在垂直群组或水平群组中,并根据新变量MatchedX和MatechedY去标记砖块。

给予匹配两次的砖块额外点数奖励

现在我们能够明确何时砖块是垂直匹配,何时是水平匹配以及基于两个方向同时匹配,所以我们需要在GivePoints函数(能够为被设为true的MatchedX和MatchedY分配额外的10个点数)中添加一个子事件。

来到GivePoints函数并添加如下子事件:

Sub-Event:
Condition: Block>Is Boolean instance variable set Instance variable = MatchedX
Condition: Block>Is Boolean instance variable set
Instance variable = MatchedY
Action: System>Add to
Variable = Score
Value = 10
Action: Text>Set text       Value = Score

你的GivePoints函数应该如下:

function(from tutsplus)

function(from tutsplus)

这时候如果你运行游戏,并再次创造如上所提到的场景,你的分数便能够准确地提高60点。

3.添加重力

现在我们执行了一个点系统,也更新了匹配系统,接下来我们将开始完善另一个重要的游戏元素。如果到目前为止你已经花了些时间去玩游戏,你便会知道当前存在的一个最大的问题便是,当砖块被摧毁时,它们上面的砖块并不会发生任何变化。特别是某些缺口上方的砖块甚至不会掉落到这些缺口中。

这在测试中是合理的,但是在最后版本中它却会对游戏玩法造成消极影响,所以我们接下来将添加“重力”,即当其它砖块被摧毁时,砖块会自然掉落并填充缺口。

我们执行这一系统的方式真的很简单。我们将使用Block > Is overlapping at offset事件去判断在我们所瞄准的砖块下方是否存在其它砖块。如果没有,我们便会移动该砖块去填补下方的空缺,否则它便会保持不变。

为了做到这点我们需要创造一个新事件:

Event:
Condition: INVERT>Block>Is overlapping at offset
Object = Block
Offset X = 0
Offset Y = 8
Action: Block>Move at angle
Angle = 90
Distance = (Block.Width + 2)/2

你的代码应该如下:

code(from tutsplus)

code(from tutsplus)

这时候如果你运行游戏,你便会看到当游戏开始时,所有砖块便会从屏幕中掉落下来。这是因为我们未在代码中阐述游戏的基底在哪里。

所以我们必须确保最底层的砖块能够意识到下方已经没有任何砖块了。然后,当最底层的砖块已经掉落时,倒数第二层的砖块便会意识到自己下方没有砖块,并下落。这一过程将不断持续着,直到所有砖块都掉完,并留下空白的屏幕。

为了修正这点,我们将在事件中添加第二个条件:

Event:
Condition: Block>Compare Y
Comparison = Less or equal
Y = SPAWNY – 1

你的代码应该如下:

code(from tutsplus)

code(from tutsplus)

通过添加这一新条件,我们能够确保只有Y轴最底层上方的砖块会受到“重力”的影响。不过尽管进行了修正,我们仍面对一些问题。

处理拖动

这里所存在的主要问题在于,着眼于砖块下方是否存在空缺的事件在玩家拖动砖块时并不能发挥作用。这便意味着,如果你不松手地将一个砖块拖到很远的位置上,那么在你拖动的这个砖块上方的砖块便会掉落到这一空缺的位置上。并且你所拖动的砖块本身也存在问题,如果你将其带离游戏领域,它便会消失在鼠标所能触及之处,从而没有一个砖块会出现在它下方。

为了解决这一问题,我们需要添加一个新的全局变量去告诉系统我们何时移动砖块。在拖动砖块和掉落事件中添加一个新行动去设置这一全局变量,并在重力事件中添加第三个条件,从而让它在激活时能够考虑到这一变量。

首先,让我们创造全局变量:

Global Variable:
Name = BlockBeingMoved
Type = Number
Initial Value = 0

你的变量应该如下:

variable(from tutsplus)

variable(from tutsplus)

现在让我们来到On DragDrop drag start事件并添加一个新的行动:

Action: System>Set Value
Variable = BlockBeingMoved
Value = 1

同样地来到On DragDrop drop事件在主要事件上添加一个新的行动:

Action: System>Set Value
Variable = BlockBeingMoved
Value = 0

如此你的DragDrop事件应该如下:

最后,切换到重力事件中添加一个新条件:

Condition: System>Compare Variable
Variable = BlockBeingMoved
Comparison = Equal to
Value = 0

你的重力代码应该如下:

gravity(from tutsplus)

gravity(from tutsplus)

我们所创造的新变量BlockBeingMoved是用于告诉系统玩家何时移动了砖块。如果变量等于0,这便意味着没有砖块被移动,它便能够像平常那样运行重力脚本。如果变量等于1,这便意味着玩家移动了砖块,系统便需要停止运行重力脚本。

如果你在这时候运行游戏,你便会发现不管你将砖块拖到哪里,都不会有问题出现了。

检查新匹配

关于重力系统我们还剩下最后一个问题。运行游戏并创造类似下图的场景:

block(from tutsplus)

block(from tutsplus)

现在交换突出的两个砖块:

block(from tutsplus)

block(from tutsplus)

你会发现,当绿色/星星砖块被摧毁时,橙色/六角形砖块便会掉落,形成3个砖块的群组,但它们却不会因此而消失。

因为我们未第二次调用FindMatches函数去明确当砖块掉到缺口时会出现哪些新的匹配。为了解决这一问题,我们需要来到检查砖块下方是否有缺口的事件中,并添加如下Else事件:

Event:
Condition: System>Else
Action: Function>Call function
Name= “FindMatches”

你的代码应该如下:

code(from tutsplus)

code(from tutsplus)

这一Else陈述意味着,当它发现没有缺口,它便会检查是否存在任何可摧毁的群组。当砖块掉落到一个新位置上时该事件便会自动运行,因为它是受到Else陈述(与检查挂钩)所激活,并且只会在确保所有砖块都依序掉落到适当的位置上时运行。

如果你在这时候运行游戏便会发现,你可以通过摧毁砖块创造其它的砖块链。另外你还会发现当你最初开始游戏时,最初产生的任何群组也会被摧毁。就像我在之前教程中所说的,我们最终将消除预制的匹配,所以到最后这一问题便不再重要了。

从最初布局中删除砖块

最后,在完成重力系统之前我们还需要做一件事。基于你所设置的最初砖块精灵的位置,你将会注意到当你开始游戏时,该精灵便会掉落并清楚地呈现出来。

如果你能理解我所说的,前往布局1,将砖块精灵的位置设为521.-32,然后运行游戏。当你在玩游戏时,如下,你便会看到最初砖块掉落在我所突出的位置上:

image(from tutsplus)

image(from tutsplus)

就像你在上图所看到的,最初的砖块会从屏幕外的位置掉落,并呈现出来。我们不想要执行这点是因为它将会在之后引出不必要的问题。为了解决这一问题,我们在On start of layout事件中添加了一个行动,即将会在最初加载时在布局中摧毁任何砖块。

Action: Block>Destroy

你的事件应该如下。

event(from tutsplus)

event(from tutsplus)

现在当你运行游戏时,你将不再会看到砖块。你也许会问自己,为什么我们不能只是将砖块从布局中删除,如此我们就无需再担心这问题了。我们不这么做是因为Construct 2不能基于事件创造任何对象类型的副本,除非在首次加载时游戏中便已经存在一个对象类型范例。通过将其从事件中删除,它便不会在之后构成危险,我们便可以通过代码创造更多砖块了。

结论

在本篇教程中我们谈及了许多内容,并且还有许多事等着我们去做,不过我认为现在最重要的便是休息一会,让自己完全吸收这些信息。在下一篇文章中,我们将继续解决一些小问题,创造最后演示版本中会出现的浮动式点文本,并设置链接系统。

本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

Make a Match-3 Game in Construct 2: Points, Matching, and Gravity

By David Silverman

In the previous tutorial, we integrated a basic match-detection system into our Match-3 game. While we are well on our way to having a playable game, there are still a few important game elements we need before we can really call what we have a “game”. This article is going to focus on filling in some of those missing details, and getting us much closer to our final product.
Final Game Demo
Here is a demo of the game we’re working towards throughout this series:

1. Awarding Points

We are going to cover points before we start improving the matching system, because it will be much easier to see the first issue in our current matching system if we have a points system implemented.

The points system in our game is going to be very simple, for every Block used to form a given group, the player will receive 10 points. In a later tutorial we will also add in a system that allows the player to gain more points by chaining together multiple groups, but for now we will focus on just introducing a simple points system for the player.

Before we start editing the events, we need to add in a Points display, so first go to Layout 1 and do the following:

1.Insert a new Sprite object.

Open the image Game Field Images/ScoreArea.png from the graphics package.

Close the animation editor.

Set the position to 491, 383.

2.Insert a new Text object.

Set the Font to Calibri, Bold, 22 using the drop-down.

Set the Name to ScoreText

Set the Color to White or 255, 255, 255

Set the Position to 419, 398.

Set the Size to 200, 50.

Set the Text to 0.

Layout 1 should now look like this:

Now that we have something to tell the player what the points text means, and a text object to display the player’s score with, we can move on to actually giving the player points. Go to Event Sheet 1 and create a new Global Variable.

The variable should look like this:

This is the variable we will modify whenever we give the player points. Next, we will create a new function which, when called, will detect how many Blocks the player has matched into groups, and give them the appropriate number of points.

Your code should look like this:

So, to reiterate, this event looks at every single Block. Every time it finds a Block which has IsMatched set to true – meaning it’s been confirmed to be part of a group – it gives the player 10 points for that Block, and updates the score text.
If you test your game at this point, it will seem like the function isn’t working. The reason for this is because we haven’t actually called the function anywhere in the code, so the points are never being incremented, and the text is never being updated. Go to your FindMatches function and add a new Action to the beginning of the final sub-event for this function.

Your FindMatches function should now look like this:

Note: Make sure that you have added this new Action at the beginning of the Sub-Event. If you add this Action to the end, it will not work since all of the matched Blocks will have been destroyed before the GivePoints function is called. This means that, when it searches for the matched Blocks, it will not find any and so the player won’t receive any points.

At this point you can test your game again and you should see the points text updating, and that the player is receiving the correct number of points for each match they make.

2. Improving the Match Detection
Now that we have the points system in, I want you to run the game, and create the scenario shown below.

Now swap the Blocks I’ve highlighted here, and watch your score to see how many points you gain.

When you formed this match, you should have seen that you gained 50 points. This is because, currently, the points system gives the player 10 points for each Block that is marked as IsMatched, as opposed to giving the player 10 points for each Block used in each match, like the system I described above.

If the point system worked correctly, it would give the player 60 points: 30 for the vertical group of three Blocks, and 30 for the horizontal group of three Blocks. This problem stems from the fact that the match system doesn’t have any way of marking when a Block is matched both horizontally and vertically; it only knows if the Block is matched at all.

To solve this problem we are first going to add two new Instance Variables to our Block object, MatchedX and MatchedY.

Your variables should look like this:

These variables are going to be used in conjunction with IsMatched to tell the system when the Block is part of horizontal, or X, groups, and when the Block is part of vertical, or Y, groups. Now that we have the variables, we are going to modify the CheckMatches function so that when it labels a Block IsMatched because it found a large enough group, it will also label that Block as being part of an X or Y group depending on whether Parameter 3 or Parameter 4 is 1.

Go to the CheckMatches function and replace the original NumMatchesFound check with these two new sub-events:

Your CheckMatches function should now look like this:

So the new version of CheckMatches functions in the same way as the previous one, except it now also checks to see whether the Block was found to be a match in a vertical group or a horizontal group, and labels the Block accordingly with the new variables MatchedX and MatchedY.

Awarding Extra Points to Blocks That Match Twice

Now that we have a way to determine when a Block is matched vertically, matched horizontally, and matched in both directions, as opposed to just knowing it has been matched in a group, we need to add a sub-event to the GivePoints function which will distribute an extra 10 points for a Block that has both MatchedX and MatchedY set to true.

Go to the GivePoints function and add this sub-event:

Your GivePoints function should now look like this:

If you run your game and again create the scenario I illustrated above, your score should now correctly increase by 60 points.

3. Adding Gravity

Now that we have a Points system implemented, and we have updated the matching system, we are going to start improving another important aspect of the gameplay. If you’ve spent any time playing with the game up to this point, you’ll know that one of the biggest issues is that when Blocks are destroyed, nothing happens to the Blocks above them. Specifically, the Blocks above empty spaces don’t fall to fill in those spaces.

This is fine in the tests, but in the final version it would be detrimental to the gameplay to leave things as they are, so the next thing we’re going to add is “gravity” which will cause the Blocks to fall and fill in empty spaces when other Blocks are destroyed.
The way we will implement this system is actually quite simple. We will perform a check using the Block > Is overlapping at offset event to see if there is a Block below the Block we are looking at. If we find there is no Block, we will move the Block we are looking at down to fill in the empty space; otherwise, we will do nothing.
To make this work we will create a new Event:

Your code should look like this:

If you run the game at this time, you will see that the moment the game begins, all of the Blocks fall off the screen! The reason for this is because we didn’t put anything into the code to tell it where the “floor” of the game field would be.

So essentially, the Blocks on the bottom row realize there are no Blocks below them and fall accordingly. Then, once the lowest row of Blocks has fallen, the next lowest row sees there are now no Blocks below them, and they too fall. This process continues, until all of the Blocks have fallen, and leaves the screen completely empty.
You can see a slightly slowed down version of this in action in the GIF below:

To fix this, we will add a second condition to the Event.

Your code should now look like this:

By adding this new condition we ensure that only Blocks that are above the Y position of the lowest row are affected by our “gravity”. Despite this fix, we still have a few problems.

Dealing With Dragging

The primary problem is that the event which looks to see whether there is an empty space below a Block does not have anything to tell it to be inactive when the player is dragging a Block. This means that, if you drag a Block too far without letting go of it, the Blocks in the position above where you dragged it from will fall into the space left by the Block you dragged. On top of that, the Block you are dragging will also have an issue if you bring it out of the game field, and it will start to fall away from the mouse cursor since there are no Blocks below it.

To fix this problem we need to add a new global variable to tell the system when we are moving a Block, a new action to the Block dragging and dropping events to set this global variable, and a third condition to the gravity event so it takes this variable into account before activating.

First, let’s make the global variable:

Your variable should look like this:

Now, go to the On DragDrop drag start event and add a new Action:

Also, go to the On DragDrop drop event and add a new Action to the primary event:

With the lines added, your DragDrop events should now look like this:

Finally, go to the gravity Event and add a new condition:

Your gravity code should now look like this:

The new variable that we created, BlockBeingMoved, is used to tell the system when a Block is being moved by the Player. If the variable equals 0 it means that no Block is being moved and it can run the gravity scripts as normal. If the variable equals 1, it means a Block is being moved, and the gravity scripts should not be run.

If you run the game at this point, you will see that no matter where you move the Block while you are dragging it, no issues occur.

Checking For New Matches

Now we just have one last issue to deal with regarding the gravity system. Run the game and create a scenario similar to this:

Now, make the swap that I have highlighted in this next image.

You should notice that when the group of Green/Star Blocks is destroyed, an Orange/Hexagon Block falls and forms a group of three Blocks, but doesn’t get destroyed.

The reason these Blocks don’t get destroyed is because we never called the FindMatches function a second time to see if any new matches were formed when the Blocks fell to fill in the empty spaces. To fix this, go to the Event which checks for empty spaces below Blocks and add this Else Event:

Your code should look like this:

This else statement means that, whenever it finds there are no empty spaces, it will perform a check to see if there are any groups to destroy. This event will automatically run whenever Blocks fall into new positions since it is activated by an Else statement which is linked to that check, and will only fire once it is sure all the Blocks have fallen into place.

If you run the game at this point you will find that you can now create chains of Blocks by destroying Blocks in a way that groups will be formed when the remaining Blocks fall. On top of that, you will also find that when you first start the game, any groups that are spawned initially will be destroyed as well. As I said in the previous tutorial, we will eventually eliminate pre-made matches, so this issue will not matter in the end.

Removing Blocks From the Initial Layout

Finally, we have to do one other thing before we can consider our gravity system complete. Depending on where you placed the initial Block sprite when you completed the first tutorial, you may notice that when you start the game it falls and becomes visible.

If you don’t know what I mean, go to Layout 1, set the position of your Block sprite to 521, -32, and run the game. When you play the game, you should see the original Block land in the position I’ve highlighted in the image below:

As you can see in the image above, the initial Block falls from its position off-screen and becomes visible. We don’t want this because it is only going to cause us issues later on. To solve this small problem we are going to add an Action to the On start of layout event that will destroy any Blocks that are in the Layout when it initially loads.

Your event should now look like this:

Now when you run the game, you should no longer see the Block. You may be asking yourself why we didn’t just delete the block from the Layout so we don’t have to worry about this problem at all. The reason we didn’t do this is because Construct 2 cannot create copies of any object type with Events, unless there is already an instance of that Object type in the game when it first loads. By deleting it within an event, we remove it so it doesn’t become an issue later, and we make it possible to spawn as many Blocks as we need through code.

Conclusion

We covered a lot of topics in this tutorial, and while there is more we could do, I think it is important to take a break and let this info sink in. In the next installment, we will fix a couple of small issues, make the fancy floating points text that you might have noticed is in the final demo, and set up the chaining system.
I hope you got a lot out of this part of the series, and I will see you back here next week.(source:tutsplus)


上一篇:

下一篇: