Статьи

Как построить Dodge с помощью Corona SDK

Что вы будете создавать

В этом уроке вы узнаете, как создать, Highway Dodge , простую, но захватывающую игру. Шоссе Dodge легко подобрать и играть, но оно обеспечивает захватывающее качество других популярных игр в App Store.

Шоссе Dodge начинается с размещения игрока в гоночной машине на шоссе. Во время игры игрок должен увернуться от встречного движения на шоссе, нажав на одну из трех доступных полос движения. За каждую уклоненную машину игрок получает одно очко, и игра заканчивается, когда встречная машина врезается в гоночную машину. Со временем встречные машины выходят все быстрее и быстрее, чтобы создать настоящий вызов для игрока.

Эта игра построена с использованием Lua и Corona SDK. Как минимум, вам необходимо иметь учетную запись в Corona Labs и установленном Corona SDK. Вы можете бесплатно загрузить Corona SDK на веб-сайте Corona Labs . Для этого урока я использовал сборку 2015.2731 Corona SDK.

Давайте сразу же запустим Highway Dodge, создав пустой шаблон. Откройте Corona Simulator и выберите « Новый проект» в меню « Файл» . Открыв окно « Создать новый проект» , введите « Уклон шоссе» в качестве имени проекта, выберите пустой шаблон и установите ширину 400 и высоту 600 . Оставьте стандартную ориентацию установленной вертикально .

Создать новый проект

После настройки проекта загрузите изображения для Highway Dodge. Создайте новую папку в вашем новом проекте, назовите ее images и добавьте изображения в эту папку. Ваш проект должен теперь выглядеть так:

Быстрый взгляд на проект

После настройки проекта давайте кратко рассмотрим два важных файла: build.settings и config.lua .

Этот файл обрабатывает свойства времени сборки игры. Он хранит информацию об ориентации вашего приложения, информацию о значках, настройках iOS и настройках Android. Настройка по умолчанию подходит для нашей игры.

Этот файл конфигурации управляет свойствами игры во время выполнения. Это включает в себя width , height , scale , fps (кадров в секунду) и imageSuffix . Свойство, на которое нам нужно взглянуть, это imageSuffix . Свойство imageSuffix используется для динамического выбора изображения. В двух словах, оно говорит приложению использовать изображение с более высоким разрешением на устройстве с более высоким разрешением.

Я предоставил изображения с высоким разрешением в папке images, поэтому нам нужно соответствующим образом обновить config.lua . Файл config.lua вашего проекта должен выглядеть так, как показано ниже. Я пропустил раздел push-уведомлений, который закомментирован.

01
02
03
04
05
06
07
08
09
10
11
12
application = {
    content = {
        width = 400,
        height = 600,
        scale = «letterBox»,
        fps = 30,
         
        imageSuffix = {
            [«@2x»] = 2,
        },
    },
}

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

Первая строка скрывает строку состояния на устройствах iOS. Откройте main.lua и добавьте следующую строку после комментария -- Your code here .

1
display.setStatusBar( display.HiddenStatusBar )

Далее мы начинаем использовать композитор, требуя библиотеки в нашей игре. Мы делаем это, добавив следующую строку:

1
local composer = require( «composer» )

Далее мы используем composer для перехода к сцене меню. Мы перемещаем сцены, вызывая функцию composer.gotoScene() , передавая значение "scene_menu" в качестве параметра. Значение "scene_menu" — это имя сцены и имя файла, который мы создадим в следующем разделе.

1
composer.gotoScene(«scene_menu»)

Composer — официальная библиотека управления сценой Corona. Composer позволяет разработчикам легко создавать сцены и переходы между сценами. В две строки я смог перейти от основной сцены к сцене меню. Если вы хотите узнать больше о Composer, посетите Руководство библиотеки Comonaser, доступное на веб-сайте Corona Labs Docs .

