
В предыдущей части этой серии мы заставляли корабль игрока двигаться, захватчики перемещались и определяли, когда пуля игрока попала в захватчика. В этой заключительной части серии мы получим захватчиков, атакующих игрока, обработаем уровни и добавим возможность смерти игрока.
1. Стреляющие пули
Время от времени один из захватчиков стреляет пулей. Мы будем использовать таймер для этого. Добавьте следующий код в gamelevel.lua .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
function fireInvaderBullet()
if(#invadersWhoCanFire >0) then
local randomIndex = math.random(#invadersWhoCanFire)
local randomInvader = invadersWhoCanFire[randomIndex]
local tempInvaderBullet = display.newImage(«laser.png», randomInvader.x , randomInvader.y + invaderSize/2)
tempInvaderBullet.name = «invaderBullet»
scene.view:insert(tempInvaderBullet)
physics.addBody(tempInvaderBullet, «dynamic» )
tempInvaderBullet.gravityScale = 0
tempInvaderBullet.isBullet = true
tempInvaderBullet.isSensor = true
tempInvaderBullet:setLinearVelocity( 0,400)
table.insert(invaderBullets, tempInvaderBullet)
else
levelComplete()
end
end
|
В этой функции мы сначала проверяем, содержит ли таблица invadersWhoCanFire хотя бы один элемент. Если это так, то мы выполняем код в операторе if. В противном случае это означает, что уровень закончен, и мы levelComplete функцию levelComplete .
Всегда будет хотя бы один захватчик, который сможет запустить пулю, пока вы не убьете последнего захватчика, после чего таблица invadersWhoCanFire будет пустой.
Внутри оператора if мы генерируем случайное число randomIndex зависимости от количества элементов в таблице randomIndex . Затем мы выбираем этот элемент randomInvader из таблицы randomInvader .
Мы создаем изображение маркера, присваиваем ему свойство name чтобы мы могли его идентифицировать позже, вставляем в сцену и устанавливаем те же свойства, что и в маркере игрока. Наконец, мы вставляем маркер в таблицу invaderBullets чтобы мы могли ссылаться на него позже.
Теперь нам нужно настроить таймер. Добавьте к scene:show метод scene:show .
|
1
2
3
4
5
6
7
|
function scene:show(event)
if ( phase == «did» ) then
—SNIP—
Runtime:addEventListener( «collision», onCollision )
invaderFireTimer = timer.performWithDelay(1500, fireInvaderBullet,-1)
end
end
|
Каждые 1500 миллисекунд вызывается fireInvaderBullet . Обратите внимание, что последний параметр, который мы передаем, равен -1 , что означает, что таймер повторяется вечно. Всякий раз, когда вы создаете таймер, который повторяется вечно, вы должны в конечном итоге отменить его. Мы делаем это в scene:hide функцию, как показано ниже.
|
1
2
3
4
5
6
7
|
function scene:hide(event)
if ( phase == «will» ) then
—SNIP—
Runtime:removeEventListener( «collision», onCollision )
timer.cancel(invaderFireTimer)
end
end
|
2. Удаление пуль
Как пули игрока, пули захватчиков будут перемещаться за пределы экрана и продолжать двигаться, занимая ценную память. Чтобы исправить это, мы удаляем их так же, как мы сделали с пулями игрока.
|
01
02
03
04
05
06
07
08
09
10
11
|
function checkInvaderBulletsOutOfBounds()
if (#invaderBullets > 0) then
for i=#invaderBullets,1,-1 do
if(invaderBullets[i].y > display.contentHeight) then
invaderBullets[i]:removeSelf()
invaderBullets[i] = nil
table.remove(invaderBullets,i)
end
end
end
end
|
Этот код очень похож на проверку, если пули игрока выходят за пределы, поэтому я не буду подробно обсуждать его реализацию.
3. Обнаружение попадания
Шаг 1: Обнаружение столкновения
Следующий шаг — определить, попала ли пуля захватчика в игрока. Добавьте следующий код в функцию onCollision .
|
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 onCollision(event)
if ( event.phase == «began» ) then
—SNIP—
if(event.object1.name == «player» and event.object2.name == «invaderBullet») then
table.remove(invaderBullets,table.indexOf(invaderBullets,event.object2))
event.object2:removeSelf()
event.object2 = nil
if(playerIsInvincible == false) then
killPlayer()
end
return
end
if(event.object1.name == «invaderBullet» and event.object2.name == «player») then
table.remove(invaderBullets,table.indexOf(invaderBullets,event.object1))
event.object1:removeSelf()
event.object1 = nil
if(playerIsInvincible == false) then
killPlayer()
end
return
end
end
end
|
Как и раньше, мы не знаем, какие будут объекты event.object1 и event.object2 , поэтому мы используем два оператора if для проверки обеих ситуаций. Мы удаляем маркер захватчика из таблицы invaderBullets , удаляем его с дисплея и устанавливаем в nil . Если игрок не непобедим, мы его убиваем.
Шаг 2: Убить игрока
Когда мы убиваем игрока, мы даем ему непродолжительное время. Это дает пользователю время, чтобы вновь сосредоточиться на игре. Если переменная numberOfLives равна 0 , мы знаем, что игра окончена и происходит переход к начальной сцене, где пользователь может начать новую игру.
|
01
02
03
04
05
06
07
08
09
10
|
function killPlayer()
numberOfLives = numberOfLives- 1;
if(numberOfLives <= 0) then
gameData.invaderNum = 1
composer.gotoScene(«start»)
else
playerIsInvincible = true
spawnNewPlayer()
end
end
|
Шаг 3: порождение нового игрока
Функция spawnNewPlayer заставляет игрока spawnNewPlayer и исчезать на несколько секунд. Это хороший эффект, чтобы пользователь знал, что корабль временно неуязвим.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
function spawnNewPlayer()
local numberOfTimesToFadePlayer = 5
local numberOfTimesPlayerHasFaded = 0
local function fadePlayer()
player.alpha = 0;
transition.to( player, {time=400, alpha=1, })
numberOfTimesPlayerHasFaded = numberOfTimesPlayerHasFaded + 1
if(numberOfTimesPlayerHasFaded == numberOfTimesToFadePlayer) then
playerIsInvincible = false
end
end
fadePlayer()
timer.performWithDelay(400, fadePlayer,numberOfTimesToFadePlayer)
end
|
Мы используем локальную функцию fadePlayer , которая использует библиотеку переходов для изменения alpha значения player . Мы отслеживаем, сколько раз player numberOfTimesToFadePlayer и numberOfTimesToFadePlayer , и устанавливаем для непобедимости игрока значение false только мы достигаем numberOfTimesToFadePlayer . Мы используем таймер для вызова функции fadePlayer однако число раз numberOfTimesToFadePlayer равно.
Запустите игру, чтобы проверить это. player должен умереть, когда пуля захватчика попадает на корабль. Если три пули попали в корабль, вы должны быть доставлены на стартовую сцену, где вы можете начать новую игру.
Чтобы упростить тестирование, закомментируйте вызов moveInvaders в функции gameLoop как показано ниже.
|
1
2
3
4
5
|
function gameLoop()
checkPlayerBulletsOutOfBounds()
—moveInvaders()
checkInvaderBulletsOutOfBounds()
end
|
4. Завершение уровня
Если бы вам удалось убить каждого захватчика, игра вызвала levelComplete функцию levelComplete , которой пока не существует. Давай исправим это. Добавьте следующий блок кода.
|
1
2
3
4
5
6
7
8
9
|
function levelComplete()
gameData.invaderNum = gameData.invaderNum + 1
if(gameData.invaderNum <= gameData.maxLevels) then
composer.gotoScene(«gameover»)
else
gameData.invaderNum = 1
composer.gotoScene(«start»)
end
end
|
Мы увеличиваем gameData.invaderNum и, если он меньше, чем gameData.maxLevels , мы переходим к сцене перехода на новую страницу . В противном случае, игрок прошел все уровни, и мы сбросили gameData.invaderNum на 1 . Мы переходим к стартовой сцене, где игрок может начать новую игру.
Простой способ проверить это — moveInvaders вызов moveInvaders в функции gameLoop и использовать кнопки для перемещения корабля. Если это все еще слишком сложно, вы можете также закомментировать два вызова killPlayer в методе onCollision .
5. Игра окончена
Добавьте следующий код в gameover.lua, чтобы реализовать игру поверх сцены.
|
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
|
local composer = require(«composer»)
local scene = composer.newScene()
local starFieldGenerator = require(«starfieldgenerator»)
local pulsatingText = require(«pulsatingtext»)
local nextLevelButton
local starGenerator
function scene:create( event )
local group = self.view
starGenerator = starFieldGenerator.new(200,group,5)
local invadersText = pulsatingText.new(«LEVEL COMPLETE», display.contentCenterX, display.contentCenterY-200,»Conquest», 20,group )
invadersText:setColor( 1, 1, 1 )
invadersText:pulsate()
nextLevelButton = display.newImage(«next_level_btn.png»,display.contentCenterX, display.contentCenterY)
group:insert(nextLevelButton)
end
function scene:show( event )
local phase = event.phase
composer.removeScene(«gamelevel» )
if ( phase == «did» ) then
nextLevelButton:addEventListener(«tap»,startNewGame)
Runtime:addEventListener ( «enterFrame», starGenerator)
end
end
function scene:hide(event )
local phase = event.phase
if ( phase == «will» ) then
Runtime:removeEventListener(«enterFrame», starGenerator)
nextLevelButton:removeEventListener(«tap»,startNewGame)
end
end
function startNewGame()
composer.gotoScene(«gamelevel»)
end
scene:addEventListener( «create», scene )
scene:addEventListener( «show», scene )
scene:addEventListener( «hide», scene )
return scene
|
Этот код очень похож на стартовую сцену, поэтому вы должны быть знакомы с ним сейчас.
6. Столкновение с захватчиком
Последняя проверка столкновений, которую мы должны выполнить, — это столкновение игрока и одного из захватчиков. Добавьте следующий блок кода в метод onCollision который мы видели ранее.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
function onCollision(event)
—SNIP—
if ( event.phase == «began» ) then
—SNIP—
if(event.object1.name == «player» and event.object2.name == «invader») then
numberOfLives = 0
killPlayer()
end
if(event.object1.name == «invader» and event.object2.name == «player») then
numberOfLives = 0
killPlayer()
end
end
|
Как обычно, нам нужно проверить обе ситуации столкновения. Мы устанавливаем numberOfLives в 0 и вызываем killPlayer . Установив numberOfLives в 0 и вызвав killPlayer , игра заканчивается, и игра переходит в начальную сцену.
7. Дополнительные функции
Это завершает нашу игру, но я предлагаю вам попробовать расширить игру с помощью нескольких дополнительных функций. Например, вы можете отобразить жизнь игрока в HUD.
Я также включил изображение НЛО в исходные файлы. Вы можете попробовать сделать случайным появление НЛО, и если игрок ударит его пулями, даст им дополнительную жизнь.
Если вам нужна помощь с этими концепциями, ознакомьтесь с серией игр о самолетах на Tuts +.
Вывод
Если вы следили за этой серией, то теперь у вас должна быть полностью функциональная игра, похожая на оригинальные Space Invaders. Расширьте его и сделайте его своим. Я надеюсь, что вы нашли этот урок полезным и освоили некоторые новые методы. Спасибо за чтение.