October 24, 2023

Глава 1 — Создание проекта

Создание проекта

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

Примечания

  • Лучший способ использовать это руководство — следовать каждому шагу - вводить код, добавлять изображения и наблюдать, как ваш проект постепенно воплощается в жизнь. Это может занять немного больше времени, но вы получите гораздо лучшее представление.
  • К каждой главе прилагается загружаемый файл, содержащий все изображения, аудиофайлы и другие ресурсы, относящиеся к этой главе. Исходные файлы этой главы доступны здесь.

Запуск проекта

Создать новый проект в Solar2D несложно. Всего за несколько простых шагов вы будете готовы создать свою первую игру!

  1. Откройте симулятор Solar2D.
  2. Нажмите "Новый проект" в окне приветствия или выберите "Новый проект ..." в меню "Файл".
  1. Для названия проекта / приложения введите BalloonTap и убедитесь, что выбран параметр Пустой шаблон. Оставьте остальные настройки по умолчанию и нажмите "ОК" (Windows) или "Далее" (Mac). В указанном вами расположении (папке) будут созданы базовые файлы для вашей первой игры. В эту же папку вы разместите все файлы / ресурсы вашего приложения, включая изображения, программные файлы и т.д.

Включая изображения

Для этого проекта вам понадобятся три файла изображений, размещенных в BalloonTap папке проекта, созданной выше:

Файл

Размер (ш × в)

Использование

background.png

360 × 570

Фон - чисто декоративный, чтобы мы не смотрели на черный фон.

platform.png

300 × 50

Платформа / пол - предотвращает выпадение воздушного шара из нижней части экрана.

balloon.png

112 × 112

Воздушный шар.

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

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

  • Solar2D поддерживает форматы PNG и JPG. SVG поддерживается с помощью плагина Nano SVG.
  • Изображения не должны содержать встроенный ICC-профиль.
  • Избегайте прогрессивных файлов JPG, поскольку их загрузка займет гораздо больше времени.

Загрузка фона

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

Используя выбранный вами текстовый редактор, найдите и откройте main.lua файл в папке вашего проекта. main.lua Файл является основополагающим "основной программный файл" для каждого проекта Solar2D, и вы не можете создать приложение без нее. Это файл Lua, с помощью которого запускается приложение при каждом запуске приложения.

В этом main.lua файле введите выделенную команду:

-----------------------------------------------------------------------------------------

--

-- main.lua

--

-----------------------------------------------------------------------------------------

local background = display.newImageRect( "background.png", 360, 570 )

С этой командой связано несколько аспектов. Давайте разберем ее:

  • Первое слово local - это команда Lua, указывающая, что следующим словом будет переменная. Переменная, как вы узнали на уроке математики, используется для хранения значения. В этом случае этим значением будет изображение, используемое в качестве фона.

Обратите внимание, что local это всегда строчная буква, и здесь она используется для объявления переменной; например, при первом использовании переменной перед ней следует добавить слово local.

  • Второе слово background - это имя нашей переменной. Каждый раз, когда мы захотим внести изменения в изображение, хранящееся в background, мы будем использовать это имя переменной.

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

  • = (знак равенства) используется для присвоения переменной background изображению.
  • display.newImageRect() является одним из API Solar2D. Он используется для загрузки изображения из файла, чтобы вы могли использовать его в приложении. Есть несколько способов загрузить изображение в ваше приложение, но display.newImageRect() особенность в том, что оно может изменять размер изображения (это будет объяснено чуть позже).
  • В круглых скобках указаны параметры, которые мы передаем display.newImageRect(), иногда называемые аргументами. Первый параметр - это имя файла изображения, которое мы хотим загрузить, включая расширение файла (.png).

Указанное имя должно точно совпадать с фактическим именем файла, включая соответствие с учетом регистра! Например, если фактическое имя файла background.png, не вводите его как "BackGround.PNG".

Следующие два параметра 360 и 570 указывают размер, который мы хотим, чтобы фоновое изображение было. В этом случае мы будем просто использовать размеры изображения в пикселях, хотя, как отмечалось выше, display.newImageRect() позволяет изменять размер изображения с помощью этих чисел.

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

7

8

9

local background = display.newImageRect( "background.png", 360, 570 )

background.x = display.contentCenterX

