Пишем Space Invaders при помощи Corona. Заключение

Оглавление (нажмите, чтобы открыть):

Некое подобие Space Invaders

26.06.2020, 17:49

Реализация боев в Space Invaders
Здравствуйте. Мне нужно написать игру на Паскале наподобие Space Invaders, где несколько уровней.

некое подобие Everesta
Доброго времени суток! Пишу курсовую работу. Задание — создать программу на подобии Everesta.

Некое подобие склада
Подскажите, плз, как сделать! Сущ таблица Склад, Заказ. Для заполнения таблицы Заказ сущ Форма.

Классическая Space Invaders, «выпадение» кораблей из нижнего ряда
Подскажите, пожалуйста, как сделать, чтобы из нижнего ряда передвигающейся матрицы врагов с.

Некое подобие файловой системы
В моем представлении это должно выглядеть так. Сперва создается файл заданного размера. Далее.

Уроки iPhone SDK: Создаем аналог Doodle Jump для iPhone с помощью Corona SDK

Держу пари, что сегодня утром, когда вы проснулись, ваша первая мысль была: «Я хочу создать игру Doodle Jump и написать 300 строчек кода».

«Ах да, и я хочу чтобы игра работала на iPhone и Android и чтобы код был прост, как три копейки».

Ваше желание исполнится!

В этом уроке состоящим из 2-х частей мы сделаем игру на популярном и простом в использовании движке для iOS и Android под названием Corona. По окончании изучения этой серии уроков вы узнаете:

• Как создать игру с помощью Corona.
• Плюсы/Минусы Corona и cocos2d.
• Как делать уровни в LevelHelper.
• И, конечно же, как сделать игру на подобие Doodle Jump!

Этот туториал нацелен на новичков в Corona, но он будет полезен и более опытным девелоперам.

Так же, неплохо, если у вас есть опыт работы с LevelHelper и SpriteHelper. Однако, если эти названия вы слышите впервые, рекомендую ознакомиться с этим уроком. Не переживайте если у вас нет этих утилит или опыта работы с ними, т.к. вы сможете использовать готовые спрайт листы (sprite sheet) и уровни, если потребуется.

Ну что ж, начнем!

Хотя большинство из вас наверняка знакомы с cocos2d , вы можете ничего не знать о движке Corona.

Начнем с того, что же такое Corona.

Corona – это кросс платформенный игровой движок, поддерживающий платформу iOS и Android. Движок разработан компанией Ansca и для разработки вы можете использовать триальную версию Corona. Когда появится желание опубликовать свое приложение в App Store придется купить лицензию. Цена колеблется от 199$ до 349$.

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

В Corona есть симулятор для тестирования ваших приложений.

В Corona встроены некоторые API необходимые для программирования (спрайты, звук и т.д.) а так же доступны API от Apple включая Game Center, In App Purchase (продажа контента внутри приложения) и TableViews.

Так же есть ряд сторонних API таких как Open Feint, inMobi (реклама), Flurry (аналитика). Ко всем API от Apple у Corona доступа нет. Например не удастся воспользоваться iAds, Game Center Multyplayer и соединением по Bluetooth.

Теперь давайте поговорим о разнице между Corona и Cocos2D.

Прежде всего, Corona и Cocos2D имеют немало общего:

• Позиционировать спрайты в Corona очень просто. Нужно установить свойства .x/.y для спрайта.
• Есть масштаб, поворот и альфа свойства (alpha property) связывающие отображаемые объекты.
• Метод “transition.to”, который анимирует позицию, масштаб, поворот и т.д.
• Corona использует физический движок Box2D, поэтому методы и свойства должны быть вам знакомы, если вы использовали Box2D в cocos2d.
• Как и cocos2d, Corona использует спрайты, текстовые лейблы (text labels), методы рисование примитивов, но все это называется иначе. Любые объекты, которые появляются на экране имеют общее название “отображаемые объекты” (display objects), которые похожи на CCNode в Cocos2D. В Corona есть отображаемая группа (display group), которая функционально похожа на CCLayer в Cocos2D.

Помимо сходств, у этих движков есть существенные различия:

• В Cocos2D используется язык Objective-C, в то время как Corona использует Lua. API в Corona находятся под сильным влиянием Action Script. Flash разработчики считают Corona очень дружелюбной средой. Код в Corona может быть написан согласно модели ООП, но модель функционального программирования более распространена.
• Cocos2D позволяет интегрировать сторонние библиотеки написанные на Objective-C. Corona в свою очередь закрытая система. Хотя на Lua и написаны некоторые сторонние библиотеки, но в настоящее время для Corona их всего несколько.
• И, конечно же, Corona платная, а Cocos2D бесплатна.

Я знаю какой вопрос вас мучает – какой движок следует выбрать? Лучшим ответом станет список плюсов и минусов Corona по отношению к Cocos2D.

Время на разработку. Создание игр в Corona зачастую быстрее и проще чем в Cocos2D. Так как используется язык Lua, добвление переменной объекта не требует ничего более, чем назначение (например: Sprite.newvalue = 0). Движок Box2D интегрирован, так что спрайт объекта и физика тела это один объект. Вам не придется писать файлы интерфейста, декларировать дубликаты переменных или декларировать типы данных переменных (здесь есть некоторые исключения).
Кросс-платформенность. Если вы создаете игру в Corona, то она будет работать как на iOS так и на Android, как говориться “из коробки”.

Нехватка поддерживаемых API. Corona не имеет доступа ко всем API от Apple, или так много сторонних дополнений как Cocos2D. Сетовой мультиплеер должен быть написан с нуля. Проходит немало времени прежде чем интегрируются новые фишки от Apple.
Низкая производительность. Так как Corona работает с Lua, ваш код не будет работать так быстро как если бы использовался Objective-C.
Corona платная. За Corona придется заплатить, если вам нужно будет опубликовать приложение. Cocos2D — совершенно бесплатна.

Если Corona у вас еще не установлена, то скачивайте триал версию и установите. Триалка полностью функциональна, за исключением возможности публиковаться в App Store. Перейдите на сайт (http://coronalabs.com/products/corona-sdk/) и скачайте/установите Corona (нажмите “DOWNLOAD NOW” внизу. придется зарегистрироваться).

С Corona я работаю на Mac, но имейте ввиду, что вы можете пользоваться Corona и на PC. Однако, вы не сможете собрать iOS билд без Mac’а, только билд для Android.

Когда закончится установка, в папке Applications появится папка Corona. В этой папке лежат различные исполняемые файлы. Я рекомендую всегда запускать Corona выполняя Corona Terminal. Это откроет окно терминала вместе с Corona Simulator. Терминал даст вам лог ошибок или print().

Как упоминалось ранее, в Corona нет IDE. Я пишу большую часть кода в простом текстовом редакторе. Дебаггер мне кажется неудобным. Вы можете использовать Xcode как текстовый редактор, а так же для Corona есть плагины для подсветки кода, но они работают не со всеми версиями Xcode.

Итак, откройте новый текстовый файл и добавьте следующий код:

Как вы поняли, этот код выводит на экран надпись “Hello World” с позициями X=20 и Y=30 в 50-ти точках шрифтом Helvetica.

Сохраните свой текстовый файл. Он должен называться “main.lua”. Запомните, что этот файл является отправной точкой любой программы в Corona.

Откройте Corona Simulator, либо запустив Corona Simulatior.app, либо, как я рекомендовал через Corona Terminal. Перейдите File->Open, найдите ваш файл. Оба этих файла лежат в папке Corona в Applications.

Поздравляю, теперь вы разработчик Corona. Что может быть легче чем это? К вашему сведению, чтобы сделать билд для устройства в Corona, вам понадобится аккаунт Apple Developer.

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

Если у вас нет SpriteHelper’а не стоит волноваться, вы сможете воспользоваться готовым “спрайт листом” и перейти к следующему разделу.

Но если вы успели приобрести SpriteHelper, продолжайте читать по порядку.

Для этого проекта вам понадобятся некоторые ресурсы, так что скачивайте все по этой ссылке и распаковывайте. Внутри папки спрайтов вы увидите изображения, которые мы будем использовать. Бесплатный пак с изображениями предоставила Viki, жена Ray’а.

Теперь нужно настроить ряд атрибутов физики. Мне нравиться делать это с помощью Sprite Helper’а, но эти свойства позже могут быть установлены и в Level Helper’е. В этом уроке я покажу вам как устанавливать атрибуты в Level Helper’е. Углубляться в подробности использования SpriteHelper/LevelHelper мы не станем, так как эти вопросы были рассмотрены в соответствующем уроке — http://www.raywenderlich.com/4622/how-to-use-spritehelper-and-levelhelper-tutorial, на английском.

Запустите SpriteHelper и перетащите все спрайты в окно.

Выберите File-> Save, укажите директорию где лежит main.lua и введите как имя “cloudSprites”. Необходимые расширения будут добавлены автоматически. В итоге создастся три файла — cloudSprites-hd.png, cloudSprites.png и cloudSprites.pshs (проект SpriteHelper’а).

Теперь воспользуемся LevelHelper’ом для создания уровня игры. Напомню, если у вас нет указанной утилиты. Можете взять готовый уровень из необходимых ресурсов для игры о которых мы говорили ранее.

Если LevelHelper у вас есть, то запустите его и кликните “+” в Project. Введите cloudJumper и выберите “iPhone Portrait (320×480)” и нажмите “Create New Project”.

Внизу окна LevelHelper найдите раздел Game World Size и замените установленные значения на 0, 0, 320, 9600. Также добавьте эти значения в Physic Boundaries. Установите gravity на 0, -10.

Нажмите “+” возле выпадающего меню выбора сцены с правой стороны. Выберите файл cloudSprites.pshs, который недавно был создан SpriteHelper’ом. Теперь все ваши спрайты загружены в панель спрайтов.

Переместитесь в самый низ уровня.

Первое, что мы сделаем, это загрузим фоновые облака. Перетащите три облака – bg_cloud1, bg_cloud2 и bg_cloud3 в нижнюю секцию. Выделите все три спрайта.

Установите тип физики для облаков на “No Physic”. Установите свойство Z Order на “-2”. Мы хотим чтобы эти облака всегда были далеко на фоне.

С помощью инструмента Clone and Align сделайте 19 (или около того) копий всех трех спрайтов. Вы можете отступ по Y на “-480” чтобы разместить один блок облаков для каждого экрана. Следуйте по экрану и располагайте облака. Не зацикливайтесь на одних и тех же положениях, чтобы не выглядело одиникого.

Когда вы закончите, выделите все спрайты облаков и нажмите кнопку с замком. Теперь, когда кнопка нажата, спрайты не могут быть выбраны в макете. Это облегчает позиционирование других спрайтов поверх этих. Если необходимо подредактировать заблокированные спрайты, сделайте это в списке “Sprites in Level” и нажмите ту же кнопку, чтобы разблокировать их.

Теперь перетащите спрайты cloud1, cloud2 и cloud3 в низ уровня. Присвойте этим спрайтам свойства как показано ниже:

Несколько моментов на заметку. Все облака имеют значение “Z Order” -1. Это установит их поверх наших фоновых облаков, но позади всего остального. Белые и серые облака являются сенсорами (sensors). Мы будем пролетать через них и отскакивать только падая на них сверху. Синие облака не являются сенсорами, поэтому их придется избегать и это сделает игру сложнее.

На всех облаках в “Category Bit” стоит значение “1”. Нам нужно, чтобы монстры и стрелы двигались через них без столкновений, поэтому мы установим атрибуты маски этих объектов соответствующим образом. Убедитесь в правильности установленных значений в physic type, shape border и TAG. Не беспокойтесь если число в tag отличается от показанного выше, только имя имеет значение.

Если у вас есть вопросы касательно этих свойств, ознакомьтесь с этим уроком — http://www.raywenderlich.com/4622/how-to-use-spritehelper-and-levelhelper-tutorial, на английском.

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

Вот несколько советов по созданию уровней:

1. Уровень должен начинаться легко, чтобы игрок успел понять что от него требуется.
2. Уровни не должны повторяться.
3. Игрок прыгает примерно на 200 пикс. максимум, поэтому убедитесь, что он может прыгнуть с одной платформы на другую.
4. Серые облака – отвлекающий маневр, они исчезнут после приземления
5. Синие облака трудны тем, что игрок не может пройти через них и они узкие.
6. Пик сложности должен наступать ближе к концу уровня.

Если вы хотите использовать мой уровень, то возьмите его в ресурсах к проекту, однако, сделать самому гораздо веселее и интереснее.

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

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

Игрок будет сталкиваться с облаками, монстрами, поэтому значение “Mask Bit” нужно установить на 5.

Теперь нам нужно сохранить уровень и сгенерировать код. Выберите File-> Save и назовите файл “level1”, остальные расширения будут созданы автоматически. Также выберите File->Generate Code->Corona, создастся файл LevelHelperLoader.lua. Этот файл надо перегенерировать всякий раз, когда мы добавляем новый TAG.

Мастер Йода рекомендует:  Система Мультипасс

Положите эти файлы в ту же директорию где лежит main.lua.

1. Импортирует код физического движка и присваивает движок объекту называемый physics.
2. Запускает физический движок с дефолтными значениями гравитации. Corona использует физический движок Box2D, так что если вы использовали его ранее многое вам будет знакомо.
3. Скрывает статус бар.

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

Давайте попробуем. Добавьте новую функцию для загрузки уровня:

local function loadLevel()
localGroup = display.newGroup()
loader = LevelHelperLoader:initWithContentOfFile(«level1.plhs»)
loader:instantiateObjectsInGroup(physics, localGroup)

worldHeight = loader:getWorldBoundariesRect().size.height
localGroup.y = -worldHeight + 480

Первая строка загружает класс LevelHelperLoader (сгенерированный нами ранее или взятый вами из ресурсов для игры), который похож на #import в Objective-C.

Следующая строка создает функцию, которая в Lua делается с помощью ключевого слова “function”. Переменные и функции в Lua по умолчанию являются глобальными по своим масштабам. Если мы хотим уменьшить масштабы текущей функции нужно включать ключевое слово “local”.

Corona называет визуальные объекты (спрайты, нарисованные объекты, слои) “отображаемыми объектами”. Каждый из наших спрайтов, фоновые облака, игрок, платформы и т.д. являются отображаемыми объектами.

Функция в первой строке создает новую отображаемую группу, которая в функциях Corona похожа на CCLayer в Cocos2D. Мы собираемся добавить все содержимое нашего уровня к отображаемым группе.

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

В Corona нам необходимо только одно обращение/вызов (call) к LevelHelper, который создает все наши отоброжаемые объекты и создает соответствующие физическте тела. Физические тела в Corona автоматически соотносятся со своими спрайтами. Это делает создание физики в игре очень простой.

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

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

Наши отображаемые объекты сейчас на экране и часть отображаемой группы называется localGroup. Однако, localGroup в данный момент находится на “y” на позиции “0″. Система координат в Corona берет отсчет в верхнем левом углу экрана и увеличивается в направлении к низу, это означает, что мы увидим самый верх нашего уровня или самый конец.

Нам нужно двигать отображаемую группу вверх, что и делают следующие две строчки кода. Сначала мы берем размер мира (world size), который мы установили в LevelHelper’е, затем мы устанавливаем localgroup.y на величину противоположную размеру мира, двигая это все вверх с минусом в одну высоту экрана.

Наконец мы создаем и инициализируем игрока. Этот код еще не добавлен, поэтому вставьте его прямо над loadLevel:

Получаем спрайт с уникальным именем “char_jump2”, который мы добавили на уровень созданный в LevelHelper.

Одна вещь, которую нужно иметь ввиду, что физическое тело уже связанно с ним. Так, в дальнейшем, у нас будет доступ к тем вызовам (calls), которые получают и устанавливают физические свойства.

Теперь, когда у нас на месте две эти функции, давайте использовать их! Добавьте следующую строку сразу после описания функции:

И это все! Сохраните main.lua, и поместите в один каталог с LevelHelperLoader.lua, cloudSprites.png, cloudSprites-hd.png, cloudSprites.pshs и level1.plhs. Затем запустите main.lua в Corona Simulator.

Вы должны увидеть облака и нашего героя падающего вниз:

Поздравляю, вы написали свою первую программу на Corona с главным героем и платформами!

Давайте двигаться дальше и сделаем так, чтобы игрок прыгал по облакам!

local function newPlayer()
local p = loader:spriteWithUniqueName(«char_jump2»)

local function pCollision(self, event)
object = event.other
if event.phase == «began» then
vx, vy = self:getLinearVelocity()
if vy > 0 then
if object.tag == LevelHelper_TAG.CLOUD then
self:setLinearVelocity(0, -350)
end
end
end
end
p.collision = pCollision
p:addEventListener(«collision», p)
return p
end

Вторая строка создает нашу callback-функцию столкновения. Второй параметр содержит информацию о событии столкновения (collision event).

Self — это объект игрока, а other эти тип события. Event.other — это объект с которым столкнулся self.

Затем нам нужно, чтобы прыжок делался только один раз. Столкновение может быть неоднократным, но нам нужен единичный прыжок с одним столкновением, так что мы будем проверять это на фазе “began”.

Затем мы проверяем должны ли мы прыгать. Мы берем линейную скорость (linear velocity) игрока и проверяем чтобы она была больше 0, потому что мы хотим прыгать только на обратном пути вниз, а не когда мы пролетаем через облака.

И наконец мы смотрим с чем мы столкнемся. Нам нужно отскакивать только от белых облаков, поэтому нам нужно проверять tag объекта с которым сталкиваемся.

Если все эти условия соблюдены, мы установим линейную скорость self до -350 или вверх до 350. Мы используем зедесь setLinearVelocity (вместо applyLinearImpulse), потому что неважно какой импульс мы получили падая вниз, нам нужно чтобы обратный прыжок был одинаковой высоты.

Наконец, мы собираемся установить только что созданную функцию, чтобы был обратный вызов столкновения (collision callback) для объекта игрока. Следующие две строчки делают это. С обратным вызовом столкновения (collision callback) мы установим свойство .collision и добавим слушателя события (event listener) к объекту.

Снова откройте проект в Corona Simulator. Теперь игрок должен отскакивать от белых облаков!

Я держу Corona Simulator открытым и использую сочетание клавиш Command-R чтобы перезагрузить проект после сохранения файла. Corona тоже обнаружит, что main.lua был обновлен и попросит вас о перезапуске.

Сейчас самое время поговорить подробнее об event listeners в Corona.

На самом деле мы столкнулись с этим вопросом немного выше, но давайте поговорим о том, как это работает.

Event listeners в Corona это callback-методы. Есть event listener’ы, которые обрабатывают касания и акселерометр, изменение ориентации или инпут GPS, столкновения (collision), приостановка или выход из приложения и т.д.

Существует два вида слушателей события:
События, которые касаются всех объектов. Они известны как runtime events (события времени исполнения) и включают в себя такие события как изменение ориентации или инпут (input) от GPS. Другой пример – слушатель enterFrame, который является событием, которое вызывается каждый раз при отрисовки кадра.

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

В нашем случае мы создали функцию столкновения, функцию pCollision. Мы закреприли слушателя события за игроком, так что когда игрок сталкивается с другим объектом, этот объект будет вызываться. Функция имеет два параметра – объект self, в данном случае это игрок, и объект события. Объект события создан и содержит информацию о типе события столкновения.

Разные слушатели события имеют разные объекты события, которые предоставляют разную информацию. Например, есть тип события “postCollision”, который отвечает за силу столкновения. Обычное событие столкновения не имеет такой информации.

Для получения более развернутой информации о типах событий в Corona взгляните на этот документ (англ.).

Теперь, когда белые облака работают, можно заняться синими и серыми облаками. Синие облака фактически не требуют никакого дополнительного кода. Они совместно с белыми облаками используют тег CLOUD и являются статическими телами (подразумевается, что вы должны пройти вокруг них, а не через них, как в случае с белыми облаками).

Перейдите снова к функции pCollision и добавьте код показанный ниже:

Если тег BCLOUD, то нужно чтобы облако (серое) исчезло, поэтому мы вызываем loader:removeSpriteWithUniqueName и передаем имя объекта с которым столкнулись. Помните, что в нашем коде “object” это переменная где мы сохранили event.other.

Любовь витает в воздухе, а скоро полетят и стрелы.

Во-первых, давайте создадим анимацию стрельбы в LevelHelper.

Переключитесь назад в LevelHelper, выберите вкладку анимации справа и нажмите кнопку “New”, чтобы создать новую анимацию. Откроется окно “Animation Builder”. Добавьте спрайты под названием front_arm, arm_front_shoot1, arm_front_shoot2, и еще раз front_arm. Выделите указанные спрайты и нажмите кнопку “+”. Снимите галочку с “Loop Forever”. Остальные дефолтные настройки нам подходят.

Нажмите “Create Animation” для сохранения. Переименуйте анимацию в “shoot”.

Перетащите объект анимации на серую зону за уровнем. Куда угодно. Нам нужно, чтобы это было доступно в нашем коде, но в коде мы собираемся расположить руки вокруг игрока, поэтому нам это не нужно внутри уровня. Если мы не перетащим их в наш уровень, мы не получим указание на них в объекте загрузчика LevelHelper’а.

После того как вы перетащили ее на уровень, выделите объект анимации и установите тип физики на “No Physic”, чтобы избежать странного поведения физики, когда мы прикрепим это к игроку.

Вернитесь к списку спрайтов. Перетащите спрайт “back_arm” за пределы уровня. Установите новую анимацию (которая называется “front_arm” потому что это первый спрайт в сете) и в “back_arm” установите “No Physic”.

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

Строка Loader:pauseAnimationOnSprite (frontarm) не дает работать анимации, когда мы запускаем игру. Мы будем вызывать анимацию каждый раз, когда необходимо стрелять, но по умолчанию, анимация запускаеся автоматически при старте игры.

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

Добавьте следующий код в конец функции newPlayer () перед строкой “return p”:

backarm.x = player.x
backarm.y = player.y
frontarm.x = player.x
frontarm.y = player.y

if self.x 320 then
self.x = 0
end

px, py = player:getLinearVelocity()
if px 0 then
frontarm.xScale = 1
backarm.xScale = 1
self.xScale = 1
end

Большая часть этого когда должна быть понятна.

Мы декларируем функции с префикстом p:. Когда мы создаем слушателя enterFrame для конкретного объекта, он должен называться “enterFrame” и он чувствителен к регистру.

Последний блок записывает линейную скорость игрока в переменных px и py. Затем, мы используем эту информацию для установки свойства XScale для всех трех.

Теперь мы должны добавть слушателя события enterFrame к объекту Runtime:

Если вы все сохраните и запустите, то увидите, что игрок вооружен и опасен!

Создаем игру Space Invaders на HTML5

В этих уроках создадим игру по типу Space Invaders на HTML5 и Javascript с помощью примитивов.

Создаем игру Space Invaders на HTML5 — 1 — Введение

Создаем игру Space Invaders на HTML5 — 2 — Цикл игры

Создаем игру Space Invaders на HTML5 — 3 — Игрок

Создаем игру Space Invaders на HTML5 — 4 — Стрельба

Создаем игру Space Invaders на HTML5 — 5 — Оптимизация

Создаем игру Space Invaders на HTML5 — 6 — Добавляем захватчиков

Создаем игру Space Invaders на HTML5 — 7 — Столкновения

Создаем игру Space Invaders на HTML5 — 8 — Стрельба Врагов

Создаем игру Space Invaders на HTML5 — 8.1 — Правим баг

Создаем игру Space Invaders на HTML5 — 9 — Добавляем звук

Создаем игру Space Invaders на HTML5 — X — Публикуем игру

Учебник по Corona SDK

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

многое переведено через гугл, но аккуратно, если есть другие уроки прошу скинуть , я добавлю в туториал (формат CHM)

Найдены возможные дубликаты

эм, чот страницы белые Win7x64

Правой кнопкой на файле, Свойства, Разблокировать. @asbcorps , в .zip надо засовывать .chm, обычная проблема.

Создание космических захватчиков с помощью Swift и Sprite Kit: реализация игрового процесса

Russian (Pусский) translation by Liliya (you can also view the original English article)

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

1. Перемещение захватчиков

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

Прежде чем мы это сделаем, нам нужно обновить свойство rightBounds . Первоначально он был установлен в , потому что нам нужно использовать size сцены для установки переменной. Мы не смогли сделать это за пределами любого из методов класса, поэтому мы обновим это свойство в методе didMoveToView(_:) .

Затем реализуем метод moveInvaders ниже метода setupPlayer , который вы создали в предыдущем учебнике.

Мы объявляем переменную changeDirection , чтобы отслеживать, когда оккупанты должны менять направление, перемещаться влево или двигаться вправо. Затем мы используем метод enumerateChildNodesWithName(usingBlock:) , который ищет дочерние узлы и вызывает замыкание один раз для каждого соответствующего узла, который он находит, с соответствующим именем «invader». Закрытие принимает два параметра, node — это узел, который соответствует name , а stop — указатель на логическую переменную, чтобы завершить перечисление. Мы не будем здесь использовать stop , но будем знать, для чего он используется.

Мы приводим node в экземпляр SKSpriteNode , который является подклассом, invader половину его ширины invaderHalfWidth и обновляет свою позицию. Затем мы проверяем, находится ли его позиция в границах, leftBounds и rightBounds , а если нет, мы устанавливаем changeDirection в true .

Если changeDirection правильное , мы отрицаем invaderSpeed , который изменит направление, в которое движется захватчик. Затем мы перечисляем через захватчиков и обновляем их позицию y. И наконец, мы изменим changeDirection на false .

Метод moveInvaders вызывается в методе update(_:) .

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

2. Стрельба пулями

Шаг 1: fireBullet

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

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

Добавьте метод fireBullet в класс InvaderBullet в InvaderBullet.swift.

В методе fireBullet мы создаем экземпляр InvaderBullet , передавая «laser» для imageName , и поскольку мы не хотим, чтобы звук воспроизводился, мы передаем nil для bulletSound . Мы устанавливаем его position таким же, как у захватчика, с небольшим смещением по позиции y и добавляем его в сцену.

Мы создаем два экземпляра SKAction , moveBulletAction и removeBulletAction . Действие moveBulletAction перемещает пулю в определенную точку за определенную продолжительность, в то время как действие removeBulletAction удаляет ее из сцены. Вызывая sequence(_:) метода для этих действий, они будут запускаться последовательно. Вот почему я упомянул метод waitForDuration при воспроизведении звука в предыдущей части этой серии. Если вы создаете объект SKAction , вызывая playSoundFileNamed(_:waitForCompletion:) и устанавливая waitForCompletion в true , тогда продолжительность этого действия будет до тех пор, пока звук будет воспроизводиться, иначе он сразу перейдет к следующему действию в последовательности.

Мастер Йода рекомендует:  Ужесточение правил монетизации YouTube-каналов

Шаг 2: invokeInvaderFire

Добавьте метод invokeInvaderFire ниже других методов, которые вы создали в GameScence.swift.

Метод runBlock(_:) класса SKAction создает экземпляр SKAction и немедленно вызывает замыкание, переданное методу runBlock(_:) . В закрытии мы вызываем метод fireInvaderBullet . Поскольку мы вызываем этот метод в закрытии, мы должны использовать self для его вызова.

Затем мы создаем экземпляр SKAction с именем waitToFireInvaderBullet , вызывая waitForDuration(_:) , передавая количество секунд ожидания перед тем, как двигаться дальше. Затем мы создаем экземпляр SKAction , invaderFire , вызывая метод sequence(_:) . Этот метод принимает набор действий, которые вызывается действием invaderFire . Мы хотим, чтобы эта последовательность повторялась постоянно, поэтому мы создаем действие с именем repeatForeverAction , передаем объекты SKAction для повторения и вызываем runAction , передавая действие repeatForeverAction . Метод runAction объявлен в классе SKNode .

Шаг 3: fireInvaderBullet

Добавьте метод fireInvaderBullet ниже метода invokeInvaderFire , введенного на предыдущем шаге.

В этом методе мы называем метод, называемый randomElement , который возвращает случайный элемент из массива invadersWhoCanFire , а затем вызывает его метод fireBullet . К сожалению, нет встроенного метода randomElement в структуре Array . Однако мы можем создать расширение Array для предоставления этой функции.

Шаг 4: Реализация randomElement

Откройте File> New> File . и выберите Swift File. Мы делаем что-то другое, чем раньше, поэтому просто убедитесь, что вы выбираете Swift File, а не Cocoa Touch Class. Нажмите Next и назовите файл Утилиты. Добавьте следующий код в Utilities.swift.

Мы расширяем структуру Array , чтобы иметь метод с именем randomElement . Функция arc4random_uniform возвращает число от 0 до того, что вы проходите. Поскольку Swift неявно не конвертирует числовые типы, мы должны сами сделать преобразование. Наконец, мы возвращаем элемент массива по индексу index .

Этот пример иллюстрирует, как легко добавлять функциональные возможности в структуру и классы. Вы можете больше узнать о создании расширений на языке Swift Programming.

Шаг 5: Стрельба пулей

С этим все в порядке, теперь мы можем стрелять из пуль. Добавьте к методу didMoveToView(_:) .

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

3. Стрельба игровыми пулями

Шаг 1: fireBullet(scene:)

Добавьте в класс Player в Player.swift следующее свойство.

Мы хотим ограничить, как часто игрок может стрелять из пули. Свойство canFire будет использоваться для его регулирования. Затем добавьте следующее к методу fireBullet(scene:) в классе Player .

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

Если игрок может стрелять, мы устанавливаем canFire на false , чтобы они не могли сразу запустить другую пулю. Затем мы создаем экземпляр PlayerBullet , передавая «laser» для параметра imageNamed . Поскольку мы хотим, чтобы звук воспроизводился, когда игрок запускает пулю, мы передаем «laser.mp3» для параметра bulletSound .

Затем мы устанавливаем позицию пули и добавляем ее на экран. Следующие несколько строк такие же, как метод FireBullet Invader , поскольку мы перемещаем маркер и удаляем его из сцены. Затем мы создаем экземпляр SKAction , waitToEnableFire , вызывая метод waitForDuration(_:) . Наконец, мы вызываем runAction , переходя в waitToEnableFire , и по завершении установки canFire возвращается к true .

Шаг 2: Стрельба игровой пулей

Всякий раз, когда пользователь прикасается к экрану, мы хотим запустить пулю. Это так же просто, как вызов fireBullet в объекте player в методе touchhesBegan(_:withEvent:) класса GameScene .

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

4. Категории столкновений

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

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

Добавьте следующее определение структуры в класс GameScene , ниже объявления invaderNum в GameScene.swift.

Мы используем структуру CollsionCategories для создания категорий для классов Invader , Player , InvaderBullet и PlayerBullet . Мы используем битовое смещение, чтобы включить бит.

5. Столкновение игроков и захватчиков

Шаг 1: Настройка InvaderBullet для Collision

Добавьте следующий код в метод init(imageName: bulletSound:) в InvaderBullet.swift.

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

Мы могли бы легко использовать инициализатор init(rectangleOfSize:) , поскольку пули имеют прямоугольную форму. В этой небольшой игре это не имеет значения. Однако имейте в виду, что использование метода init(texture:size:) может быть дорогостоящим, поскольку оно должно вычислять точную форму текстуры. Если у вас есть объекты прямоугольной или круглой формы, вы должны использовать эти типы инициализаторов, если производительность игры становится проблемой.

Для обнаружения столкновения для работы, по крайней мере одно из тел, которое вы тестируете, должно быть отмечено как динамическое. Установив для свойства usesPreciseCollisionDetection значение true , Sprite Kit использует более точное обнаружение столкновений. Установите для этого свойства значение true на маленьких, быстро движущихся телах, таких как наши пули.

Каждое тело будет принадлежать к категории, и вы определяете это, установив его categoryBitMask . Поскольку это класс InvaderBullet , мы устанавливаем его в CollisionCategories.InvaderBullet .

Чтобы сообщить, когда это тело коснулось другого тела, которое вас интересует, вы устанавливаете contactBitMask . Здесь мы хотим знать, когда InvaderBullet установил контакт с игроком, поэтому мы используем CollisionCategories.Player . Поскольку столкновение не должно вызывать никаких физических сил, мы устанавливаем collisionBitMask в 0x0 .

Шаг 2: Настройка Player для столкновения

Добавьте в метод init в Player.swift следующее.

Большая часть этого должна быть знакома с предыдущего шага, поэтому я не буду перефразировать его здесь. Однако есть две отличия. Один из них заключается в том, что usePreciseCollsionDetection имеет значение false , которое является значением по умолчанию. Важно понимать, что только одно из контактирующих органов требует, чтобы это свойство было true (это была пуля). Другое отличие состоит в том, что мы также хотим знать, когда игрок связывается с захватчиком. Вы можете иметь более одной категории contactBitMask , разделяя их с помощью побитового или ( | ) оператора. Кроме этого, вы должны заметить, что это просто в основном противоположно InvaderBullet .

6. Invader and PlayerBullet столкновение

Шаг 1. Настройка Invader для столкновения

Добавьте метод init в Invader.swift.

Это должно иметь смысл, если вы следовали. Мы создали physicsBody , categoryBitMask и contactBitMask .

Шаг 2: Настройка PlayerBullet для столкновения

Добавьте в init(imageName:bulletSound:) в PlayerBullet.swift следующее. Опять же, реализация должна быть в настоящем времени.

7. Настройка физики для GameScene

Шаг 1. Настройка физического мира

Мы должны создать класс GameScene для реализации SKPhysicsContactDelegate , чтобы мы могли ответить, когда сталкиваются два тела. Добавьте следующее, чтобы класс GameScene соответствовал протоколу SKPhysicsContactDelegate .

Затем мы должны создать некоторые свойства на physicsWorld сцены. Введите в верхней части метода didMoveToView(_:) в GameScene.swift следующее.

Мы устанавливаем gravity свойство physicsWorld равным , так что ни одно физическое тело на сцене не влияет на гравитацию. Вы также можете сделать это на основе тела, вместо того, чтобы заставить весь мир не иметь силы тяжести, установив свойство affectedByGravity . Мы также установили свойство contactDelegate в физическом мире для self — экземпляра GameScene .

Шаг 2: Внедрение SKPhysicsContactDelegate протокола

Чтобы соответствовать классу GameScene протоколу SKPhysicsContactDelegate , нам необходимо реализовать метод didBeginContact(_:) . Этот метод вызывается при контакте двух тел. Реализация метода didBeginContact(_:) выглядит так.

Сначала объявляем две переменные firstBody и secondBody . Когда два объекта контактируют, мы не знаем, какое именно тело есть. Это означает, что сначала нам нужно сделать некоторые проверки, чтобы убедиться, что firstBody — это тот, у которого нижняя categoryBitMask .

Затем мы рассмотрим каждый возможный сценарий, используя побитовый & оператор и категории столкновений, которые мы определили ранее, чтобы проверить, что делает контакт. Мы регистрируем результат на экране, чтобы убедиться, что все работает так, как должно. Если вы протестируете приложение, все контакты должны работать правильно.

Заключение

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

Пишем Space Invaders при помощи Corona. Заключение

void Main()
<
// varitables
IMyPistonBase DoorPiston = GridTerminalSystem.GetBlockWithName(«DoorPiston») as IMyPistonBase;
IMySensorBlock DoorSensor = GridTerminalSystem.GetBlockWithName(«DoorSensor») as IMySensorBlock;
IMySensorBlock CloseSensor = GridTerminalSystem.GetBlockWithName(«CloseSensor») as IMySensorBlock;
IMySoundBlock SoundMessage = GridTerminalSystem.GetBlockWithName(«SoundMessage») as IMySoundBlock;
IMyLargeInteriorTurret DoorTurret= GridTerminalSystem.GetBlockWithName(«DoorTurret») as IMyLargeInteriorTurret; //
IMyLargeInteriorTurret DoorTurret2= GridTerminalSystem.GetBlockWithName(«DoorTurret2») as IMyLargeInteriorTurret; //

if (DoorSensor.DetectOwner == true || CloseSensor.DetectOwner == true)
<
DoorPiston.GetActionWithName(«Reverse»).Apply(DoorPiston);
DoorTurret.GetActionWithName(«OnOff»).Apply(DoorTurret);
DoorTurret2.GetActionWithName(«OnOff»).Apply(DoorTurret2);
SoundMessage.GetActionWithName(«PlaySound»).Apply(SoundMessage);
>
>

Я далек от программирования, однако любопытство взяло верх над полным отсутствием грамотности в этом вопросе. Решил по аналогии запилить освещение.
Суть: Подходишь к прожектору (в зону досягаемости сенсора) — он включается. Отходишь (в зону недосягаемости сенсора) — выключается.
Первое действие получилось — прожектор включается. А вот с выключением не выходит.
Для того чтобы прожектор погас, нужно выйти из зоны действия прожектора и зайти в нее обратно. Причем значение «true» для сенсора совсем не обязательно и сенсор вызывает действие скрипта на прог.блоке без него.

Но все по прежнему.

Пробовал и в таком:

В таком случае прожектор включается и не выключается вообще.
Т.е. значения true и false в нашем случае не работают? И почему? Или они вообще в игровом коде не задействуются?
Подумал о такой вещи как бездействие сенсора, т.е. код отсутствия действия, или состояние бездействия как еще одно действие. Например: Sensor.NotDetect
Но такого не нашел.

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

Если кому-то поможет сделать скрипт проще и короче.

В данном примере есть создание новой переменной блока «Поршня»

IMyPistonBase DoorPiston = GridTerminalSystem.GetBlockWithName(«DoorPiston») as IMyPistonBase

IMyPistonBase — тип переменной блока;

DoorPiston — имя переменной; (лучше его задавать одноимённым с названием блока в терминале, дабы избежать путаницы)

«DoorPiston» — то, что в скобках после GetBlockWithName(), есть название блока в терминале

as IMyPistonBase — данное выражение говорит о том, что всю строку после знака «=» надо считать как тип данных IMyPistonBase (важно, иначе присвоение значения переменной не произойдёт);

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

Внутри есть строка действия:

Данный код понимается так: блок DoorPiston, произведи поиск действия в своём списке действий по точному названию «Reverse» (GetActionWithName() — поиск и получения действия из списка действий, доступных блоку по точному названию) и примени его к блоку DoorPiston (Apply() — применить к блоку в скобках действие, которое мы получили).

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

Читается так: блок DoorPiston, произведи действие Reverse.

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

IMyPistonBase DoorPiston = GridTerminalSystem.GetBlockWithName(«DoorPiston») as IMyPistonBase;
DoorPiston.Reverse();
//вместо DoorPiston.GetActionWithName(«Reverse»).Apply(DoorPiston);

IMyLargeInteriorTurret DoorTurret= GridTerminalSystem.GetBlockWithName(«DoorTurret») as IMyLargeInteriorTurret;
DoorTurret.OnOff();
//вместо DoorTurret.GetActionWithName(«OnOff»).Apply(DoorTurret);

IMySoundBlock SoundMessage = GridTerminalSystem.GetBlockWithName(«SoundMessage») as IMySoundBlock;
SoundMessage.PlaySound();
//вместо SoundMessage.GetActionWithName(«PlaySound»).Apply(SoundMessage);

. да и еще не совсем понятно куда делся оператор ELSE или в данной версии языка он отсутствует?

Оператор else в коде работает и он записывается так:

Продолжим рассматривать всё тот же поршень DoorPiston.

В различных источниках можно найти все возможные действия и значения для каждого типа блока. Вот весь список типов переменных полей и действий для блока «Поршень»

Interface name: IMyPistonBase
Parent: IMyFunctionalBlock
Fields:
float Velocity
float MinLimit
float MaxLimit
Terminal properties:
Velocity -> Single
UpperLimit -> Single
LowerLimit -> Single
Actions:
OnOff -> Toggle block On/Off
OnOff_On -> Toggle block On
OnOff_Off -> Toggle block Off
Reverse -> Reverse
IncreaseVelocity -> Increase Velocity
DecreaseVelocity -> Decrease Velocity
ResetVelocity -> Reset Velocity
IncreaseUpperLimit -> Increase Maximal distance
DecreaseUpperLimit -> Decrease Maximal distance
IncreaseLowerLimit -> Increase Minimal distance
DecreaseLowerLimit -> Decrease Minimal distance

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

Fields:
float Velocity
float MinLimit
float MaxLimit

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

Terminal properties:
Velocity -> Single
UpperLimit -> Single
LowerLimit -> Single

sasha_lotus

Старость меня дома не застанет!

Фоторассказы о путешествиях, стрит-арт и мысли вслух

Космические захватчики от Invader

Многие забывают, что в фильме Banksy «Выход через сувенирную лавку» помимо самого художника, Shepard Fairey и оператора, впоследствии известного как Mr Brainwash, мелькает ещё одна культовая в мире уличного искусства фигура — француз Invader со своими мозаичными «космическими захватчиками».

Мозаики от Invader уже встречались мне в Вене, Амстердаме и Берлине, и теперь я нагрянул в его родной город, где хранящий анонимность уличный художник оставил почти 1400 работ, начиная с 1998го года. Одни из них превосходно сохранились, другие оказались испорченными вандалами, пытавшимися сорвать их и продать на аукционе, часть после отреставрирована как самим Invader, так и поклонниками его творчества. Гуляя по улицам Парижа, Вы непременно встретите десяток другой пиксельных панно с различными забавными сюжетами или персонажами популярной аркадной видеоигры «Space Invaders», выпущенной японской компанией в 1978м году. Всего на данный момент Invader создал более 3700 уникальных работ в 77 городах мира, каждой мозаике присвоен свой порядковый номер, например, чеширский кот носит код PA_1156.

Мастер Йода рекомендует:  Управление паролями краткое руководство

В Париже я обнаружил чуть больше двух десятков работ Invader, две из них, Доктора Хауса PA_1205 и коллаб с американским граффитчиком Адамом Cost PA_1104, я показывал в первой части обзора парижского стрит-арта ранее, сейчас же покажу оставшиеся и немного расскажу про захватнический концепт, что художник вложил в свой масштабный проект «Space Invaders».

Invader всегда работает в маске, и даже на редких выставках, на открытии которых он появляется официально, художник предстаёт перед посетителями в маске, чтобы сохранить анонимность. После Вы можете ходить и рассматривать мозаики, а Invader будет стоять рядом с Вами в роли такого же рядового посетителя, наблюдая за реакцией. Что же вдохновило француза на столь масштабный проект, которому он верен более 20 лет? В первую очередь, Invader стремился вывести искусство на свободу, за границы галерей и музеев; во-вторых, ему хотелось, чтобы космические захватчики из видеоигры попали в реальный мир. Всё началось в тот день, когда его посетила идея сначала воспроизвести пиксельность с помощью керамических плиток — тогда первичный замысел создания серии панно для галерей современного искусства переродился в стремление размещать мозаики на стенах зданий в Париже, а затем и в других городах мира.

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

Документируя, фотографируя и ведя архив всех своих мозаик, начиная с 1998го года, художник создал мобильное приложение «Flash Invaders», представляющее собой своеобразную игру. Вы фотографируете найденную мозаику, загружаете в приложение, оно идентифицирует, видимо, через GPS, что вы не просто загрузили скриншот из инстаграма, и присваивает очки в зависимости от степени доступности мозаики. Рейтинги и всё прочее прилагается.

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

6.
PA_1057, PA_1062 и PA_470

Принцесса Лея PA_1190 в компании маленьких работ от местных художников, чьи имена я назову в другом посте.

Самый труднодоступный космический захватчик размещён на Международной Космической Станции. Также придётся постараться и уметь хорошо нырять, чтобы найти мозаику в море около Канкуна в Мексике. Наверное, кому-то может показаться весь этот процесс поиска плиточек по городам не серьёзным занятием, но поверьте — у творчества Invader за столько лет скопилось очень много поклонников всех возрастов.

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

Invader обычно создаёт в городе в пределах 20-50 работ в рамках так называемого «invasion» — вторжения, иногда он возвращается по нескольку раз, как он сам говорит, волнами — «invasion waves». В некоторые страны въезд ему уже запрещён из-за проблем с полицией, думается, что речь идёт о Гонконге и Японии, где до недавнего времени со стрит-артом дела обстояли очень плохо, и любое вмешательство уличных художников в городскую среду каралось большими штрафами или мерами посерьёзнее.

Поэтому Invader, как и многие художники, привык клеить на цемент или самый инновационный клей своих захватчиков по ночам, 99% создаётся нелегально. Иногда их быстро отдирают недовольные владельцы зданий, но подобное случается довольно редко.

PA_802 в компании затеганного портрета от француза Atma.

Надеюсь, что во время следующего визита в Париж я обзаведусь местной сим-картой или куплю заранее Drimsim, чтобы было проще опознавать захватчиков через приложение. Keep it locked!

Другие посты о стрит-арте Парижа:

Стрит-арт Парижа. Часть 1 INTI. Латиноамериканский синкретизм

Понравился пост? Подписывайтесь на обновления журнала
Если Вам удобнее следить за анонсами постов в социальных сетях, подписывайтесь на мои странички в VK, Facebook и Instagram.

Часто задаваемые вопросы

Corona правда бесплатный движок?

Corona, включая Corona Simulator и Corona Native, совершенно бесплатный. Мы также предлагаем ассортимент плагинов, расширяющих базовый функционал движка — большинство из них бесплатные, но некоторые нужно покупать, например Splash Screen Contol. Более того, в Маркетплейсе есть более 100 сторонних плагинов — многие из них также бесплатны, но на некоторых установлена цена, заданная их разработчиками.

Внимание: у Apple, Google и Amazon есть требования о наличии сертификата разработчика, чтобы публиковать приложения в их магазинах. Такие сертификаты вы оплачиваете сами.

Как удалить или изменить заставку Corona Labs?

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

В чем разница между базовым фреймворком Corona и Corona Native?

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

Corona Native (доступен разработчикам для Windows и macOS) позволяет интегрировать проекты на Xcode или Android Studio в ваш код на Lua. Вы сможете использовать API и плагины Corona, но также — добавлять библиотеки через Objective-C и Swift для платформ Apple, Java — для Android и C / C++ — для обоих типов платформ. Вы сможете делать сборки в командной строке, и вам не нужен постоянный доступ к интернету.

Как мне получить движок?

Вы просто скачиваете его здесь. После загрузки и установки вы сразу сможете приступить к разработке приложений. Мы не поставляем дисков или других физических объектов.

На каких платформах работает Corona?

Corona работает на macOS и Windows, Corona Native — только на macOS.

Внимание: из-за ограничений Apple вы не можете создавать приложения для iOS, macOS, или tvOS, используя версию Corona для Windows. Но вы сможете использовать код Lua, написанный на машине с Windows, в Mac, если вы хотите работать на платформы Apple.

Для каких платформ я могу создавать приложения?

На данный момент Corona поддерживает разработку приложений для:

  • iOS
  • Android
  • Amazon Kindle
  • Windows desktop
  • macOS desktop
  • Apple TV
  • Android TV
  • Steam
  • HTML5 (BETA)

Почему Corona требуется выход в интернет, чтобы делать сборки?

Потому, что часть сборки выполняется на серверах Corona Labs. Однако мы никогда не видим ваш “исходный” код, изображения, звуки или другие данные. Когда вы запускаете онлайн-сборку, код Lua пре-компилируется в байткод перед отправкой на сервер (убираются комментарии, информация для отладки и т.п.). Сервер вставляет эти данные в движок Corona, но не сохраняет и не архивирует их. Когда сборка завершена — вы получаете приложение или исполняемый файл, как если бы вы использовали нативный SDK.

Что делать, если мне нужны офлайн-сборки?

Как мне рассказать вам о баге?

Пожалуйста, используйте для этого специальную форму. Чтобы мы смогли изолировать и проверить баг, мы просим вас приложить простой тестовый проект, который наглядно иллюстрирует проблему. Такой проект должен содержать файлы config.lua и main.lua, а также — дополнительные ресурсы, необходимые для демонстрации бага.

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

Публикует ли Corona Labs мои приложения?

Нет. Вы делаете приложение на фреймворке Corona и самостоятельно отсылаете его на Apple, Google или Amazon через их программы для разработчиков. Но мы работаем с паблишинг-партнерами и можем вам помочь. Если вас это заинтересовало — пишите на publishing@coronalabs.com.

Удерживает ли Corona Labs комиссию от дохода моих приложений?

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

Обеспечивает ли Corona Labs техподдержку?

Разработчики Corona могут обратиться за помощью к сообществу коллег через наш форум. Это коммьюнити очень активно, и зачастую там отвечают на все вопросы сразу после того, как их запостили. Команда движка также принимает активное участие в жизни форума и регулярно отвечает на вопросы наравне с другими пользователями.

Мы также предлагаем платную техподдержку. Она будет полезна разработчикам, которым требуется:

  • Выявление и решение проблем
  • Примеры кода
  • Отладка
  • Анализ кода

Предлагает ли Corona Labs обучение?

Да, мы предлагаем базовое персональное обучение для разработчиков, которые хотят начать работать с Corona. Перейдите на страницу Техподдержка и Обучение.

Как мне найти разработчика на Corona?

Мы рекомендуем сделать пост в ветке Corona Classifieds Jobs на форуме.

Corona не поддерживает функцию, которая мне нужна. Что делать?

Мы постоянно работаем над добавлением новых функций в движок. Если вам не хватает расширенной базы и плагинов, Corona Native позволяет интегрировать проекты на Xcode или Android Studio в ваш код на Lua. Вы также можете голосовать за нововведения на специальном сайте.

Если я использую Corona, нужно ли мне платить за программы для разработчиков Apple, Google и Amazon?

Да. Вы можете бесплатно использовать Corona для разработки приложений и тестировать их в Corona Simulator, но у Apple, Google и Amazon есть требования о присоединении к программам для разработчиков перед деплоем приложения в их магазинах. Такие программы подразумевают взносы, которые вы оплачиваете сами.

Что такое daily builds?

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

Пишем Space Invaders при помощи Corona. Заключение

Создадим папку с проектом и сделаем файл main.lua, в котором пропишем дефолтные три функции:

200?’200px’:»+(this.scrollHeight+5)+’px’);»>
function love.load()

