GMS2官方教程文档翻译-My First Game Chap3&4我的第一个小行星游戏第三、四章

3、分数,生命值和效果

3.1 控制器物体

In this chapter we’re going to be creating lives and score to make our game more interesting, as well as a few more rooms to deal with different game “states”.

NOTE: If you have problems seeing the images in this tutorial, you can expand the tutorial window by dragging the edge into the IDE.

NOTE: If you close the accompanying video then you can get it back by clicking here

To get started we’re going to make a new object. This will be a “controller” object, which means that we won’t be assigning it a sprite as it’s going to sit in a room and deal with things “behind the scenes”. So, make a new object now and call it “” and give it a Create Event:

在本章中,我们将创建生命和分数,以使我们的游戏更加有趣,还有一些房间来处理不同的游戏 “状态”。

注意:如果你看不清本教程中的图片,你可以通过拖动边缘到IDE中来扩展教程窗口。

注意:如果你关闭了附带的视频,那么你可以通过点击这里重新获得它。 为了开始,我们要做一个新的对象。这将是一个 “控制器 “对象,这意味着我们不会给它分配一个精灵,因为它将坐在一个房间里,处理 “幕后 “的事情。所以,现在做一个新的对象,叫它 “obj_game “并给它一个创建事件

We want this object to track the player’s score and lives values, so we’ll just use the built-in global variables and . A global variable is basically a variable that has no “owner”. It belongs to the entire game, and not one particular object, and can be accessed and changed by everything at anytime.

Add these lines into the Create Event:

我们想让这个对象追踪玩家的分数和生命值,所以我们将直接使用内置的全局变量分数和生命。全局变量基本上是一个没有 “所有者 “的变量。它属于整个游戏,而不是某个特定的对象,并且可以随时被所有的东西访问和改变。

在 “创建事件 “中添加这几行。

score = 0;
lives = 3;

We want to show these values to the player too, so for that we’ll add in a Draw Event:

我们也想把这些值显示给玩家,因此我们添加一个绘制事件

As the name implies, this event will draw things to the screen. Like the Step Event, it will run every game frame, otherwise the things you drew would only be visible for one frame and then disappear. We want to use it to display the player score and lives, so we’ll use the function for this. Add the following code now:

顾名思义,这个事件将在屏幕上绘制东西。像Step事件一样,它将在每一帧游戏中运行,否则你画的东西只会在一帧内可见,然后消失。我们想用它来显示玩家的分数和生命值,所以我们将使用draw_text()函数来实现。现在添加以下代码。

draw_text(20, 20, “SCORE: ” + string(score));
draw_text(20, 40, “LIVES: ” + string(lives));

You’ll notice we call the function on the global variables. This is because you can’t add two values that are of different data types, and in this case we have a string “SCORE:” and a number (the value of the global variable). To avoid this issue, we use the function to turn the value of into a string data type – so if the score was 300, would return “300”, and that can then be added onto the “SCORE: ” string (adding strings like this, concatenates them).

你会注意到我们在全局变量上调用了函数string()。这是因为你不能添加两个不同数据类型的值,在这个例子中,我们有一个字符串 “SCORE: “和一个数字( score 全局变量的值)。为了避免这个问题,我们使用string()函数把score的值变成一个字符串数据类型–所以如果分数是300,string(score)将返回 “300”,然后就可以把它加到 “SCORE: “的字符串(像这样添加字符串,将它们连接起来)。

We can now add this controller object into the room, so open up the room “” and drag an instance of this object up to the (0,0) position in the room (you’ll see it is shown with a (?) symbol in the room editor – this is because we haven’t assigned it a sprite):

我们现在可以把这个控制器对象添加到房间里,所以打开房间 “rm_game”,把这个对象的一个实例拖到房间的(0,0)位置(你会看到它在房间编辑器中显示有一个(?)符号–这是因为我们还没有给它分配一个精灵)。

Press play now and test the game! The score and lives values should be displayed in the top-left corner:

现在就按下播放键,测试一下游戏! 分数和生命值应该显示在左上角。

Click the “Next” button to see how to set a font for drawing the values…

下一章

3.2 字体

Before we continue with the programming of the display we’re going to add a new resource type to our game: a font resource, which is simply a collection of characters to use when drawing text. To create this, use the right mouse button on Fonts in the resource tree and select Create Font, which will open the Font Editor:

完成这些后,我们需要告诉GameMaker Studio 2使用这种字体来显示文字,为此你可以调用函数draw_set_font()。如果你的项目使用多种字体,那么你需要在你想用不同字体写的行之前调用这个函数来设置字体,但在我们的小游戏中,我们只想对所有文本使用一种字体,所以我们将它添加到对象 “obj_game “的创建事件中,像这样。

