Вступление
В четвертой и последней части этой серии мы продолжим с того места, на котором остановились в предыдущем уроке . Мы создадим вражеские самолеты, которые игрок должен избегать или стрелять, а также создадим игру на экране.
1. generateEnemys
Функция generateEnemys
генерирует число от трех до семи и вызывает функцию generateEnemyPlane
каждые две секунды, сколько бы раз numberOfEnemysToGenerate
равно numberOfEnemysToGenerate
. Введите следующий фрагмент кода в gamelevel.lua .
1
2
3
4
|
function generateEnemys()
numberOfEnemysToGenerate = math.random(3,7)
timer.performWithDelay( 2000, generateEnemyPlane,numberOfEnemysToGenerate)
end
|
Нам также нужно вызвать эту функцию в методе enterScene
как показано ниже.
1
2
3
4
5
6
|
function scene:enterScene( event )
—SNIP—
Runtime:addEventListener(«enterFrame», gameLoop)
startTimers()
generateEnemys()
end
|
Давайте посмотрим, как выглядит реализация generateEnemyPlane
.
2. generateEnemyPlane
вражеский generateEnemyPlane
Функция generateEnemyPlane
генерирует один вражеский самолет. В этой игре есть три типа самолетов противника.
- Обычный , движется вниз по экрану по прямой линии
- Вейвер , движется по волновой схеме по оси х
- Chaser , преследует самолет игрока
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
|
function generateEnemyPlane()
if(gameOver ~= true) then
local randomGridSpace = math.random(11)
local randomEnemyNumber = math.random(3)
local tempEnemy
if(planeGrid[randomGridSpace]~=0) then
generateEnemyPlane()
return
else
if(randomEnemyNumber == 1)then
tempEnemy = display.newImage(«enemy1.png», (randomGridSpace*65)-28,-60)
tempEnemy.type = «regular»
elseif(randomEnemyNumber == 2) then
tempEnemy = display.newImage(«enemy2.png», display.contentWidth/2 -playerWidth/2,-60)
tempEnemy.type = «waver»
else
tempEnemy = display.newImage(«enemy3.png», (randomGridSpace*65)-28,-60)
tempEnemy.type = «chaser»
end
planeGrid[randomGridSpace] = 1
table.insert(enemyPlanes,tempEnemy)
planeGroup:insert(tempEnemy)
numberOfEnemysGenerated = numberOfEnemysGenerated+1;
end
if(numberOfEnemysGenerated == numberOfEnemysToGenerate)then
numberOfEnemysGenerated = 0;
resetPlaneGrid()
timer.performWithDelay(2000,generateEnemys,1)
end
end
end
|
Сначала мы проверим, чтобы игра еще не закончилась. Затем мы генерируем randomGridSpace
, число от 1 до 11 , и случайный randomEnemyNumber
, число от 1 до 3 . randomGridSpace
используется для позиционирования плоскости в одном из одиннадцати слотов в верхней части экрана по оси x. Если вы думаете, что игровая зона разделена на одиннадцать разделов, то мы хотим разместить только новые самолеты в слоте, который еще не был занят другим самолетом. Таблица planeGrid
содержит одиннадцать planeGrid
и когда мы planeGrid
новую плоскость в один из слотов, мы устанавливаем соответствующую позицию в таблице planeGrid
1
чтобы указать, что слот занят плоскостью.
Мы проверяем, не равен ли индекс randomGridSpace
в таблице 0
. Если это не так, мы знаем, что слот в настоящее время занят, и мы не должны продолжать, поэтому мы вызываем generateEnemyPlane
и возвращаемся из функции.
Затем мы проверяем, что randomEnemyNumber
равен и устанавливаем tempEnemy
одно из трех изображений врага, мы также присваиваем ему свойство regular
, waver
или chaser
. Поскольку Lua является динамическим языком, мы можем добавлять новые свойства к объекту во время выполнения. Затем мы устанавливаем любой индекс, равный randomGridSpace
1
в таблице planeGrid
.
Мы вставляем tempEnemy
в таблицу enemyPlanes
для enemyPlanes
последующего увеличения numberOfEnemysGenerated
. Если numberOfEnemysGenerated
равно numberOfEnemysToGenerate
, мы сбрасываем numberOfEnemysGenerated
в 0
, вызываем resetPlaneGrid
и устанавливаем таймер, который будет вызывать generateEnemys
снова через две секунды. Этот процесс повторяется до тех пор, пока игра не закончена.
3. moveEnemyPlanes
Как вы уже догадались, функция moveEnemyPlanes
отвечает за перемещение самолетов противника. В зависимости от type
плоскости вызывается соответствующая функция.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
function moveEnemyPlanes()
if(#enemyPlanes > 0) then
for i=1, #enemyPlanes do
if(enemyPlanes[i].type == «regular») then
moveRegularPlane(enemyPlanes[i])
elseif(enemyPlanes[i].type == «waver») then
moveWaverPlane(enemyPlanes[i])
else
moveChaserPlane(enemyPlanes[i])
end
end
end
end
|
Эта функция должна быть вызвана в функции gameLoop
.
1
2
3
4
5
6
|
function gameLoop()
—SNIP—
checkFreeLifesOutOfBounds()
checkPlayerCollidesWithFreeLife()
moveEnemyPlanes()
end
|
4. moveRegularPlane
moveRegularPlane
просто перемещает плоскость вниз по экрану по оси Y.
1
2
3
|
function moveRegularPlane(plane)
plane.y = plane.y+4
end
|
5. moveWaverPlane
Функция moveWaverPlane
перемещает плоскость вниз по экрану по оси y и, в виде волны, по оси x. Это достигается с помощью функции cos
математической библиотеки Lua .
Если вам не нравится эта концепция, Майкл Джеймс Уильямс написал отличное введение в Синусоидальное движение . Применяются те же понятия, с той лишь разницей, что мы используем косинус . Вы должны думать синус при работе с осью Y и косинус при работе с осью X.
1
2
3
|
function moveWaverPlane(plane)
plane.y =plane.y+4 plane.x = (display.contentWidth/2)+ 250* math.cos(numberOfTicks * 0.5 * math.pi/30)
end
|
В приведенном выше фрагменте мы используем переменную numberOfTicks
. Мы должны увеличивать это каждый раз, когда gameLoop
функция gameLoop
. Добавьте следующее как самую первую строку в функцию gameLoop
.
1
2
3
|
function gameLoop()
numberOfTicks = numberOfTicks + 1
end
|
6. moveChaserPlane
Функция moveChaserPlane
позволяет самолету преследовать игрока. Он движется вниз по оси Y с постоянной скоростью и движется к позиции игрока на оси X. Посмотрите на реализацию moveChaserPlane
для уточнения.
1
2
3
4
5
6
7
8
9
|
function moveChaserPlane(plane)
if(plane.x < player.x)then
plane.x =plane.x +4
end
if(plane.x > player.x)then
plane.x = plane.x — 4
end
plane.y = plane.y + 4
end
|
Если вы сейчас протестируете игру, вы увидите, как самолеты движутся вниз по экрану.
7. fireEnemyBullets
Время от времени мы хотим, чтобы вражеские самолеты выпустили пулю. Однако мы не хотим, чтобы все они стреляли одновременно, поэтому мы выбираем только пару самолетов для стрельбы.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
function fireEnemyBullets()
if(#enemyPlanes >= 2) then
local numberOfEnemyPlanesToFire = math.floor(#enemyPlanes/2)
local tempEnemyPlanes = table.copy(enemyPlanes)
local function fireBullet()
local randIndex = math.random(#tempEnemyPlanes)
local tempBullet = display.newImage(«bullet.png», (tempEnemyPlanes[randIndex].x+playerWidth/2) + bulletWidth,tempEnemyPlanes[randIndex].y+playerHeight+bulletHeight)
tempBullet.rotation = 180
planeGroup:insert(tempBullet)
table.insert(enemyBullets,tempBullet);
table.remove(tempEnemyPlanes,randIndex)
end
for i = 0, numberOfEnemyPlanesToFire do
fireBullet()
end
end
end
|
Сначала мы проверяем, чтобы в таблице enemyPlanes
было больше двух самолетов. Если это произойдет, мы получим numberOfEnemyPlanes
для стрельбы, взяв длину таблицы enemyPlanes
, enemyPlanes
ее на два, и enemyPlanes
. Мы также делаем копию таблицы enemyPlanes
и enemyPlanes
, чтобы мы могли управлять ею отдельно.
Функция fireBullet
выбирает самолет из таблицы tempEnemyPlanes
и заставляет самолет выстрелить. Мы генерируем случайное число на основе длины таблицы tempEnemyPlanes
, создаем изображение randIndex
и tempEnemyPlanes
его, используя любую плоскость в randIndex
в таблице tempEnemyPlanes
. Затем мы удаляем эту плоскость из временной таблицы, чтобы гарантировать, что она не будет выбрана снова при следующем fireBullet
.
Мы повторяем этот процесс, однако много раз numerOfEnemyPlanesToFire
равен и вызываем функцию fireBullet
.
Нам нужно запустить таймер, который вызывает эту функцию очень часто. Для этого добавьте следующее в функцию startTimers
.
1
2
3
4
5
6
|
function startTimers()
firePlayerBulletTimer = timer.performWithDelay(2000, firePlayerBullet ,-1)
generateIslandTimer = timer.performWithDelay( 5000, generateIsland ,-1)
generateFreeLifeTimer = timer.performWithDelay(7000,generateFreeLife, — 1)
fireEnemyBulletsTimer = timer.performWithDelay(2000,fireEnemyBullets,-1)
end
|
8. moveEnemyBullets
Нам также нужно переместить вражеские пули, которые на экране. Это довольно просто, используя следующий фрагмент кода.
1
2
3
4
5
6
7
|
function moveEnemyBullets()
if(#enemyBullets > 0) then
for i=1,#enemyBullets do
enemyBullets[i].
end
end
end
|
Вызовите эту функцию в функции gameLoop
.
1
2
3
4
5
6
|
function gameLoop()
—SNIP—
checkPlayerCollidesWithFreeLife()
moveEnemyPlanes()
moveEnemyBullets()
end
|
9. checkEnemyBulletsOutOfBounds
Помимо перемещения вражеских пуль, мы должны проверить, когда пули противника исчезли за пределы экрана, и убрать их, когда они это сделают. Реализация checkEnemyBulletsOutOfBounds
должна казаться знакомой к настоящему времени.
01
02
03
04
05
06
07
08
09
10
11
|
function checkEnemyBulletsOutOfBounds()
if(#enemyBullets > 0) then
for i=#enemyBullets,1,-1 do
if(enemyBullets[i].y > display.contentHeight) then
enemyBullets[i]:removeSelf()
enemyBullets[i] = nil
table.remove(enemyBullets,i)
end
end
end
end
|
Вызовите эту функцию в функции gameLoop
.
1
2
3
4
5
|
function gameLoop()
—SNIP—
moveEnemyBullets()
checkEnemyBulletsOutOfBounds()
end
|
10. checkEnemyPlanesOutOfBounds
Мы также должны проверить, не переместились ли самолеты противника за пределы экрана.
01
02
03
04
05
06
07
08
09
10
11
|
function checkEnemyPlanesOutOfBounds()
if(#enemyPlanes> 0) then
for i=#enemyPlanes,1,-1 do
if(enemyPlanes[i].y > display.contentHeight) then
enemyPlanes[i]:removeSelf()
enemyPlanes[i] = nil
table.remove(enemyPlanes,i)
end
end
end
end
|
Вызовите эту функцию в функции gameLoop
1
2
3
4
5
6
|
function gameLoop()
—SNIP—
moveEnemyBullets()
checkEnemyBulletsOutOfBounds()
checkEnemyPlanesOutOfBounds()
end
|
11. checkPlayerBulletsCollideWithEnemyPlanes
Функция checkPlayerBulletCollidesWithEnemyPlanes
использует функцию hasCollided
чтобы проверить, столкнулась ли какая-либо из пуль игрока с любым из вражеских самолетов.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
function checkPlayerBulletsCollideWithEnemyPlanes()
if(#playerBullets > 0 and #enemyPlanes > 0) then
for i=#playerBullets,1,-1 do
for j=#enemyPlanes,1,-1 do
if(hasCollided(playerBullets[i], enemyPlanes[j])) then
playerBullets[i]:removeSelf()
playerBullets[i] = nil
table.remove(playerBullets,i)
generateExplosion(enemyPlanes[j].x,enemyPlanes[j].y)
enemyPlanes[j]:removeSelf()
enemyPlanes[j] = nil
table.remove(enemyPlanes,j)
local explosion = audio.loadStream(«explosion.mp3»)
local backgroundMusicChannel = audio.play( explosion, {fadein=1000 } )
end
end
end
end
end
|
Эта функция использует два вложенных цикла for
для проверки столкновения объектов. Для каждого playerBullets
мы пробегаем все плоскости в таблице enemyPlanes
и вызываем функцию hasCollided
. Если происходит столкновение, мы удаляем пулю и плоскость, вызываем функцию generateExplosion
, загружаем и воспроизводим звук взрыва.
Вызовите эту функцию в функции gameLoop
.
1
2
3
4
5
6
|
function gameLoop()
—SNIP—
checkEnemyBulletsOutOfBounds()
checkEnemyPlanesOutOfBounds()
checkPlayerBulletsCollideWithEnemyPlanes()
end
|
12. generateExplosion
Функция generateExplosion
использует класс Corona SpriteObject . Спрайты допускают анимированные последовательности кадров, которые находятся на листах изображений или спрайтов . Группируя изображения в одно изображение, вы можете извлечь определенные кадры из этого изображения и создать анимационную последовательность.
01
02
03
04
05
06
07
08
09
10
11
12
|
function generateExplosion(xPosition , yPosition)
local options = { width = 60,height = 49,numFrames = 6}
local explosionSheet = graphics.newImageSheet( «explosion.png», options )
local sequenceData = {
{ name = «explosion», start=1, count=6, time=400, loopCount=1 }
}
local explosionSprite = display.newSprite( explosionSheet, sequenceData )
explosionSprite.x = xPosition
explosionSprite.y = yPosition
explosionSprite:addEventListener( «sprite», explosionListener )
explosionSprite:play()
end
|
Метод newImageSheet
принимает в качестве параметров путь к изображению и таблицу параметров для листа Sprite. Мы устанавливаем параметры width
, height
и числа numFrames
, сколько отдельных изображений составляют этот лист. Есть шесть отдельных изображений взрыва, как показано на рисунке ниже.
Далее мы настраиваем таблицу sequenceData
, которая необходима для SpriteObject
. Мы устанавливаем для свойства start
значение 1
, для count
6
и время для 400
. Свойство start
— это кадр, с которого начинается анимация, count
— это количество кадров, которое включает анимация, а свойство time
— сколько времени занимает анимация, чтобы просмотреть ее.
Затем мы создаем SpriteObject
передавая в SpriteObject
и sequenceData
, устанавливаем позиции x и y и добавляем прослушиватель в спрайт. Слушатель будет использоваться для удаления спрайта после завершения анимации.
13. Взрывной explosionListener
Функция explosionListener
используется для удаления спрайта. Если свойство phase
event
равно окончено, то мы знаем, что спрайт завершил анимацию, и мы можем удалить его.
1
2
3
4
5
6
7
|
function explosionListener( event )
if ( event.phase == «ended» ) then
local explosion = event.target
explosion:removeSelf()
explosion = nil
end
end
|
14. checkEnemyBulletsCollideWithPlayer
checkEnemyBulletsCollideWithPlayer
проверяет, не столкнулись ли какие-либо пули противника с плоскостью игрока.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
function checkEnemyBulletsCollideWithPlayer()
if(#enemyBullets > 0) then
for i=#enemyBullets,1,-1 do
if(hasCollided(enemyBullets[i],player)) then
enemyBullets[i]:removeSelf()
enemyBullets[i] = nil
table.remove(enemyBullets,i)
if(playerIsInvincible == false) then
killPlayer()
end
end
end
end
end
|
Мы enemyBullets
таблицу enemyBullets
и проверяем, столкнулся ли кто-нибудь из них с игроком. Если true, мы удаляем эту конкретную пулю, и, если playerIsInvincible
равен false
, мы вызываем killPlayer
.
Вызовите эту функцию в функции gameLoop
.
1
2
3
4
5
6
|
function gameLoop()
—SNIP—
checkEnemyPlanesOutOfBounds()
checkPlayerBulletsCollideWithEnemyPlanes()
checkEnemyBulletsCollideWithPlayer()
end
|
15. killPlayer
Функция killPlayer
отвечает за проверку, закончилась ли игра, и порождает нового игрока, если это не так.
01
02
03
04
05
06
07
08
09
10
11
12
|
function killPlayer()
numberOfLives = numberOfLives- 1;
if(numberOfLives == 0) then
gameOver = true
doGameOver()
else
spawnNewPlayer()
hideLives()
showLives()
playerIsInvincible = true
end
end
|
Сначала мы уменьшаем число numberOfLives
на 1
, и, если оно равно 0
, мы вызываем функцию gameOver
. Если у игрока остались жизни, мы вызываем spawnNewPlayer
, затем следует hideLives
, showLives
и устанавливаем для playerIsInvincible
значение true
.
16. doGameOver
Функция doGameOver
говорит раскадровке перейти на сцену игры.
1
2
3
|
function doGameOver()
storyboard.gotoScene(«gameover»)
end
|
17. spawnNewPlayer
Функция spawnNewPlayer
отвечает за порождение нового игрока после его смерти. Самолет игрока мигает несколько секунд, показывая, что он временно неуязвим.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
function spawnNewPlayer()
local numberOfTimesToFadePlayer = 5
local numberOfTimesPlayerHasFaded = 0
local function fadePlayer()
player.alpha = 0;
transition.to( player, {time=200, alpha=1})
numberOfTimesPlayerHasFaded = numberOfTimesPlayerHasFaded + 1
if(numberOfTimesPlayerHasFaded == numberOfTimesToFadePlayer) then
playerIsInvincible = false
end
end
timer.performWithDelay(400, fadePlayer,numberOfTimesToFadePlayer)
end
|
Чтобы заставить моргать самолет игрока, мы увеличиваем и уменьшаем его пять раз. В функции fadePlayer
мы устанавливаем свойство alpha
плоскости на 0
, что делает его прозрачным. Затем мы используем библиотеку переходов, чтобы постепенно уменьшить alpha
до 1
в течение 200 миллисекунд. Метод to
объекта transition
принимает таблицу параметров. В нашем примере таблица параметров включает время в миллисекундах и свойство, которое мы хотели бы анимировать, alpha
и желаемое значение 1
.
Мы увеличиваем numberOfTimesThePlayerHasFaded
и проверяем, равно ли оно числу раз, когда мы хотели, чтобы проигрыватель исчез. Затем мы устанавливаем playerIsInvincible
в false
. Мы используем таймер для вызова функции fadePlayer
однако во много раз numberOfTimerToFadePlayer
равно.
Есть способ сделать все это без использования таймера, а именно с помощью свойства iterations
transition
в сочетании с его обработчиком onComplete
. Прочитайте документацию, чтобы узнать больше об этом альтернативном подходе.
18. checkEnemyPlaneCollidesWithPlayer
Мы должны сделать еще одну проверку столкновения, чтобы увидеть, сталкивается ли вражеский самолет с самолетом игрока.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
function checkEnemyPlaneCollideWithPlayer()
if(#enemyPlanes > 0) then
for i=#enemyPlanes,1,-1 do
if(hasCollided(enemyPlanes[i], player)) then
enemyPlanes[i]:removeSelf()
enemyPlanes[i] = nil
table.remove(enemyPlanes,i)
if(playerIsInvincible == false) then
killPlayer()
end
end
end
end
end
|
Мы перебираем вражеские самолеты и видим, сталкивается ли какой-либо из них с самолетом игрока. Если это правда, мы удаляем этот вражеский самолет и вызываем killPlayer
. Если вы думаете, что это делает игру более интересной, вы можете также произвести взрыв здесь.
19. exitScene
Когда игра окончена, мы переходим на сцену игры. Помните из предыдущего урока, функция exitScene
— это место, где вы удаляете любые прослушиватели событий, останавливаете таймеры и останавливаете воспроизводимый звук.
01
02
03
04
05
06
07
08
09
10
11
12
|
function scene:exitScene( event )
local group = self.view
rectUp:removeEventListener( «touch», movePlane)
rectDown:removeEventListener( «touch», movePlane)
rectLeft:removeEventListener( «touch», movePlane)
rectRight:removeEventListener( «touch», movePlane)
audio.stop(planeSoundChannel)
audio.dispose(planeSoundChannel)
Runtime:removeEventListener(«enterFrame», gameLoop)
cancelTimers()
end
scene:addEventListener( «exitScene», scene )
|
Мы в основном отменяем то, что мы сделали в функции enterScene
. Мы вызываем метод dispose
для audio
объекта, чтобы освободить память, связанную с аудио каналом. Один только вызов stop
не освобождает память.
20. cancelTimers
Как видно из cancelTimers
функция cancelTimers
делает противоположность startTimers
, она отменяет все таймеры.
1
2
3
4
5
6
|
function cancelTimers()
timer.cancel( firePlayerBulletTimer )
timer.cancel(generateIslandTimer)
timer.cancel(fireEnemyBulletsTimer)
timer.cancel(generateFreeLifeTimer)
end
|
21. Игра окончена
Пришло время создать игровую сцену. Начните с добавления нового Lua-файла в ваш проект с именем gameover.lua и добавьте в него следующий код.
1
2
3
4
5
6
|
local storyboard = require( «storyboard» )
local scene = storyboard.newScene()
local gameOverText
local newGameButton
return scene
|
22. createScene
Добавьте следующее в gameover.lua выше return scene
. С этого момента весь код должен быть помещен над оператором return scene
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
function scene:createScene( event )
local group = self.view
local background = display.newRect( 0, 0, display.contentWidth, display.contentHeight)
background:setFillColor( 0,.39,.75)
group:insert(background)
gameOverText = display.newText( «Game Over», display.contentWidth/2,400, native.systemFont, 16 )
gameOverText:setFillColor( 1, 1, 0 )
gameOverText.anchorX = .5
gameOverText.anchorY = .5
group:insert(gameOverText)
newGameButton = display.newImage(«newgamebutton.png»,264,670)
group:insert(newGameButton)
newGameButton.isVisible = false
end
|
Как мы делали в предыдущих двух сценах, мы даем сцене игры за синий фон. Затем мы создаем экземпляр TextObject
, вызывая newText
на display
. Метод newText
принимает несколько параметров: текст объекта, его положение и используемый шрифт. Мы даем ему желтый цвет, вызывая setFillColor
, передавая значения RGB в процентах. Наконец, мы создаем кнопку и скрываем ее пока.
23. enterScene
Когда раскадровка полностью перешла на сцену игры, enterScene
метод enterScene
.
В enterScene
мы удаляем предыдущую сцену из раскадровки. Мы используем удобный метод scaleTo
из библиотеки переходов для масштабирования gameOverText
в 4
раза. Мы добавляем прослушиватель onComplete
к переходу, который вызывает Функция showButton
после завершения перехода. Наконец, мы добавляем слушатель события касания к кнопке игры, которая вызывает функцию startNewGame
.
1
2
3
4
5
6
|
function scene:enterScene( event )
local group = self.view
storyboard.removeScene(«gamelevel» )
transition.scaleTo( gameOverText, { xScale=4.0, yScale=4.0, time=2000,onComplete=showButton} )
newGameButton:addEventListener(«tap», startNewGame)
end
|
24. showButton
Функция showButton
скрывает gameOverText
и показывает newGameButton
.
1
2
3
4
|
function showButton()
gameOverText.isVisible = false
newGameButton.isVisible= true
end
|
25. startNewGame
Функция startNewGame
сообщает раскадровке о переходе на сцену уровня игры.
1
2
3
|
function startNewGame()
storyboard.gotoScene(«gamelevel»)
end
|
26. exitScene
Нам нужно сделать некоторую очистку, когда мы покидаем сцену игры. Мы удаляем прослушиватель события tap, который мы добавили ранее в newGameButton
.
1
2
3
4
|
function scene:exitScene( event )
local group = self.view
newGameButton:removeEventListener(«tap»,startNewGame)
end
|
27. Добавить слушателей сцены
Последняя часть головоломки — добавление слушателей событий сцены, о которых мы говорили ранее. Для этого добавьте следующий фрагмент кода в gameover.lua .
1
2
3
|
scene:addEventListener( «createScene», scene )
scene:addEventListener( «enterScene», scene )
scene:addEventListener( «exitScene», scene )
|
Вывод
Мы подошли к концу этой серии и теперь имеем полностью функциональную игру в файтинг. Я надеюсь, что вы нашли эти учебники полезными и научились чему-то на этом пути. Спасибо за прочтение.