Привет, друзья! Продолжаем наш цикл статей про разработку своего рогалика. На прошлом занятии мы с вами сделали пассивный предметы. Сегодня займемся состояниями нашего игрока, одним из которых будет смерть.
В объекте нашего игрока добавим событие столкновение с врагом. Желательно сделать либо одного противника родителем, или вообще всем противникам сделать отдельный родительский объект, чтобы его использовать тут, и при столкновении с любыми врагами запускалось это событие. В моем случае я пока покажу на гоблине, так как сегодня мы сегодня всё равно будет работать с отдельным скриптом.
Напишем в новосозданном событии следующее:
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) и сделаем его.
Всем peace!
Статью подготовил Oliver Orangers специально для ims.
Ссылка на vk: https://vk.com/oliora
Ссылка на telegram: https://t.me/oliver_orangers