Call the font ““, and then select a font you like from the drop down Select Font menu. In the tutorial we’ll use Consolas, and we will also switch off the anti-aliasing option to give a more pixelated and retro look to the font.

将字体命名为 “fnt_text”,然后从下拉选择字体菜单中选择一种你喜欢的字体。在本教程中,我们将使用Consolas,我们还将关闭抗锯齿选项,使字体看起来更有像素感和复古感。

With that done, we need to tell GameMaker Studio 2 to use this font for the text, and for that you can call the function . If your project uses multiple fonts, then you would need to call this function to set the font in the Draw Event before the lines you want to write using the different fonts, but in our small game we only want to use one font for all text, so we’ll add it to the Create Event of the object ““, like this:

完成这些后,我们需要告诉GameMaker Studio 2使用这种字体来显示文字,为此你可以调用函数draw_set_font()。如果你的项目使用多种字体,那么你需要在你想用不同字体写的行之前调用这个函数来设置字体,但在我们的小游戏中,我们只想对所有文本使用一种字体,所以我们将它添加到对象 “obj_game “的创建事件中,像这样:

draw_set_font(fnt_text);

After calling this function, all text in our game will be written using ““.

Click the “Next” button to see how to update the values…

调用这个函数后,我们游戏中的所有文本都将使用 “fnt_text “书写。

点击 “下一步 “按钮,看看如何更新这些数值…

3.3 设置分数和生命

We can now look at updating the and values as we play. For that, open up the object “” and go into the Collision Event with the object “

我们现在可以看看在游戏中更新分数和生命值。为此,打开 “obj_bullet “对象,进入 “obj_asteroid “对象的碰撞事件。

At the top of the code block, before everything else we’re simply going to add in the following:

在代码块的顶部,在其他所有内容之前,我们只需加入以下内容。

score += 10;

Since the variable is global in scope, if we add to it from any instance then it will be updated for all instances, meaning that if we run the game now, we’d see the score value being drawn to the screen go up as we destroyed the asteroids. However, before testing this, let’s deal with the player lives too.

由于score变量是全局范围的,如果我们从任何实例中添加它,那么它就会对所有实例进行更新,这意味着,如果我们现在运行游戏,我们会看到屏幕上的分数值随着我们摧毁小行星而上升。然而,在测试之前,让我们也处理一下玩家的生命。

NOTE: The way we add 10 here looks different to the way we’ve shown previously, ie: . This is just to show that using is exactly the same as doing , and you can use either method to add to a variable, depending on your own programing style.

注意:我们在这里加10的方式看起来与我们之前展示的方式不同,即:得分=得分+10。这只是为了说明,使用变量+=值和做变量=变量+值是完全一样的,你可以使用任何一种方法来添加到一个变量,这取决于你自己的编程风格。

Open the player ship object ““, and go to the Collision Event with the object ““:

打开玩家的飞船对象 “obj_ship”,并进入与对象 “obj_asteroid “的碰撞事件中。

Here we’re going to deduct 1 from the lives, so go ahead and add the following code before the rest of the code in the event:

船只对象的碰撞事件 这里我们要从生命值中扣除1,所以继续在事件的其他代码前添加以下代码。

lives -= 1;

This will subtract 1 from the variable (and is the same as doing ).

这将从lives变量中减去1(与做lives = lives – 1相同)。

If you test the room now you’ll see the score go up when you shoot the asteroids and if the player ship collides with an asteroid the lives will go down. There’s still work to be done here, but we’ll come back to it later after we’ve set up some more rooms. Click the “Next” button to continue…

如果你现在测试这个房间,你会看到当你射击小行星时,分数会上升,如果玩家的飞船与小行星相撞,生命值会下降。这里还有一些工作要做,但我们在设置了更多的房间后再来讨论。

点击 “下一步 “按钮,继续…

3.4 房间

In order to add things like menu screens into our game we will create a few more rooms. Duplicate “” (use the right mouse button on the room and select Duplicate) and name this new room ““. Open “” and delete everything apart from the “” instance:

为了在我们的游戏中添加菜单屏幕等东西,我们将创建更多的房间。复制 “rm_game”(在房间上使用鼠标右键,选择复制)并将这个新房间命名为 “rm_start”。打开 “rm_start”,删除除 “obj_game “实例之外的所有东西。

Remove Instances From Room. You can hold down and then click  shift and drag to select the instances to remove and then press  to remove them.

从房间中删除实例.你可以按住Shift不放,然后点击并拖动来选择要删除的实例,然后按下删除它们。

This leaves an instance of our controller object “” in the room. We are now going to make this object persistent. Persistent objects are retained as you move from room to room; unlike regular objects, which are cleared from memory each time you leave a room. Note that a persistent object will therefore not trigger its Create or Destroy events when changing rooms, but it will trigger its Room Start and Room End events if it has them.

这就在房间里留下了我们的控制器对象 “obj_game “的一个实例。我们现在要让这个对象持久化。持久对象在你从一个房间移动到另一个房间时被保留下来;不像普通对象,每次你离开一个房间时都会从内存中清除。注意,一个持久化的对象因此不会在改变房间时触发其创建或销毁事件,但如果它有房间开始和房间结束事件,它将会触发这些事件。

Open the object “” now and check the box marked Persistent:

现在打开对象 “obj_game”,勾选标有持久性的方框。

We can go back and remove the instance of “” from the room ““, as the instance created in the “” room will now persist into all subsequent rooms.

我们可以回去将 “obj_game “的实例从 “rm_game “房间中移除,因为在 “rm_start “房间中创建的实例现在将持续存在于所有后续房间中。

We also need to reorder the two rooms so “” is above ““, as GameMaker Studio 2 will always start by loading the first room in the resource tree when your game is run:

我们还需要对这两个房间重新排序,使 “rm_start “位于 “rm_game “之上,因为GameMaker Studio 2在运行游戏时总是从资源树中的第一个房间开始加载。

Now we can add the rest of the rooms that our game requires. For that, duplicate the room ““, rename the new room as “” and remove the instance of “” (so the room should have no instances in it).

现在我们可以添加我们的游戏需要的其他房间。为此,复制房间 “rm_start”,将新房间重命名为 “rm_win”,并删除 “obj_game “的实例(所以房间中应该没有实例)。

We need one last room before we can go back to the programming, so duplicate the room “” and call it ““. Your resource tree should now look like this:

在我们回到编程之前,我们需要最后一个房间,所以复制房间 “rm_win “并将其称为 “rm_gameover”。你的资源树现在应该看起来像这样。

Click the “Next” button to continue…

点击 “下一步 “按钮,继续…

3.5 房间里的文字

Open up the object “” (if it’s not already) and go to the Draw Event. Since our object is persistent now, the code we have for drawing the score and lives will run in all the rooms the instance is persisted across and not just the main game room. Now, we could resolve this using a few “” checks to see which room we are in and draw the text that’s appropriate, but instead we’ll use a statement.

打开对象 “obj_game”(如果还没有的话),进入 “绘图事件”。由于我们的对象现在是持久化的,我们用于绘制分数和生命的代码将在所有实例持久化的房间中运行,而不仅仅是主游戏室。现在,我们可以通过一些 “如果……否则如果…… “的检查来解决这个问题,看看我们在哪个房间,并绘制相应的文字,但我们将使用switch()语句。

Using a function we can check the global variable, which holds the ID of the current room this instance is in, and add different for each of the possible values. In each we can have the controller draw different things.

使用switch()函数,我们可以检查房间的全局变量,该变量保存着这个实例当前所在房间的ID,并为每个可能的值添加不同的情况。在每种情况下,我们可以让控制器绘制不同的东西。

So, let’s change the draw code to look like this:

所以,让我们把绘图代码改成这样:

switch (room)
{
case rm_game:
   draw_text(20, 20, “SCORE: ” + string(score));
   draw_text(20, 40, “LIVES: ” + string(lives));
   break;
}

This code will only draw the text if the room ID is equal to ““. Note that at the end of the we add the keyword . This is required to separate the different s in the , and if you omit it then subsequent s after the one that meets the condition will be run.

We’ll set up the framework for the rest of the code now too, then go back and fill in the blanks, so continue to add s into the Draw Event:

这段代码只有在房间ID等于 “rm_game “时才会绘制文本。请注意,在案例的最后,我们添加了关键字break。这是在switch中分离不同案例的需要,如果你省略了它,那么在满足switch条件的案例之后的后续案例将被运行。

我们现在也要为其余的代码建立框架,然后再回去填空,所以要继续向Draw Event中添加案例:

switch (room)
{
case rm_game:
   draw_text(20, 20, “SCORE: ” + string(score));
   draw_text(20, 40, “LIVES: ” + string(lives));
   break;

case rm_start:
   
   break;

case rm_win:
   
   break;

case rm_gameover:
   
   break;
}

We’ll do the start room “” first, so in that case we want to add the following code:

我们做开始界面 “rm_start”,所以在这种情况下,我们要添加以下代码。【添加文字】

var c = c_yellow;
draw_text_transformed_colour(room_width / 2, 100, “SPACE ROCKS”, 3, 3, 0, c, c, c, c, 1);

This simply sets a local variable, , to a colour constant which is then used in the to draw the game title scaled by 3 (see the manual for full information about the values this function takes).

Following that we add:

这只是将一个局部变量c设置为一个颜色常数,然后在draw_text_transformed_colour()中使用,以绘制缩放为3的游戏标题(关于该函数取值的完整信息,请参见手册)。

在这之后,我们添加:

draw_text(room_width / 2, 200,
@”Score 1,000 points to win!

UP: move
LEFT/RIGHT: change direction
SPACE: shoot

\>>PRESS ENTER TO START<<“);

Note that we use the “” to prefix the string we are wanting to draw, and we have the string split over multiple lines. Using “” like this tells GameMaker Studio 2 that you are defining a string literal, which means that the string will be drawn with line breaks and other special characters, without the need for escape characters (see the section of the manual on Strings for more information).

Why not try running the game now and seeing how it looks?

请注意,我们使用”@”来给我们想要绘制的字符串加前缀,并且我们将字符串分成多行。像这样使用”@”告诉GameMaker Studio 2,你正在定义一个字符串字面,这意味着该字符串将被绘制,并带有换行符和其他特殊字符,而不需要转义字符(更多信息见手册中关于字符串的章节)。

为什么不试试现在就运行游戏,看看它看起来怎么样?

That doesn’t look quite right, does it? What’s happened is that GameMaker Studio 2 has left justified all the text, so we need to tell it to center justify it using the function and set it to the constant . Our complete case should now look like this:

这看起来不大对劲,是吗?发生的情况是GameMaker Studio 2将所有的文字都左对齐了,所以我们需要告诉它用函数draw_set_halign()将其居中对齐,并将其设置为常数fa_center。我们完整的案例现在看起来应该是这样的:【现在看起来正常了】

case rm_start:
 draw_set_halign(fa_center);
 var c = c_yellow;
 draw_text_transformed_colour(room_width / 2, 100, “SPACE ROCKS”, 3, 3, 0, c, c, c, c, 1);
 draw_text(room_width / 2, 200,
 @”Score 1,000 points to win!
 
 UP: move
 LEFT/RIGHT: change direction
 SPACE: shoot
 
 \>>PRESS ENTER TO START<<“);
 draw_set_halign(fa_left);
 break;

Notice that we call the function at the end of the to reset the alignment for the text. If you test the game now, then the text should be in the center of the room and look much better:

请注意,我们在案例的最后调用了函数draw_set_halign()来重置文本的对齐方式。如果你现在测试游戏,那么文本应该在房间的中心,看起来会好很多。

The final thing to do now, is add in similar code for each of the other room cases, only changing the colour and position slightly to suit the different text. To start with we’ll do the “” case, which should look like this:

现在要做的最后一件事,是为其他每个房间的情况添加类似的代码,只是稍微改变颜色和位置以适应不同的文本。首先,我们将做 “rm_win “案例,它应该看起来像这样。

case rm_win:
 draw_set_halign(fa_center);
 var c = c_lime;
 draw_text_transformed_colour(room_width / 2, 200, “YOU WON!”, 3, 3, 0, c, c, c, c, 1);
 draw_text(room_width / 2, 300, “PRESS ENTER TO RESTART”);
 draw_set_halign(fa_left);
 break;

And then for the room “” case:

然后是房间 “rm_gameover “:

case rm_gameover:
 draw_set_halign(fa_center);
 var c = c_red;
 draw_text_transformed_colour(room_width / 2, 150, “GAME OVER”, 3, 3, 0, c, c, c, c, 1);
 draw_text(room_width / 2, 250, “FINAL SCORE: ” + string(score));
 draw_text(room_width / 2, 300, “PRESS ENTER TO RESTART”);
 draw_set_halign(fa_left);
 break;

Click the “Next” button to continue…

点击Next继续

3.6 游戏控制

Most of the text we’ve just added can’t be seen, as we haven’t actually coded anything into change between the different rooms. We’ll do that now, starting with detecting the press of the  key to start/restart the game, depending on the room the player is in.

我们刚刚添加的大部分文字是看不到的,因为我们实际上还没有把任何东西编码为不同房间之间的变化。我们现在要做的是,根据玩家所在的房间,从检测开始/重新开始游戏的按键开始。

In the object ““, add a Step Event. This event will check for the  keypress and then run a on the variable to see what action should be taken using the following code:

在对象 “obj_game “中,添加一个步骤事件。这个事件将检查按键,然后在房间变量上运行一个开关,看看应该使用以下代码:

if (keyboard_check_pressed(vk_enter))
{
switch(room)
 {
 case rm_start:
   room_goto(rm_game);
   break;
 case rm_win:
 case rm_gameover:
   game_restart();
   break;
 }
}

Here we use two new functions which are pretty self-explanatory: which will end the current room and then go to the room given as its argument, and then , which takes no arguments and will simply restart the game again, as if the player was running it for the first time. Note how we have the two room cases together there for the win and gameover states. As was mentioned previously, omitting a means that the case detected will run, and then subsequent cases will run too until the end of the or a is met. Here we use this behavior to our advantage to detect two values and run a single code block.

这里我们使用了两个不言自明的新函数:room_goto(),它将结束当前的房间,然后进入作为其参数的房间;然后是game_restart(),它不需要参数,将简单地再次重启游戏,就像玩家第一次运行游戏一样。请注意我们是如何将两个房间的情况放在一起的,以获得胜利和结束游戏的状态。正如之前提到的,省略断点意味着检测到的案例会运行,然后后续的案例也会运行,直到切换结束或遇到断点。在这里,我们利用这一行为的优势来检测两个值并运行一个代码块。

We want to add in some more code now to detect the “win” and “lose” conditions, which in the case of our game is going to be 1000 points for the score to win, or 0 lives to lose. So, we’ll want to first check that the current room is the game room (we don’t want to perform these checks in any other room), and then we want to check the and variables, like this:

我们现在要添加一些代码来检测 “赢 “和 “输 “的条件,在我们的游戏中,赢的条件是1000分的分数,输的条件是0生命。因此,我们首先要检查当前房间是游戏室(我们不想在任何其他房间进行这些检查),然后我们要检查生命和分数变量,像这样。

if room == rm_game
{
if score >= 1000
 {
 room_goto(rm_win);
 }
if lives <= 0
 {
 room_goto(rm_gameover);
 }
}

We can quickly test this by opening up the Create Event of the “” and editing the score and lives to be 990 and 1 respectively:

我们可以通过打开 “obj_game “的创建事件并将分数和生命值分别编辑为990和1来快速测试。

And now if we test the game we will get the “Win” and “Game Over” screens depending on whether we shoot an asteroid or crash into it:

而现在,如果我们测试游戏,我们会得到 “胜利 “和 “游戏结束 “的屏幕,这取决于我们是射击小行星还是撞上它。

Click the “Next” button to continue on to the last chapter, where we’ll add some polish to the overall gameplay…

点击 “下一步 “按钮,继续阅读最后一章,在这里我们将对整体游戏性进行一些润色……

4、声效和细节

4.1 生成小行星

In this final chapter we’re going to be looking at making the game a bit more polished and interesting for the player.

NOTE: If you have problems seeing the images in this tutorial, you can expand the tutorial window by dragging the edge into the IDE.

NOTE: If you close the accompanying video then you can get it back by clicking here

To start with, we’re going to change how the asteroids are created, so open the room “” and remove all the instances of the object ““:

在这最后一章中,我们将着眼于使游戏更加精炼,让玩家更感兴趣。

注意:如果你在看到本教程中的图片时有问题,你可以通过拖动边缘到IDE来扩展教程窗口。

注意:如果你关闭了附带的视频,那么你可以通过点击这里取回它 首先,我们要改变小行星的创建方式,所以打开房间 “rm_game”,删除所有对象 “obj_asteroid “的实例。

You can remove an instance by clicking left button on mouse on it to select it and then using the Delete key.

With that done, we go back to our controller “” and add a Room Start event:

你可以通过点击鼠标左键来选择一个实例,然后使用Delete键来删除它。

完成这些后,我们回到我们的控制器 “obj_game”,添加一个房间开始事件。

This event will be run at the start of every room, so our persistent object will trigger this event each time a new room is entered. In this event we add the following code:

这个事件将在每个房间开始时运行,所以我们的持久化对象将在每次进入一个新房间时触发这个事件。在这个事件中,我们添加以下代码。

if (room == rm_game)
{
repeat(6)
   {
   var xx = choose(irandom_range(0, room_width * 0.3), irandom_range(room_width * 0.7, room_width));
   var yy = choose(irandom_range(0, room_height * 0.3), irandom_range(room_height * 0.7, room_height));
   instance_create_layer(xx, yy, “Instances”, obj_asteroid);
   }
alarm[0] = 60;
}