Я думаю вы знаете что они делают, иначе бы вы не зашли сюда. Теперь мы создадим таблицу с нашем героем, love.load пишем:

Тут все просто, мы создаем таблицу в который записаны будущие координаты по осям x,y и ширина с высотой. Давайте заставим его двигаться. В love.update(dt) пишем:

Тут так же все просто, мы проверяем нажатия клавиш и изменяем положение персонажа по оси x. Настало время нарисовать его, в love.draw:

200?’200px’:»+(this.scrollHeight+5)+’px’);»>
function love.draw()
love.graphics.setColor(0,0,255,255)
love.graphics.rectangle(«fill»,hero.x,hero.y,hero.w,hero.h)

love.graphics.setColor(0,255,0,255)
love.graphics.rectangle(«fill»,0,532,love.graphics:getWidth(),68)
end

Тут так же все просто, первой строчкой мы изменяем цвет в палитре RGB на синий, ведь Red Green Blue, первое значение отвечает за красный цвет, второе за зеленый, третье за синий и наконец четвертое за прозрачность. Дальше мы рисуем прямоугольник в координатах и с такой шириной/высотой, которые мы указали в love.load. Следующие строчки делают тоже самое, но ставят другой цвет и рисуют землю ниже нашего героя. ( метод love.graphics:getWidth() получает ширину экрана, полезная функция). Дальше мы сделаем врагов. В love.load добавляем:

for i =0,7 do
enemy = <>
enemy.w = 32
enemy.h = 16
enemy.x = i * (enemy.w + 50) + 100
enemy.y = 120
table.insert(enemies, enemy)
end

Тут чуть сложнее, мы создаем пустую таблицу, о ней чуть позже, а потом циклом создаем еще 8 в которых мы описываем наших врагов и потом вставляем это в нашу пустую таблицу. Думаю все понятно, теперь можно и нарисовать их. В love.draw добавляем:

Тут мы выставляем красный цвет и потом перебираем таблиц enemies и рисуем в нужных координатах, с нужной высотой/шириной.
Теперь пусть они начнут падать, в love.update(dt) пишем:

Тут мы перебираем таблицу enemies и и изменяем переменную y. Можно тестить.

Теперь мы подошли к финальной части нашего урока, стрельба. В love.load пишем:

Мы просто создаем пустую таблицу. Дальше дописываем функцию:

В ней мы проверяем нажатие пробела и дальше создаем новую таблицу с данными пули и запихиваем это в нашу пустую таблицу bullets
Теперь в love.update(dt) пишем:

Объяснять не буду, это было ранее. Теперь нарисуем их. love.draw:

Остался финальный штрих, столкновение пули и врага. Дописываем функцию взятую с вики.

