粒子使用指南

本指南适合那些想要了解如何在 GML 中使用和创建粒子的人。 本页将介绍粒子的 GML 代码函数GML 视觉操作,因此,如果您以前从未使用过粒子,我们希望本指南能够帮助您 开始是因为它们非常有用并且制作起来很有趣。

提示如果您更喜欢采用更直观的方法来创建粒子,并且不需要 GML 代码提供的广泛功能,您还可以创建粒子系统资源 使用粒子系统编辑器。 此外,您还可以使用编辑器的将 GML 复制到剪贴板为其生成 GML 代码

在进一步讨论之前,我们或许应该解释一下粒子实际上是什么...基本上,粒子是一种图形资源,具有由粒子系统定义的某些属性。 这些属性无法直接针对单个粒子进行操作,但可以通过用于定义其所属系统的代码或操作来集体更改。 它们对于在游戏中创建美丽而华丽的效果(或微妙而谨慎的效果)非常有用,而无需像使用实例那样产生 CPU 开销。

我们已经提到了粒子系统,所以在继续之前让我们解释一下它是什么。 将粒子系统想象成一个容器,您将用它来保存可供使用的粒子。 您可以使用代码或动作来定义粒子的视觉方面,然后将它们放置在“容器”(粒子系统)中,以便您以后可以随时随地将其取出并使用。

在设置系统和创建粒子之前,了解一些事情很重要...首先,大多数粒子系统代码仅在游戏中调用一次,通常在游戏中的某种类型的控制器对象中 第一个房间或关卡 - 情况并非总是如此,但为了简单起见,我们将在以下简要指南中使用此场景,因为这是使用粒子的最常见方法。 这样做是因为粒子系统一旦创建,就会保留在内存中并随时可以使用,因此如果多次创建它,它会迅速增加内存使用量并导致严重的滞后,甚至有可能 使游戏崩溃。 为了在本指南中简单起见,您将创建一个可供游戏中任何实例随时使用的全局 粒子系统。

 

创建粒子系统创建粒子系统

首先,你需要定义粒子系统,并给它给予一个名字,这样你就可以使用它。因为这将是一个任何对象都可以使用的全局系统,所以你可以在游戏开始时运行的控制器对象或初始化函数中完成这一点。GML代码看起来像这样:

global.P_System = part_system_create_layer("Instance_Layer", false);

使用GML Visual

Create Particle System

注意 提供的图层名称必须是使用系统的房间中的有效图层,否则将看不到任何粒子。 另请注意,我们将 persistent 标志(在代码和 GML Visual 中)设置为 false。 持久性粒子系统是一种可以在各个房间“持久”的粒子系统,无需为每个房间销毁并重新创建它。 然而,为了使本指南简单起见,我们不会使用持久性。

这就是我们创造的系统,但是粒子呢?我们也必须定义它们,否则这个系统是无用的,什么也不做。

如果已经有粒子系统资源,则可以使用上述函数/操作通过将资源传递到partsys/"System"参数中来创建它。

 

定义粒子类型定义粒子类型

要在系统中使用粒子,您必须首先定义其一般属性。这些属性类似于对象属性,但它们仅以一般方式应用于单个粒子。这意味着,如果您给予粒子最小移动速度1和最大移动速度2,则系统创建的任何粒子将具有每步1到2像素的随机速度,它们的平均速度为1.5。您应该在创建系统时使用的同一对象中使用以下GML命名第一个粒子并将其添加到系统中:

global.Particle1 = part_type_create();

对于那些使用GML Visual的用户:

Create Particle Type

注意使用GML Visual,我们还勾选混合选项。这将为粒子启用加法混合,我们将在下面进一步讨论。

你现在有一个系统和一个粒子,但你还没有准备好创造你的杰出效果!你仍然需要定义粒子的属性,即:它的外观-它的速度,它的旋转,它的阿尔法,等等。有很多细节可以用来定义粒子效果,因此,下面是最重要的设置的概述,然后是使用所有这些设置的代码片段和GML Visual

现在,在我们向你展示定义粒子的代码之前,让我们先讨论一下"wiggle".这意味着如果你在1和20之间放置一个数字,粒子将在属性的最小值和最大值之间"摆动"或波动,其中1表示缓慢摆动,20表示非常快。因此,粒子速度最小值为2,最大值为5,摆动为20,将在所创建的每个粒子的寿命内在最小/最大速度之间非常快地振荡。