You should know at this point what each of the functions used here does individually, but together what they are doing is generating an x/y position within the room that is limited to only the corners of the room, as illustrated here:

在这一点上,你应该知道这里使用的每个函数单独做什么,但它们一起做的是在房间内生成一个X/Y位置,这个位置只限于房间的角落,如图所示【图见文档】

This gives the player the best possible starting circumstances as there will be no asteroids created near them. We now need to continue to create asteroids as the player progresses and destroys them, otherwise there’ll quickly be no asteroids left for them to shoot at, which is why we set the instance variable. An alarm is an event that will be triggered some time after it is set, and it is set using the variable. In this case we are setting the Alarm 0 Event to be triggered 60 steps after we set it.

Add the Alarm 0 Event to the object now:

这给了玩家一个最好的开始环境,因为他们附近不会有小行星产生。我们现在需要随着玩家的进展继续创造小行星并摧毁它们,否则很快就没有小行星可供他们射击了,这就是我们设置报警实例变量的原因。警报是一个事件,它将在设置后的一段时间内被触发,它是用警报变量设置的。在本例中,我们将报警0事件设置为在设置后60步触发。

现在将报警0事件添加到对象中。

In this event, we’re going to spawn the asteroids not in the corner of the room, but at the boundaries of the room. This will make it a lot less obvious to the player when they are created. For this to work we need to choose either a random position along the x-axis and a value for y of either 0 or the room height, or a value of either 0 or the room width for x and a random value for y. The following code does just that, so add it into the Alarm 0 event:

在这个事件中,我们将不在房间的角落里生成小行星,而是在房间的边界处生成。这将使玩家在创建它们的时候不那么明显。要做到这一点,我们需要沿x轴选择一个随机位置,y值为0或房间高度,或者x值为0或房间宽度,y值为一个随机值:

if (choose(0,1) == 0)
{
var xx = choose(0, room_width);
var yy = irandom_range(0, room_height);
}
else
{
var xx = irandom_range(0, room_width);
var yy = choose(0, room_height);
}

We also need to add in the code to spawn the asteroid and also to reset the alarm so that it will lopp and continually create asteroids:

我们还需要在代码中加入催生小行星的代码,同时也要重置警报器,以便它能够持续不断地创造小行星。

instance_create_layer(xx, yy, “instances”, obj_asteroid);
alarm[0] = 4 * room_speed;

To set the alarm we have used the global variable. This variable holds the number of steps the room will perform in a second (the game speed), which is what we set right at the start of this tutorial: 60FPS. So, by setting the alarm to 4 * we are setting it to trigger again in 4 seconds.

为了设置警报,我们使用了room_speed全局变量。这个变量保存了房间在一秒钟内将执行的步数(游戏速度),这就是我们在本教程开始时设置的内容。60FPS。因此,通过将警报设置为4 * room_speed,我们将其设置为4秒后再次触发。

There is one problem with this event, however… Because the object “” is persistent and the alarm is always reset, we would end up with asteroids in rooms other than the game room, since the alarm will be running even after the player has won or lost. To avoid this, add this code at the start of the code block, before the code given above:

然而,这个事件有一个问题…… 因为对象 “obj_game “是持久性的,而且警报总是被重置的,所以我们最终会在游戏室以外的房间里出现小行星,因为即使在玩家赢了或输了之后,警报仍会运行。为了避免这种情况,在代码块的开头,在上面给出的代码之前添加这段代码:

if (room != rm_game)
{
exit;
}

Using “” in the above code is checking to see if something is not equal to the given value (“” means “not”), so if the current room is not the game room, the rest of the event will be skipped (the statement will end the event that it is called in immediately, so any code after it will not be run).

在上面的代码中使用”!=”是为了检查某些东西是否不等于给定的值(”!”意味着 “不”),所以如果当前房间不是游戏房间,事件的其余部分将被跳过(exit语句将立即结束它所调用的事件,所以它之后的任何代码都不会被运行)。

If you run the game now and wait a few seconds you should see that asteroids are spawning constantly around the room edges. Click “Next” to continue…

如果你现在运行游戏并等待几秒钟,你应该看到小行星在房间边缘不断产生。

点击 “下一步 “继续…

4.2 添加声效

The time has come to add sounds to our game, but before we get on with that, take a moment to reset the and variables in the Create Event of ““. These should be set to 0 and 3 respectively, as we no longer need them set to other values for testing:

现在是给我们的游戏添加声音的时候了,但在我们开始之前,花点时间在 “obj_game “的创建事件中重置分数和生命变量。这些变量应该分别设置为0和3,因为我们不再需要将它们设置为其他的测试值。

