Статьи

Портирование игр ActionScript для iOS с помощью Corona SDK: Часть 2

В этом руководстве будет рассказано о портировании Flash / Flex-игры на Corona SDK. В частности, мы будем портировать с ActionScript на Lua с конечной целью играть в игры, которые раньше были только для Flash, на iPhone. В дополнение к демонстрации языковых и API-различий, эта серия учебников также будет учитывать аппаратные ограничения, такие как размер экрана и отсутствие физических кнопок на iPhone.

Этот урок начинает с того места, где началась первая часть .

Начните с переноса синтаксиса из исходного файла «de / pixelate / flixelprimer / Ship.as», как описано
в день один пост. В частности, вам нужно:

  • Избавьтесь от объявлений пакетов и классов.
  • Удалить объявления типов.
  • Добавить «локальный» для всех переменных.
  • Замените условные скобки словами «then» и «end».
  • Удалите все точки с запятой и закомментируйте все.

Теперь добавьте объявление модуля вверху файла, чтобы мы могли импортировать файл в PlayState.
Кроме того, продолжайте и оберните весь код с помощью функции конструктора Ship (). Ваш код теперь должен быть устроен так:

1
2
3
4
5
module(…, package.seeall)
 
function Ship()
    … all of the commented code goes here …
end

У нас нет flixel, поэтому избавьтесь от оператора import для него. Теперь посмотрим на верхние строчки нашего
Функция Ship ():

01
02
03
04
05
06
07
08
09
10
11
function Ship()
    —[Embed(source=»../../../../assets/png/Ship.png»)] private var ImgShip:Class
 
    —[[
    function Ship() —:void
        super(50, 50, ImgShip)
    end
    —]]
 
    …
end

Первая строка загружает изображение корабля и сохраняет его в переменной ImgShip. Следующие несколько строк
старый метод конструктора. Когда корабль был создан, эта функция присвоила ему изображение ImgShip и координаты (50, 50). Мы можем сделать то же самое, создав переменную Ship, содержащую изображение. Мы можем загрузить изображение, используя модуль отображения, который мы использовали в уроке 1. Затем мы можем установить переменные x и y для свойств равными 50. Замените приведенные выше строки, чтобы сделать следующее:

1
2
3
4
5
6
7
8
function Ship()
    local Ship = display.newImage(«Ship.png»)
 
    Ship.x = 50
    Ship.y = 50
 
    … commented code …
end

Теперь давайте заставим нашу функцию Ship () возвращать Ship, чтобы наш PlayState мог ее использовать.

01
02
03
04
05
06
07
08
09
10
function Ship()
    local Ship = display.newImage(«Ship.png»)
 
    Ship.x = 50
    Ship.y = 50
 
    … commented code …
     
    return Ship
end

В нашем исходном коде ActionScript корабль создается в PlayState с помощью вызова «новый корабль ()». Пока мы в
это, давайте сделаем новую функцию (), которая возвращает Ship ().

01
02
03
04
05
06
07
08
09
10
11
12
13
14
function Ship()
    local Ship = display.newImage(«Ship.png»)
 
    Ship.x = 50
    Ship.y = 50
 
    … commented code …
     
    return Ship
end
 
function new()
    return Ship()
end

На самом деле в этом нет технической необходимости, но это делает наш код немного более читабельным.

Вернитесь к PlayState.lua и запросите наш модуль Ship сверху.

1
2
3
4
5
6
module(…, package.seeall)
 
local Ship = require(«Ship»)
 
function PlayState()

Теперь мы можем создать новый корабль. Сначала переместите все объявления закомментированных переменных из верхней части файла в функцию create ().

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
module(…, package.seeall)
 
local Ship = require(«Ship»)
 