Сцена меню нашей игры будет состоять только из пары элементов. Сцена будет содержать фоновый рисунок, заголовок и кнопку запуска. Мы будем использовать встроенную библиотеку виджетов Corona для создания кнопки запуска. Библиотека виджетов позволяет нам быстро и легко создавать общие элементы пользовательского интерфейса. В нашей игре мы будем использовать ее только для создания кнопок.

В папке проекта Highway Dodge создайте новый файл scene_menu.lua и откройте его в любом текстовом редакторе. Вместо того, чтобы начинать с нуля, мы собираемся использовать шаблон сцены, доступный на веб-сайте Corona Labs Docs. С их шаблоном мы сможем двигаться намного быстрее. Перейдите в Документы Corona Labs и скопируйте / вставьте шаблон сцены в scene_menu.lua .

Добавьте библиотеку виджетов в нашу игру, добавив следующую строку, чуть ниже библиотеки композитора.

1
local widget = require( «widget» )

Мы добавляем наш фон, используя загруженную ранее графику. Фон должен быть расположен в центре экрана. В функции scene:create() и после screenGroup переменной screenGroup добавьте следующий код:

1
2
3
local background = display.newImageRect(sceneGroup, «images/background.png», 475, 713)
background.x = display.contentCenterX
background.y = display.contentCenterY

Далее нам нужно добавить три полосы, которые представляют шоссе. Мы делаем это, используя таблицу для хранения дорожек и создавая цикл for который выполняется три раза. Поместите этот фрагмент после фонового рисунка:

1
2
3
4
5
6
local lanes = {}
for i=1,3 do
    lanes[i] = display.newImageRect(sceneGroup, «images/lane.png», 79, 713)
    lanes[i].x = (display.contentCenterX — 79*2) + (i*80)
    lanes[i].y = display.contentCenterY
end

Чтобы полосы были всегда центрированы, я разместил полосы на оси X, используя немного математики. Это гарантирует, что дорожки остаются в центре, независимо от устройства, на котором работает игра.

Мы также добавляем наш логотип для Highway Dodge, размещая изображение объекта в верхней части экрана.

1
2
3
local logo = display.newImageRect(sceneGroup, «images/logo.png», 300, 150)
logo.x = display.contentCenterX
logo.y = 75

Прежде чем мы сможем добавить наш виджет кнопки, нам нужно создать функцию, которая реагирует на нажатие кнопки. Мы handleButtonEvent() функцию handleButtonEvent() и переместим игрока на игровую сцену с помощью composer.gotoScene() . Мы ответим только тогда, когда игрок снял палец с кнопки или на ended фазе события.

1
2
3
4
5
local function handleButtonEvent( event )
    if ( «ended» == event.phase ) then
        composer.gotoScene(«scene_game», «slideLeft»)
    end
end

С добавленной функцией мы можем добавить кнопку. Мы создаем кнопку, используя widget.newButton и передавая ей некоторые значения. Мы указываем ширину и высоту кнопки, файлы изображений для кнопки, отображаемый текст, тип шрифта, размер шрифта и цвет шрифта.

Мы также сообщаем Corona, какую функцию вызывать при нажатии кнопки, и помещаем ее в середину экрана. Исходные файлы этого руководства содержат комментарии, объясняющие каждое из значений, используемых для настройки кнопки.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
local btn_startPlaying = widget.newButton {
    width = 220,
    height = 100,
    defaultFile = «images/btn-blank.png»,
    overFile = «images/btn-blank-over.png»,
    label = «Start Playing»,
    font = system.defaultFontBold,
    fontSize = 32,
    labelColor = { default={ 0, 0, 0 }, over={ 0, 0, 0, 0.5 } },
    onEvent = handleButtonEvent
}
btn_startPlaying.x = display.contentCenterX
btn_startPlaying.y = display.contentCenterY
sceneGroup:insert(btn_startPlaying)

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