background.y = display.contentCenterY

По умолчанию Solar2D позиционирует центр объекта в точке с координатами 0,0, которая находится в вверху слева угол экрана. Однако, изменив x и y свойства объекта, мы можем переместить фоновое изображение в новое место.

Для этого проекта мы разместим фон в центре экрана — но что, если мы не знаем точно, какие значения координат представляют центр? К счастью, Solar2D предоставляет для этого несколько удобных сокращений. Когда вы укажете значения display.contentCenterX и display.contentCenterY, Solar2D установит координаты центра экрана в качестве background.x и background.y свойств.

Давайте проверим результат вашего кода! Сохраните измененный main.lua файл, а затем из симулятора "перезапустите" его с помощью ⌘-R (Command-R). Если все прошло хорошо, теперь должен отображаться фон по центру экрана.

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

  • Одна из команд была введена неправильно.
  • Файл изображения находится не в той папке, что main.lua.
  • Указанное имя файла и / или расширение неверно или не соответствует в случае.

Помните, что Консоль симулятора окно является ценным местом для проверки и диагностики потенциальных ошибок в вашем коде. В Windows эта панель всегда доступна в симуляторе. На Mac, если это окно еще не открыто, вы можете просмотреть его, выбрав ОкноКонсоль.

Загрузка платформы

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

7

8

9

10

11

12

13

local background = display.newImageRect( "background.png", 360, 570 )

background.x = display.contentCenterX

background.y = display.contentCenterY

local platform = display.newImageRect( "platform.png", 300, 50 )

platform.x = display.contentCenterX

platform.y = display.contentHeight-25

Как вы, наверное, заметили, есть одно незначительное изменение по сравнению с фоном: вместо расположения платформы по центру по вертикали мы хотим, чтобы она располагалась в нижней части экрана. Используя команду display.contentHeight, мы узнаем высоту области содержимого. Но помните, что она platform.y помещает центр объекта в указанное местоположение. Итак, поскольку высота этого объекта равна 50 пикселям, мы вычитаем 25 пикселов из значения, гарантируя, что вся платформа будет видна на экране.

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

Загрузка воздушного шара

Чтобы загрузить balloon, мы проделаем тот же процесс. Под предыдущими командами введите эти строки:

15

16

17

local balloon = display.newImageRect( "balloon.png", 112, 112 )

balloon.x = display.contentCenterX

balloon.y = display.contentCenterY

Кроме того, чтобы придать воздушному шару слегка прозрачный вид, мы немного уменьшим непрозрачность объекта (альфа). В следующей строке установите для alpha свойства balloon значение 80% (0.8):

15

16

17

18

local balloon = display.newImageRect( "balloon.png", 112, 112 )

balloon.x = display.contentCenterX

balloon.y = display.contentCenterY

balloon.alpha = 0.8

Save your main.lua file and relaunch the Simulator. There should now be a balloon in the center of the screen.

Adding Physics

Time to get into physics! Box2D is the included physics engine for your use in building apps and games. While using physics is not required to make a game, it makes it much easier to handle many game situations.

Including physics is very easy. Below the previous lines, add these commands:

20

21

local physics = require( "physics" )

physics.start()

Let's explain these two lines in a little more detail:

  • The command local physics = require( "physics" ) loads the Box2D physics engine into your app and associates it with the local variable physics for later reference. This gives you the ability to call other commands within the physics library using the physics namespace variable, as you'll see in a moment.
  • physics.start() does exactly what you might guess — it starts the physics engine.

If you save and relaunch you won't see any difference in your game... yet. That is because we haven't given the physics engine anything to do. For physics to work, we need to convert the images/objects that were created into physical objects. This is done with the command physics.addBody:

20

21

22

23

local physics = require( "physics" )

physics.start()

physics.addBody( platform, "static" )

This tells the physics engine to add a physical "body" to the image that is stored in platform. In addition, the second parameter tells Solar2D to treat it as a static physical object. What does this mean? Basically, static physical objects are not affected by gravity or other physical forces, so anytime you have an object which shouldn't move, set its type to "static".

Now add a physical body to the balloon:

20

21

22

23

24

local physics = require( "physics" )

physics.start()

physics.addBody( platform, "static" )

physics.addBody( balloon, "dynamic", { radius=50, bounce=0.3 } )

In contrast to the platform, the balloon is a dynamic physical object. This means that it's affected by gravity, that it will respond physically to collisions with other physical objects, etc. In this case, the second parameter ("dynamic") is actually optional because the default body type is already dynamic, but we include it here to help with the learning process.

The final part of this physics.addBody command is used to adjust the balloon's body properties — in this case we'll give it a round shape and adjust its bounce/restitution value. Parameters must be placed in curly brackets ({}) (referred to as a table in the Lua programming language).

  • Because the balloon is a round object, we assign it a radius property with a value of 50. This value basically matches the size of our balloon image, but you may need to adjust it slightly if you created your own balloon image.
  • The bounce value can be any non-negative decimal or integer value. A value of 0 means that the balloon has no bounce, while a value of 1 will make it bounce back with 100% of its collision energy. A value of 0.3, as seen above, will make it bounce back with 30% of its energy.

Notes

  • A bounce value greater than 1 will make an object bounce back with more than 100% of its collision energy. Be careful if you set values above 1 since the object may quickly gain momentum beyond what is typical or expected.
  • Even if you change the balloon's bounce property to 0, it will still bounce off the platform object because, by default, objects have a bounce value of 0.2. To completely remove bouncing in this game, set both the balloon and the platform to bounce=0.

Save your main.lua file and relaunch the Simulator. As a fun experiment, you can try adjusting the bounce value and relaunch the project to see the effect.

Functions

At this point, we have a balloon that drops onto a platform and bounces slightly. That's not very fun, so let's make this into a game! For our balloon tap game to work, we need to be able to push the balloon up a little each time it's tapped.

To perform this kind of feature, programming languages use what are called functions. Functions are short (usually) sections of code that only run when we tell them to, like when the player taps the balloon.

Let's create our first function:

local function pushBalloon()

end

Functions are essential for both app and game development, so let's examine the basic structure:

  • As before, we use the keyword local to declare the function.
  • The keyword function tells Solar2D that this is a function and that its set of commands will be called by the name pushBalloon.
  • The ending parentheses (()) are required. In later chapters we will put something inside these parentheses, but for now you can leave this as shown.
  • As mentioned above, functions are self-contained sections (blocks) of code which run only when we tell them to. Thus, whenever you create a function, you must close it with the keyword end. This tells Lua that the function is finished.

Excellent, we now have a function! However, it's currently an empty function so it won't actually do anything if we run it. Let's fix that by adding the following line of code inside the function, between where we declare the function (its opening line) and the closing end keyword:

local function pushBalloon()

balloon:applyLinearImpulse( 0, -0.75, balloon.x, balloon.y )

end

Хорошей практикой программирования считается делать отступ хотя бы на одну табуляцию или 3-4 пробелы при добавлении строк кода внутри функций. Это делает ваш код более читаемым и облегчает распознавание функций в более длинных программах.

balloon:applyLinearImpulse это действительно классная команда. При применении к динамическому физическому объекту, такому как воздушный шар, она "подталкивает" объект в любом направлении. Передаваемые нами параметры сообщают физическому движку, какую силу нужно приложить (как по горизонтали, так и по вертикали), а также к какому участку тела объекта приложить усилие.

Первые два параметра, 0 и -0.75, указывают величину направленного усилия. Первое число - это горизонтальное направление, или x, а второе число - вертикальное направление, или y. Поскольку мы хотим толкать воздушный шар только вверх (не слева и не справа) В качестве первого параметра мы используем 0. Для второго параметра со значением -0.75 мы указываем физическому движку немного подтолкнуть воздушный шар вверх. Значение этого числа определяет величину прилагаемого усилия: чем больше число, тем выше усилие.

Как показано на диаграмме справа, положительные значения x в Solar2D простираются вправо, в то время как положительные значения y простираются вниз (не вверх, как в декартовой системе координат). Вот почему мы используем отрицательное значение (-0.75), чтобы подтолкнуть воздушный шар вверх.

Третий и четвертый параметры, balloon.x и balloon.y, указывают физическому движку, куда приложить усилие относительно самого воздушного шара. Если вы приложите усилие в месте, которое не является центром баллона, это может привести к тому, что баллон переместится в неожиданном направлении или начнет вращаться. В этой игре мы будем фокусировать силу в центре воздушного шара.

Вот и все! Мы могли бы, при необходимости, добавить дополнительные команды внутри pushBalloon() функции, но для этой простой игры нам нужно только с небольшим усилием подтолкнуть шарик вверх.

Мероприятия

События - это то, что создает интерактивность, и во многих отношениях Solar2D является на основе событий платформа, в которой информация отправляется во время определенного события прослушивателю событий. Будь то прикосновение пользователя к объекту / кнопке, нажатие на экран или (в этой игре) нажатие на воздушный шарик, Solar2D может отреагировать, запустив событие.

Добавить прослушиватель событий несложно — сделайте это сейчас, следуя функции:

local function pushBalloon()

balloon:applyLinearImpulse( 0, -0.75, balloon.x, balloon.y )

end

balloon:addEventListener( "tap", pushBalloon )

Давайте ознакомимся со структурой этой новой команды:

  • Сначала мы должны сообщить Solar2D, какой объект задействован в прослушивателе событий. В этой игре мы хотим обнаружить событие, связанное непосредственно с balloon объектом.
  • Сразу после этого добавьте двоеточие (:), затем addEventListener. В Lua это называется объектным методом. По сути, addEventListener после двоеточия Solar2D сообщает, что мы хотим добавить прослушиватель событий в balloon, указанный перед двоеточием.
  • Внутри круглых скобок указаны два параметра, которые завершают выполнение команды. Первый параметр - это тип события, которое в данном случае будет прослушивать Solar2D "tap". Второй параметр - это функция, которая должна запускаться (вызываться) при возникновении события, в данном случае pushBalloon() функция, о которой мы писали в предыдущем разделе. По сути, мы говорим Solar2D запускать pushBalloon() функцию каждый раз, когда пользователь нажимает на воздушный шар.

Вот и все — теперь у вас есть функциональная игра! Если вы сохраните свой main.lua файл и повторно запустите симулятор, он должен быть готов к работе. Делайте все возможное, чтобы продолжать нажимать на воздушный шар и не допускать его касания платформы!

Вот полная программа, на случай, если вы что-то пропустили:

-----------------------------------------------------------------------------------------

--

-- main.lua

--

-----------------------------------------------------------------------------------------

local background = display.newImageRect( "background.png", 360, 570 )

background.x = display.contentCenterX

background.y = display.contentCenterY

local platform = display.newImageRect( "platform.png", 300, 50 )

platform.x = display.contentCenterX

platform.y = display.contentHeight-25

local balloon = display.newImageRect( "balloon.png", 112, 112 )

balloon.x = display.contentCenterX

balloon.y = display.contentCenterY

balloon.alpha = 0.8

local physics = require( "physics" )

physics.start()

physics.addBody( platform, "static" )

physics.addBody( balloon, "dynamic", { radius=50, bounce=0.3 } )

local function pushBalloon()

balloon:applyLinearImpulse( 0, -0.75, balloon.x, balloon.y )

end

balloon:addEventListener( "tap", pushBalloon )

Дополнительный кредит

Поздравляем, вы создали базовую игру всего за 30 строк кода! Но чего-то не хватает, не так ли? Разве не было бы неплохо, если бы игра отслеживала, сколько раз был нажат шарик? К счастью, это легко добавить!

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

7

8

9

10

11

local tapCount = 0

local background = display.newImageRect( "background.png", 360, 570 )

background.x = display.contentCenterX

background.y = display.contentCenterY

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

После трех строк, которые загружают / позиционируют фон, добавьте следующую выделенную команду:

7

8

9

10

11

12

13

local tapCount = 0

local background = display.newImageRect( "background.png", 360, 570 )

background.x = display.contentCenterX

background.y = display.contentCenterY

local tapText = display.newText( tapCount, display.contentCenterX, 20, native.systemFont, 40 )

