translatedTitles.Article
OO
Oliver Orangers
11/18/25

Урок 13. Фрагменты, выпадающие после смерти противника

Привет, друзья! Продолжаем наш цикл статей про разработку своего рогалика. На прошлом занятии мы с вами сделали и настроили UI - пользовательский интерфейс, а сегодня узнаем, как физические обломки от монстров усиливают чувство вознаграждения и воздействия.


Представьте две сцены:

  1. Вы стреляете в стеклянную колбу. Она исчезает.
  2. Вы стреляете в стеклянную колбу. Она со звоном разлетается на десятки осколков, которые отскакивают от пола, сталкиваются друг с другом и еще несколько секунд лежат на месте преступления.


Лут — это награда. Но что, если самая важная награда — не опыт в цифрах и не случайный предмет в инвентаре, а мгновенное, визуальное удовлетворение? Психология игрока устроена так, что мы обожаем наблюдать прямую связь между действием и результатом. Когда после убийства монстра из него высыпаются фрагменты — будь то клыки, куски хитина, брызги слизи или осколки доспехов — мы видим материальное доказательство нашего успеха. Это лут-бокс, который вскрывается автоматически и дарит не предмет, а чистый, концентрированный fun. В этом руководстве мы поговорим о том, как правильно «настроить» этот процесс.


Сегодня мы узнаем, как добавить немного больше фана в такое обычное действие... как убийство монстра!


Для начала нарисуем спрайт, который я назову sFragmentEnemy.

Это очень простой спрайт 6 на 6, с разными кадрами.

Именно этот объект со случайным кадров будем выпадать из нашего противника после смерти.


Далее создадим новый объект oFragmentEnemy и привяжем этот спрайт к нему.

У этого объекта будет 3 события. В "Создать" мы заинициализируем все необходимые переменные, в "Шаге" добавим анимацию - bounce от пола, время пропадания и т.п., в "Изобразить" будем корректно отображать наш объект, например его высоту.


Давайте для начала добавим все необходимые переменные в "Создать"

GMLbounce = 0; //Количество уже совершенных прыжков
deteriorate = 0; //Время, которое объект существует. Со временем он исчезнет
z = 0; //Текущая высота от точки старта, для прыжков
spd = 0; //Скорость
fric = 0; //Разброс
image_speed = 0; //Скорость анимации
image_index = irandom(image_number-1); //Рандомное выпадение кадра
collisionMap = layer_tilemap_get_id(layer_get_id("Col")); //Коллизия, чтобы наши фрагменты не вышли за пределы
image_xscale = choose(-1,1); //Случайное отзеркаливание по оси X, для разнообразия

depth = oPlayer.depth+100; //Глубина всегда под игроком


Теперь мы можем также заинициализировать несколько особых переменных, которые мы, вероятно, захотим менять часто и в разных других аналогичных "Фрагментов".

Запишем эти переменные в "Определение переменных под объектом":


bounceCount - частота прыжков нашего фрагмента, его отскоки;

bounceSpeed - скорость прыжков;

bounceHeight - их высота;

deteriorateAfter - через сколько кадров начнет исчезать;

deteriorateTime - сколько кадров будет исчезать;


Вот теперь добавим код в "Шаг":

GML//Bounce
	if (bounceCount != 0)
	{
		bounce += (pi * bounceSpeed);
		if (bounce > pi)
		{
			bounce -= pi;
			bounceHeight *= 0.6;
			bounceCount--;
		}
		z = sin(bounce) * bounceHeight;
	}
	else z = 0;

В приведенном выше коде мы видим, что если наш объект еще не прыгал, то он начнем прыгать, пока bounceCount не станет равный 0.


Теперь добавим и исчезновение фрагмента там же, в "Шаге", но чуть ниже.

GML//Deteriorate
	deteriorate++;
	if (deteriorate > deteriorateAfter)
	{
		image_alpha -= 1/deteriorateTime;
		if (image_alpha <= 0) instance_destroy();
	}