We’ll now need some sounds for our game to use…

The sounds can be , or format and have a “retro” sound to them. There are a number of different programs available free online for making sound effects and music for you to make your own sounds with, but note that the tutorial comes with a few to get you started as well in the TutorialResources folder, within the project files. If you open the file explorer to load a sound, it should open on that folder location where you can find the sounds used in the “SFX” sub-folder. If you have any issues, you can also find the sounds here.

我们现在需要一些声音供我们的游戏使用…。

这些声音可以是.wav、.ogg或*.mp3格式,并有一个 “复古 “的声音。网上有许多不同的程序可以免费制作音效和音乐,你可以用它们来制作自己的声音,但请注意,本教程在项目文件中的TutorialResources文件夹中也有一些可以让你开始使用。如果你打开文件资源管理器加载一个声音,它应该在该文件夹位置上打开,你可以在 “SFX “子文件夹中找到使用的声音。如果你有任何问题,你也可以在这里找到这些声音。

NOTE: In general you’d use for short sound effects, and or for music or longer, looping sounds. This tutorial uses format sounds simply because they keep the download size small.

Once you have located the example sounds or created your own, we need to add them to our project. Create a Sound in the resource tree and this will open up the Sound Editor, ready for you to add your first sound:

注意:一般来说,你会用wav来制作短的声音效果,用mp3或ogg来制作音乐或更长的循环声音。本教程使用OGG格式的声音,只是因为它们的下载量小。 一旦你找到了例子的声音或创建了你自己的声音,我们需要将它们添加到我们的项目中。在资源树中创建一个声音,这将打开声音编辑器,准备让你添加你的第一个声音。

The sounds we’ll need are as follows:

  • ” – Some kind of background music

  • ” – A sound for the asteroid or the player exploding

  • ” – A sound for the player winning

  • ” – A sound for the player losing

  • ” – A sound for the player shooting

Go ahead and create each of those sounds now (naming them as shown in the list above) and give them an appropriate sound to use. When you’re finished your resource tree should look like this:

我们将需要的声音如下。

  • “msc_song” – 背景音乐

  • “snd_die” – 小行星或玩家爆炸的声音

  • “snd_win” – 玩家获胜的声音

  • “snd_lose” – 玩家失败的声音

  • “snd_zap” – 玩家射击时的声音

现在就去创建这些声音的每一个(按上面的列表命名),并给他们一个适当的声音来使用。当你完成后,你的资源树应该看起来像这样。

Click the “Next” button to see how to get your game to play these sounds…

4.3 播放声效

The first thing we’ll do is add the music for the game when playing. For that, open the object “” and go to the Room Start Event. We want the music to play when we enter the room ““, so we need to modify the code already in the event to look like this:

我们要做的第一件事是为游戏添加播放时的音乐。为此,打开对象 “obj_game “并转到房间开始事件。我们想让音乐在我们进入房间 “rm_game “时播放,所以我们需要修改事件中已有的代码,看起来像这样:

if (room == rm_game)
{
audio_play_sound(msc_song, 2, true);
repeat(6)
   {
   var xx = choose(irandom_range(0, room_width * 0.3), irandom_range(room_width * 0.7, room_width));
   var yy = choose(irandom_range(0, room_height * 0.3), irandom_range(room_height * 0.7, room_height));
   instance_create_layer(xx, yy, “Instances”, obj_asteroid);
   }
alarm[0] = 60;
}

Here we use the function to play the music resource that we added. We set the “priority” to 2 and also set the loop argument to as we want the music to play constantly while the player is in the game room. Once you’ve added that we can then add in the win/lose sounds to this object. For that, go to the Step Event and modify the and checks to look like this:

这里我们使用函数 audio_play_sound() 来播放我们添加的音乐资源。我们将 “优先级 “设置为2,并将循环参数设置为 “true”,因为我们希望当玩家在游戏室中时,音乐能够持续播放。一旦你添加了这些,我们就可以在这个对象中添加赢/输的声音。为此,进入步骤事件,修改分数和生命检查,使其看起来像这样:

if room == rm_game
{
if score >= 1000
   {
   audio_play_sound(snd_win, 1, false);
   room_goto(rm_win);
   }
if lives <= 0
   {
   audio_play_sound(snd_lose, 1, false);
   room_goto(rm_gameover);
   }
}

Note that this time we set the priority argument to 1, as we want the sound effects to have less priority than the music, and we also set the loop argument to , as we don’t want these sounds to play more than once.

You need to go to the object “” now, and open the Step Event. Here we want to modify the code block like this:

注意,这次我们把优先级参数设为1,因为我们希望音效的优先级低于音乐,我们还把循环参数设为false,因为我们不希望这些声音播放超过一次。

你现在需要到对象 “obj_ship”,打开步骤事件。在这里,我们要这样修改 keyboard_check_pressed(vk_space) 代码块:

if (keyboard_check_pressed(vk_space))
{
audio_play_sound(snd_zap, 1, false);
var inst = instance_create_layer(x, y, “Instances”, obj_bullet);
inst.direction = image_angle;
}

The player ship object also needs to have a sound for when it collides with an asteroid, so open the Collision Event with the object “” and add this line to the top:

当玩家飞船对象与小行星相撞时,也需要有一个声音,所以用对象 “obj_asteroid “打开碰撞事件,并在顶部添加这一行:

audio_play_sound(snd_die, 1, false);

We’ll use the same sound in the object “” for when it hits an asteroid, so open that object too and in the Collision Event with the object “” and add the same line:

我们将在 “obj_bullet “对象中使用同样的声音,用于撞击小行星时,所以也要打开该对象,并在碰撞事件中与 “obj_asteroid “对象添加同样的行:

audio_play_sound(snd_die, 1, false);

And that’s it! You should run the game now and see how it sounds… it should feel a lot different playing! There’s only a couple of things we have left to do before the game is finished, so click the “Next” button to continue…

就这样了! 你现在应该运行游戏,看看它的声音如何……玩起来的感觉应该有很大的不同

在游戏完成之前,我们只剩下几件事要做,所以点击 “下一步 “按钮继续……

4.4 最后的润色

Before we can call the game finished, there is one loose end that we need to fix up. Currently, when the player dies, a life is removed and nothing else happens. What we really want to happen is to have the room start again so the player can keep playing until the 3 lives are lost and the game ends. To achieve this we need to add another Alarm Event into the object ““, and in that we’ll restart the room, so that when the player dies there is a short pause, and then they can start to play again with a life less.

Open the object “” now, and add an Alarm 1 event to it:

在我们宣布游戏完成之前,有一个松散的问题我们需要解决。目前,当玩家死亡时,一条生命被移除,其他的事情就不会发生。我们真正想做的是让房间重新开始,这样玩家就可以继续玩下去,直到3条生命消失,游戏结束。为了实现这个目标,我们需要在对象 “obj_game “中添加另一个报警事件,在这个事件中我们将重新启动房间,这样当玩家死亡时就会有一个短暂的停顿,然后他们可以在少了一条命的情况下重新开始游戏。

现在打开对象 “obj_ship”,并向其添加一个Alarm 1事件。

In this event we simply want to call the following code:

在这个事件中,我们只需调用以下代码:

room_restart();

The function does just what it says and restarts the room as if it had never been entered, so the player and asteroids are all created again and the player can keep playing. To set this alarm, we need to open the object “” again, and in the collision event with the object “” add the following code:

room_restart()函数就像它所说的那样,重新启动房间,就像它从未被进入过一样,所以玩家和小行星都被重新创建,玩家可以继续游戏。 为了设置这个警报,我们需要再次打开对象 “obj_ship”,并在与对象 “obj_asteroid “的碰撞事件中添加以下代码:

with (obj_game)
{
alarm[1] = room_speed;
}

The last thing we are going to do is fix the music so it restarts when the room restarts too. As we have it now, we’ll be playing the song again when the room is restarted, so we’ll have two (out of sync) versions of the song playing. This is because sounds will not stop playing when a room is changed or restarted, so you must explicitly tell GameMaker Studio 2 to stop a sound if you don’t want to hear it after a restart or change. To have our music restart and only play once, we need to open the Room Start Event of the object ““, and add the following in just before the call to :

我们要做的最后一件事是修复音乐,使它在房间重新启动时也能重新启动。按照我们现在的做法,当房间重新启动时,我们将再次播放这首歌,所以我们将有两个(不同步的)版本的歌曲在播放。这是因为当一个房间被改变或重启时,声音不会停止播放,所以你必须明确告诉GameMaker Studio 2,如果你不想在重启或改变后听到声音,就停止它。为了让我们的音乐重新开始并且只播放一次,我们需要打开对象 “obj_game “的房间开始事件,并在调用 audio_play_sound() 之前加入以下内容。

if audio_is_playing(msc_song)
{
audio_stop_sound(msc_song);
}

All we’re doing is checking to see if the sound “” is playing, and if it is then we stop it (the next line will restart it again). Click the “Next” button to see how to continue…

资源下载: