Статьи

Corona SDK: создайте игру «Баланс пинг-понг»

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


Обзор приложения

Используя готовую графику, мы создадим развлекательную игру с использованием Lua и API Corona SDK. Игрок сможет перемещать ракетку для пинг-понга по экрану, чтобы ударить по мячу. Вы можете изменить параметры в коде, чтобы настроить игру.


Целевое устройство

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

Платформа iOS предъявляет следующие требования:

  • iPad 1/2 / Mini: 1024px x 768px, 132 ppi
  • Сетчатка iPad: 2048px x 1536px, 264 ppi
  • iPhone / iPod Touch: 320px x 480px, 163 ppi
  • Сетчатка iPhone / iPod: 960 x 640 пикселей, 326 ppi
  • iPhone 5 / iPod Touch: 1136 пикселей x 640 пикселей, 326 пикселей на дюйм

Поскольку Android является открытой платформой, существует множество различных устройств и разрешений. Вот некоторые из наиболее распространенных характеристик экрана:

  • Asus Nexus 7 Tablet: 800px x 1280px, 216 ppi
  • Motorola Droid X: 854 x 480 пикселей, 228 ppi
  • Samsung Galaxy SIII: 720px x 1280px, 306 ppi

В этом уроке мы сосредоточимся на платформе iOS с точки зрения графики. В частности, мы будем разрабатывать для iPhone и iPod touch. Тем не менее, код этого руководства также может быть использован, если вы ориентируетесь на платформу Android.


Интерфейс

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


Экспорт графики

В зависимости от выбранного устройства вам может потребоваться преобразовать графику в рекомендованное разрешение (ppi), что вы можете сделать в своем любимом графическом редакторе. Я использовал параметр « Настроить размер …» в меню « Инструменты» приложения «Просмотр» в OS X. Не забудьте дать изображениям описательное имя и сохранить их в папке проекта.


Мы будем использовать файл конфигурации config.lua , чтобы приложение работало на всех устройствах в полноэкранном режиме. Файл конфигурации показывает исходный размер экрана и метод, используемый для масштабирования содержимого в случае, если приложение запускается с другим разрешением.

1
2
3
4
5
6
7
8
9
application =
{
    content =
    {
        width = 320,
        height = 480,
        scale = «letterbox»
    },
}

Давайте напишем фактическое приложение. Откройте предпочитаемый вами редактор Lua. Подойдет любой текстовый редактор, но рекомендуется использовать текстовый редактор с подсветкой синтаксиса. Создайте новый файл и сохраните его как main.lua в папке вашего проекта.


Мы будем структурировать наш код, как если бы он был классом. Если вы знакомы с ActionScript или Java, вы должны найти структуру проекта знакомой.

01
02
03
04
05
06
07
08
09
10
11
Necesary Classes
 
Variables and Constants
 
Declare Functions
 
    contructor (Main function)
 
    class methods (other functions)
 
call Main function

1
display.setStatusBar(display.HiddenStatusBar)

Этот фрагмент кода скрывает строку состояния. Строка состояния — это строка в верхней части экрана устройства, на которой отображаются время, сигнал и другие индикаторы.


Фон

Простой фон для пользовательского интерфейса приложения. Фрагмент кода ниже рисует фон на экране.

1
2
3
4
5
— Graphics
 
— [Background]
 
local bg = display.newImage(‘bg.png’)

Заголовок

Это заголовок. Это первый интерактивный экран, который появится в нашей игре. Эти переменные хранят его компоненты.

1
2
3
4
5
6
— [Title View]
 
local title
local playBtn
local creditsBtn
local titleView

Вид кредитов

Представление «Кредиты» отображает кредиты и авторские права на приложение. Эта переменная используется для ее хранения.

1
2
3
— [CreditsView]
 
local creditsView

инструкции

В начале игры появится сообщение с инструкциями, которое исчезнет после первого нажатия.

1
2
3
— Instructions
 
local ins

весло

Это рисунок для пинг-понга. Он будет размещен в центре просмотра.

1
2
3
4
5
— Paddle
 
local paddle
local paddleTop
local paddleBottom

Мяч

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

1
2
3
— Ball
 
local ball

бдительный

Предупреждение отображается, когда игрок пропускает мяч и игра окончена. Он отображает сообщение и заканчивает игру.

1
2
3
— Alert
 
local alertView

Звуки

Мы будем использовать звуковые эффекты, чтобы оживить игру. Звуки, используемые в этой игре, можно найти в freesound , базе данных лицензированных звуков Creative Commons.

1
2
3
— Sounds
 
local pong = audio.loadSound(‘pong.mp3’)

Следующий фрагмент кода показывает переменные, которые мы будем использовать. Прочитайте комментарии, чтобы понять, для чего используется каждая переменная.