function PlayState()
    local PlayState = {}
     
    … a whole bunch of commented code …
     
    function create() —:void
        PlayState._inGame = true
        —local _ship
        —local _aliens
        —local _bullets
        —local _scoreText
        —local _gameOverText
        —local _spawnTimer
        —local _spawnInterval = 2.5
     
        PlayState._background = display.newRect(0, 0, display.contentWidth, display.contentHeight)
        PlayState._background:setFillColor(171, 204, 125)
        … commented create() logic …
    end
     
    create()
end

Оставляя их закомментированными, сделайте их свойствами PlayState. Установите свойства без значения в ноль. Пока мы на этом, нам нужно сделать объявление _background.

01
02
03
04
05
06
07
08
09
10
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

Раскомментируйте объявление переменной _ship и создайте новый корабль следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
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
         
    — variable assignments
    PlayState._background = display.newRect(0, 0, display.contentWidth, display.contentHeight)
    PlayState._background:setFillColor(171, 204, 125)
     
    PlayState._ship = Ship.new()
     
    … commented create() logic …
end

Обратите внимание, что мы сначала назначаем _background. Это потому, что экранные объекты в Corona упорядочены
к тому времени, когда они созданы. Мы не сможем увидеть _ship, если мы создали его до _background.

Если вы запустите код, то теперь корабль будет отображаться в (50, 50).

Судно

Мы должны быть в состоянии переместить корабль, чтобы играть, но как мы можем? Оригинальная игра была сделана для управления с клавиатуры. У iPhone нет клавиатуры. Как мы можем обойти это? Это один из многих факторов, которые необходимо учитывать при создании приложения для мобильного устройства, независимо от того, переносите ли вы существующий код или создаете приложение с нуля. Другой вопрос, с которым мы уже имеем дело, — это размер экрана. На компьютере программы бывают разных размеров. Большинство из них даже изменяемого размера. На iPhone приложения имеют одинаковый размер.

Решение этих проблем может быть сложной задачей. Чтобы обойти эти ограничения, нужно подумать
вне коробки. Например, мы можем управлять нашим кораблем с помощью экранных кнопок. Мы также можем нажать на место на экране, куда мы хотим переместить корабль. Мы могли бы даже использовать устройство
акселерометр для управления кораблем на основе наклона устройства. Может быть, мы немного изменим игровой процесс и сделаем управление кораблем самим. Тогда игрок просто будет отвечать за стрельбу. Как видите, у iPhone есть множество возможностей для новых стилей игры.

Как бы ни были хороши эти идеи, мы собираемся создать экранные кнопки для управления кораблем. Таким образом, игра будет максимально соответствовать оригиналу. Я оставляю эти другие варианты для вас, чтобы изучить.

Ansca Mobile создала библиотеку с открытым исходным кодом для создания виртуальных кнопок. Вместо того
встроив библиотеку в Corona, они добавили ее на свой веб-сайт в качестве дополнительной. у меня есть
включил новейшую версию на момент написания этой статьи в исходный код этого руководства. Добавить
Файл «ui.lua» в папку вашего проекта.

Перейдите к вашему файлу Ship.lua, чтобы мы могли выполнить некоторые настройки. Давайте добавим несколько свойств к нашей переменной Ship. Чуть ниже кода, где мы устанавливаем координаты корабля (50, 50), добавьте это:

01
02
03
04
05
06
07
08
09
10
11
local Ship = display.newImage(«Ship.png»)
 
Ship.x = 50
Ship.y = 50
 
Ship._up = false
Ship._down = false
Ship._left = false
Ship._right = false

Мы собираемся использовать их, чтобы сообщить кораблю, когда двигаться. Если значения изменяются на true, это означает, что нажаты соответствующие кнопки.

Вернувшись в PlayState.lua, нам нужно создать новую функцию для обработки кнопок. Мы будем обрабатывать это вне PlayState (), потому что это будет немного грязно. Прежде чем мы продолжим,
убедитесь, что у вас есть все изображения кнопок из источника, включенного в это руководство. Теперь создайте функцию buttonHandler () под PlayState ().

1
2
3
4
5
6
7
8
function PlayState()
end
 
function buttonHandler(PlayState)
 
end

Обратите внимание, что buttonHandler () принимает аргумент: PlayState. Это потому, что мы создаем
buttonHandler () вне функции PlayState (). Если вы помните из первого урока, это означает,
что buttonHandler () не знает, что такое переменная PlayState, потому что PlayState является локальным для
Функция PlayState (). Мы будем вызывать buttonHandler () из PlayState (). Мимоходом
PlayState to buttonHandler (), мы позволяем buttonHandler () видеть и изменять все PlayState и его свойства. Поскольку _ship является свойством PlayState, buttonHandler () сможет установить
_ship_up, _ship._down и т. д.

Мы собираемся импортировать модуль ui.lua в нашу функцию buttonHandler вместо верхней части
PlayState.lua. Вы поймете, почему позже.

1
2
3
4
5
6
7
8
function PlayState()
end
 
function buttonHandler(PlayState)
    local ui = require(«ui»)
end

Теперь это будет немного грязно. Мы собираемся создать две функции для каждой кнопки. Один
когда кнопка нажата, а одна — когда кнопка отпущена. Когда они вызываются, они устанавливают свойства корабля на true или false.

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
function buttonHandler(PlayState)
    local ui = require(«ui»)
     
    function upPressed()
        PlayState._ship._up = true
    end
     
    function upReleased()
        PlayState._ship._up = false
    end
     
    function downPressed()
        PlayState._ship._down = true
    end
     
    function downReleased()
        PlayState._ship._down = false
    end
     
    function leftPressed()
        PlayState._ship._left = true
    end
     
    function leftReleased()
        PlayState._ship._left = false
    end
     
    function rightPressed()
        PlayState._ship._right = true
    end
     
    function rightReleased()
        PlayState._ship._right = false
    end
end

Потому что мы передаем PlayState в buttonHandler (), а _ship и все его свойства принадлежат
PlayState, мы можем изменить их с истинного на ложное и наоборот. Теперь нам нужно создать актуальные кнопки. С импортированным модулем пользовательского интерфейса мы можем использовать один из его методов: newButton. У этого есть некоторый интересный синтаксис, так что держитесь.

1
2
3
4
5
6
7
PlayState._upButton = ui.newButton {
    defaultSrc = «up.png» , defaultX = «50» , defaultY = «50»,
    overSrc = «up.png» , overX = «50» , overY = «50»,
    onPress = upPressed,
    onRelease = upReleased,
    id = «_upButton»
}

Этот код вызывает newButton и присваивает результат _upButton (свойство PlayState). Во-первых,
Вы можете быть удивлены, почему есть скобки. Это не исключение из правил синтаксиса lua.
На самом деле, скобки содержат массив параметров, которые передаются в метод newButton. Здесь много параметров, поэтому давайте рассмотрим их по одному. Первый defaultSrc — это расположение изображения, которое нужно использовать, когда кнопка не нажата. defaultX и defaultY — размеры изображения. overSrc — это местоположение изображения, которое будет отображаться при нажатии кнопки. В этом случае мы будем использовать одно и то же изображение. overX и overY работают так же, как defaultX и defaultY, но они являются измерениями для overSrc. onPress — это функция, которая вызывается при нажатии кнопки. Это одна из функций, которые мы сделали ранее. onRelease аналогичен onPress, но вызывается при отпускании кнопки. id — это именованная строка, которая отличает эту кнопку от других. Каждая кнопка имеет свойство ax и ay, как и другие экранные объекты, которые можно настроить в любое время.

Теперь, когда мы знаем, как работают кнопки, давайте сделаем остальные из них. Добавьте это в конец
buttonHandler ():

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
45
46
47
function buttonHandler()
 
    PlayState._upButton = ui.newButton {
        defaultSrc = «up.png» , defaultX = «50» , defaultY = «50»,
        overSrc = «up.png» , overX = «50» , overY = «50»,
        onPress = upPressed,
        onRelease = upReleased,
        id = «_upButton»
    }
     
    PlayState._upButton.x = display.contentWidth — 100
    PlayState._upButton.y = display.contentHeight — 100
         
    PlayState._downButton = ui.newButton {
        defaultSrc = «down.png» , defaultX = «50» , defaultY = «50»,
        overSrc = «down.png» , overX = «50» , overY = «50»,
        onPress = downPressed,
        onRelease = downReleased,
        id = «_downButton»
    }
     
    PlayState._downButton.x = display.contentWidth — 100
    PlayState._downButton.y = display.contentHeight — 50
         
    PlayState._leftButton = ui.newButton {
        defaultSrc = «left.png» , defaultX = «50» , defaultY = «50»,
        overSrc = «left.png» , overX = «50» , overY = «50»,
        onPress = leftPressed,
        onRelease = leftReleased,
        id = «_leftButton»
    }
     
    PlayState._leftButton.x = display.contentWidth — 150
    PlayState._leftButton.y = display.contentHeight — 75
         
    PlayState._rightButton = ui.newButton {
        defaultSrc = «right.png» , defaultX = «50» , defaultY = «50»,
        overSrc = «right.png» , overX = «50» , overY = «50»,
        onPress = rightPressed,
        onRelease = rightReleased,
        id = «_rightButton»
    }
     
    PlayState._rightButton.x = display.contentWidth — 50
    PlayState._rightButton.y = display.contentHeight — 75
end

Если мы добавим вызов buttonHandler () в функцию create () PlayState (), у нас будет четыре
стрелки расположены в правом нижнем углу экрана.

01
02
03
04
05
06
07
08
09
10
11
12
13
function PlayState()
 
 
    function create()
    …
     
    PlayState._ship = Ship.new()
     
    buttonHandler(PlayState)
     
    …
end

Теперь нам нужно создать цикл для обработки игрового процесса. В исходном коде для этой цели использовалась функция update (). Мы сделаем то же самое с нашим кодом. Как и в случае с функцией create (), раскомментируйте функцию update () и поместите однострочные комментарии перед всеми внутренними строками.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
function update() —:void
    —FlxU.overlap(_aliens, _bullets, overlapAlienBullet)
    —FlxU.overlap(_aliens, _ship, overlapAlienShip)
     
    —if(FlxG.keys.justPressed(«SPACE») and _ship.dead == false) then
    — spawnBullet(_ship.getBulletSpawnPosition())
    —end
 
    —if(FlxG.keys.ENTER and _ship.dead) then
    — FlxG.state = new PlayState();
    —end
     
    —_spawnTimer = _spawnTimer — FlxG.elapsed
     
    —if(_spawnTimer < 0) then
    — spawnAlien()
    — resetSpawnTimer()
    —end
                 
    —super.update()
end

Чтобы получить функцию update (), вызываемую повторно, как в flixel, нам нужно добавить событие
слушатель. Эта строка кода установит update () для вызова каждого нового кадра. Добавьте его в create ()
функция.

1
2
3
4
5
6
7
8
9
function create() —:void
    …
         
    buttonHandler(PlayState)
     
    Runtime:addEventListener( «enterFrame», update )
     
    …
end

Давайте проверим, работает ли игра до того, как выпустим игровой цикл. Запомнить эту переменную
«PlayState._inGame»? Мы собираемся использовать его, чтобы проверить, закончилась ли игра. Поэтому окружите закомментированный код update () этим утверждением.

1
2
3
4
5
6
7
function update() —:void
    if PlayState._inGame then
     
        … commented code …
     
    end
end

Чтобы заставить корабль двигаться, мы должны заставить работать функцию update () Ship.lua. Как и раньше, измените комментарии так, чтобы объявление функции больше не комментировалось, а все остальное было. Добавьте того же слушателя событий прямо перед концом Ship ().

1
2
3
4
5
6
7
8
function Ship()
 
    …
     
    Runtime:addEventListener( «enterFrame», update )
     
    return Ship
