В этом руководстве будет рассказано о портировании 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
|
Вывод
Это начинает выглядеть как игра сейчас. Наш корабль может передвигаться по экрану и стрелять пулями.
пули заботятся о себе, когда они больше не нужны. Теперь нам просто нужно несколько врагов.