Чтобы удалить предыдущую сцену, мы получаем имя сцены и вызываем composer.removeScene() чтобы удалить ее, если она существует. Добавьте следующий код на scene:show() функция scene:show() .

1
2
3
4
local prevScene = composer.getSceneName( «previous» )
if(prevScene) then
    composer.removeScene(prevScene)
end

Теперь мы можем начать работать над игровой сценой. Мы будем использовать тот же рабочий процесс, который мы использовали для создания сцены меню. Создайте новый файл scene_game.lua и скопируйте / вставьте шаблон сцены, доступный в Документах Corona Labs . Как только у вас есть код, откройте scene_game.lua в вашем любимом текстовом редакторе.

Чтобы упростить нам кодирование игровой сцены, мы собираемся использовать библиотеку виджетов и библиотеку физики. Последний используется для обнаружения столкновений. Добавьте следующий код в scene_game.lua :

1
2
3
4
local widget = require( «widget» )
local physics = require(«physics»)
physics.start()
physics.setGravity(0,0)

В первой и второй строках нам требуются библиотека виджетов и библиотека физики соответственно. Затем мы начинаем физику и отключаем гравитацию. Нам не нужна гравитация для нашей игры на шоссе, вместо этого мы будем использовать transition.to() для перемещения машин.

В функции scene:create() мы объявляем ряд переменных, которые будем использовать в игре. Эти переменные будут отвечать за машину игрока, полосы движения, машины противника и очки игрока. Чтобы было легче читать, я добавил несколько комментариев.

01
02
03
04
05
06
07
08
09
10
11
12
— «scene:create()»
function scene:create( event )
    local lanes = {} — create a table called lanes
    local playerCar — a variable for the player car
    local laneID = 1 — a variable for the land id
    local enemyCars = {} — a table to hold the enemy cars
    local enemyCounter = 1 — start the enemy counter at 1 to keep track of the enemy cars
    local sendEnemyFrequency = 2500 — defines how often to send enemy cars in milliseconds
    local tmrToSendCars — a variable to hold a reference to the timer of sending cars
    local playerScore = 0 — start the player score at 0
    local playerScoreText — an object to hold the score text object
end

Ниже объявлений переменных мы устанавливаем функции для игры. Мы реализуем каждую функцию на следующем шаге. Добавьте следующий код после объявления переменных в scene:create() функция scene:create() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
local function incrementScore()
end
 
local function moveCar(event)
end
 
local function sendEnemyCar()
end
 
local function onPlayAgainTouch()
end
 
local function onGlobalCollision(event)
end

После функций добавляем фон и полосы. Для дорожек мы прикрепляем прослушиватель событий к каждой полосе, чтобы они реагировали на сенсорные события. При касании слушатель вызывает moveCar() .

01
02
03
04
05
06
07
08
09
10
11
local background = display.newImageRect(sceneGroup, «images/background.png», 475, 713)
    background.x = display.contentCenterX
    background.y = display.contentCenterY
 
for i=1,3 do
    lanes[i] = display.newImageRect(sceneGroup, «images/lane.png», 79, 713)
        lanes[i].x = (display.contentCenterX — 79*2) + (i*80)
        lanes[i].y = display.contentCenterY
        lanes[i].id = i
        lanes[i]:addEventListener(«touch», moveCar)
end

С заданными фоном и полосами пора создать текстовый объект, чтобы держать счет игрока и создать автомобиль игрока. Счет будет в верхней части экрана, а автомобиль игрока будет расположен на самой левой полосе. Кроме того, мы сделаем автомобиль игрока динамическим физическим объектом.

01
02
03
04
05
06
07
08
09
10
playerScoreText = display.newText(sceneGroup, «Score: «..playerScore, 0, 0, native.systemFont, 36)
    playerScoreText.x = display.contentCenterX
    playerScoreText.y = 25
 
playerCar = display.newImageRect(sceneGroup, «images/playerCar.png», 50, 100)
    playerCar.anchorY = 1
    playerCar.x = lanes[1].x
    playerCar.y = display.contentHeight
    physics.addBody(playerCar)
    playerCar.bodyType = «dynamic»

Затем мы устанавливаем таймер для отправки автомобиля на основе значения переменной sendEnemyFrequency и создаем прослушиватель событий времени выполнения для глобальных коллизий.

1
2
tmrToSendCars = timer.performWithDelay(sendEnemyFrequency, sendEnemyCar, 0)
Runtime:addEventListener( «collision», onGlobalCollision)

Мы наконец можем добавить функциональность в нашу игру. В этом разделе будет добавлен дополнительный код для каждой функции, которую мы объявили в предыдущем разделе. Мы onGlobalCollision() с incrementScore() и onGlobalCollision() .

Эта функция вызывается, когда вражеская машина проходит мимо игрока и переход завершен. При вызове счет игрока увеличивается на 1 . Добавьте следующую реализацию в функцию incrementScore() .

1
2
3
4
5
— This function will increment the player score by 1. This function is called when the transition for the enemy car is complete and is off screen.
local function incrementScore()
    playerScore = playerScore + 1
    playerScoreText.text = «Score: «..playerScore
end

Функция moveCar() вызывается при касании полосы. На ended фазе события мы берем идентификатор полосы движения и перемещаем машину в нужную полосу. В конце мы возвращаем true чтобы указать на успешное сенсорное событие. Добавьте следующую реализацию в moveCar() .

1
2
3
4
5
6
7
8
— moveCar will respond to the touch event on the lanes
local function moveCar(event)
    if(event.phase == «ended») then
        laneID = event.target.id — grab the lane id which will be 1, 2, or 3
        transition.to(playerCar, {x=lanes[laneID].x,time=50}) — move the player car to the appropriate lane
        return true — to indicate a successful touch event, return true
    end
end

Функция sendEnemyCar() создает вражескую машину, назначает машину на полосу движения, прикрепляет к ней физическое тело и использует transition.to() чтобы отправить машину к нижней части экрана. Для начала давайте создадим объект вражеской машины.

1
enemyCars[enemyCounter] = display.newImageRect(sceneGroup, «images/enemyCar»..math.random(1,3)..».png», 50, 100)

Далее мы устанавливаем координаты x и y вражеской машины. Мы также создаем оператор if-then чтобы назначить вражеский автомобиль той же полосе, в которой автомобиль игрока находится в 50% случаев. Это заставит игрока двигаться чаще и сделает игру веселее.