end

Мы также должны проверить, что переменная «Ship» существует, и что она не равна нулю. Завернуть комментарий
код с этим оператором if.

1
2
3
4
5
function update()
    if Ship then
        … commented code …
    end
end

Теперь давайте начнем работать через закомментированные строки. Первые два устанавливают скорость корабля на 0.
У Corona нет скорости, как у flixel, поэтому мы в конечном итоге будем работать с x и y корабля
непосредственно. Следующие восемь строк проверяют, нажаты ли клавиши клавиатуры влево или вправо на клавиатуре. Мы
может заменить это проверками переменных _left и _right. Поскольку у нас нет скорости
работать с, мы просто установим значение x корабля на плюс или минус 5.

1
2
3
4
5
if(Ship._left) then
    Ship.x = Ship.x — 5
elseif(Ship._right) then
    Ship.x = Ship.x + 5
end

То же самое касается и вверх и вниз.

1
2
3
4
5
if(Ship._up) then
    Ship.y = Ship.y — 5
elseif(Ship._down) then
    Ship.y = Ship.y + 5
end

Вы можете удалить «super.update ()»

Если бы вы запустили игру сейчас, корабль бы двигался правильно. Проблема в том, что он полетит
прямо с экрана или в элементы управления, если мы позволим. Оригинальная игра тоже имела эту проблему. Так,
следующие строки кода в функции update () не дают кораблю покинуть границу. Это
выполняет это, проверяя, находится ли корабль за границей после всех его изменений x и y
через. Если корабль находится снаружи, он возвращается к максимально допустимому значению. Мы сделаем то же самое, и мы можем использовать те же display.contentWidth и contentHeight, что и с фоном, чтобы найти размер экрана.

01
02
03
04
05
06
07
08
09
10
11
if(Ship.x > display.contentWidth-Ship.contentWidth-16) then
    Ship.x = display.contentWidth-Ship.contentWidth-16
elseif(Ship.x < Ship.contentWidth+16) then
    Ship.x = Ship.contentWidth+16
end
     
if(Ship.y > display.contentHeight-Ship.contentHeight-150) then
    Ship.y = display.contentHeight-Ship.contentHeight-150
elseif(Ship.y < Ship.contentHeight+16) then
    Ship.y = Ship.contentHeight+16
end

Я немного изменил цифры, чтобы корабль не перекрывал кнопки.

Теперь нам нужно заставить наш корабль стрелять. Давайте посмотрим на «de / pixelate / flixelprimer / Bullet.as». Идти
через обычный процесс преобразования синтаксиса. К счастью, это действительно легко. Удалите все, кроме конструктора Bullet (). Добавьте объявление модуля и сохраните его как Bullet.lua в папке вашего проекта.

Теперь у нас есть функция Bullet (), которая принимает значения X и Y. Это создает зеленый прямоугольник на тех
координаты, и он устанавливает свою скорость до 1000.

Давайте создадим прямоугольник, который действует как пуля так же, как мы сделали фон в первом
урок.

1
2
3
4
5
6
7
8
9
module(…, package.seeall)
 
function Bullet(x, y) —:void
    local Bullet = display.newRect(x, y, 16, 4)
     
    —super(x, y)
    —createGraphic(16, 4, 0xFF597137)
    —velocity.x = 1000
end

Этот код создает квадрат 16 на 4 пикселя и устанавливает его координаты X и Y в числа, переданные в Bullet (x, y). Теперь давайте изменим цвет заливки пули. # 587137 конвертируется в RGB (89, 113, 55).

1
2
3
4
5
6
7
8
module(…, package.seeall)
 
function Bullet(x, y) —:void
    local Bullet = display.newRect(x, y, 16, 4)
    Bullet:setFillColor(89, 113, 55)
     
    —velocity.x = 1000
end

Давайте добавим новую функцию () для удобства, как мы сделали с Ship.lua.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
module(…, package.seeall)
 
