В этом руководстве будет рассказано о портировании Flash / Flex-игры на Corona SDK. В частности, мы будем портировать с ActionScript на Lua с конечной целью играть в игры, которые раньше были только для Flash, на iPhone. В дополнение к демонстрации языковых и API-различий, эта серия учебников также будет учитывать аппаратные ограничения, такие как размер экрана и отсутствие физических кнопок на iPhone.
Добавление врагов
Теперь мы начнем работать с нашим врагом: «de / pixelate / flixelprimer / Alien.as». Как всегда, синтаксис конвертируется первым.
Когда вы закончите с этим, добавьте описание модуля и оберните все функции в Alien ().
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
module(…, package.seeall)
—[Embed(source=»../../../../assets/png/Alien.png»)] private var ImgAlien:Class
function Alien(x, y) —:void
super(x, y, ImgAlien)
velocity.x = -200
function update() —:void
velocity.y = Math.cos(x / 50) * 50
super.update()
end
end
|
Инопланетянин работает очень похоже на нашу пулю. Он создает изображение, устанавливает его х и у
координаты, и дает ему скорость. Итак, подойдем к этому так же. Две верхние строки внутри функции можно заменить почти таким же кодом, который мы использовали для маркера. На этот раз мы будем использовать изображение, хотя.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
module(…, package.seeall)
function Alien(x, y) —:void
local Alien = display.newImage(«Alien.png»)
Alien.x = x
Alien.y = y
function update() —:void
…
end
end
|
Теперь, когда изображение загружено и установлено, давайте переместим его влево. Опять же, мы создадим что-то вроде кода нашей (update)). Оставьте старые строки внутри update () с комментариями.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
module(…, package.seeall)
function Alien(x, y) —:void
…
function update() —:void
if Alien then
if (Alien.x > 0 — Alien.contentWidth) then
Alien.x = Alien.x — 2
end
end
… commented code…
end
Runtime:addEventListener( «enterFrame», update )
end
|
Теперь давайте сделаем функцию kill () и заставим Alien возвращать Alien.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
module(…, package.seeall)
function Alien(x, y) —:void
…
function update() —:void
…
end
function Alien:kill()
Alien.parent:remove( Alien )
Alien = nil
end
Runtime:addEventListener( «enterFrame», update )
return Alien
end
|
Теперь мы можем убить () нашего пришельца, если его x находится за пределами экрана слева. Мы также можем добавить функцию new () для удобства.
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
|
module(…, package.seeall)
function Alien(x, y) —:void
local Alien = display.newImage(«Alien.png»)
Alien.x = x
Alien.y = y
function update() —:void
if Alien then
if (Alien.x > 0 — Alien.contentWidth) then
Alien.x = Alien.x — 2
else
Alien:kill()
end
end
end
function Alien:kill()
Alien.parent:remove( Alien )
Alien = nil
end
Runtime:addEventListener( «enterFrame», update )
return Alien
end
function new(x, y)
return Alien(x, y)
end
|
Использование таймеров для контроля над иностранцами
Создать инопланетянина было довольно легко. Теперь нам нужно начать добавлять их в игру через PlayState.
Сначала импортируйте модуль в PlayState.lua.
1
2
3
4
5
6
|
module(…, package.seeall)
local Ship = require(«Ship»)
local Bullet = require(«Bullet»)
local Alien = require(«Alien»)
local Buttons = require(«Buttons»)
|
Теперь нам нужно настроить таймер. Исходный код имел переменную _spawnInterval, которая использовалась для установки _spawnTimer. Каждый раз, когда _spawnTimer достигает 0, его значение сбрасывается до значения _spawnInterval._spawnInterval будет затем уменьшаться на 0,1, в результате чего инопланетяне будут появляться быстрее.
Для начала раскомментируйте объявления свойств _spawnInterval и _spawnTimer в create ().
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
function create() —:void
— variable declarations
PlayState._inGame = true
PlayState._background = nil
PlayState._ship = nil
—PlayState._aliens = nil
PlayState._bullets = nil
—PlayState._scoreText = nil
—PlayState._gameOverText = nil
PlayState._spawnTimer = nil
PlayState._spawnInterval = nil
—PlayState.SoundExplosionShip = nil
—PlayState.SoundExplosionAlien = nil
PlayState.SoundBullet = nil
…
end
|
Теперь в присваиваниях переменных установите _spawnTimer равным 0 и _spawnInterval равным 2,5. Также добавьте вызов resetSpawnTimer (). Мы создадим эту функцию за секунду.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
function create() —:void
…
— variable assignments
PlayState._background = display.newRect(0, 0, display.contentWidth, display.contentHeight)
PlayState._background:setFillColor(171, 204, 125)
PlayState._ship = Ship.new()
PlayState._shoot = false
PlayState._bullets = display.newGroup()
PlayState.SoundBullet = media.newEventSound( «Bullet.caf» )
PlayState._spawnTimer = 0
PlayState._spawnInterval = 2.5
resetSpawnTimer()
end
|
Теперь найдите закомментированную функцию resetSpawnTimer (). Это будет выглядеть примерно так.
1
2
3
4
5
6
7
|
function resetSpawnTimer() —:void
_spawnTimer = _spawnInterval
_spawnInterval = _spawnInterval*0.95
if(_spawnInterval < 0.1) then
_spawnInterval = 0.1
end
end
|
Удивительно, но это именно то, что нам нужно. Нам просто нужно сделать переменные свойства PlayState. Таким образом, функция будет знать, о каких _spawnInterval и _spawnTimer мы говорим.
1
2
3
4
5
6
7
|
function resetSpawnTimer() —:void
PlayState._spawnTimer = PlayState._spawnInterval
PlayState._spawnInterval = PlayState._spawnInterval*0.95
if(PlayState._spawnInterval < 0.1) then
PlayState._spawnInterval = 0.1
end
end
|
Теперь нам нужно добавить код для update (). В оригинальном коде игра создала инопланетян, даже если игра была закончена, а корабль мертв. Чтобы сделать то же самое, давайте разместим наш код обработки инопланетян за пределами того места, где мы проверяем, не закончилась ли игра.
1
|
function update() PlayState._spawnTimer = PlayState._spawnTimer — (30/1000) if(PlayState._spawnTimer < 0) then spawnAlien() resetSpawnTimer() end if PlayState._inGame then … end end
|
Этот код работает так же, как исходный код. Он вычитает стоимость одного кадра из _spawnTimer. Затем он проверяет, является ли _spawnTimer меньше нуля. Если это так, он сбрасывает таймер и порождает нового пришельца.
Другая группа отображения
Прежде чем мы сможем породить инопланетян, нам нужна группа отображения, чтобы добавить их. Как и _bullets, раскомментируйте объявление _aliens и назначьте его новой группе отображения.
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
|
function create() —:void
— variable declarations
PlayState._inGame = true
PlayState._background = nil
PlayState._ship = nil
PlayState._aliens = nil
PlayState._bullets = nil
—PlayState._scoreText = nil
—PlayState._gameOverText = nil
PlayState._spawnTimer = nil
PlayState._spawnInterval = 2.5
—PlayState.SoundExplosionShip = nil
—PlayState.SoundExplosionAlien = nil
PlayState.SoundBullet = nil
— variable assignments
PlayState._background = display.newRect(0, 0, display.contentWidth, display.contentHeight)
PlayState._background:setFillColor(171, 204, 125)
PlayState._ship = Ship.new()
PlayState._shoot = false
PlayState._aliens = display.newGroup()
PlayState._bullets = display.newGroup()
PlayState.SoundBullet = media.newEventSound( «Bullet.caf» )
PlayState._spawnTimer = 0
PlayState._spawnInterval = 2.5
resetSpawnTimer()
…
end
|
Теперь найдите функцию spawnAlien () и раскомментируйте ее. Это должно выглядеть примерно так:
1
2
3
4
5
|
function spawnAlien() —:void
local x = FlxG.width
local y = Math.random() * (FlxG.height — 100) + 50
_aliens.add(new Alien(x, y))
end
|
Этот код создает пришельца справа от экрана и на произвольной высоте. Затем он добавляет нового пришельца в группу отображения. Мы можем сделать то же самое с этим кодом:
1
2
3
4
5
|
function spawnAlien() —:void
local x = display.contentWidth
local y = math.random() * (display.contentHeight — 240) + 50
PlayState._aliens:insert(Alien.new(x, y))
end
|
Воссоздание оригинального косинусоидального движения для пришельцев
Если мы запустим код сейчас, он будет работать почти как оригинал. Инопланетяне появляются на случайных высотах и медленно появляются чаще. Когда они выходят за пределы экрана, они сами вызывают kill (). Теперь нам просто нужно заставить их двигаться так же, как в исходном коде. В оригинальной игре инопланетяне следовали по пути косинусоидальной волны, которая генерировалась на основе их местоположения x. Мы закомментировали этот код в функции чужих update (). Этот код потребовал немного поиграть. Поскольку у нас нет скорости для работы, трудно использовать оригинальный код. Это время портирования, когда вам просто нужно играть с числами. Я нашел этот код работал ближе всего к оригиналу:
01
02
03
04
05
06
07
08
09
10
|
function update() —:void
if Alien then
if (Alien.x > 0 — Alien.contentWidth) then
Alien.x = Alien.x — 2
Alien.y = Alien.y + math.cos(Alien.x / 10) * 2
else
Alien:kill()
end
end
end
|
Обработка коллизий
Теперь, когда все наши игровые объекты работают как оригиналы, нам нужно проверить наличие столкновений между пулями и пришельцами, а также пришельцами и кораблем. В исходном коде коллизии проверялись в функции update (). Если произошло столкновение, два объекта были переданы в функции overlapAlienBullet () и overlapAlienShip (). Давайте сначала создадим эти функции. Если мы раскомментируем overlapAlienBullet (), у нас есть код, который выглядит следующим образом:
1
2
3
4
5
6
7
8
9
|
function overlapAlienBullet(alien, bullet) —:void
local emitter = createEmitter()
emitter.at(alien)
alien.kill()
bullet.kill()
FlxG.play(SoundExplosionAlien)
FlxG.score = FlxG.score + 1
_scoreText.text = FlxG.score.toString()
end
|
Этот код создает излучатель частиц, убивает оба объекта, воспроизводит звуковой эффект и обновляет счет. Воссоздание системы частиц flixel выходит за рамки данного руководства, и нам еще предстоит внедрить систему оценок. А пока давайте просто закомментируем эти строки и уничтожим объекты.
1
2
3
4
5
6
7
|
function overlapAlienBullet(alien, bullet) —:void
alien:kill()
bullet:kill()
—FlxG.play(SoundExplosionAlien)
—FlxG.score = FlxG.score + 1
—_scoreText.text = FlxG.score.toString()
end
|
Сделайте то же самое для overlapAlienShip ():
1
2
3
4
5
6
7
8
9
|
function overlapAlienShip(alien, ship) —:void
ship:kill()
alien:kill()
—FlxG.play(SoundExplosionShip)
—_gameOverText = new FlxText(0, FlxG.height / 2, FlxG.width, «GAME OVER\nPRESS ENTER TO PLAY AGAIN»)
—_gameOverText.setFormat(null, 16, 0xFF597137, «center»)
—add(_gameOverText)
end
|
Теперь давайте создадим звуковой эффект для использования в этих функциях. Раскомментируйте объявления звуковых переменных в create ().
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
function create() —:void
— variable declarations
PlayState._inGame = true
PlayState._background = nil
PlayState._ship = nil
PlayState._aliens = nil
PlayState._bullets = nil
—PlayState._scoreText = nil
—PlayState._gameOverText = nil
PlayState._spawnTimer = nil
PlayState._spawnInterval = 2.5
PlayState.SoundExplosionShip = nil
PlayState.SoundExplosionAlien = nil
PlayState.SoundBullet = nil
…
end
|
Теперь присвойте им звуки:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
function create() —:void
…
— variable assignments
PlayState._background = display.newRect(0, 0, display.contentWidth, display.contentHeight)
PlayState._background:setFillColor(171, 204, 125)
PlayState._ship = Ship.new()
PlayState._shoot = false
PlayState._aliens = display.newGroup()
PlayState._bullets = display.newGroup()
PlayState.SoundBullet = media.newEventSound( «Bullet.caf» )
PlayState.SoundExplosionShip = media.newEventSound( «ExplosionShip.caf» )
PlayState.SoundExplosionAlien = media.newEventSound( «ExplosionAlien.caf» )
PlayState._spawnTimer = 0
PlayState._spawnInterval = 2.5
resetSpawnTimer()
end
|
Воспроизведение звуков так же просто, как одна строка на функцию ().
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
function overlapAlienBullet(alien, bullet) —:void
alien:kill()
bullet:kill()
media.playEventSound( PlayState.SoundExplosionAlien )
—FlxG.score = FlxG.score + 1
—_scoreText.text = FlxG.score.toString()
end
function overlapAlienShip(alien, ship) —:void
ship:kill()
alien:kill()
media.playEventSound( PlayState.SoundExplosionShip )
—_gameOverText = new FlxText(0, FlxG.height / 2, FlxG.width, «GAME OVER\nPRESS ENTER TO PLAY AGAIN»)
—_gameOverText.setFormat(null, 16, 0xFF597137, «center»)
—add(_gameOverText)
end
|
Сверните наш собственный код столкновения
Теперь, когда у нас есть наши функции перекрытия, нам нужно проверять наличие коллизий в каждом кадре. В Corona у нас нет простого способа проверить перекрывающиеся экранные объекты. Мы должны будем сделать проверки вручную. Это довольно простая концепция, но в реализации она становится довольно грязной. Давайте подумаем об этом на секунду. Что определяет перекрытие? Ваш первый инстинкт может заключаться в том, чтобы проверить, находится ли объект внутри другого. Это будет работать, но в этом случае перекрытие может быть просто частью объектов. Объект не должен быть полностью внутри другого, чтобы перекрываться. Как это выглядит в коде? Все, что нам нужно сделать, это проверить, больше ли максимальное значение х объекта, чем минимум х
стоимость другого объекта. Затем мы проверяем, меньше ли минимальное значение x того же объекта, чем максимальное значение x других объектов. Это вернет истину для каждого перекрытия. Затем мы выполняем те же проверки значений у объектов. Если мы перебираем все объекты в группах отображения, которые мы создали ранее, у нас должна быть работающая система столкновений.
Давайте попробуем это с пулями и пришельцами. Нам нужно выполнять эти проверки только в игре. Поэтому поместите этот код в правильную часть функции update ():
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
|
function update() —:void
…
if PlayState._inGame then
if PlayState._shoot == true and PlayState._ship then
local p = PlayState._ship:getBulletSpawnPosition()
spawnBullet(p)
end
if PlayState._bullets.numChildren > 0 and PlayState._aliens.numChildren > 0 then
for b=1, PlayState._bullets.numChildren do
local bulletXMax = PlayState._bullets[b].contentBounds.xMax
local bulletXMin = PlayState._bullets[b].contentBounds.xMin
local bulletYMax = PlayState._bullets[b].contentBounds.yMax
local bulletYMin = PlayState._bullets[b].contentBounds.yMin
for a=1, PlayState._aliens.numChildren do
if (PlayState._aliens[a].contentBounds.xMin <= bulletXMax) then
if (PlayState._aliens[a].contentBounds.xMax >= bulletXMin) then
if (PlayState._aliens[a].contentBounds.yMin <= bulletYMax) then
if (PlayState._aliens[a].contentBounds.yMax >= bulletYMin) then
overlapAlienBullet(PlayState._aliens[a], PlayState._bullets[b])
end
end
end
end
end
end
end
end
end
|
Как я уже сказал, этот код выглядит немного грязно, но он работает. Так, что это делает? Во-первых, он проверяет, существует ли пуля или инопланетянин. Этот код может стать очень требовательным к памяти, поэтому мы должны все проверить. Мы не хотим терять время, если у нас даже нет обоих видов объектов. Как только этот код проходит, мы запускаем цикл for. Этот цикл устанавливает переменную («b» с именем «bullets») в 1 и запускает остальную часть кода для каждого маркера в _bullets. Следующие четыре строки кода создают локальную копию минимальных и максимальных значений маркера. Как я уже говорил, нам нужно сохранить память здесь. Нам не нужно вычислять значения маркеров x и y снова и снова, если они не меняются. Следующая строка начинает еще один цикл. Это повторяется для всех пришельцев в _aliens. Код внутри второго цикла for просто выполняет проверки, о которых мы говорили ранее. Значение max x пули больше, чем значение min x иностранца? Я не могу подчеркнуть память здесь достаточно, чтобы
Вот почему мы проверяем каждое условие в отдельном операторе if. Если один из этих тестов не пройден, мы можем просто выйти из цикла. Нет необходимости продолжать проверять, нет ли столкновения. Наконец, в самом центре, если все эти проверки пройдены, мы вызываем нашу функцию overlapAlienBullet () со встречной пулей и пришельцем.
Уф. Это было много кода. Теперь нам нужно сделать то же самое для корабля.
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
|
function update() —:void
…
if PlayState._inGame then
if PlayState._shoot == true and PlayState._ship then
local p = PlayState._ship:getBulletSpawnPosition()
spawnBullet(p)
end
…
if PlayState._aliens.numChildren > 0 then
local shipXMax = PlayState._ship.contentBounds.xMax
local shipXMin = PlayState._ship.contentBounds.xMin
local shipYMax = PlayState._ship.contentBounds.yMax
local shipYMin = PlayState._ship.contentBounds.yMin
for a=1, PlayState._aliens.numChildren do
if (PlayState._aliens[a].contentBounds.xMin <= shipXMax) then
if (PlayState._aliens[a].contentBounds.xMax >= shipXMin) then
if (PlayState._aliens[a].contentBounds.yMin <= shipYMax) then
if (PlayState._aliens[a].contentBounds.yMax >= shipYMin) then
overlapAlienShip(PlayState._aliens[a], PlayState._ship)
end
end
end
end
end
end
end
end
|
Этот код идентичен маркеру и чужому коду. Разница лишь в том, что у нас только один корабль. Мы знаем, что есть корабль, в противном случае PlayState._inGame будет ложным. Нам не нужно циклически просматривать группу кораблей, потому что у нас есть только один.
Прежде чем мы сможем протестировать этот код, нам нужно завершить игру в overlapAlienShip (). Измените _inGame на false.
01
02
03
04
05
06
07
08
09
10
11
|
function overlapAlienShip(alien, ship) —:void
ship:kill()
alien:kill()
media.playEventSound( PlayState.SoundExplosionShip )
PlayState._inGame = false
—_gameOverText = new FlxText(0, FlxG.height / 2, FlxG.width, «GAME OVER\nPRESS ENTER TO PLAY AGAIN»)
—_gameOverText.setFormat(null, 16, 0xFF597137, «center»)
—add(_gameOverText)
end
|
Запуск кода теперь показывает, что наша тяжелая работа окупилась. Теперь у нас есть полностью рабочий порт оригинальной игры. Нам все еще нужно пересоздать систему оценок, и мы должны иметь возможность перезапустить игру, но жесткий перенос завершен.
Внедрение системы оценки
Начнем с системы оценок. Это так же просто, как создание текстовой метки и ее обновление при изменении оценки. Раскомментируйте строку _scoreText и добавьте новое свойство _score в объявлениях create ().
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
function create() —:void
— variable declarations
PlayState._inGame = true
PlayState._background = nil
PlayState._ship = nil
PlayState._aliens = nil
PlayState._bullets = nil
PlayState._score = nil
PlayState._scoreText = nil
—PlayState._gameOverText = nil
PlayState._spawnTimer = nil
PlayState._spawnInterval = 2.5
PlayState.SoundExplosionShip = nil
PlayState.SoundExplosionAlien = nil
PlayState.SoundBullet = nil
…
end
|
Теперь нам нужно присвоить им некоторые значения. _score можно просто установить на 0. _scoreText необходимо назначить новый текстовый объект, расположенный в верхнем левом углу экрана.
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 create() —:void
…
— variable assignments
PlayState._background = display.newRect(0, 0, display.contentWidth, display.contentHeight)
PlayState._background:setFillColor(171, 204, 125)
PlayState._ship = Ship.new()
PlayState._shoot = false
PlayState._aliens = display.newGroup()
PlayState._bullets = display.newGroup()
PlayState._score = 0
PlayState._scoreText = display.newText( «0», 10, 8, nil, 32 )
PlayState._scoreText:setTextColor(89, 113, 55)
PlayState.SoundBullet = media.newEventSound( «Bullet.caf» )
PlayState.SoundExplosionShip = media.newEventSound( «ExplosionShip.caf» )
PlayState.SoundExplosionAlien = media.newEventSound( «ExplosionAlien.caf» )
PlayState._spawnTimer = 0
PlayState._spawnInterval = 2.5
resetSpawnTimer()
end
|
API newText прост: display.newText («текст для отображения», позиция x, позиция y, шрифт, размер). «PlayState._scoreText: setTextColor (89, 113, 55)» просто устанавливает цвет заливки на тот же зеленый цвет, что и маркеры. Все, что нам нужно сейчас, это обновить счет в overlapAlienBullet ():
1
2
3
4
5
6
7
8
|
function overlapAlienBullet(alien, bullet) —:void
alien:kill()
bullet:kill()
media.playEventSound( PlayState.SoundExplosionAlien )
PlayState._score = PlayState._score + 1
PlayState._scoreText.text = PlayState._score
end
|
Это просто добавляет 1 к свойству _score, когда инопланетянин убит пулей. Затем он изменяет текстовое свойство _scoreText на значение оценки.
Игра закончена
Прежде чем закончить эту игру, нам нужно как-то ее переустановить. Таким образом, пользователь может начать все сначала, когда он умрет. Нам нужно будет сделать это в два этапа. Во-первых, мы должны остановить все, что пользователь может контролировать, как только корабль погибнет. Затем мы должны сбросить все остальное, когда пользователь нажимает, чтобы начать новую игру.
Давайте отключим все кнопки в функции overlapAlienShip (). Также давайте удалим свойство корабля. Мы можем сделать это, установив все значения в ноль.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
function overlapAlienShip(alien, ship) —:void
ship:kill()
alien:kill()
media.playEventSound( PlayState.SoundExplosionShip )
PlayState._inGame = false
PlayState._ship = nil
PlayState._upButton.onPress = nil
PlayState._upButton.onRelease = nil
PlayState._downButton.onPress = nil
PlayState._downButton.onRelease = nil
PlayState._leftButton.onPress = nil
PlayState._leftButton.onRelease = nil
PlayState._rightButton.onPress = nil
PlayState._rightButton.onRelease = nil
PlayState._shootButton.onPress = nil
PlayState._shootButton.onRelease = nil
—_gameOverText = new FlxText(0, FlxG.height / 2, FlxG.width, «GAME OVER\nPRESS ENTER TO PLAY AGAIN»)
—_gameOverText.setFormat(null, 16, 0xFF597137, «center»)
—add(_gameOverText)
end
|
Этот код просто назначает все значения onPress и onRelease для nil. Кнопки все равно
дисплей, но они не будут вызывать код при нажатии.
Game Over Message
Теперь мы видим, что оригинальная функция overlapAlienShip () отображала текстовую метку, чтобы сообщить пользователю, что игра окончена. Мы сделаем то же самое. Сначала раскомментируйте наше свойство _gameOverText в функции create ().
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
function create() —:void
— variable declarations
PlayState._inGame = true
PlayState._background = nil
PlayState._ship = nil
PlayState._aliens = nil
PlayState._bullets = nil
PlayState._score = nil
PlayState._scoreText = nil
PlayState._gameOverText = nil
PlayState._spawnTimer = nil
PlayState._spawnInterval = 2.5
PlayState.SoundExplosionShip = nil
PlayState.SoundExplosionAlien = nil
PlayState.SoundBullet = nil
…
end
|
Вернемся к overlapAlienShip (), нам нужно заменить закомментированный код на эти строки.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
function overlapAlienShip(alien, ship) —:void
ship:kill()
alien:kill()
media.playEventSound( PlayState.SoundExplosionShip )
PlayState._inGame = false
PlayState._ship = nil
PlayState._upButton.onPress = nil
PlayState._upButton.onRelease = nil
PlayState._downButton.onPress = nil
PlayState._downButton.onRelease = nil
PlayState._leftButton.onPress = nil
PlayState._leftButton.onRelease = nil
PlayState._rightButton.onPress = nil
PlayState._rightButton.onRelease = nil
PlayState._shootButton.onPress = nil
PlayState._shootButton.onRelease = nil
PlayState._gameOverText = display.newText( «GAME OVER TAP TO PLAY AGAIN», 35, display.contentHeight/2 — 50, nil, 16 )
PlayState._gameOverText:setTextColor(89, 113, 55)
end
|
Это тот же код, который мы использовали для создания и заполнения текста партитуры. Положение изменяется, чтобы центрировать текст на экране, и текст говорит «НАЖМИТЕ, ЧТОБЫ ИГРАТЬ СНОВА» вместо «НАЖМИТЕ ВВОД, ЧТОБЫ ИГРАТЬ СНОВА»
Добавить прослушиватель A Tap
Так как мы собираемся нажать, чтобы перезапустить игру, нам нужно добавить новый слушатель событий. Мы можем сделать это так же, как мы добавили прослушиватель enterFrame, но на этот раз прослушиваемое событие «tap». Также нам нужно добавить функцию для вызова слушателя. В нижней части overlapAlienShip () добавьте прослушиватель этого события:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
function overlapAlienShip(alien, ship) —:void
ship:kill()
alien:kill()
media.playEventSound( PlayState.SoundExplosionShip )
PlayState._inGame = false
PlayState._ship = nil
PlayState._upButton.onPress = nil
PlayState._upButton.onRelease = nil
PlayState._downButton.onPress = nil
PlayState._downButton.onRelease = nil
PlayState._leftButton.onPress = nil
PlayState._leftButton.onRelease = nil
PlayState._rightButton.onPress = nil
PlayState._rightButton.onRelease = nil
PlayState._shootButton.onPress = nil
PlayState._shootButton.onRelease = nil
PlayState._gameOverText = display.newText( «GAME OVER TAP TO PLAY AGAIN», 35, display.contentHeight/2 — 50, nil, 16 )
PlayState._gameOverText:setTextColor(89, 113, 55)
Runtime:addEventListener( «tap», tap )
end
|
Этот код проверяет наличие касания в любом месте экрана. Когда происходит «тап», вызывается функция тап (). Давайте создадим пустую функцию tap () для вызова. Поместите это чуть выше create ().
1
2
3
4
5
6
7
8
|
…
function tap( event )
— we just need this as a placeholder for now
end
function create()
…
end
|
Очистка всего
Давайте создадим функцию remove () для очистки всех игровых объектов. Поместите эту функцию выше нашей новой функции tap ().
01
02
03
04
05
06
07
08
09
10
11
12
|
…
function remove()
—
end
function tap( event )
— we just need this as a placeholder for now
end
function create()
…
end
|
Удаление экранных объектов и групп
Для начала мы должны убить всех оставшихся инопланетян и пуль. Для этого мы можем просто перебрать соответствующие группы отображения. Это правильный способ удаления объектов в Corona:
01
02
03
04
05
06
07
08
09
10
11
12
|
function remove()
for i=PlayState._bullets.numChildren, 1, -1 do
PlayState._bullets[i]:kill()
end
for i=PlayState._aliens.numChildren, 1, -1 do
PlayState._aliens[i]:kill()
end
PlayState._bullets:removeSelf()
PlayState._bullets = nil
PlayState._aliens:removeSelf()
PlayState._aliens = nil
end
|
Эти петли работают особым образом. Каждый цикл начинается с «i», установленного на последний объект в группах отображения. Затем цикл убивает этот объект и вычитает 1 из «я». Цикл повторяется до тех пор, пока «i» не станет равным 1. Причина, по которой мы вычитаем из «i», состоит в том, что каждый раз, когда мы убиваем объект, он удаляет себя из группы отображения. Это означает, что в группе отображения на один объект меньше. Если бы мы добавили 1 к «i», мы бы в конечном итоге вызвали kill () для объектов, которые больше не существуют. Чтобы исправить это, мы считаем в обратном порядке каждый раз, когда удаляем объект
После того, как все объекты удалены из групп, мы удаляем группы. Мы также установили свойства, которые удерживали группы на ноль. Теперь их можно использовать снова.
Переменные обнуления для сборщика мусора
Давайте установим все используемые переменные в ноль. Таким образом, сборщик мусора может освободить память. Это также гарантирует, что все переменные будут сброшены перед началом новой игры. Сначала удалите все кнопки:
1
|
function remove() for i=PlayState._bullets.numChildren, 1, -1 do PlayState._bullets[i]:kill() end for i=PlayState._aliens.numChildren, 1, -1 do PlayState._aliens[i]:kill() end PlayState._bullets:removeSelf() PlayState._bullets = nil PlayState._aliens:removeSelf() PlayState._aliens = nil PlayState._upButton:removeSelf() PlayState._upButton = nil PlayState._downButton:removeSelf() PlayState._downButton = nil PlayState._leftButton:removeSelf() PlayState._leftButton = nil PlayState._rightButton:removeSelf() PlayState._rightButton = nil PlayState._shootButton:removeSelf() PlayState._shootButton = nil end
|
Нам нужно сначала удалить их с дисплея, а затем установить их на ноль. Давайте позаботимся о наших звуках сейчас.
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
|
function remove()
for i=PlayState._bullets.numChildren, 1, -1 do
PlayState._bullets[i]:kill()
end
for i=PlayState._aliens.numChildren, 1, -1 do
PlayState._aliens[i]:kill()
end
PlayState._bullets:removeSelf()
PlayState._bullets = nil
PlayState._aliens:removeSelf()
PlayState._aliens = nil
PlayState._upButton:removeSelf()
PlayState._upButton = nil
PlayState._downButton:removeSelf()
PlayState._downButton = nil
PlayState._leftButton:removeSelf()
PlayState._leftButton = nil
PlayState._rightButton:removeSelf()
PlayState._rightButton = nil
PlayState._shootButton:removeSelf()
PlayState._shootButton = nil
PlayState.SoundExplosionShip = nil
PlayState.SoundExplosionAlien = nil
PlayState.SoundBullet = nil
end
|
Теперь наши текстовые объекты:
1
2
3
4
5
6
7
8
|
function remove()
…
PlayState._scoreText:removeSelf()
PlayState._scoreText = nil
PlayState._gameOverText:removeSelf()
PlayState._gameOverText = nil
end
|
Удаление прослушивателей событий
Чтобы закончить очистку, нам нужно удалить наших слушателей событий. После этого мы можем наконец сбросить PlayState в пустой массив:
1
2
3
4
5
6
7
8
|
function remove()
…
Runtime:removeEventListener( «enterFrame», update )
Runtime:removeEventListener( «tap», tap )
PlayState = nil
PlayState = {}
end
|
Перезапуск игры
Перезапустить игру так же просто, как вызвать remove () и затем создать () в нашей функции tap ().
1
2
3
4
|
function tap( event )
remove()
create()
end
|
Финальные настройки
Мы почти закончили. Нам просто нужно сделать несколько последних штрихов. Начните с удаления всех закомментированных строк из старого исходного кода. Это очистит наши файлы до тонны.
Включение мультитача
Еще одна быстрая настройка, которую мы можем сделать, — включить мультитач. Вы не заметите изменения на симуляторе, но приятно иметь возможность одновременно нажимать несколько кнопок на реальном устройстве. Это корректировка в одну строку. Нам нужно, чтобы это произошло один раз, поэтому давайте добавим его в файл main.lua.
01
02
03
04
05
06
07
08
09
10
|
local PlayState = require(«PlayState»)
function Main()
system.activate( «multitouch» )
display.setStatusBar( display.HiddenStatusBar )
PlayState.PlayState()
end
Main()
|
Модификация библиотеки пользовательского интерфейса в соответствии с нашими потребностями
У меня также были некоторые проблемы с модулем ui.lua. Кнопки не считаются, когда вы отпускаете палец с устройства, если только последним не коснулся кнопки. Это не всегда работает должным образом для этой конкретной игры, и иногда кажется, что корабль движется сам по себе, потому что функция выпуска не вызывается. Это было просто исправить. Я снял флажок, чтобы увидеть, была ли нажата кнопка при отпускании пальца пользователя. Это просто означало закомментировать операторы if и end в строках 91 и 98.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
…
if «ended» == phase then
— Only consider this a «click» if the user lifts their finger inside button’s stageBounds
—if isWithinBounds then
if onEvent then
buttonEvent.phase = «release»
result = onEvent( buttonEvent )
elseif onRelease then
result = onRelease( event )
end
—end
end
…
|
Эти изменения включены в файл ui.lua, включенный в это руководство.
Вывод
Мы закончили. Теперь у нас есть полностью рабочая копия оригинальной флеш игры. У нас есть корабль, который движется и стреляет, инопланетяне и система очков. Игра управляет памятью всех объектов, имеет возможность сброса и запуска заново. Помимо системы частиц, эта игра является идентичным портом. Теперь, когда вы завершили это руководство, у вас должно быть достаточно знаний, чтобы перенести практически любую игру flash / actioncript на iPhone.
Упражнения и дополнительные функции
Многие разработчики любят добавлять функциональность или немного менять игровой процесс при переносе игры на мобильное устройство. Я хотел бы предложить вам улучшить / изменить эту игру, чтобы она стала больше похожа на настоящее приложение. Вы можете изменить схему управления на любой из типов, о которых мы говорили в уроке 2. Вы можете создать меню с настройками или несколькими уровнями. Вы можете добавить различные типы врагов. Вы можете добавить локальную или онлайн систему рекордов. Возможности безграничны. К счастью, Corona делает разработку действительно быстрой и действительно плавной. Надеюсь, вам понравился этот урок и, что более важно, вы узнали много нового. Оставьте комментарий ниже, чтобы рассказать нам, как все прошло.