Статьи

Создайте бесконечную игру для раннеров с нуля: Game Over & Scoring

В этом уроке мы создадим две новые функции, которые действительно добавят блеска нашей игре: выигрыш и игра окончена. Это может показаться большой работой, но наша игра настроена так, чтобы сделать это быстро и легко. Итак, давайте добавим эти функции и завершим нашу игру!

Первой функцией, которую мы будем решать, является система подсчета очков. Чтобы создать хорошую систему подсчета очков, нам нужно добавить в нашу игру визуально приятный текст. Вы можете начать с загрузки нового шрифта по вашему выбору или с загрузки того шрифта, который я включил в файл загрузки этого руководства. Если вы выбираете свой собственный, убедитесь, что вы загружаете файл «.ttf». Есть много отличных сайтов с бесплатными шрифтами; Вы можете начать с простого поиска «бесплатных шрифтов» и просматривать, пока не найдете то, что вам нравится. Файл загрузок настраивается так же, как и в предыдущих проектах. Существует старая и новая папка: старая папка содержит все, что мы делали до сих пор, а новая папка содержит проект так, как он будет выглядеть в конце этого урока.

Если у вас есть это в папке, содержащей ваш файл main.lua, откройте и файл main.lua, и ваш файл build.settings, и давайте заставим его работать. Первое, что мы собираемся сделать, это работать с нашим файлом build.settings. Именно отсюда мы сообщим программе о включении пользовательского шрифта.

Измените файл build.settings, чтобы он выглядел так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
settings = {
     orientation = {
          default = «landscapeRight»,
          content = «landscapeRight»,
          supported = {
               «landscapeRight»
          },
     },
     —adds the font BorisBlackBloxx.ttf to our game
     —you will still need to reference the name in the
     —code, but if you don’t have it referenced here it
     —will not work
     iphone = {
          plist = {
                UIAppFonts = {
                    «BorisBlackBloxx.ttf»
               }
          },
     },
}

Обратите внимание, что это будет то же самое, даже если у вас есть устройство Android. Я в основном разработчик iOS, когда дело доходит до разработки игр, поэтому я не проверял это на устройстве Android. Тем не менее, этот точный скрипт должен работать на вашем устройстве Android, а также на вашем устройстве iOS. (Если какие-либо пользователи Android, сталкивающиеся с этим учебным пособием, сталкиваются с проблемами, пожалуйста, дайте мне знать, и я рассмотрю его дальше!) Теперь, когда приведенный выше код готов, вы можете закрыть этот файл, так как нам не нужно его трогать больше Мы проверим, что это сработало, добавив текст в игру.

Давайте откроем наш файл main.lua и предоставим нашим игрокам систему подсчета очков! Прямо выше, где мы создаем наши группы отображения, вставьте следующее:

01
02
03
04
05
06
07
08
09
10
11
12
—variable to hold our game’s score
local score = 0
—scoreText is another variable that holds a string that has the score information
—when we update the score we will always need to update this string as well
—*****Note for android users, you may need to include the file extension of the font
— that you choose here, so it would be BorisBlackBloxx.ttf there******
local scoreText = display.newText(«score: » .. score, 0, 0, «BorisBlackBloxx», 50)
—This is important because if you dont have this line the text will constantly keep
—centering itself rather than aligning itself up neatly along a fixed point
scoreText:setReferencePoint(display.CenterLeftReferencePoint)
scoreText.x = 0
scoreText.y = 30

Следующее, что мы сделаем, это добавим его на наш экран displayGroup. Поместите это после того, как все другие группы были добавлены в группу экрана:

1
screen:insert(scoreText)

Давай, беги. Это будет происходить очень быстро, поэтому убедитесь, что все работает правильно, прежде чем двигаться дальше. Вы должны увидеть что-то вроде этого:

После того, как вы проверили это, чтобы убедиться, что оно работает, мы обновим счет, чтобы он сразу имел значение в нашей игре. В функции updateBlocks () прямо под строкой написано:

if ((blocks [a]). x <-40), тогда поместите этот код:

1
2
3
4
5
score = score + 1
scoreText.text = «score: » .. score
scoreText:setReferencePoint(display.CenterLeftReferencePoint)
scoreText.x = 0
scoreText.y = 30

Это будет просто обновлять нашу оценку на одно очко каждый раз, когда блок проходит с левой стороны экрана и снова вставляется справа. Конечно, это можно использовать по-разному. Это хорошая идея, чтобы обновить счет здесь, потому что он будет держать его согласованным во всех играх. Он основан на том, как далеко продвинулись игроки. Фактически, если вы хотите записать, как далеко они продвинулись, вы просто сохраните счет в переменную под названием distanceTraveled, метры или что-то подобное.

Следующая функция, которую мы рассмотрим, — это то, что происходит, когда игрок умирает. В этом уроке мы будем просты, но вы сможете увидеть, как вы можете включить в него больше, когда мы закончим. Имейте в виду, что добавление к счету действительно довольно просто сделать. Допустим, вы хотели увеличить счет на пять каждый раз, когда убиваете призрака или препятствие. Вы просто поместите приведенный выше код в раздел обнаружения столкновений прямо там, где уничтожаете указанные объекты.

Когда наш персонаж умрет, мы собираемся сделать несколько вещей:

1) Остановить движение игрока.

2) Заставьте игрока вращаться по кругу для драматического эффекта. Мы делаем это здесь, потому что я просто хочу показать вам функцию поворота для экранных объектов. Конечно, вы можете изменить его смерть на то, что хотите.

3) Отображение экрана над игрой, который позволяет игроку перезапустить игру, если они того пожелают.

Во-первых, нам нужно убедиться, что скорость игры установлена ​​на 0. Затем нам нужно добавить переменную к нашему экранному объекту монстра. Сделайте это, добавив следующую строку кода, где мы создаем экземпляр monster:

1
monster.isAlive = true

После этого мы проверим его статус в нескольких разных местах игры. Первое изменение, которое мы собираемся сделать, — это updateMonster (), сделать так, чтобы updateMonster () выглядел следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function updateMonster()
     —if our monster is jumping then switch to the jumping animation
     —if not keep playing the running animation
     if(monster.isAlive == true) then
          if(onGround) then
               if(wasOnGround) then
 
               else
                    monster:prepare(«running»)
                    monster:play()
               end
          else
               monster:prepare(«jumping»)
               monster:play()
          end
          if(monster.accel > 0) then
               monster.accel = monster.accel — 1
          end
          monster.y = monster.y — monster.accel
          monster.y = monster.y — monster.gravity
     else
          monster:rotate(5)
     end
     —update the collisionRect to stay in front of the monster
     collisionRect.y = monster.y
end

Обратите внимание, что прежде чем мы обновим позицию монстра, мы сначала проверяем, жив игрок или нет. Если игрок жив, мы просто идем дальше, как обычно. Если игрок не жив, мы отправляем нашего монстра в бесконечное вращение. Далее нам нужно изменить статус monster.isAlive. Перейдите к функции checkCollisions (), и мы внесем изменения. Внутри проверочных коллизий положение нашего монстра проверяется на предмет столкновений с 3 различными предметами: блоками, призраками и шипами. В каждом из этих разделов мы устанавливаем скорость игры равной 0 при обнаружении столкновения. Теперь вам нужно добавить этот код в каждый из трех разделов сразу после того, как мы установили скорость на 0.

1
2
3
monster.isAlive = false
—this simply pauses the current animation
monster:pause()

Теперь он установлен так, что любая анимация, которую вы играете, остановится, когда вы умрете. Кроме того, монстр будет вращаться на месте из-за кода, который мы уже добавили в updateMonster (). Запустите игру еще раз, и вы должны увидеть это:

На данный момент у нас есть две из трех сделанных вещей, которые нам нужны, чтобы улучшить процесс смерти в нашей игре. Последнее, что нужно сделать, это сделать маленький экран смерти, который позволит игроку перезапустить игру, если они того пожелают. Вернитесь к началу файла, где мы добавим все displayObjects и добавим это:

1
2
3
4
local gameOver = display.newImage(«gameOver.png»)
gameOver.name = «gameOver»
gameOver.x = 0
gameOver.y = 500

Поскольку это простое меню, я выбрал одно большое изображение. Я использовал тот же метод, который мы использовали для остальных изображений, и сохранил его за кадром для дальнейшего использования. Теперь нам нужно перемещать это на экране каждый раз, когда умирает наш игрок. Вернитесь к разделу кода, с которым мы только что работали, где мы установили monster.isAlive = false. Прямо под этим добавьте следующее к каждому из этих разделов:

1
2
gameOver.x = display.contentWidth*.65
gameOver.y = display.contentHeight/2

Обязательно добавлю

1
screen:insert(gameOver)

в ваш код, чтобы gameOver правильно отображался. Поместите это как последнюю вещь, которая будет добавлена ​​сразу после textScore. Таким образом, он обязательно будет виден, когда мы его переместим.

Это поместит экран «Game Over» справа от вращающегося монстра, чтобы мы могли увидеть наш окончательный счет в верхней левой части экрана. Наконец, чтобы все это работало, нам нужно заставить его что-то делать, когда мы к нему прикасаемся. Далее мы отредактируем функцию touch (). Измените вашу прикосновенную функцию, чтобы она выглядела так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function touched( event )
     if(event.x < gameOver.x + 150 and event.x > gameOver.x — 150 and event.y < gameOver.y + 95 and event.y > gameOver.y — 95) then
          restartGame()
     else
          if(monster.isAlive == true) then
               if(event.phase == «began») then
                    if(event.x < 241) then
                         if(onGround) then
                              monster.accel = monster.accel + 20
                         end
                    else
                         for a=1, blasts.numChildren, 1 do
                              if(blasts[a].isAlive == false) then
                                   blasts[a].isAlive = true
                                   blasts[a].x = monster.x + 50
                                   blasts[a].y = monster.y
                                   break
                              end
                         end
                    end
               end
          end
     end
end

Обратите внимание, что мы внесли только одно изменение: вместо того, чтобы сначала проверять, на какой стороне экрана находится касание (как мы определяем прыжок или стрельбу), мы проверяем, не был ли затронут объект gameOverdisplay. Если его не трогали и монстр жив, продолжайте прыгать и стрелять, как обычно. Но если коснуться gameOver, это означает, что игра окончена, потому что она теперь видна, и мы просто вызываем функцию restartGame (). Где-то выше, что добавить функцию restartGame (), и это сделает все за нас.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function restartGame()
     —move menu
     gameOver.x = 0
     gameOver.y = 500
     —reset the score
     score = 0
     —reset the game speed
     speed = 5
     —reset the monster
     monster.isAlive = true
     monster.x = 110
     monster.y = 200
     monster:prepare(«running»)
     monster:play()
     monster.rotation = 0
     —reset the groundLevel
     groundLevel = groundMin
     for a = 1, blocks.numChildren, 1 do
          blocks[a].x = (a * 79) — 79
          blocks[a].y = groundLevel
     end
     —reset the ghosts
     for a = 1, ghosts.numChildren, 1 do
          ghosts[a].x = 800
          ghosts[a].y = 600
     end
     —reset the spikes
     for a = 1, spikes.numChildren, 1 do
          spikes[a].x = 900
          spikes[a].y = 500
     end
     —reset the blasts
     for a = 1, blasts.numChildren, 1 do
          blasts[a].x = 800
          blasts[a].y = 500
     end
     —reset the backgrounds
     backgroundfar.x = 480
     backgroundfar.y = 160
     backgroundnear1.x = 240
     backgroundnear1.y = 160
     backgroundnear2.x = 760
     backgroundnear2.y = 160
end

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

С этими небольшими изменениями у нас теперь должна быть полностью функциональная игра! Игрок зарабатывает очки, и мы справляемся со смертью немного более изящно. Все, что мы здесь делали, было очень просто, но, надеюсь, теперь у вас есть представление о том, как вы могли бы создавать более крупные меню или как вы могли бы сделать более сложную сцену смерти после смерти. Самый простой способ — добавить пару новых анимаций, подходящих для разных смертей, но я позволю вам решить, что лучше всего подходит для вашей игры. Если у вас есть какие-либо вопросы об изменениях, которые мы внесли, пожалуйста, дайте мне знать в комментариях. Спасибо за подписку!