function Bullet(x, y) —:void
    local Bullet = display.newRect(x, y, 16, 4)
    Bullet:setFillColor(89, 113, 55)
     
    —velocity.x = 1000
     
    return Bullet
end
 
function new(x, y)
    return Bullet(x, y)
end

Этот код просто принимает значения X и Y и возвращает новый маркер в этом месте. Убедитесь, что
добавьте «return Bullet» в конце Bullet (), чтобы PlayState мог использовать его.

Теперь нам нужно воссоздать скорость. Давайте создадим функцию update (), как мы сделали для корабля.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
function Bullet(x, y) —:void
    local Bullet = display.newRect(x, y, 16, 4)
    Bullet:setFillColor(89, 113, 55)
     
    function update()
        if Bullet then
            Bullet.x = Bullet.x + 10
        end
    end
     
    Runtime:addEventListener( «enterFrame», update )
     
    return Bullet
end

Теперь пуля будет перемещаться на десять пикселей вправо на каждый кадр.

Теперь нам понадобится какой-нибудь метод для стрельбы пулями. Давайте добавим еще одну кнопку. Во-первых, нам нужна переменная, чтобы отслеживать, нажата кнопка или нет. Вместо того, чтобы обрабатывать это в файле Ship.lua, давайте создадим эту переменную в функции create () в PlayState.lua в нижней части
объявления переменных.

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._shoot = nil
     
    …
end

Идите вперед и установите значение false под новой линией корабля в назначениях переменных.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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
     
    …
end

Теперь нам нужно добавить несколько строк в нашу функцию buttonHandler (). Нам нужно еще две функции для обработки нажатия и отпускания новой кнопки. Добавьте эти две функции после rightPressed () и
rightReleased ().

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 buttonHandler(PlayState)
    …
     
    function leftPressed()
        PlayState._ship._left = true
    end
     
    function leftReleased()
        PlayState._ship._left = false
    end
     
    function rightPressed()
        PlayState._ship._right = true
    end
     
    function rightReleased()
        PlayState._ship._right = false
    end
     
    function shootPressed()
        PlayState._shoot = true
    end
     
    function shootReleased()
        PlayState._shoot = false
    end
     
    …
end

Теперь загрузите кнопку, как раньше.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
function buttonHandler(PlayState)
    …
     
    PlayState._shootButton = ui.newButton {
        defaultSrc = «shoot.png» , defaultX = «100» , defaultY = «100»,
        overSrc = «shoot.png» , overX = «100» , overY = «100»,
        onRelease = shootReleased,
        onPress = shootPressed,
        id = «_shootButton»
    }
 
    PlayState._shootButton.x = 75
    PlayState._shootButton.y = display.contentHeight — 75
end

Наш код кнопки становится немного грязным. Давайте переместим все buttonHandler () в новый файл с именем
Buttons.lua. Не забудьте модуль декларации.

Импортируйте новый модуль Button в верхней части PlayState. Пока мы там, давайте импортируем Bullet.

1
2
3
4
5
6
7
8
module(…, package.seeall)
 
local Ship = require(«Ship»)
local Bullet = require(«Bullet»)
local Buttons = require(«Buttons»)
 
function PlayState()

Измените строку buttonHandler () в create (), чтобы вызвать функцию модуля.

01
02
03
04
05
06
07
08
09
10
11
function create() —:void
    …
         
    PlayState._ship = Ship.new()
         
    PlayState._shoot = false
         
    Buttons.buttonHandler(PlayState)
         
    …
end

Теперь нам нужно восстановить некоторые функции обработки маркеров. Давайте посмотрим на getBulletSpawnPosition ()
в Ship.lua.

1
2
3
4
5
6
—[[
function getBulletSpawnPosition() —:FlxPoint
    local p = new FlxPoint(x + 36, y + 12)
    return p
end
—]]

Первоначальная функция была экземпляром метода ship. Он вернул значения X и Y, чтобы создать новый маркер. Так как это должен быть метод экземпляра, мы должны заставить его работать как один. Давайте сделаем так, чтобы переменная Ship принадлежала ей.

1
2
3
4
function Ship:getBulletSpawnPosition() —:FlxPoint
    local p = new FlxPoint(x + 36, y + 12)
    return p
end

Поскольку FlxPoint является типом, эксклюзивным для Flixel, давайте вместо этого вернем массив.

1
2
3
4
function Ship:getBulletSpawnPosition()
    local p = {x=Ship.x + 36, y=Ship.y + 2}
    return p
end

Этот код добавляет 36 к значению X корабля и 2 (я использовал 2 вместо 12 для настройки пули
положение для короны) к значению Y корабля. Они хранятся в ассоциативном массиве. Теперь мы можем
доступ к каждому номеру с помощью px и py

В исходном коде переменная в PlayState с именем _bullets содержала все экземпляры маркеров.
Мы можем сделать то же самое с группой отображения. Группа отображения в Corona просто содержит несколько экранных объектов и отображает их. Это полезно для отслеживания группы одного и того же вида
объект. Когда объекты добавляются в группы отображения, они отображаются в том порядке, в котором
группы созданы. Например, если у нас есть куча пуль и кучка пришельцев, и мы хотим, чтобы все пришельцы были сверху, мы могли бы создать группу отображения пули, а затем группу отображения пришельцев. Если мы добавим все пули и инопланетные экземпляры в их группы, они всегда будут отображаться в правильном порядке. Это произойдет, даже если, скажем, пуля создана после инопланетянина. Инопланетянин будет сверху, потому что группа отображения управляет порядком отображения.

Раскомментируйте строку «—PlayState._bullets = nil» в функции create () в PlayState.lua.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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._shoot = nil
     
    …
end

Сделайте _bullets новой группой отображения в назначениях переменных.

01
02
03
04
05
06
07
08
09
10
11
12
13
function create() —:void
    …
         
    PlayState._ship = Ship.new()
         
    PlayState._shoot = false
     
    PlayState._bullets = display.newGroup()
         
    Buttons.buttonHandler(PlayState)
         
    …
end

Теперь взгляните на spawnBullet ().

1
2
3
4
5
6
7
—[[
function spawnBullet(p) —:void
    local bullet = new Bullet(px, py)
    _bullets.add(bullet)
    FlxG.play(SoundBullet)
end
—]]

Этот код на самом деле очень близок к тому, что мы хотим. Сделайте так, чтобы это выглядело так:

1
2
3
4
5
6
function spawnBullet(p) —:void
    local _bullet = Bullet.new(px, py)
    PlayState._bullets:insert(_bullet)
    PlayState._shoot = false
    —FlxG.play(SoundBullet)
end

Когда пуля создана, для _shoot должно быть установлено значение false. Таким образом, пользователь должен поднять
их палец, прежде чем они стреляют снова.

Corona имеет базовый API для воспроизведения коротких звуковых эффектов. Чтобы использовать это, мы должны использовать звук .caf
файлы. Преобразованная версия оригинальных звуковых эффектов MP3 включена в источник для этого
руководство.

Сначала нам нужно создать переменные для хранения звуков. В верхней части PlayState находятся три строки.
Переместите их в объявления переменных в 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._scoreText = nil
    —PlayState._gameOverText = nil
    —PlayState._spawnTimer = nil
    —PlayState._spawnInterval = 2.5
    PlayState._shoot = nil
    —[Embed(source=»../../../../assets/mp3/ExplosionShip.mp3″)] private var SoundExplosionShip:Class
    —[Embed(source=»../../../../assets/mp3/ExplosionAlien.mp3″)] private var SoundExplosionAlien:Class
    —[Embed(source=»../../../../assets/mp3/Bullet.mp3″)] private var SoundBullet:Class
     
    …
end