Тут всё легко, каждый кадр наша переменная deteriorate увеличивается на 1.

Если она станет больше deteriorateAfter, то каждый кадр непрозрачность будет уменьшаться, доведя объект до 0 видимости.

Затем фрагмент уничтожится.


А теперь к фрикшину.

Без него все наши фрагменты будут выпадать строго в одной точке, слипаясь.


А мы хотим, чтобы они разлетались в разные стороны.

GML//Friction
	fric = 0.05;
	if (z == 0) fric = 0.10;
	
	//Move
	x+= lengthdir_x(spd, direction);
	y+= lengthdir_y(spd, direction);
	if (tilemap_get_at_pixel(collisionMap, x, y) > 0) spd = 0;
	spd = max(spd-fric, 0);


Отлично!

Теперь осталось только добавить одну маленькую вещь. В Gamemaker нет понятия 3 координата. Там нет z - высоты, так как движок заточен для 2д игр, не 3д.

Поэтому мы сделаем свое отображение z. В событии "Изобразить":

GMLdraw_sprite_ext(
	sprite_index,
	image_index,
	x,
	y-z,
	image_xscale,
	image_yscale,
	image_angle,
	image_blend,
	image_alpha
)	


С помощью draw_sprite_ext мы легко смогли выдать все важные данные о том, как корректно отображать наш объект, его позицию z и прозрачность.

Половина сделана. Теперь нужно зайти в наших монстров и настроить им выпадение этих самых фрагментов после их уничтожения. Начнем с двух вещей:

Создадим событие "Уничтожить", ведь именно в нем должен срабатывать код выпадения фрагментов. Именно после уничтожения противника.

И добавим еще 3 определенных переменных, опять же, для удобного редактирования вне событий.


entityFragmentCoun - количество объектов, выпадающих из противника

entityFragment - название объекта, который должен выпадать из противника

soundDeath - звук смерти монстра (опционально)


А теперь запишем код в новосозданное событие "Уничтожить":

GML//Проигрывается звук смерти
audio_play_sound(soundDeath,1,-1,global.soundVolume);

//Скрип выпадения фрагментов после смерти
if (entityFragmentCount > 0)
{
	fragmentArray = array_create(entityFragmentCount, entityFragment);
	DropItems(x,y,fragmentArray);
}

if (entityDropList != -1)
{
	DropItems(x,y,entityDropList);
}


Тут всё просто, мы будем проверять наличие entityFragmentCount. Если должно выпасть больше 0, то создастся список "fragmentArray", который будет хранить название и количество фрагментов. Сейчас мы добавим этот скрипт - DropItems, но сначала не забудем добавить новую переменную "entityDropList" в событие "Создать".


Отлично, создаем теперь новую функцию в папке "Скрипты".

Назовем ее DropItems

GMLfunction DropItems(){
	var _items = array_length_1d(argument2);
	
	if (_items > 1)
	{
		var _anglePerItem = 360/_items;
		var _angle = random(360);
		for (var i = 0; i < _items; i++)
		{
			with (instance_create_layer(argument0, argument1, "Instances", argument2[i]))
			{
				direction = _angle;
				spd = 0.75 + (_items * 0.1) + random(0.1);
			}
			_angle += _anglePerItem;
		}
		
		
	}else instance_create_layer(argument0, argument1, "Instances", argument2[0]);
}


Тут тоже ничего сложного нет! Просто каждому фрагменту при появлении присвоится угол, куда он отскочит и с нужной скоростью он полетит в ту часть. Ну и самое важное - сам фрагмент появится. Мы же для этого и писали этот код, верно?

Давайте попробуем запустить игру на F5!


Вот такая красота у нас вышла, а если ты, разработчик, добавил еще и звук смерти, то вообще умничка!

Ссылка на следующую статью: https://cr5.space/h/ru-gamemaker/post/22


Всем peace!


Статью подготовил Oliver Orangers специально для ims.

Ссылка на vk: https://vk.com/oliora

Ссылка на telegram: https://t.me/oliver_orangers

hub.comments