Как исключить объект из Corona Sky?

С этим материалом свет проходит в комнату, но если не заполняю gi, не видно картинку фоновую. Как настроить материал, чтобы было ее видно? Пока что получается только в случае, если кинуть ее на gi, но тогда закрываем свет corona sky опять..

С этим материалом свет проходит в комнату, но если не заполняю gi, не видно картинку фоновую. Как настроить материал, чтобы было ее видно? Пока что получается только в случае, если кинуть ее на gi, но тогда закрываем свет corona sky опять..

Не может такого быть, gi никак не влияет на отображение текстуры, да и вообще самого материала на рендере. За это отвечает directly visible.

Должно быть так:

Global illumination — пустой (None). Удалите всё, что там есть.

Reflection — ваш материал

Refraction — ваш материал

Directly visible — ваш материал

оставте материал фона только в Directly visible, остальные слоты оставте пустыми

а зачем исключать из ская? чтобы фон не был таким ярким?

делайте его материалом corona light и сможете регулировать его яркость. и не придется исключать из ская

оставте материал фона только в Directly visible, остальные слоты оставте пустыми

а зачем исключать из ская? чтобы фон не был таким ярким?

делайте его материалом corona light и сможете регулировать его яркость. и не придется исключать из ская

Оставляю только в Directly visible и получаю тот же результат(первая картинка)- не видно фон..может в настройках Directly visible у меня что-то неверно..

Исключать из ская, потому что иначе «фоновый полуцилиндр» становится барьером для него) Как на второй картинке) Фон есть, он сам по себе светится, а свет корона скай не проходит в сцену. Ну вот думала, что это было бы решением)) По аналогии с солнцем)

С этим материалом свет проходит в комнату, но если не заполняю gi, не видно картинку фоновую. Как настроить материал, чтобы было ее видно? Пока что получается только в случае, если кинуть ее на gi, но тогда закрываем свет corona sky опять..

Не может такого быть, gi никак не влияет на отображение текстуры, да и вообще самого материала на рендере. За это отвечает directly visible.

Должно быть так:

Global illumination — пустой (None). Удалите всё, что там есть.

Reflection — ваш материал

Refraction — ваш материал

Directly visible — ваш материал

Да, я понимаю, что работать должно) Но у меня видимо что-то не так в настройках Directly visible. Не видно картинку.

Добавить комментарий