1
2
3
4
5
6
7
8
— Variables
 
local scoreTF — score textfield
local scale = 1.1 — initial ball scale
local variation = 0.05 — controls the scale variation
local ballX = 0
local ballY = 0
local ballVar = 0.5 — ball variation

Объявите все функции как local в начале.

01
02
03
04
05
06
07
08
09
10
11
12
13
— Functions
 
local Main = {}
local startButtonListeners = {}
local showCredits = {}
local hideCredits = {}
local showGameView = {}
local gameListeners = {}
local startGame = {}
local hitTestObjects = {}
local update = {}
local movePaddle = {}
local alert = {}

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

1
2
3
function Main()
  — code…
end

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

1
2
3
4
5
6
7
8
function Main()
  titleBg = display.newImage(‘titleBg.png’, 26, 72)
  playBtn = display.newImage(‘playBtn.png’, 125, 225)
  creditsBtn = display.newImage(‘creditsBtn.png’, 115, 290)
  titleView = display.newGroup(titleBg, playBtn, creditsBtn)
 
  startButtonListeners(‘add’)
end

Следующая функция добавляет необходимых слушателей к TitleView .

1
2
3
4
5
6
7
8
9
function startButtonListeners(action)
  if(action == ‘add’) then
    playBtn:addEventListener(‘tap’, showGameView)
    creditsBtn:addEventListener(‘tap’, showCredits)
  else
    playBtn:removeEventListener(‘tap’, showGameView)
    creditsBtn:removeEventListener(‘tap’, showCredits)
  end
end

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

1
2
3
4
5
6
7
8
9
function showCredits:tap(e)
  playBtn.isVisible = false
  creditsBtn.isVisible = false
  creditsView = display.newImage(‘credits.png’, 0, display.contentHeight)
 
  lastY = titleBg.y
  transition.to(titleBg, {time = 300, y = (display.contentHeight * 0.5) — (titleBg.height + 50)})
  transition.to(creditsView, {time = 300, y = (display.contentHeight * 0.5) + 35, onComplete = function() creditsView:addEventListener(‘tap’, hideCredits) end})
end

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

1
2
3
4
function hideCredits:tap(e)
  transition.to(creditsView, {time = 300, y = display.contentHeight + 25, onComplete = function() creditsBtn.isVisible = true playBtn.isVisible = true creditsView:removeEventListener(‘tap’, hideCredits) display.remove(creditsView) creditsView = nil end})
  transition.to(titleBg, {time = 300, y = lastY});
end

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

1
2
function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners(‘rmv’) display.remove(titleView) titleView = nil bg:addEventListener(‘tap’, startGame) end})

Следующий фрагмент кода добавляет сообщение с инструкцией.

1
ins = display.newImage(‘ins.png’, 112, 370)

Далее мы добавляем части весла. Это разделено на две части для лучшего обнаружения столкновения.

1
2
3
4
5
— Paddle
 
  paddleBottom = display.newImage(‘paddleBottom.png’, 138.6, 262)
  paddleTop = display.newImage(‘paddleTop.png’, 110, 166)
  paddle = display.newGroup(paddleBottom, paddleTop)

Добавляем шар и устанавливаем его масштаб.

1
2
3
4
— Ball
 
  ball = display.newImage(‘ball.png’, 146, 201)
  ball:scale(scale, scale)

Мы создаем текстовое поле счета в правом верхнем углу сцены.

1
2
3
4
5
6
7
8
— Score
 
  score = display.newImage(‘score.png’, 260, 0)
  scoreTF = display.newText(‘0’, 278, 21, ‘Marker Felt’, 30)
  scoreTF:setTextColor(238, 238, 238)
 
  gameListeners(‘add’)
end

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

1
2
3
4
5
6
7
8
function gameListeners(action)
  if(action == ‘add’) then
    bg:addEventListener(‘touch’, movePaddle)
  else
    bg:removeEventListener(‘touch’, movePaddle)
    Runtime:removeEventListener(‘enterFrame’, update)
  end
end

Функция startGame удаляет сообщение с инструкциями и добавляет слушателя в основную функцию игры. В функции случайная позиция y выбирается из ранее созданной таблицы и впоследствии добавляет физику к вновь созданному объекту. Мы добавляем слушателя столкновений каждому врагу, а также добавляем его в таблицу врагов.

1
2
3
4
5
function startGame()
  display.remove(ins)
  bg:removeEventListener(‘tap’, startGame)
  Runtime:addEventListener(‘enterFrame’, update)
end

Для обнаружения столкновений без использования физики мы используем замечательную функцию, которую вы можете найти на веб-сайте Corona Labs Code Exchange .