Давайте рассмотрим эту команду более подробно:

  • Команда начинается с local tapText которую вы должны легко распознать как объявление переменной tapText.
  • display.newText() это еще один API, но вместо загрузки изображения, как мы делали ранее, эта команда создает текстовый объект. Поскольку мы присваиваем переменную tapText этому объекту, мы сможем вносить изменения в текст во время нашей игры, например, изменять напечатанный номер, чтобы он соответствовал количеству нажатий на шарик.
  • Внутри круглых скобок указаны параметры, которые мы передаем display.newText(). Первый параметр - это начальное печатное значение для текста, но обратите внимание, что вместо установки прямого строкового значения типа "0", мы фактически присваиваем переменную, которую мы объявили ранее (tapCount). В Solar2D совершенно допустимо указывать переменную в качестве параметра API, при условии, что это допустимая переменная и API принимает тип переменной в качестве этого параметра.

Два вторых параметра, display.contentCenterX и 20, используются для позиционирования этого текстового объекта на экране. Вы заметите, что мы используем одно и то же сочетание клавиш display.contentCenterX для позиционирования объекта в горизонтальном центре экрана и 20 для установки его вертикального положения y в верхней части экрана.

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

Конечным параметром (40) является предполагаемый размер отображаемого текста.

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

Продолжаем работу с нашей программой — по умолчанию текст, созданный с помощью display.newText(), будет белым. К счастью, это легко изменить. Непосредственно после только что добавленной строки введите выделенную команду:

13

14

local tapText = display.newText( tapCount, display.contentCenterX, 20, native.systemFont, 40 )

tapText:setFillColor( 0, 0, 0 )

Проще говоря, эта setFillColor() команда изменяет цвет заливки объекта tapText. setFillColor() Команда принимает до четырех числовых параметров в диапазоне 0 Для 1 по одному для цветовых каналов красный, зеленый, синий и альфа. В этой игре мы заливаем объект сплошным черным цветом, поэтому для первых трех каналов установлено значение 0 (по умолчанию используется значение альфа 1, и в данном случае его можно опустить).

Давайте двигаться дальше! Новый текстовый объект выглядит красиво, но на самом деле он ничего не делает. Чтобы он обновлялся, когда игрок нажимает на шарик, нам нужно изменить нашу pushBalloon() функцию. Внутри этой функции, следуя balloon:applyLinearImpulse() команде, вставьте две выделенные строки:

31

32

33

34

35

local function pushBalloon()

balloon:applyLinearImpulse( 0, -0.75, balloon.x, balloon.y )

tapCount = tapCount + 1

tapText.text = tapCount

end

Давайте рассмотрим эти строки по отдельности:

  • Тот самый tapCount = tapCount + 1 команда увеличивает tapCount переменную на 1 при каждом нажатии на воздушный шар.
  • Вторая новая команда, tapText.text = tapCount обновляет text свойство нашего tapText объекта. Это позволяет нам быстро изменять текст без необходимости каждый раз создавать новый объект.

Смотрите внимательно — чтобы обновить текст на экране, мы обновляем свойство текстового объекта, а не сам объект. В этом случае мы изменяем text свойство tapText, написав tapText.text, далее =, затем его новое значение. Поскольку мы увеличиваем значение tapCount переменной непосредственно в предыдущей строке, а затем обновляем текстовый объект тем же значением переменной, визуальное отображение всегда будет отражать внутреннее tapCount значение.

Вот и все! Если вы сохраните свой main.lua файл и перезапустите симулятор, ваша игра, по сути, закончена — теперь каждый раз, когда вы нажимаете на шарик, счетчик в верхней части экрана увеличивается на 1, эффективно сохраняя счет.

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

Концепции глав

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

Вот краткий обзор того, что вы узнали в этой главе:

Команда / Свойство

Описание

display.newImageRect()

Загружает и отображает изображение на экране.

object.x

Задает горизонтальное положение объекта по x.

object.y

Задает вертикальное y положение объекта.

display.contentCenterX

Ярлык для центра области содержимого вдоль оси x.

display.contentCenterY

Ярлык для центра области содержимого вдоль оси y.

object.alpha

Устанавливает альфа-уровень объекта (непрозрачность).

требуется()

Загружает, например, данный модуль или библиотеку "physics".

physics.start()

Запускает физический движок.

physics.addBody()

Добавляет физическое тело к объекту.

объект: ApplyLinearImpulse()

Применяет имитируемую импульсную силу к физическому объекту.

объект:addEventListener()

Добавляет прослушиватель событий к объекту, например "tap".

display.newText()

Создает текстовый объект на экране.

объект:setFillColor()

Задает цвет заливки для текстовых и векторных объектов.