Делаем игру для iPhone в стиле Mega Jump – Часть 2

1
в закладки
29 минут
Делаем игру для iPhone в стиле Mega Jump – Часть 2

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

"Нажмите, чтобы начать"

Оригинал статьи на английском языке можно прочитать здесь.

Теперь, когда гравитация в игре работает, нужно сделать физическое тело героя неподвижным до тех пор, пока пользователь не решит начать игру. Внутри файла GameScene.swift найдите строку в методе createPlayer, которая устанавливает свойство dynamic физическому телу объекта playerNode. Измените его значение на false, так как показано здесь:

playerNode.physicsBody?.dynamic <span style="color: #002200;">=</span> <span style="color: #a61390;">false</span>

Мы собираемся написать код, который позволит пользователю начать игру нажав на экран. И об этом его нужно предупредить какой-нибудь надписью. Например: "Tap to start" (Нажмите, чтобы начать). Давайте разместим эту инструкцию на экране.

Поскольку инструкции будут располагаться в слое HUD, создайте его первым. В методе init(size:) сразу после места, где вы добавили узел переднего плана на сцену, вставьте следующий код:

<span style="color: #11740a; font-style: italic;">// HUD</span> hudNode <span style="color: #002200;">=</span> <span style="color: #400080;">SKNode</span><span style="color: #002200;">(</span><span style="color: #002200;">)</span> addChild<span style="color: #002200;">(</span>hudNode<span style="color: #002200;">)</span>

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

<span style="color: #11740a; font-style: italic;">// Tap To Start node</span> <span style="color: #a61390;">let</span> tapToStartNode <span style="color: #002200;">=</span> <span style="color: #400080;">SKSpriteNode</span><span style="color: #002200;">(</span>imageNamed<span style="color: #002200;">:</span> <span style="color: #bf1d1a;">"TapToStart"</span><span style="color: #002200;">)</span>

Чтобы отобразить созданный узел, добавьте следующий код в метод init(size:) сразу после строки, которая добавляет узел героя:

<span style="color: #11740a; font-style: italic;">// Tap to Start</span> tapToStartNode.position <span style="color: #002200;">=</span> <span style="color: #400080;">CGPoint</span><span style="color: #002200;">(</span>x<span style="color: #002200;">:</span> <span style="color: #a61390;">self</span>.size.width <span style="color: #002200;">/</span> <span style="color: #2400d9;">2</span>, y<span style="color: #002200;">:</span> <span style="color: #2400d9;">180.0</span><span style="color: #002200;">)</span> hudNode.addChild<span style="color: #002200;">(</span>tapToStartNode<span style="color: #002200;">)</span>

Постройте и запустите, чтобы увидеть надпись "Tap to start" чуть выше спрайта героя:

Делаем игру для iPhone в стиле Mega Jump – Часть 2

Для обработки касаний и для начала игры, добавьте следующий метод в класс GameScene.swift:

<span style="color: #a61390;">override</span> <span style="color: #a61390;">func</span> touchesBegan<span style="color: #002200;">(</span>touches<span style="color: #002200;">:</span> <span style="color: #400080;">NSSet</span>, withEvent event<span style="color: #002200;">:</span> <span style="color: #400080;">UIEvent</span><span style="color: #002200;">)</span> <span style="color: #002200;">{</span> <span style="color: #11740a; font-style: italic;">// 1</span> <span style="color: #11740a; font-style: italic;">// If we're already playing, ignore touches</span> <span style="color: #a61390;">if</span> player.physicsBody<span style="color: #002200;">!</span>.dynamic <span style="color: #002200;">{</span> <span style="color: #a61390;">return</span> <span style="color: #002200;">}</span> <span style="color: #11740a; font-style: italic;">// 2</span> <span style="color: #11740a; font-style: italic;">// Remove the Tap to Start node</span> tapToStartNode.removeFromParent<span style="color: #002200;">(</span><span style="color: #002200;">)</span> <span style="color: #11740a; font-style: italic;">// 3</span> <span style="color: #11740a; font-style: italic;">// Start the player by putting them into the physics simulation</span> player.physicsBody?.dynamic <span style="color: #002200;">=</span> <span style="color: #a61390;">true</span> <span style="color: #11740a; font-style: italic;">// 4</span> player.physicsBody?.applyImpulse<span style="color: #002200;">(</span>CGVector<span style="color: #002200;">(</span>dx<span style="color: #002200;">:</span> <span style="color: #2400d9;">0.0</span>, dy<span style="color: #002200;">:</span> <span style="color: #2400d9;">20.0</span><span style="color: #002200;">)</span><span style="color: #002200;">)</span> <span style="color: #002200;">}</span>

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

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

2. Удаляем узел с надписью "Tap to start".

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

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

Постройте и запустите. При нажатии на экран, надпись «Tab to start» исчезнет, а спрайт героя подбросится вверх, но ненадолго - сила тяжести берет своё.

Игровые объекты: дотянуться до звезд!

Чтобы вашему герою было чем заняться, кроме как просто прыгать вверх, самое время добавить "звезды". Звезды играют важную роль в Uber Jump: именно их игрок должен собирать для перехода на следующий уровень.

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

По сценарию игры Uber Jump, как и Mega Jump, когда спрайт героя поднимается на определенное расстояние выше звезды, платформы или любого другого объекта, этот объект удаляется со сцены. Так как и узлы и звезды и платформы попадают под это условие, имеет смысл создать подкласс SKNode для всех игровых объектов.

Создайте новый класс Cocoa Touch Class с названием GameObjectNode и сделайте его подклассом SKNode. Убедитесь, что в качестве языка установлен Swift.

Делаем игру для iPhone в стиле Mega Jump – Часть 2

Класс GameObjectNode будет включать в себя следующий функционал:

1. Удаление из сцены того объекта, от которого узел героя отдалился на расстояние, больше чем заданное.

2. Обнаружение столкновений между узлом героя и объектом. Этот метод вернет булевое значение, которое будет уведомлять, произошло ли пересечение героя с каким-либо объектом игры, которое приведет к необходимости обновления слоя HUD. Например, если игрок набрал очки.

Замените код в GameObjectNode.swift на следующий:

<span style="color: #a61390;">import</span> SpriteKit <span style="color: #a61390;">class</span> GameObjectNode<span style="color: #002200;">:</span> <span style="color: #400080;">SKNode</span> <span style="color: #002200;">{</span> <span style="color: #a61390;">func</span> collisionWithPlayer<span style="color: #002200;">(</span>player<span style="color: #002200;">:</span> <span style="color: #400080;">SKNode</span><span style="color: #002200;">)</span> <span style="color: #002200;">-</span>> <span style="color: #a61390;">Bool</span> <span style="color: #002200;">{</span> <span style="color: #a61390;">return</span> <span style="color: #a61390;">false</span> <span style="color: #002200;">}</span> <span style="color: #a61390;">func</span> checkNodeRemoval<span style="color: #002200;">(</span>playerY<span style="color: #002200;">:</span> <span style="color: #400080;">CGFloat</span><span style="color: #002200;">)</span> <span style="color: #002200;">{</span> <span style="color: #a61390;">if</span> playerY > <span style="color: #a61390;">self</span>.position.y <span style="color: #002200;">+</span> <span style="color: #2400d9;">300.0</span> <span style="color: #002200;">{</span> <span style="color: #a61390;">self</span>.removeFromParent<span style="color: #002200;">(</span><span style="color: #002200;">)</span> <span style="color: #002200;">}</span> <span style="color: #002200;">}</span> <span style="color: #002200;">}</span>

Мы вызываем метод collisionWithPlayer всякий раз, когда узел героя сталкивается с объектом, а также вызываем checkNodeRemoval в каждом кадре, чтоб была возможность сразу удалить ненужный узел со сцены.

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

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

Класс звезды

Теперь, когда у нас есть базовый класс для интерактивных узлов игры, мы можем создать класс для звезд. Чтобы не усложнять, добавим все подклассы GameObjectNode сразу в файл GameObjectNode.swift. Добавьте следующий код после класса GameObjectNode.

<span style="color: #a61390;">class</span> StarNode<span style="color: #002200;">:</span> GameObjectNode <span style="color: #002200;">{</span> <span style="color: #a61390;">override</span> <span style="color: #a61390;">func</span> collisionWithPlayer<span style="color: #002200;">(</span>player<span style="color: #002200;">:</span> <span style="color: #400080;">SKNode</span><span style="color: #002200;">)</span> <span style="color: #002200;">-</span>> <span style="color: #a61390;">Bool</span> <span style="color: #002200;">{</span> <span style="color: #11740a; font-style: italic;">// Boost the player up</span> player.physicsBody?.velocity <span style="color: #002200;">=</span> CGVector<span style="color: #002200;">(</span>dx<span style="color: #002200;">:</span> player.physicsBody<span style="color: #002200;">!</span>.velocity.dx, dy<span style="color: #002200;">:</span> <span style="color: #2400d9;">400.0</span><span style="color: #002200;">)</span> <span style="color: #11740a; font-style: italic;">// Remove this Star</span> <span style="color: #a61390;">self</span>.removeFromParent<span style="color: #002200;">(</span><span style="color: #002200;">)</span> <span style="color: #11740a; font-style: italic;">// The HUD needs updating to show the new stars and score</span> <span style="color: #a61390;">return</span> <span style="color: #a61390;">true</span> <span style="color: #002200;">}</span> <span style="color: #002200;">}</span>

Столкновение со звездой подбрасывает узел героя вверх по оси ординат. Вы можете спросить: "почему для этого мы не используем силу или импульс физического движка?"

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

Следующая диаграмма очень наглядно это показывает:

Делаем игру для iPhone в стиле Mega Jump – Часть 2

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

Скорость по оси X не должна изменяться, так как на неё влияет только акселерометр, который мы реализуем позже. В вышеуказанном способе при столкновении мы устанавливаем фиксированное значение скорости вертикальной проекции - 400. Чтобы столкновение имело одинаковые последствия независимо от того, что делал игрок перед тем как столкнуться со звездой.

Откройте GameScene.swift и добавьте следующий метод:

<span style="color: #a61390;">func</span> createStarAtPosition<span style="color: #002200;">(</span>position<span style="color: #002200;">:</span> <span style="color: #400080;">CGPoint</span><span style="color: #002200;">)</span> <span style="color: #002200;">-</span>> StarNode <span style="color: #002200;">{</span> <span style="color: #11740a; font-style: italic;">// 1</span> <span style="color: #a61390;">let</span> node <span style="color: #002200;">=</span> StarNode<span style="color: #002200;">(</span><span style="color: #002200;">)</span> <span style="color: #a61390;">let</span> thePosition <span style="color: #002200;">=</span> <span style="color: #400080;">CGPoint</span><span style="color: #002200;">(</span>x<span style="color: #002200;">:</span> position.x <span style="color: #002200;">*</span> scaleFactor, y<span style="color: #002200;">:</span> position.y<span style="color: #002200;">)</span> node.position <span style="color: #002200;">=</span> thePosition node.name <span style="color: #002200;">=</span> <span style="color: #bf1d1a;">"NODE_STAR"</span> <span style="color: #11740a; font-style: italic;">// 2</span> <span style="color: #a61390;">var</span> sprite<span style="color: #002200;">:</span> <span style="color: #400080;">SKSpriteNode</span> sprite <span style="color: #002200;">=</span> <span style="color: #400080;">SKSpriteNode</span><span style="color: #002200;">(</span>imageNamed<span style="color: #002200;">:</span> <span style="color: #bf1d1a;">"Star"</span><span style="color: #002200;">)</span> node.addChild<span style="color: #002200;">(</span>sprite<span style="color: #002200;">)</span> <span style="color: #11740a; font-style: italic;">// 3</span> node.physicsBody <span style="color: #002200;">=</span> SKPhysicsBody<span style="color: #002200;">(</span>circleOfRadius<span style="color: #002200;">:</span> sprite.size.width <span style="color: #002200;">/</span> <span style="color: #2400d9;">2</span><span style="color: #002200;">)</span> <span style="color: #11740a; font-style: italic;">// 4</span> node.physicsBody?.dynamic <span style="color: #002200;">=</span> <span style="color: #a61390;">false</span> <span style="color: #a61390;">return</span> node <span style="color: #002200;">}</span>

Код, представленный выше, должен быть вам уже знаком:

1. Мы создаем экземпляр класса StarNode и устанавливаем его позицию.

2. Затем добавляем графику звезды, используя SKSpriteNode.

3. Добавляем к узлу физическое тело в форме круга, которое нам понадобится для обнаружения столкновений с другими объектами игры.

4. Наконец, мы делаем физическое тело статическим, потому что не хотим, чтобы гравитация или любые другие физические симуляции влияли на звезды.

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

<span style="color: #11740a; font-style: italic;">// Add a star</span> <span style="color: #a61390;">let</span> star <span style="color: #002200;">=</span> createStarAtPosition<span style="color: #002200;">(</span><span style="color: #400080;">CGPoint</span><span style="color: #002200;">(</span>x<span style="color: #002200;">:</span> <span style="color: #2400d9;">160</span>, y<span style="color: #002200;">:</span> <span style="color: #2400d9;">220</span><span style="color: #002200;">)</span><span style="color: #002200;">)</span> foregroundNode.addChild<span style="color: #002200;">(</span>star<span style="color: #002200;">)</span>

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

Делаем игру для iPhone в стиле Mega Jump – Часть 2

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

А вот решение:

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

Обнаружение столкновений и битовые маски

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

Самое время рассмотреть тему пересечения битовых масок в Sprite Kit.

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

1. categoryBitMask определяет категорию столкновения, к которой относится физическое тело.

2. collisionBitMask устанавливает категорию столкновения тел, с которыми данное тело будет пересекаться. Здесь слово "пересекаться" означает, что объекты будут именно сталкиваться друг с другом. Например, играя в качестве стрелка от третьего лица, вам будет нужно, чтобы спрайт героя сталкивался с вражескими спрайтами, но проходил сквозь спрайты других игроков.

3. contactTestBitMask сообщает движку Sprite Kit, чтобы он уведомлял вас, когда данное физическое тело вступает в контакт с физическими телами, принадлежащими к одной из указанных вами категорий. Например, в нашей игре мы хотим, чтобы Sprite Kit сообщал нам, когда спрайт игрока касается звезды или платформы. Используя правильные сочетания настроек для contactTestBitMask и collisionBitMask, можно запрограммировать Sprite Kit так, чтобы объекты проходили друг сквозь друга, а он уведомлял вас, когда это происходит. Таким образом мы можем инициировать события.

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

<span style="color: #a61390;">struct</span> CollisionCategoryBitmask <span style="color: #002200;">{</span> <span style="color: #a61390;">static</span> <span style="color: #a61390;">let</span> Player<span style="color: #002200;">:</span> UInt32 <span style="color: #002200;">=</span> 0x00 <span style="color: #a61390;">static</span> <span style="color: #a61390;">let</span> Star<span style="color: #002200;">:</span> UInt32 <span style="color: #002200;">=</span> 0x01 <span style="color: #a61390;">static</span> <span style="color: #a61390;">let</span> Platform<span style="color: #002200;">:</span> UInt32 <span style="color: #002200;">=</span> 0x02 <span style="color: #002200;">}</span>

Вернитесь в GameScene.swift. Чтобы задать поведение героя во время столкновений, добавьте следующий код в нижнюю часть метода createPlayer, сразу перед оператором return:

<span style="color: #11740a; font-style: italic;">// 1</span> playerNode.physicsBody?.usesPreciseCollisionDetection <span style="color: #002200;">=</span> <span style="color: #a61390;">true</span> <span style="color: #11740a; font-style: italic;">// 2</span> playerNode.physicsBody?.categoryBitMask <span style="color: #002200;">=</span> CollisionCategoryBitmask.Player <span style="color: #11740a; font-style: italic;">// 3</span> playerNode.physicsBody?.collisionBitMask <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span> <span style="color: #11740a; font-style: italic;">// 4</span> playerNode.physicsBody?.contactTestBitMask <span style="color: #002200;">=</span> CollisionCategoryBitmask.Star | CollisionCategoryBitmask.Platform

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

интересное
Apple просит подрядчиков активнее переносить производство за пределы Китая
Раскрыто название ОС для гарнитуры смешанной реальности Apple

1. Так как эта игра на быстрое движение, Sprite Kit должен использовать точное обнаружение столкновений для физического тела узла героя. Ведь весь сюжет Uber Jump строится на этих столкновениях. Поэтому нам нужно, чтобы они фиксировались настолько точно, насколько возможно! (будет задействовано несколько циклов процессора)

2. Мы назначаем категорию битовой маски физического тела. Она относится к категории CollisionCategoryPlayer.

3. Приравняв collisionBitMask к нулю, мы сообщаем Sprite Kit, что не хотим, чтобы он имитировал какие-либо столкновения узла игрока. Это потому, что мы собираемся обрабатывать эти столкновения сами!

4. Мы сообщаем Sprite Kit, что хотим получать уведомления, когда узел героя будет касаться каких-либо звезд или платформ.

Теперь настроим узел звезды. Добавьте следующий код в нижней части метода createStarAtPosition перед оператором return:

node.physicsBody?.categoryBitMask <span style="color: #002200;">=</span> CollisionCategoryBitmask.Star node.physicsBody?.collisionBitMask <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>

Здесь всё аналогично настройке узла героя. Мы назначаем категорию звезды и обнуляем свойство collisionBitMask, поэтому она не будет ни с чем сталкиваться. Однако здесь мы не назначаем contactTestBitMask, это значит, что Sprite Kit не будет уведомлять нас, когда какой-либо объект будет касаться звезды. Мы ведь уже поручили Sprite Kit отправлять уведомления при соприкосновении героя со звездой, а кроме звезды он ни с чем соприкасаться не будет, поэтому нет никакой необходимости отправлять уведомления со стороны звезды.

Sprite Kit посылает уведомления о контактах выбранных нами узлов, вызывая метод didBeginContact делегата SKPhysicsContactDelegate. В качестве делегата физического мира установите саму сцену, добавив протокол SKPhysicsContactDelegate к определению класса GameScene. Это должно выглядеть примерно так:

<span style="color: #a61390;">class</span> GameScene<span style="color: #002200;">:</span> <span style="color: #400080;">SKScene</span>, SKPhysicsContactDelegate <span style="color: #002200;">{</span> <span style="color: #11740a; font-style: italic;">// Layered Nodes</span> ...

Теперь назначьте сцену делегатом для получения контактных уведомлений, добавив следующую строку в init(size:), сразу после строки, которая устанавливает гравитацию:

<span style="color: #11740a; font-style: italic;">// Set contact delegate</span> physicsWorld.contactDelegate <span style="color: #002200;">=</span> <span style="color: #a61390;">self</span>

И, наконец, добавьте следующий метод в GameScene.swift для обработки событий столкновений:

<span style="color: #a61390;">func</span> didBeginContact<span style="color: #002200;">(</span>contact<span style="color: #002200;">:</span> SKPhysicsContact<span style="color: #002200;">)</span> <span style="color: #002200;">{</span> <span style="color: #11740a; font-style: italic;">// 1</span> <span style="color: #a61390;">var</span> updateHUD <span style="color: #002200;">=</span> <span style="color: #a61390;">false</span> <span style="color: #11740a; font-style: italic;">// 2</span> <span style="color: #a61390;">let</span> whichNode <span style="color: #002200;">=</span> <span style="color: #002200;">(</span>contact.bodyA.node <span style="color: #002200;">!=</span> player<span style="color: #002200;">)</span> ? contact.bodyA.node <span style="color: #002200;">:</span> contact.bodyB.node <span style="color: #a61390;">let</span> other <span style="color: #002200;">=</span> whichNode <span style="color: #a61390;">as</span> GameObjectNode <span style="color: #11740a; font-style: italic;">// 3</span> updateHUD <span style="color: #002200;">=</span> other.collisionWithPlayer<span style="color: #002200;">(</span>player<span style="color: #002200;">)</span> <span style="color: #11740a; font-style: italic;">// Update the HUD if necessary</span> <span style="color: #a61390;">if</span> updateHUD <span style="color: #002200;">{</span> <span style="color: #11740a; font-style: italic;">// 4 TODO: Update HUD in Part 2</span> <span style="color: #002200;">}</span> <span style="color: #002200;">}</span>

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

1. Мы инициализируем флаг updateHUD, который будем использовать в конце метода, чтобы обновлять HUD во время этих столкновений и подсчитывать очки.

2. SKPhysicsContact не гарантирует, какое физическое тело выступит в роли bodyA, а какое в роли bodyB. Но мы знаем, что все столкновения в этой игре будут между узлом героя и узлом GameObjectNode. Поэтому эта строка определяет, какой из них не является узлом героя.

3. После того, как мы определили, какой объект не является узлом героя, мы вызываем метод collisionWithPlayer: в классе GameObjectNode.

4. Здесь мы обновляем HUD, если это требуется. Реализацию HUD мы будем делать во второй части урока, поэтому здесь кроме комментария пока ничего нет.

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

Делаем игру для iPhone в стиле Mega Jump – Часть 2

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

Примечание: Если вы хотите узнать больше об определении контактов и столкновений в Sprite Kit, то изучите руководство "iOS Games by Tutorials", в котором содержится три больших главы по физике движка.

Несколько видов звезд

Uber Jump будет содержать два вида звезд: те, которые добавляют одно очко и специальные звезды, добавляющие сразу пять очков. Звезда каждого типа будет иметь свою графику. Идентифицировать тип звезды нам поможет коллекция в классе StarNode.

В верхнюю часть GameObjectNode.swift добавьте следующую коллекцию:

<span style="color: #a61390;">enum</span> StarType<span style="color: #002200;">:</span> <span style="color: #a61390;">Int</span> <span style="color: #002200;">{</span> <span style="color: #a61390;">case</span> Normal <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span> <span style="color: #a61390;">case</span> Special <span style="color: #002200;">}</span>

Для хранения типа звезды, добавьте следующее свойство в верхнюю часть класса StarNode:

<span style="color: #a61390;">var</span> starType<span style="color: #002200;">:</span> StarType<span style="color: #002200;">!</span>

Теперь при создании звезды нам нужно будет указывать её тип, поэтому в GameScene.swift добавьте параметр starType к сигнатуре метода createStarAtPosition:, чтобы это выглядело так:

<span style="color: #a61390;">func</span> createStarAtPosition<span style="color: #002200;">(</span>position<span style="color: #002200;">:</span> <span style="color: #400080;">CGPoint</span>, ofType type<span style="color: #002200;">:</span> StarType<span style="color: #002200;">)</span> <span style="color: #002200;">-</span>> StarNode <span style="color: #002200;">{</span>

Внутри createStarAtPosition(position: ofType:), замените три строчки кода (которые создают и добавляют SKSpriteNode) на следующие:

node.starType <span style="color: #002200;">=</span> type <span style="color: #a61390;">var</span> sprite<span style="color: #002200;">:</span> <span style="color: #400080;">SKSpriteNode</span> <span style="color: #a61390;">if</span> type <span style="color: #002200;">==</span> .Special <span style="color: #002200;">{</span> sprite <span style="color: #002200;">=</span> <span style="color: #400080;">SKSpriteNode</span><span style="color: #002200;">(</span>imageNamed<span style="color: #002200;">:</span> <span style="color: #bf1d1a;">"StarSpecial"</span><span style="color: #002200;">)</span> <span style="color: #002200;">}</span> <span style="color: #a61390;">else</span> <span style="color: #002200;">{</span> sprite <span style="color: #002200;">=</span> <span style="color: #400080;">SKSpriteNode</span><span style="color: #002200;">(</span>imageNamed<span style="color: #002200;">:</span> <span style="color: #bf1d1a;">"Star"</span><span style="color: #002200;">)</span> <span style="color: #002200;">}</span> node.addChild<span style="color: #002200;">(</span>sprite<span style="color: #002200;">)</span>

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

Осталось только указать тип звезды при её создании. В методе init(size:) класса GameScene.swift найдите строку, в которой вызываем createStarAtPosition и измените его следующим образом:

<span style="color: #a61390;">let</span> star <span style="color: #002200;">=</span> createStarAtPosition<span style="color: #002200;">(</span><span style="color: #400080;">CGPoint</span><span style="color: #002200;">(</span>x<span style="color: #002200;">:</span> <span style="color: #2400d9;">160</span>, y<span style="color: #002200;">:</span> <span style="color: #2400d9;">220</span><span style="color: #002200;">)</span>, ofType<span style="color: #002200;">:</span> .Special<span style="color: #002200;">)</span>

Назначаем нашей звезде тип StarType.Special.

Постройте запустите. Наша звезда стала розовой! Позже мы добавим систему подсчета очков и различия в типах звезд станут более понятными.

Делаем игру для iPhone в стиле Mega Jump – Часть 2

Звон! Добавление звукового эффекта

Было бы неплохо, если бы столкновение героя со звездой сопровождалось звуковым сигналом. Скачайте звуковой эффект для звезды отсюда и перетащите файл в наш проект Xcode. Убедитесь, что указана опция “Destination: Copy items if needed” и что выбран таргет UberJump.

Для воспроизведения звуков в Sprite Kit используется SKAction. Откройте GameObjectNode.swift и добавьте следующее свойство класса выше StarNode:

<span style="color: #a61390;">let</span> starSound <span style="color: #002200;">=</span> <span style="color: #400080;">SKAction</span>.playSoundFileNamed<span style="color: #002200;">(</span><span style="color: #bf1d1a;">"StarPing.wav"</span>, waitForCompletion<span style="color: #002200;">:</span> <span style="color: #a61390;">false</span><span style="color: #002200;">)</span>

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

В методе collisionWithPlayer() класса StarNode замените self.removeFromParent() на:

<span style="color: #11740a; font-style: italic;">// Play sound</span> runAction<span style="color: #002200;">(</span>starSound, completion<span style="color: #002200;">:</span> <span style="color: #002200;">{</span> <span style="color: #11740a; font-style: italic;">// Remove this Star</span> <span style="color: #a61390;">self</span>.removeFromParent<span style="color: #002200;">(</span><span style="color: #002200;">)</span> <span style="color: #002200;">}</span><span style="color: #002200;">)</span>

Код запускает SKAction, воспроизводит звуковой файл и удаляет звезду, когда действие закончено.

Постройте и запустите. Как только спрайт героя столкнётся со звездой, вы услышите звенящий звук.

Игровые объекты: платформы

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

В GameObjectNode.swift добавьте следующую коллекцию над определением класса GameObjectNode, чтобы задать два типа платформ:

<span style="color: #a61390;">enum</span> PlatformType<span style="color: #002200;">:</span> <span style="color: #a61390;">Int</span> <span style="color: #002200;">{</span> <span style="color: #a61390;">case</span> Normal <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span> <span style="color: #a61390;">case</span> Break <span style="color: #002200;">}</span>

Далее нам нужно создать класс PlatformNode. Добавьте следующий код в GameObjectNode.swift:

<span style="color: #a61390;">class</span> PlatformNode<span style="color: #002200;">:</span> GameObjectNode <span style="color: #002200;">{</span> <span style="color: #a61390;">var</span> platformType<span style="color: #002200;">:</span> PlatformType<span style="color: #002200;">!</span> <span style="color: #a61390;">override</span> <span style="color: #a61390;">func</span> collisionWithPlayer<span style="color: #002200;">(</span>player<span style="color: #002200;">:</span> <span style="color: #400080;">SKNode</span><span style="color: #002200;">)</span> <span style="color: #002200;">-</span>> <span style="color: #a61390;">Bool</span> <span style="color: #002200;">{</span> <span style="color: #11740a; font-style: italic;">// 1</span> <span style="color: #11740a; font-style: italic;">// Only bounce the player if he's falling</span> <span style="color: #a61390;">if</span> player.physicsBody?.velocity.dy < <span style="color: #2400d9;">0</span> <span style="color: #002200;">{</span> <span style="color: #11740a; font-style: italic;">// 2</span> player.physicsBody?.velocity <span style="color: #002200;">=</span> CGVector<span style="color: #002200;">(</span>dx<span style="color: #002200;">:</span> player.physicsBody<span style="color: #002200;">!</span>.velocity.dx, dy<span style="color: #002200;">:</span> <span style="color: #2400d9;">250.0</span><span style="color: #002200;">)</span> <span style="color: #11740a; font-style: italic;">// 3</span> <span style="color: #11740a; font-style: italic;">// Remove if it is a Break type platform</span> <span style="color: #a61390;">if</span> platformType <span style="color: #002200;">==</span> .Break <span style="color: #002200;">{</span> <span style="color: #a61390;">self</span>.removeFromParent<span style="color: #002200;">(</span><span style="color: #002200;">)</span> <span style="color: #002200;">}</span> <span style="color: #002200;">}</span> <span style="color: #11740a; font-style: italic;">// 4</span> <span style="color: #11740a; font-style: italic;">// No stars for platforms</span> <span style="color: #a61390;">return</span> <span style="color: #a61390;">false</span> <span style="color: #002200;">}</span> <span style="color: #002200;">}</span>

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

1. В соответствии со сценарием игры, узел героя должен отскакивать от платформы только при падении, т.е тогда, когда он имеет отрицательное значение dy своей скорости (свойство velocity). Эта проверка также позволяет узлу героя не пересекаться с платформами, двигаясь вверх по экрану.

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

3. Если платформа относится к типу platformType.Break, то мы удаляем её со сцены.

4. Ультра прыгун не получает очки ни когда спрыгивает с платформы, ни когда запрыгивает на нее, поэтому нет никакой необходимости обновлять HUD.

Для добавления платформы для сцену откройте GameScene.swift и впишите следующий метод:

<span style="color: #a61390;">func</span> createPlatformAtPosition<span style="color: #002200;">(</span>position<span style="color: #002200;">:</span> <span style="color: #400080;">CGPoint</span>, ofType type<span style="color: #002200;">:</span> PlatformType<span style="color: #002200;">)</span> <span style="color: #002200;">-</span>> PlatformNode <span style="color: #002200;">{</span> <span style="color: #11740a; font-style: italic;">// 1</span> <span style="color: #a61390;">let</span> node <span style="color: #002200;">=</span> PlatformNode<span style="color: #002200;">(</span><span style="color: #002200;">)</span> <span style="color: #a61390;">let</span> thePosition <span style="color: #002200;">=</span> <span style="color: #400080;">CGPoint</span><span style="color: #002200;">(</span>x<span style="color: #002200;">:</span> position.x <span style="color: #002200;">*</span> scaleFactor, y<span style="color: #002200;">:</span> position.y<span style="color: #002200;">)</span> node.position <span style="color: #002200;">=</span> thePosition node.name <span style="color: #002200;">=</span> <span style="color: #bf1d1a;">"NODE_PLATFORM"</span> node.platformType <span style="color: #002200;">=</span> type <span style="color: #11740a; font-style: italic;">// 2</span> <span style="color: #a61390;">var</span> sprite<span style="color: #002200;">:</span> <span style="color: #400080;">SKSpriteNode</span> <span style="color: #a61390;">if</span> type <span style="color: #002200;">==</span> .Break <span style="color: #002200;">{</span> sprite <span style="color: #002200;">=</span> <span style="color: #400080;">SKSpriteNode</span><span style="color: #002200;">(</span>imageNamed<span style="color: #002200;">:</span> <span style="color: #bf1d1a;">"PlatformBreak"</span><span style="color: #002200;">)</span> <span style="color: #002200;">}</span> <span style="color: #a61390;">else</span> <span style="color: #002200;">{</span> sprite <span style="color: #002200;">=</span> <span style="color: #400080;">SKSpriteNode</span><span style="color: #002200;">(</span>imageNamed<span style="color: #002200;">:</span> <span style="color: #bf1d1a;">"Platform"</span><span style="color: #002200;">)</span> <span style="color: #002200;">}</span> node.addChild<span style="color: #002200;">(</span>sprite<span style="color: #002200;">)</span> <span style="color: #11740a; font-style: italic;">// 3</span> node.physicsBody <span style="color: #002200;">=</span> SKPhysicsBody<span style="color: #002200;">(</span>rectangleOfSize<span style="color: #002200;">:</span> sprite.size<span style="color: #002200;">)</span> node.physicsBody?.dynamic <span style="color: #002200;">=</span> <span style="color: #a61390;">false</span> node.physicsBody?.categoryBitMask <span style="color: #002200;">=</span> CollisionCategoryBitmask.Platform node.physicsBody?.collisionBitMask <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span> <span style="color: #a61390;">return</span> node <span style="color: #002200;">}</span>

Этот метод очень похож на createStarAtPosition (_: OfType : ), но обратите внимание на следующие основные моменты:

1. Мы создаем экземпляр PlatformNode и задаем его позицию, имя и тип.

2. Выбираем соответствующее изображение для SKSpriteNode в зависимости от типа платформы.

3. Создаем физику платформы, в том числе тип столкновений.

Теперь, чтобы добавить платформу, вставьте следующий код в метод init(size:), сразу перед строчкой, которая создает звезду:

<span style="color: #11740a; font-style: italic;">// Add a platform</span> <span style="color: #a61390;">let</span> platform <span style="color: #002200;">=</span> createPlatformAtPosition<span style="color: #002200;">(</span><span style="color: #400080;">CGPoint</span><span style="color: #002200;">(</span>x<span style="color: #002200;">:</span> <span style="color: #2400d9;">160</span>, y<span style="color: #002200;">:</span> <span style="color: #2400d9;">320</span><span style="color: #002200;">)</span>, ofType<span style="color: #002200;">:</span> .Normal<span style="color: #002200;">)</span> foregroundNode.addChild<span style="color: #002200;">(</span>platform<span style="color: #002200;">)</span>

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

Делаем игру для iPhone в стиле Mega Jump – Часть 2

Куда идти дальше?

Отлично! Как вы уже поняли, создание игры подобной Mega Jump - это не так уж и сложно. С помощью Uber Jump мы изучили все базовые основы, которые сможем использовать далее, на пути к созданию отличной игры.

В конце статьи вы можете скачать проект Xcode содержащий всё, что мы изучали в этом курсе.

В следующей части мы задействуем акселерометр для управления перемещением героя по оси Х. А также будем загружать ряд свойств из внешнего файла plist и добавим систему подсчёта очков. Много всего интересного ещё предстоит.

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

Продолжение через неделю....

Скачать Исходник Xcode.

Скачать графические ресурсы для урока.

Экономьте время: все самое интересное каждый день по почте
1 комментарий

Авторизоваться или зарегистрироваться для комментирования.

19351099
19351099
31 Марта 2018

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

- 0 +
19351099
19351099
31 Марта 2018

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

- 0 +
Чем мы можем вам помочь?

End of content

No more pages to load

Это тоже интересно: