Привет, друзья! Продолжаем наш цикл статей про разработку своего рогалика. На прошлом занятии мы с вами сделали пассивный предметы. Сегодня займемся состояниями нашего игрока, одним из которых будет смерть.
В объекте нашего игрока добавим событие столкновение с врагом. Желательно сделать либо одного противника родителем, или вообще всем противникам сделать отдельный родительский объект, чтобы его использовать тут, и при столкновении с любыми врагами запускалось это событие. В моем случае я пока покажу на гоблине, так как сегодня мы сегодня всё равно будет работать с отдельным скриптом.
Напишем в новосозданном событии следующее:
state = PlayerStateHurt;
Это новая переменная состояния. State. Она будет отвечать за то, какое именно у нас сейчас состояние.
Добавим его и в создать и запишем туда новое состояние PlayerStateFree - оно будет отвечать за всё то, что мы уже накодили. Что если всё в порядке, мы можем ходить и стрелять. А если нас ударили, то будет работать состояние PlayerStateHurt.
Оба этих состояния будут написаны в скриптах.
Напишем новую строчку в "Создать" игрока.
Перед тем как мы начнем писать состояние PlayerStateHurt надо навести порядок в событии "Шаг" игрока:
depth = -bbox_bottom;
//Клавиши управления
keyLeft = keyboard_check(ord("A"));
keyRight = keyboard_check(ord("D"));
keyUp = keyboard_check(ord("W"));
keyDown = keyboard_check(ord("S"));
//Клавиши стрельбы
keyShootLeft = keyboard_check(vk_left);
keyShootRight = keyboard_check(vk_right);
keyShootUp = keyboard_check(vk_up);
keyShootDown = keyboard_check(vk_down);
//Блок стрельбы
if (shootCooldown > 0) shootCooldown--;
if (keyShootLeft) and (shootCooldown <= 0)
{
shootCooldown = shootTimer;
with(instance_create_layer(x,y-5,"Bullets",oBullet))
{
direction = 180;
}
}
if (keyShootRight) and (shootCooldown <= 0)
{
shootCooldown = shootTimer;
with(instance_create_layer(x,y-5,"Bullets",oBullet))
{
direction = 0;
}
}
if (keyShootUp) and (shootCooldown <= 0)
{
shootCooldown = shootTimer;
with(instance_create_layer(x,y-5,"Bullets",oBullet))
{
direction = 90;
}
}
if (keyShootDown) and (shootCooldown <= 0)
{
shootCooldown = shootTimer;
with(instance_create_layer(x,y-5,"Bullets",oBullet))
{
direction = 270;
}
}
//Движение
inputDirection = point_direction(0, 0, keyRight - keyLeft, keyDown - keyUp);
inputMagnitude = (keyRight - keyLeft != 0) or (keyDown - keyUp != 0);
//Строчка, которая каждый шаг выполняет наш скрипт состояния
script_execute(state);Отлично, теперь можно и написать, что же будет в скрипте PlayerStateFree!
Для этого создадим новый скрипт в папке "Скрипты" справа и назовем его "PlayerStateFree":
В нем напишем следующее:
function PlayerStateFree()
{
//Movement
hSpeed = lengthdir_x(inputMagnitude * global.speedWalk, inputDirection);
vSpeed = lengthdir_y(inputMagnitude * global.speedWalk, inputDirection);
PlayerCollision();
//Update Sprite Index
var _oldSprite = sprite_index;
if (inputMagnitude != 0)
{
direction = inputDirection
sprite_index = spriteRun;
if keyShootDown
{
direction = 270;
sprite_index = spriteRunAndFire;
}
else if keyShootUp
{
direction = 90;
sprite_index = spriteRunAndFire;
}
else if keyShootLeft
{
direction = 180;
sprite_index = spriteRunAndFire;
}
else if keyShootRight
{
direction = 0;
sprite_index = spriteRunAndFire;
}
}
else if keyShootDown
{
direction = 270;
sprite_index = spriteFire;
}
else if keyShootUp
{
direction = 90;
sprite_index = spriteFire;
}
else if keyShootLeft
{
direction = 180;
sprite_index = spriteFire;
}
else if keyShootRight
{
direction = 0;
sprite_index = spriteFire;
}
else sprite_index = spriteIdle;
if (_oldSprite != sprite_index) localFrame = 0;
//Update Image Index
PlayerAnimateSprite();
}Таким образом выходит, что если мы свободны, то можем двигаться, а в иных состояниях уже будет что-то другое выполняться. Понятно? Теперь в "шаге" игрока выполняется state, а вот какой state, зависит от нас. Их может быть много у нас. Но для начала напишем, что будет, если игрока коснется противник. Произойдет PlayerStateHurt. Добавим же такой скрипт:
function PlayerStateHurt()
{
//Уничтожаем и не даем появиться следующему:
instance_destroy(oGoblin);
instance_destroy(oBullet);
hSpeed = 0;
vSpeed = 0;
//Если только начался скрипт
if (sprite_index != sPlayerDie) && (sprite_index != sPlayerDead)
{
//Обнови спрайт и если надо пусть играет звук
//audio_play_sound(snDeath,10,false,global.soundVolume);
sprite_index = sPlayerDie;
image_index = 0;
image_speed = 0.5;
}
}Мы уничтожаем всех врагов, все снаряды, по желанию можно удалять что-то еще, например монетки и предметы. Затем наш код говорит, что скорость нашего игрока равна нулю. И если спрайт еще старый, то заменить его на нужный, в моем случае sPlayerDie и начать воспроизводиться со скоростью 0.5
Давайте посмотрим, что такое sPlayerDie у меня:
Анимация, где игрок просто вращается. Я хочу, чтобы после того, как до игрок получил урон, он начинал крутиться, а скорость анимации уменьшалась, пока не достигнет нуля. Там и будет воскрешение.
Пойдем, допишем код PlayerStateHurt:
Полный код теперь выглядит так:
function PlayerStateHurt()
{
//Уничтожаем и не даем появиться следующему:
instance_destroy(oGoblin);
instance_destroy(oBullet);
hSpeed = 0;
vSpeed = 0;
//Если только начался скрипт
if (sprite_index != sPlayerDie) && (sprite_index != sPlayerDead)
{
//Обнови спрайт и если надо пусть играет звук
//audio_play_sound(snDeath,10,false,global.soundVolume);
sprite_index = sPlayerDie;
image_index = 0;
image_speed = 0.5;
}
//Анимация закончилась?
if (image_index + image_speed > image_number)
{
if (sprite_index == sPlayerDie)
{
//Скорость игрока уменьшается пока не достигнет нуля
image_speed = max(0, image_speed - 0.03);
//Скорость почти достигла нуля
if (image_speed < 0.07)
{
//Возвращаем дееспособное состояние
state = PlayerStateFree;
//Куда отправляется игрок после воскрешения
x = room_width/2;
y = room_height/2;
image_index = 0;
image_speed = 1.0;
}
}
}
}Запустим тест же и посмотрим, как это сейчас выглядит. Как можем видеть, после получения урона наш персонаж начинает свою анимацию, а затем освобождается от этого события.
Отлично! Теперь осталось за малым. Надо просто вычитать нашу переменную "жизни" после получения урона. Если жизни останется 0, то игра будет перезапущена и игрок начнет все уровни с самого начала.
Естественно заходим в "создать" oPlayer или oGame и добавляет переменную global.playerLifes.
Теперь зайдем опять в событие "столкновение с противником" игрока.
Там усложним наш код условием:
if state != PlayerStateHurt and state != PlayerStateDead
{
if global.playerLifes > 0
{
global.playerLifes--;
state = PlayerStateHurt;
}
else
{
state = PlayerStateDead;
}
}Выходит, что если хп больше 0, то будет исполняться скрипт PlayerStateHurt, а если меньше, то PlayerStateDead.
Что же тогда мы напишем в новом скрипте PlayerStateDead?
Да в целом, он похож на PlayerStateHurt, но как несложно догадаться в конце будет функция, которая перезапустит нашу игру - game_restart(). Ну и монстров я удалять не буду, пусть игрок насладиться армией, которая его одолела :D
function PlayerStateDead()
{
instance_destroy(oBullet);
hSpeed = 0;
vSpeed = 0;
//if just arriving in this state
if (sprite_index != sPlayerDie) && (sprite_index != sPlayerDead)
{
//update the sprite
audio_play_sound(snDeath,10,false,global.soundVolume);
sprite_index = sPlayerDie;
image_index = 0;
image_speed = 0.5;
}
//Animation ending this frame?
if (image_index + image_speed > image_number)
{
if (sprite_index == sPlayerDie)
{
image_speed = max(0, image_speed - 0.03);
if (image_speed < 0.07)
{
image_index = 0;
sprite_index = sPlayerDead;
image_speed = 1.0;
}
}
else
{
image_speed = 0;
image_index = image_number-1;
game_restart();
}
}
}Давайте затестим! Вот такой результат у нас получился, так что теперь мы можем умереть в нашей игре)
Отлично, на сегодня всё, все свободны :D
Нам осталось только сделать для этой системы отображение очков здоровья, да и монетки надо бы тоже увидеть.
На следующем уроке рассмотрим понятие интерфейса(UI) и сделаем его.
Ссылка на следующую статью: https://cr5.space/h/ru-gamemaker/post/17
Всем peace!
Статью подготовил Oliver Orangers специально для ims.
Ссылка на vk: https://vk.com/oliora
Ссылка на telegram: https://t.me/oliver_orangers