Статьи

Создайте бесконечную игру для бегунов с нуля: фоновое движение

Добро пожаловать во второй урок из нашей серии, посвященный созданию игры с нуля с помощью Corona SDK. В этом разделе мы рассмотрим, как быстро получить и запустить базовый уровень фоновой прокрутки. Откройте ваш текстовый редактор и начнем!


Если вы еще не видели первый учебник из этой серии, проверьте его очень быстро, прежде чем переходить к этому. Если у вас есть небольшой опыт написания кода, то вы быстро пропустите весь первый шаг, но если у вас его нет, то стоит потратить время на изучение некоторых основ программирования работы с Lua и Corona SDK.


Итак, первое, что нам нужно рассмотреть, это файл build.settings. Это файл, который должен находиться в той же папке, что и файл main.lua, и который мы используем для установки некоторых спецификаций нашего приложения. Теперь, в зависимости от того, с какой платформой вы работаете (Android или iOS), ваш файл build.settings будет использоваться для установки разных вещей. Например, разработчикам Android приходится иметь дело с файлом манифеста своих приложений и своими разрешениями, в то время как разработчикам iOS нужно беспокоиться о файле * .plist. Файл build.settings — это место, где будут обрабатываться многие настройки уровня приложения. Одна из приятных особенностей Corona SDK заключается в том, что мы можем объединить наши данные build.settings для Android и iOS в один файл build.settings. Таким образом, если вы работаете на обоих типах устройств, вам не нужно управлять несколькими файлами build.settings. Для наших целей сейчас мы будем использовать только файл build.settings для установки ориентации нашего устройства. Итак, создайте новый файл с именем build.settings в той же папке, в которой будет находиться файл main.lua, и вставьте в него этот код:

1
2
3
4
5
6
7
8
9
—Notice you can use comments in this section as well
settings = {
orientation = {
default = «landscapeRight»,
supported = {
«landscapeRight», «landscapeLeft»
},
},
}

Это дает нам ландшафтно-ориентированное устройство на устройствах iOS и Android. Код довольно прост, и мы рассмотрим больше настроек, которые вы захотите использовать в будущих уроках, но пока это все, что нам нужно для продвижения вперед. Если вы хотите копать глубже самостоятельно, вы можете найти здесь все опции build.settings. Чтобы убедиться, что файл работает, давайте проведем быстрый тест в симуляторе. Откройте файл main.lua и поместите в него этот код:

1
2
3
4
5
6
—This line will remove the status bar at the top of screen,
—some apps you might want to keep it in, but not for games!
display.setStatusBar(display.HiddenStatusBar)
 
local testRect = display.newRect(0,0,50,50)
testRect:setFillColor(150,0,0);

Итак, после того, как вы собрали все это вместе, если ваш build.settings находится в правильном месте, у вас должно получиться что-то похожее на это:

Альбомная ориентация

Если устройство находится в вертикальном положении (портретный режим) и не лежит на боку, как указано выше (ландшафтный режим), значит, что-то пошло не так. Однако, надеюсь, что это не так, поскольку приведенный выше код — это все, что нужно сделать. Итак, теперь, когда это не так, мы можем приступить к запуску нашей игры. Цель каждого учебника — создать новый рабочий раздел. Каждый будет основываться на последнем, но после каждой итерации у вас будет что-то, что должно быть играбельным.


Первое, что мы хотим сделать, это чтобы наш уровень двигался. В нашей игре будет несколько слоев, которые будут прокручиваться на разных уровнях скорости. Это даст нам иллюзию того, что мы любим называть параллаксной прокруткой. Когда вы запустите программу в симуляторе, выберите iPhone на данный момент (если вы еще этого не сделали). Измените это, перейдя в Window> View As> iPhone в симуляторе. Ресурсы были разработаны для устройства, работающего с разрешением 480x320px. Конечно, есть способы убедиться, что он работает для всех разрешений, но для наших целей тестирования сейчас просто придерживайтесь симулятора iPhone. Идите вперед и вставьте этот код в вашу программу:

01
02
03
04
05
06
07
08
09
10
11
—takes away the display bar at the top of the screen
display.setStatusBar(display.HiddenStatusBar)
 
—adds an image to our game centered at x and y coordinates
local backbackground = display.newImage(«images/background.png»)
backbackground.x = 240
backbackground.y = 160
 
local backgroundfar = display.newImage(«images/bgfar1.png»)
backgroundfar.x = 480
backgroundfar.y = 160

Чтобы это работало, используйте изображения, которые я предоставил. Чтобы код работал «как есть», вам нужно будет поместить изображения в папку с названием images. Папка с изображениями должна находиться в той же папке, что и ваши файлы main.lua и build.settings. Как только вы это сделаете, вы должны получить экран, который выглядит следующим образом:

Заметьте, как фоновое изображение находится перед фоном? Представьте себе, сложив несколько тарелок друг на друга. Чем больше вы кладете, тем больше вы покрываете первую пластину, с которой вы начали все. Это также верно для изображений и текста, которые вы вставляете в свой код. Каждый раз, когда вы хотите добавить что-то на экран, если не указано иное, этот добавленный элемент будет охватывать все, что вы уже сложили. Давайте продолжим и добавим еще несколько вещей на наш экран. Добавьте это в свой код ниже других строк:

1
2
3
4
5
6
7
local backgroundnear1 = display.newImage(«images/bgnear2.png»)
backgroundnear1.x = 240
backgroundnear1.y = 160
 
local backgroundnear2 = display.newImage(«images/bgnear2.png»)
backgroundnear2.x = 760
backgroundnear2.y = 160

Теперь у нас должен быть другой слой, сложенный сверху так:

Теперь, когда вы посмотрите на это, вы заметите, что, хотя мы добавили 2 экземпляра bgnear2.png, только один из них отображается на экране. Причина, по которой мы это делаем, станет более очевидной, когда мы начнем все перемещать. А пока идите и переключите ваш взгляд с iPhone на iPhone 4 так же, как мы делали раньше. Поскольку мы разработали наши изображения с разрешением iPhone без сетчатки (например, 480x320px), если мы переключимся на более высокое разрешение, такое как iPhone 4 (например, 960x640px), все сжимается, и мы можем видеть больше происходящего. Измените разрешение на iPhone 4, и вы должны увидеть это сейчас:

Всякий раз, когда мы хотим увидеть, что происходит, переключение на более широкое представление будет очень полезным. Обратите внимание, что то, что вы ничего не видите, не означает, что ничего не происходит. Теперь, когда у нас есть эти фоновые изображения, давайте заставим их двигаться. Проходя учебники, я постараюсь задокументировать все, что происходит внутри самого кода, поэтому, если вы когда-либо не уверены в том, что что-то делает, обязательно прочитайте комментарии в коде! Теперь, под кодом, добавленным ранее, добавьте это:

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
—the update function will control most everything that happens in our game
—this will be called every frame(30 frames per second in our case, which is the Corona SDK default)
local function update( event )
—updateBackgrounds will call a function made specifically to handle the background movement
updateBackgrounds()
end
 
function updateBackgrounds()
—far background movement
backgroundfar.x = backgroundfar.x — (.25)
 
—near background movement
backgroundnear1.x = backgroundnear1.x — (3)
—if the sprite has moved off the screen move it back to the
—other side so it will move back on
if(backgroundnear1.x < -239) then
backgroundnear1.x = 760
end
 
backgroundnear2.x = backgroundnear2.x — (3)
if(backgroundnear2.x < -239) then
backgroundnear2.x = 760
end
end
 
—this is how we call the update function, make sure that this line comes after the
—actual function or it will not be able to find it
—timer.performWithDelay(how often it will run in milliseconds, function to call,
—how many times to call(-1 means forever))
timer.performWithDelay(1, update, -1)

Когда вы запустите эту программу, сначала запустите ее, оставаясь в представлении iPhone 4. Это позволит вам увидеть, что на самом деле происходит со всеми различными спрайтами, которые мы перемещаем. После того, как вы посмотрите на это в этом представлении, вернитесь к обычному представлению iPhone, чтобы вы могли увидеть, как оно действительно выглядит. Со всем этим теперь у вас должно быть все движущееся! Теперь пара вещей, на которые нужно посмотреть, прежде чем мы продолжим. Обратите внимание, что существует только один экземпляр backgroundfar, но есть два экземпляра backgroundnear? Это то, что полностью зависит от вас. Причина в том, что их два, потому что для более близкого фона (например, пролетающих деревьев) мы хотим, чтобы это повторялось снова и снова. Для дальнего фона я хотел, чтобы это было что-то, что будет проходить очень медленно и не повторяться (например, вы обычно делаете это только для фонов, которые находятся далеко назад, это дает уровням уникальное ощущение). Это то, что вы будете делать, когда вашему уровню прокрутки придет конец. Если вы действительно хотели бесконечный скроллер, вы бы хотели использовать тот же подход, что и для ближнего фона, и поместить два рядом. Размещая два рядом, или три, или сколько угодно, это позволит вам создать, казалось бы, большие миры с минимальными активами. Единственное, что нам нужно сделать, это переместить спрайт обратно на другую сторону экрана.

Следующее, что мы собираемся сделать, это добавить землю. Это займет еще пару шагов, но мы пойдем медленно и позаботимся о том, чтобы все было правильно расположено. Если вы не прошли первый урок и не понимаете, как использовать группы или не понимаете, что они из себя представляют, сейчас самое время проверить это. Вот код для добавления (поместите его прямо под кодом, где мы установили backgroundnear2, перед функцией обновления):

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
—create a new group to hold all of our blocks
local blocks = display.newGroup()
 
—setup some variables that we will use to position the ground
local groundMin = 420
local groundMax = 340
local groundLevel = groundMin
 
—this for loop will generate all of your ground pieces, we are going to
—make 8 in all.
for a = 1, 8, 1 do
isDone = false
 
—get a random number between 1 and 2, this is what we will use to decide which
—texture to use for our ground sprites.
—pieces so it seems like the ground goes on forever.
—textures as you want.
—up the number in math.random(x) to however many textures you have.
numGen = math.random(2)
local newBlock
print (numGen)
if(numGen == 1 and isDone == false) then
newBlock = display.newImage(«images/ground1.png»)
isDone = true
end
 
if(numGen == 2 and isDone == false) then
newBlock = display.newImage(«images/ground2.png»)
isDone = true
end
 
—now that we have the right image for the block we are going
—to give it some member variables that will help us keep track
—of each block as well as position them where we want them.
newBlock.name = («block» .. a)
newBlock.id = a
 
—because a is a variable that is being changed each run we can assign
—values to the block based on a.
—be positioned the width of a block apart.
newBlock.x = (a * 79) — 79
newBlock.y = groundLevel
blocks:insert(newBlock)
end

Комментарии в коде должны помочь вам понять, что все делает до сих пор. Надеюсь, то, на что вы сейчас смотрите, выглядит примерно так:

Форт Фон

Теперь, последнее, что нам нужно сделать, это начать движение блоков. Давайте продолжим и добавим код для этого. Поместите этот код после окончания функции обновления:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
function updateBlocks()
for a = 1, blocks.numChildren, 1 do
 
if(a > 1) then
newX = (blocks[a — 1]).x + 79
else
newX = (blocks[8]).x + 79
end
 
if((blocks[a]).x < -40) then
(blocks[a]).x, (blocks[a]).y = newX, groundLevel
else
(blocks[a]):translate(-5, 0)
end
 
end
end

Прежде чем что-либо действительно будет двигаться, мы должны быть уверены, что мы на самом деле вызываем функцию из функции обновления. Итак, внутри функции обновления ниже строки updateBackgrounds () вызовите функцию updateBlocks (). С этим там у нас должно быть все движение сейчас. Обратите внимание, что вместо ручного перемещения блоков мы использовали функцию перевода. Эта функция доступна для любого объекта, который мы создаем с помощью вызова newImage. Любой метод будет работать, поэтому используйте один или другой, или используйте смесь. Еще одна вещь, которую вы должны заметить, это то, что между некоторыми блоками есть промежуток. Это происходит потому, что мы устанавливаем местоположение новых блоков прямо рядом со старым местоположением последнего блока. Проблема в том, что когда мы делаем это таким образом, мы пытаемся поместить его рядом с движущимся объектом, поэтому мы не всегда можем попасть прямо рядом с ним. Эта проблема станет еще больше, если мы попытаемся увеличить скорость перемещения блоков, чем быстрее они движутся, тем больше будет зазор. Эту проблему можно решить, просто добавив переменную скорости и выполнив наши расчеты на основе этой скорости. Вернитесь к строкам кода, где мы инициализировали переменные groundMin и groundMax , и добавьте следующее:

1
local speed = 5;

Затем замените функции updateBlocks () и updateBackgrounds () на эти:

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
function updateBlocks()
for a = 1, blocks.numChildren, 1 do
 
if(a > 1) then
newX = (blocks[a — 1]).x + 79
else
newX = (blocks[8]).x + 79 — speed
end
 
if((blocks[a]).x < -40) then
(blocks[a]).x, (blocks[a]).y = newX, (blocks[a]).y
else
(blocks[a]):translate(speed * -1, 0)
end
 
end
end
 
function updateBackgrounds()
—far background movement
backgroundfar.x = backgroundfar.x — (speed/55)
 
—near background movement
backgroundnear1.x = backgroundnear1.x — (speed/5)
if(backgroundnear1.x < -239) then
backgroundnear1.x = 760
end
 
backgroundnear2.x = backgroundnear2.x — (speed/5)
if(backgroundnear2.x < -239) then
backgroundnear2.x = 760
end
end

Благодаря этим обновленным функциям ваши блоки должны двигаться без пробелов!

Еще одна вещь, которая сделает игру более похожей на игру. Вернитесь в функцию обновления и вставьте эту строку кода после функций обновления:

1
speed = speed + .05

Как только это произойдет, вы сможете наблюдать за ускорением уровня на ваших глазах! Точное значение, конечно, является лишь примером того, насколько быстро вы обновите игру, будет зависеть от вашей игры и вашей аудитории. Все, что осталось на данный момент, это добавить героя и некоторые препятствия, и мы сами играем. Это будет позже, хотя, поскольку это завершает проект на данный момент. Выполнение всего этого было не так уж и плохо, и мы продолжим добавлять множество функциональных возможностей в каждой итерации. Обязательно зайдите на следующий урок, и если у вас есть какие-либо вопросы или пожелания, дайте мне знать в комментариях ниже!