注意还有其他一些次要的粒子性质,我们在这里不涉及,只是为了让事情尽可能简单地开始。请参阅相应的页面了解粒子性质的完整细节。

现在让我们看一些定义粒子的代码:

// This defines the particle's shape
part_type_shape(global.Particle1,pt_shape_pixel);

// This is for the size
part_type_size(global.Particle1,1,1,0,2);

// This sets its colour. There are three different codes for this
part_type_color1(global.Particle1,c_white);

// This is its alpha. There are three different codes for this
part_type_alpha1(global.Particle1,1);

// The particles speed
part_type_speed(global.Particle1,0.50,2,-0.10,0);

// The direction
part_type_direction(global.Particle1,0,359,0,20);

// This changes the rotation of the particle
part_type_orientation(global.Particle1,0,0,0,0,true);

// This is the blend mode, either additive or normal
part_type_blend(global.Particle1,1);

// This is its lifespan in steps
part_type_life(global.Particle1,5,30);

要在GML Visual中创建相同的粒子定义,我们需要执行以下操作:

Define Particle Type所以就是这样!我们现在已经定义了我们的粒子,它们可以使用了。

 

直接在游戏中创建粒子直接在游戏中创建粒子

有几种方法可以创建粒子,每种方法都有其优点和缺点。你可以使用发射器爆裂粒子,或者你可以在一个点直接创建粒子。你使用哪一种实际上取决于你想要实现的目标和你想要创建的效果,但我们将从两种方法中最简单的开始,也就是直接创建粒子。在GML中,我们使用以下函数:

part_particles_create(global.P_System, x, y, global.Particle1, 10);

GML Visual中,它将是:

Create Particles At A Position上面的代码/动作将在全局系统中的给定x/y坐标处创建10个粒子,类型为"Particle 1"。简单!这行代码/动作的伟大之处在于,它可以在任何地方使用,没有任何大惊小怪。例如,如果你把它放在对象中鼠标的全局左按事件中,并改变mouse_x/y位置的x/y值,它会在你每次按下按钮时在鼠标位置创建粒子。或者如果你有一个火箭,那么你可以把它放在step事件中,每一步都有烟雾粒子(尽管1或2可能比10更好!)。你甚至可以让它通过随机改变x/y坐标在一个区域上创建粒子,例如:

repeat(50)
{
var xx = x + 20 - random(40);
var yy = y + 20 - random(40);
part_particles_create(global.P_System, xx, yy, global.Particle1, 1);
}

Create Particles Over A Random Area上面的代码将在一个40px的正方形区域内的随机位置创建50个粒子。

 

使用发射器创建粒子使用发射器创建粒子

既然我们已经解释了创建粒子的简单方法,现在让我们来看看稍微复杂一点的方法,即使用发射器。发射器是粒子系统的另一部分,必须在使用之前定义,所以我们要做一个全局发射器,就像我们为系统和粒子做的一样。我们还必须决定是否要有一个静态的(非移动)发射器与否,以及我们是否要爆裂粒子,以及决定在什么区域和什么样的分布,我们要有发射器使用。

这是什么意思好吧,一个静态发射器是一个你可以定义一次,忘记,因为它不会移动任何地方的游戏期间,即:想想一个木头火-它不移动,它只是发出火焰,所以是静态的,但火球需要一个动态发射器,并会在屏幕上移动。至于爆裂或流,爆裂是粒子的一次性爆炸,对于面积和分布,您可以使用发射器定义发射粒子的面积(可以是矩形,椭圆,菱形或直线)以及分布曲线(高斯,逆高斯或线性)。

下图说明了发射器可用的不同类型的区域形状:

 

Rectangle Emitter Line Emitter
Ellipse Emitter Diamond Emitter


分布曲线(线性、高斯和逆高斯)如下所示:

Particle distribution

下面是定义两个发射器所需的代码/动作的示例,它们通常会在定义粒子系统之后进入控制器对象的创建事件。一个是静态的,并在整个房间的区域上流动粒子,而另一个是动态的,并跟随鼠标从一个小椭圆每30步爆发一次:

 

global.Particle1_Emitter1 = part_emitter_create(global.P_System);
global.Particle1_Emitter2 = part_emitter_create(global.P_System);

// Set up the area that will emit particles
part_emitter_region(global.P_System, global.Particle1_Emitter1, 0, room_width, 0, room_height, ps_shape_rectangle, ps_distr_linear);
part_emitter_region(global.P_System, global.Particle1_Emitter2, mouse_x-10, mouse_x+10, mouse_y-10, mouse_y+10, ps_shape_ellipse, ps_distr_gaussian);

// Set the first to stream 10 particles every step
part_emitter_stream(global.P_System, global.Particle1_Emitter1, global.Particle1, 10);

// This can now be forgotten as it will function until told to stop...
// Set the alarm[0] event to burst the emitter2 particles...
alarm[0] = 30;

Define Particle Emitters这里我们创建两个粒子发射器,它们被分配给我们在开始时创建的粒子系统。创建这些发射器的函数/动作允许我们在房间中定义一个区域,它们将在该区域上发射粒子,以及给予要发射的粒子类型,以及在定义的区域内使用的形状和分布。函数/动作将返回一个数值,即发射器ID值,我们将其存储在一个变量中,这样我们就可以使用更多的发射器函数来定位这些发射器。

现在我们有代码/GML Visual来流式传输粒子(这些粒子将在每个游戏步骤中发射,而无需我们做任何其他事情),但我们还想添加Alarm[0]Event来以特定的时间间隔爆发粒子:

part_emitter_region(global.P_System, global.Particle1_Emitter2, mouse_x - 10, mouse_x + 10, mouse_y - 10, mouse_y + 10, ps_shape_ellipse, ps_distr_gaussian);
part_emitter_burst(global.P_System, global.Particle1_Emitter2, global.Particle1, 30);
alarm[0] = 30;

Burst Particles From Emitter粒子爆发只是发射器中粒子的一次性爆炸,而不是每个游戏步骤中粒子的恒定流。

但是如果你想让几个物体发射粒子呢?全局发射器一次只能在一个地方,所以你需要在每个对象中创建局部发射器。这些发射器仍然使用全局粒子系统和其中的任何粒子,但它们可以是不同的形状和大小,并随对象移动,使它们成为火箭或子弹或其他东西的理想选择。您将使用的代码与上面完全相同,但在发射器名称之前没有"global"前缀。

这就是粒子的创建,但还有一件事非常重要......当你完成它们时,清理它们。

 

清理清理

正如开始时提到的,一旦创建,粒子系统(以及它的粒子,发射器等)都存储在内存中供即时使用。太好了,但是当你重新启动游戏时会发生什么呢?或者如果你的玩家死了,重新启动房间呢?好吧,如果你没有正确地管理游戏,你会得到一个内存泄漏。这是当一些东西被创建并耗尽内存时,但是已经被"de-referenced"了,这意味着GameMaker不再可以访问它。这种事情会慢慢消耗内存,导致游戏延迟,甚至阻塞计算机,导致游戏崩溃,这是第一次使用粒子系统的用户经常遇到的问题。

如何避免这种情况?好吧,GameMakerGML Code函数和GML Visual操作,可以在不使用时从内存中删除粒子系统及其发射器和粒子,并且在使用以下代码/操作时进行一点规划,您可以轻松防止此潜在问题。

您要做的第一件事是决定在哪里创建系统以及如何使用它。 您可以在第一个房间中某个对象(如菜单或标题屏幕)的游戏开始事件中创建一个全局系统,但这意味着如果您使用 game_restart 函数,它将被重新创建并导致内存泄漏。 或者,您可以在任何房间的实例中创建本地系统,但同样,如果您离开房间,则系统将无法访问并导致内存泄漏。 为了避免这种情况,您需要在对象的游戏结束事件清理事件中添加类似的内容:

part_type_destroy(global.Particle1);
part_emitter_destroy(global.P_System, global.Particle1_Emitter);
part_emitter_destroy(global.P_System, global.Particle2_Emitter);
part_system_destroy(global.P_System);

Clean Up Particle System这将删除定义的粒子,发射器,然后系统从内存中准备重新启动游戏或改变房间。只要记住,在思考哪里是最合适的地方创建系统后,想想哪里是最合适的地方摧毁系统太!

 

 

现在你已经了解了粒子、粒子系统和发射器的基础知识,所以你现在可以将它们添加到你的游戏中了!但在匆忙离开并将粒子效果添加到所有内容之前,请注意,虽然粒子比实例占用的CPU少,它们并不是解决一切问题的方法,因为如果你一次有数千个它们,它们会导致滞后。最好的办法是仔细试验和使用它们来"充实"视觉上的游戏和给予它多一点眼睛糖果没有做过。