Мы просто хотим, чтобы имена были одинаковыми. Сделайте их свойствами PlayState и установите для них значение nil.
Раскомментируйте последнюю строку.

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._scoreText = nil
    —PlayState._gameOverText = nil
    —PlayState._spawnTimer = nil
    —PlayState._spawnInterval = 2.5
    PlayState._shoot = nil
    —PlayState.SoundExplosionShip = nil
    —PlayState.SoundExplosionAlien:Class = nil
    PlayState.SoundBullet = nil
     
    …
end

Теперь мы будем использовать медиа-модуль для загрузки нового звука. Поместите это в конец назначения:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
function create() —:void
    …
         
    PlayState._ship = Ship.new()
         
    PlayState._shoot = false
     
    PlayState._bullets = display.newGroup()
     
    PlayState.SoundBullet = media.newEventSound( «Bullet.caf» )
         
    Buttons.buttonHandler(PlayState)
         
    …
end

Возвращаясь к spawnBullet (). Мы можем заменить последнюю строку, чтобы воспроизвести наш новый звук.

1
2
3
4
5
6
function spawnBullet(p) —:void
    local _bullet = Bullet.new(px, py)
    PlayState._bullets:insert(_bullet)
    PlayState._shoot = false
    media.playEventSound( PlayState.SoundBullet )
end

Теперь нам просто нужно изменить нашу функцию update (), чтобы получить некоторые стреляющие пули. Проверьте, является ли _shoot
истина и если _ship существует. Если это так, вызовите наш метод экземпляра getBulletSpawnPosition () для нашего корабля
и spawnBullet () в этом месте.

01
02
03
04
05
06
07
08
09
10
function update()
    if PlayState._inGame then
        if PlayState._shoot == true and PlayState._ship then
            local p = PlayState._ship:getBulletSpawnPosition()
            spawnBullet(p)
        end
         
        …
    end
end

В Flixel память о нас позаботилась. В Короне нам придется убирать объекты, которые мы
сделано с. Давайте добавим метод экземпляра kill () к каждой пуле, чтобы выручить нас. kill () это
функция, которую flixel автоматически вызывает, когда объект больше не нужен. Добавьте это к Bullet.lua:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
function Bullet(x, y)
    …
     
    function update()
        …
    end
     
    function Bullet:kill()
        Bullet.parent:remove( Bullet )
        Bullet = nil
    end
 
    …
end

Это правильный способ очистить объект в Короне. Во-первых, мы удалили все связи с ним, удалив
это из группы отображения. Затем мы устанавливаем переменную Bullet на ноль. Давайте сделаем то же самое для корабля.
Добавьте это ниже getBulletSpawnPosition () в Ship.lua:

1
2
3
4
5
function Ship:kill()
    Ship:removeSelf()
    Ship = nil
    Runtime:removeEventListener( «enterFrame», update )
end

Обратите внимание, что мы остановили обновление от вызова каждого кадра (так как корабль не существует
больше).

Вернемся к Bullet.lua, давайте добавим проверку каждого кадра, чтобы увидеть, находится ли пуля за пределами экрана.
В настоящее время наш код продолжает рисовать маркеры, даже когда они не видны. Это пустая трата ограниченных ресурсов устройства. Замените update () на это:

1
2
3
4
5
6
7
8
9
function update()
    if Bullet then
        if (Bullet.x < display.contentWidth) then
            Bullet.x = Bullet.x + 10
        else
            Bullet:kill()
        end
    end
end

Теперь пуля будет двигаться только вправо, если ее можно увидеть. В противном случае, это будет полезно
функция kill ().

У Lua есть автоматический сборщик мусора, который обрабатывает мусор как неиспользуемые переменные, отображает объекты, которые были удалены с экрана, и все остальное, что больше не нужно. Мы можем сказать Lua собирать мусор, добавив эту строку в конце create в нашем PlayState:

1
2
3
4
5
function create()
    …
     
    collectgarbage(«collect»)
end

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