1
2
3
4
5
6
7
function hitTestObjects(obj1, obj2)
        local left = obj1.contentBounds.xMin <= obj2.contentBounds.xMin and obj1.contentBounds.xMax >= obj2.contentBounds.xMin
        local right = obj1.contentBounds.xMin >= obj2.contentBounds.xMin and obj1.contentBounds.xMin <= obj2.contentBounds.xMax
        local up = obj1.contentBounds.yMin <= obj2.contentBounds.yMin and obj1.contentBounds.yMax >= obj2.contentBounds.yMin
        local down = obj1.contentBounds.yMin >= obj2.contentBounds.yMin and obj1.contentBounds.yMin <= obj2.contentBounds.yMax
        return (left or right) and (up or down)
end

Функция update , показанная ниже, запускает каждый кадр. В этой функции мы сначала масштабируем шар, основываясь на значениях, установленных переменными.

1
2
3
4
5
function update()
  — Scale Balls
  scale = scale — variation
  ball.xScale = scale
  ball.yScale = scale

Эта часть увеличивает размер шара, чтобы имитировать увеличение его высоты.

1
2
3
4
— Raising
  if(math.floor(ball.xScale * 10) >= 15) then
    variation = 0.05
  end

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

1
2
3
4
— Missed
  if(math.floor(ball.xScale * 10) < 3) then
    alert()
  end

В следующем фрагменте кода мы перемещаем мяч. В зависимости от значения переменных он перемещается вверх или вниз.

1
2
3
4
— Move Ball
 
  ball.x = ball.x — ballX
  ball.y = ball.y — ballY

Шкала устанавливается на одинаковую высоту весла, что означает, что мяч коснулся весла. Затем мы увеличиваем счет и воспроизводим звук, чтобы предоставить обратную связь пользователю.

1
2
3
4
5
6
7
— Falling and Hit with paddle
  if(math.floor(ball.xScale * 10) == 3 and hitTestObjects(paddleTop, ball)) then
    variation = -0.05
    — Increase Score
    scoreTF.text = tostring(tonumber(scoreTF.text) + 1)
    — Play Sound
    audio.play(pong)

Весло разделено на четыре секции: верхний левый, верхний правый, нижний левый и нижний правый. Каждая секция перемещает мяч в другом направлении.

01
02
03
04
05
06
07
08
09
10
11
— Move Ball based on where it hits
    if(ball.x < paddle.x + 50) then ballX = (math.random() * 0.5) + ballVar end if(ball.x > paddle.x) then
      ballX = (math.random() * -0.5) — ballVar
    end
    if(ball.y < paddle.y + 75) then ballY = (math.random() * 0.5) + ballVar end if(ball.y > paddle.y — 70) then
      ballY = (math.random() * -0.5) — ballVar
    end
    — Increase moving distance
    ballVar = ballVar + 0.025 — Every four hits increases 0.1
  end
end

Функция movePaddle управляет движением весла с помощью сенсорного управления.

1
2
3
4
5
6
function movePaddle(e)
  if(e.phase == ‘moved’) then
    paddle.x = ex
    paddle.y = ey
  end
end

Функция alert создает представление оповещения. Функция анимирует вид оповещения на экран и завершает игру.

1
2
3
4
5
function alert()
  gameListeners(‘rmv’)
  alert = display.newImage(‘alert.png’, (display.contentWidth * 0.5) — 105, (display.contentHeight * 0.5) — 55)
  transition.from(alert, {time = 300, xScale = 0.5, yScale = 0.5})
end

Чтобы начать игру, необходимо вызвать функцию Main . С остальной частью кода мы делаем это здесь.

1
Main()

Экран загрузки

На платформе iOS файл с именем Default.png отображается во время запуска приложения. Добавьте это изображение в исходную папку вашего проекта, оно будет автоматически добавлено компилятором Corona.


Икона

Используя графику, которую вы создали ранее, теперь вы можете создать красивый значок. Размеры значков для iPhone без сетчатки составляют 57px x 57px, в то время как версия Retina должна быть 114px x 114px. Иллюстрации для iTunes должны быть размером 1024px x 1024px. Я предлагаю сначала создать обложку iTunes, а затем создать изображения меньшего размера, уменьшив обложку iTunes до нужных размеров. Нет необходимости делать значок приложения глянцевым или добавлять закругленные углы, поскольку об этом позаботится операционная система.


тестирование

Пришло время протестировать наше приложение в симуляторе. Откройте Corona Simulator, перейдите в папку вашего проекта и нажмите « Открыть» . Если все работает, как ожидалось, вы готовы к последнему шагу.


Сложение

В Corona Simulator перейдите в File> Build и выберите целевое устройство. Заполните обязательные поля и нажмите « Построить» . Подождите несколько секунд, и ваше приложение готово к тестированию на устройстве и / или будет отправлено для распространения.


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