1
2
3
enemyCars[enemyCounter].x = lanes[math.random(1,#lanes)].x — place the car on a random lane
  if(math.random(1,2) == 1) then enemyCars[enemyCounter].x = lanes[laneID].x;
enemyCars[enemyCounter].y = -125 — place the enemy off screen at the top

Нам также нужно повернуть вражескую машину так, чтобы она была обращена вниз, и добавить к телу кинематическую физику.

1
2
3
enemyCars[enemyCounter]:scale(1,-1) — rotate the cars so they are facing down
physics.addBody(enemyCars[enemyCounter]) — add a physics body to enemy cars
enemyCars[enemyCounter].bodyType = «kinematic» — make the bodies kinematic

Когда вражеская машина настроена, мы используем функцию transition.to() чтобы отправить вражескую машину по одной из полос движения. Когда переход завершен, игра вызывает функцию для удаления объекта. Я также увеличиваю переменную enemyCounter на 1, чтобы отслеживать количество машин противника в игре.

1
2
3
transition.to(enemyCars[enemyCounter], {y=display.contentHeight+enemyCars[enemyCounter].height+20, time=math.random(2250,3000), onComplete=function(self) display.remove(self); incrementScore(); end}) — a transition that moves the enemy car towards the bottom of the screen.
 
enemyCounter = enemyCounter + 1 — increase enemy counter by one for tracking

Наконец, мы хотим, чтобы игра стала быстрее. Для каждой отправляемой машины игра воссоздает таймер и устанавливает его на 200 мс быстрее.

1
2
3
4
5
6
if(enemyCounter%2 == 0) then
    sendEnemyFrequency = sendEnemyFrequency — 200
    if(sendEnemyFrequency < 800) then sendEnemyFrequency = 800;
    timer.cancel(tmrToSendCars)
    tmrToSendCars = timer.performWithDelay(sendEnemyFrequency, sendEnemyCar, 0)
end

Самая короткая функция onPlayAgainTouch() возвращает проигрыватель в главное меню. Вот как выглядит реализация функции onPlayAgainTouch() .

1
2
3
4
— Allow the player to return to the menu
local function onPlayAgainTouch()
    composer.gotoScene(«scene_menu», «fade») — move player to menu
end

Функция onGlobalCollision() используется для обнаружения столкновений между любыми двумя физическими объектами на экране. В нашей игре у нас есть только два типа физических объектов:

  • машина игрока
  • вражеская машина

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

Сначала мы создаем оператор if-then который прослушивает начальную фазу.

1
2
3
if(event.phase == «began») then
 
end

В заявлении if-then мы останавливаем игру. Мы приостанавливаем все переходы, отменяем таймер, приостанавливаем физику (даже если она не нужна, это хороший вызов очистки) и удаляем прослушиватель событий из всех трех полос. Добавьте следующий код внутри оператора if-then :

1
2
3
4
5
6
7
transition.pause()
timer.cancel(tmrToSendCars)
physics.pause()
 
for i=1,3 do
    lanes[i]:removeEventListener(«touch», moveCar)
end

После того, как игровая механика остановилась, мы добавляем непрозрачный прямоугольник и текстовый объект с надписью «Game Over!» , Это дает хорошее визуальное представление о том, что игра окончена.

01
02
03
04
05
06
07
08
09
10
local gameOverBackground = display.newRect(sceneGroup, 0, 0, display.actualContentWidth, display.actualContentHeight)
    gameOverBackground.x = display.contentCenterX
    gameOverBackground.y = display.contentCenterY
    gameOverBackground:setFillColor(0)
    gameOverBackground.alpha = 0.5
 
local gameOverText = display.newText( sceneGroup, «Game Over!», 100, 200, native.systemFontBold, 36 )
    gameOverText.x = display.contentCenterX
    gameOverText.y = 150
    gameOverText:setFillColor( 1, 1, 1 )

Чтобы завершить глобальную функцию столкновения, мы добавляем кнопку воспроизведения еще раз, которая возвращает игрока в меню.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
local playAgain = widget.newButton {
    width = 220,
    height = 100,
    defaultFile = «images/btn-blank.png»,
    overFile = «images/btn-blank.png»,
    label = «Menu»,
    font = system.defaultFontBold,
    fontSize = 32,
    labelColor = { default={ 0, 0, 0 }, over={ 0, 0, 0, 0.5 } },
    onEvent = onPlayAgainTouch
}
playAgain.x = display.contentCenterX
playAgain.y = gameOverText.y + 100
sceneGroup:insert(playAgain)

Запустите приложение, чтобы увидеть, все ли работает как положено. Вы должны быть в состоянии играть в игру, вражеские машины должны появиться в верхней части экрана, и ваш счет должен увеличиваться для каждой вражеской машины, которую вы успешно уклоняетесь.

Спасибо за чтение моего руководства по созданию Highway Dodge с помощью Corona SDK. Вы можете скачать исходные файлы для этой игры с GitHub . Найдите время, чтобы подумать, как вы можете улучшить игру. Если вы ищете некоторые предложения, я бы порекомендовал добавить еще одну линию, добавить больше типов врагов и даже добавить несколько интересных бонусов.