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

0
в закладки
27 минут
Делаем игру для iPhone в стиле Mega Jump – Часть 3

Добро пожаловать на третью часть урока, в котором мы создаем игру в стиле Mega Jump на Sprite Kit и Swift. В предыдущих части 1 и части 2 мы создали игру на движке Sprite Kit под названием Uber Jump. Мы добавили графику, спрайт героя и некоторые элементы геймплея.

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

Продолжение

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

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

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

Откройте Level01.plist и изучите его содержимое. По сути он состоит из трех элементов:

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

- Stars определяет положение всех звезд на уровне.

- Platforms определяет положение всех платформ в уровне.

Элементы Stars и Platforms содержат в себе по два вложенных элемента:

- Patterns содержит ряд шаблонов для звезд и платформ.

- Positions определяет месторасположение шаблонов звезд и платформ по всей площади уровня.

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

Для лучшего понимания структуры файла, откройте элемент Stars/Positions/Item 0. Он содержит три вложенных элемента, в которых находится информация такого содержания: звезды нужно расположить крест-накрест в точке с координатами 160 и 240.

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

Теперь взгляните на Patterns/Cross, и вы увидите шаблон, содержащий пять элементов, в том числе координаты X и Y относительно позиции, заданной в Stars/Positions, а также тип звезды. Нормальный тип обозначен нулём, а специальный - единицей.

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

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

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

Загрузка уровня с данными

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

<span style="color: #11740a; font-style: italic;">// Height at which level ends</span>
<span style="color: #a61390;">let</span> endLevelY <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>

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

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

<span style="color: #11740a; font-style: italic;">// Load the level</span>
<span style="color: #a61390;">let</span> levelPlist <span style="color: #002200;">=</span> <span style="color: #400080;">NSBundle</span>.mainBundle<span style="color: #002200;">(</span><span style="color: #002200;">)</span>.pathForResource<span style="color: #002200;">(</span><span style="color: #bf1d1a;">"Level01"</span>, ofType<span style="color: #002200;">:</span> <span style="color: #bf1d1a;">"plist"</span><span style="color: #002200;">)</span>
<span style="color: #a61390;">let</span> levelData <span style="color: #002200;">=</span> <span style="color: #400080;">NSDictionary</span><span style="color: #002200;">(</span>contentsOfFile<span style="color: #002200;">:</span> levelPlist<span style="color: #002200;">!</span><span style="color: #002200;">)</span><span style="color: #002200;">!</span>
 
<span style="color: #11740a; font-style: italic;">// Height at which the player ends the level</span>
endLevelY <span style="color: #002200;">=</span> levelData<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"EndY"</span><span style="color: #002200;">]</span><span style="color: #002200;">!</span>.integerValue<span style="color: #002200;">!</span>

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

Теперь займемся звездами и платформами. Начнём с платформ: в методе 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>

этим кодом:

<span style="color: #11740a; font-style: italic;">// Add the platforms</span>
<span style="color: #a61390;">let</span> platforms <span style="color: #002200;">=</span> levelData<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"Platforms"</span><span style="color: #002200;">]</span> <span style="color: #a61390;">as</span> <span style="color: #400080;">NSDictionary</span>
<span style="color: #a61390;">let</span> platformPatterns <span style="color: #002200;">=</span> platforms<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"Patterns"</span><span style="color: #002200;">]</span> <span style="color: #a61390;">as</span> <span style="color: #400080;">NSDictionary</span>
<span style="color: #a61390;">let</span> platformPositions <span style="color: #002200;">=</span> platforms<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"Positions"</span><span style="color: #002200;">]</span> <span style="color: #a61390;">as</span> <span style="color: #002200;">[</span><span style="color: #400080;">NSDictionary</span><span style="color: #002200;">]</span>
 
<span style="color: #a61390;">for</span> platformPosition <span style="color: #a61390;">in</span> platformPositions <span style="color: #002200;">{</span>
  <span style="color: #a61390;">let</span> patternX <span style="color: #002200;">=</span> platformPosition<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"x"</span><span style="color: #002200;">]</span>?.floatValue
  <span style="color: #a61390;">let</span> patternY <span style="color: #002200;">=</span> platformPosition<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"y"</span><span style="color: #002200;">]</span>?.floatValue
  <span style="color: #a61390;">let</span> pattern <span style="color: #002200;">=</span> platformPosition<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"pattern"</span><span style="color: #002200;">]</span> <span style="color: #a61390;">as</span> <span style="color: #400080;">NSString</span>
 
  <span style="color: #11740a; font-style: italic;">// Look up the pattern</span>
  <span style="color: #a61390;">let</span> platformPattern <span style="color: #002200;">=</span> platformPatterns<span style="color: #002200;">[</span>pattern<span style="color: #002200;">]</span> <span style="color: #a61390;">as</span> <span style="color: #002200;">[</span><span style="color: #400080;">NSDictionary</span><span style="color: #002200;">]</span>
  <span style="color: #a61390;">for</span> platformPoint <span style="color: #a61390;">in</span> platformPattern <span style="color: #002200;">{</span>
    <span style="color: #a61390;">let</span> x <span style="color: #002200;">=</span> platformPoint<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"x"</span><span style="color: #002200;">]</span>?.floatValue
    <span style="color: #a61390;">let</span> y <span style="color: #002200;">=</span> platformPoint<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"y"</span><span style="color: #002200;">]</span>?.floatValue
    <span style="color: #a61390;">let</span> type <span style="color: #002200;">=</span> PlatformType<span style="color: #002200;">(</span>rawValue<span style="color: #002200;">:</span> platformPoint<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"type"</span><span style="color: #002200;">]</span><span style="color: #002200;">!</span>.integerValue<span style="color: #002200;">)</span>
    <span style="color: #a61390;">let</span> positionX <span style="color: #002200;">=</span> <span style="color: #400080;">CGFloat</span><span style="color: #002200;">(</span>x<span style="color: #002200;">!</span> <span style="color: #002200;">+</span> patternX<span style="color: #002200;">!</span><span style="color: #002200;">)</span>
    <span style="color: #a61390;">let</span> positionY <span style="color: #002200;">=</span> <span style="color: #400080;">CGFloat</span><span style="color: #002200;">(</span>y<span style="color: #002200;">!</span> <span style="color: #002200;">+</span> patternY<span style="color: #002200;">!</span><span style="color: #002200;">)</span>
    <span style="color: #a61390;">let</span> platformNode <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> positionX, y<span style="color: #002200;">:</span> positionY<span style="color: #002200;">)</span>, ofType<span style="color: #002200;">:</span> type<span style="color: #002200;">!</span><span style="color: #002200;">)</span>
    foregroundNode.addChild<span style="color: #002200;">(</span>platformNode<span style="color: #002200;">)</span>
  <span style="color: #002200;">}</span>
<span style="color: #002200;">}</span>

Здесь очень много всего происходит, но эти вещи достаточно просты. Мы загрузили словарь платформ (Platforms) из levelData, а затем запустили цикл позиций платформ из этого словаря. Для каждого элемента мы получили соответствующий шаблон и инициировали экземпляр PlatformNode нужного типа с заданными позициями X и Y. И, наконец, мы добавили все узлы платформ в узел переднего плана, который должен содержать все игровые объекты.

Постройте и запустите. Вы увидите ряд из трех платформ, выровненных по горизонтали, которые имеют тип "Triple" , заданный в файле в Level01.plist.

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

Теперь сделайте то же самое для звезд. Внутри GameScene.swift, замените следующую строку в 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>, ofType<span style="color: #002200;">:</span> .Special<span style="color: #002200;">)</span>
foregroundNode.addChild<span style="color: #002200;">(</span>star<span style="color: #002200;">)</span>

кодом:

<span style="color: #11740a; font-style: italic;">// Add the stars</span>
<span style="color: #a61390;">let</span> stars <span style="color: #002200;">=</span> levelData<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"Stars"</span><span style="color: #002200;">]</span> <span style="color: #a61390;">as</span> <span style="color: #400080;">NSDictionary</span>
<span style="color: #a61390;">let</span> starPatterns <span style="color: #002200;">=</span> stars<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"Patterns"</span><span style="color: #002200;">]</span> <span style="color: #a61390;">as</span> <span style="color: #400080;">NSDictionary</span>
<span style="color: #a61390;">let</span> starPositions <span style="color: #002200;">=</span> stars<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"Positions"</span><span style="color: #002200;">]</span> <span style="color: #a61390;">as</span> <span style="color: #002200;">[</span><span style="color: #400080;">NSDictionary</span><span style="color: #002200;">]</span>
 
<span style="color: #a61390;">for</span> starPosition <span style="color: #a61390;">in</span> starPositions <span style="color: #002200;">{</span>
  <span style="color: #a61390;">let</span> patternX <span style="color: #002200;">=</span> starPosition<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"x"</span><span style="color: #002200;">]</span>?.floatValue
  <span style="color: #a61390;">let</span> patternY <span style="color: #002200;">=</span> starPosition<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"y"</span><span style="color: #002200;">]</span>?.floatValue
  <span style="color: #a61390;">let</span> pattern <span style="color: #002200;">=</span> starPosition<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"pattern"</span><span style="color: #002200;">]</span> <span style="color: #a61390;">as</span> <span style="color: #400080;">NSString</span>
 
  <span style="color: #11740a; font-style: italic;">// Look up the pattern</span>
  <span style="color: #a61390;">let</span> starPattern <span style="color: #002200;">=</span> starPatterns<span style="color: #002200;">[</span>pattern<span style="color: #002200;">]</span> <span style="color: #a61390;">as</span> <span style="color: #002200;">[</span><span style="color: #400080;">NSDictionary</span><span style="color: #002200;">]</span>
  <span style="color: #a61390;">for</span> starPoint <span style="color: #a61390;">in</span> starPattern <span style="color: #002200;">{</span>
    <span style="color: #a61390;">let</span> x <span style="color: #002200;">=</span> starPoint<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"x"</span><span style="color: #002200;">]</span>?.floatValue
    <span style="color: #a61390;">let</span> y <span style="color: #002200;">=</span> starPoint<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"y"</span><span style="color: #002200;">]</span>?.floatValue
    <span style="color: #a61390;">let</span> type <span style="color: #002200;">=</span> StarType<span style="color: #002200;">(</span>rawValue<span style="color: #002200;">:</span> starPoint<span style="color: #002200;">[</span><span style="color: #bf1d1a;">"type"</span><span style="color: #002200;">]</span><span style="color: #002200;">!</span>.integerValue<span style="color: #002200;">)</span>
    <span style="color: #a61390;">let</span> positionX <span style="color: #002200;">=</span> <span style="color: #400080;">CGFloat</span><span style="color: #002200;">(</span>x<span style="color: #002200;">!</span> <span style="color: #002200;">+</span> patternX<span style="color: #002200;">!</span><span style="color: #002200;">)</span>
    <span style="color: #a61390;">let</span> positionY <span style="color: #002200;">=</span> <span style="color: #400080;">CGFloat</span><span style="color: #002200;">(</span>y<span style="color: #002200;">!</span> <span style="color: #002200;">+</span> patternY<span style="color: #002200;">!</span><span style="color: #002200;">)</span>
    <span style="color: #a61390;">let</span> starNode <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> positionX, y<span style="color: #002200;">:</span> positionY<span style="color: #002200;">)</span>, ofType<span style="color: #002200;">:</span> type<span style="color: #002200;">!</span><span style="color: #002200;">)</span>
    foregroundNode.addChild<span style="color: #002200;">(</span>starNode<span style="color: #002200;">)</span>
  <span style="color: #002200;">}</span>
<span style="color: #002200;">}</span>

Здесь всё в точности тоже самое, что мы сделали при создании платформ, но в этом случае мы используем словарь Stars вместо словаря Platforms.

Постройте и запустите. Это уже становится похожим на настоящую игру!

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

Слой среднего плана

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

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

<span style="color: #a61390;">func</span> createMidgroundNode<span style="color: #002200;">(</span><span style="color: #002200;">)</span> <span style="color: #002200;">-</span>> <span style="color: #400080;">SKNode</span> <span style="color: #002200;">{</span>
  <span style="color: #11740a; font-style: italic;">// Create the node</span>
  <span style="color: #a61390;">let</span> theMidgroundNode <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;">var</span> anchor<span style="color: #002200;">:</span> <span style="color: #400080;">CGPoint</span><span style="color: #002200;">!</span>
  <span style="color: #a61390;">var</span> xPosition<span style="color: #002200;">:</span> <span style="color: #400080;">CGFloat</span><span style="color: #002200;">!</span>
 
  <span style="color: #11740a; font-style: italic;">// 1</span>
  <span style="color: #11740a; font-style: italic;">// Add some branches to the midground</span>
  <span style="color: #a61390;">for</span> index <span style="color: #a61390;">in</span> <span style="color: #2400d9;">0</span>...9 <span style="color: #002200;">{</span>
    <span style="color: #a61390;">var</span> spriteName<span style="color: #002200;">:</span> <span style="color: #a61390;">String</span>
    <span style="color: #11740a; font-style: italic;">// 2</span>
    <span style="color: #a61390;">let</span> r <span style="color: #002200;">=</span> arc4random<span style="color: #002200;">(</span><span style="color: #002200;">)</span> <span style="color: #002200;">%</span> <span style="color: #2400d9;">2</span>
    <span style="color: #a61390;">if</span> r > <span style="color: #2400d9;">0</span> <span style="color: #002200;">{</span>
      spriteName <span style="color: #002200;">=</span> <span style="color: #bf1d1a;">"BranchRight"</span>
      anchor <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;">1.0</span>, y<span style="color: #002200;">:</span> <span style="color: #2400d9;">0.5</span><span style="color: #002200;">)</span>
      xPosition <span style="color: #002200;">=</span> <span style="color: #a61390;">self</span>.size.width
    <span style="color: #002200;">}</span> <span style="color: #a61390;">else</span> <span style="color: #002200;">{</span>
      spriteName <span style="color: #002200;">=</span> <span style="color: #bf1d1a;">"BranchLeft"</span>
      anchor <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;">0.0</span>, y<span style="color: #002200;">:</span> <span style="color: #2400d9;">0.5</span><span style="color: #002200;">)</span>
      xPosition <span style="color: #002200;">=</span> <span style="color: #2400d9;">0.0</span>
    <span style="color: #002200;">}</span>
    <span style="color: #11740a; font-style: italic;">// 3</span>
    <span style="color: #a61390;">let</span> branchNode <span style="color: #002200;">=</span> <span style="color: #400080;">SKSpriteNode</span><span style="color: #002200;">(</span>imageNamed<span style="color: #002200;">:</span> spriteName<span style="color: #002200;">)</span>
    branchNode.anchorPoint <span style="color: #002200;">=</span> anchor
    branchNode.position <span style="color: #002200;">=</span> <span style="color: #400080;">CGPoint</span><span style="color: #002200;">(</span>x<span style="color: #002200;">:</span> xPosition, y<span style="color: #002200;">:</span> <span style="color: #2400d9;">500.0</span> <span style="color: #002200;">*</span> <span style="color: #400080;">CGFloat</span><span style="color: #002200;">(</span>index<span style="color: #002200;">)</span><span style="color: #002200;">)</span>
    theMidgroundNode.addChild<span style="color: #002200;">(</span>branchNode<span style="color: #002200;">)</span>
  <span style="color: #002200;">}</span>
 
  <span style="color: #11740a; font-style: italic;">// Return the completed midground node</span>
  <span style="color: #a61390;">return</span> theMidgroundNode
<span style="color: #002200;">}</span>

Рассмотрим этот код подробнее:

1. Мы добавляем в midgroundNode десять веток, расположенных равномерно по всему уровню.

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

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

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

<span style="color: #11740a; font-style: italic;">// Midground</span>
midgroundNode <span style="color: #002200;">=</span> createMidgroundNode<span style="color: #002200;">(</span><span style="color: #002200;">)</span>
addChild<span style="color: #002200;">(</span>midgroundNode<span style="color: #002200;">)</span>

Постройте и запустите.

Смотрите! Появились ветви разного вида, а некоторые даже с розовыми бабочками!

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

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

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

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

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

Параллакс

интересное
Для чего у iPhone 12 вставка сбоку корпуса? Есть ответ
Лучше, чем у iPhone SE, но хуже, чем у iPhone 12: время автономной работы iPhone 12 mini

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

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

<span style="color: #a61390;">override</span> <span style="color: #a61390;">func</span> update<span style="color: #002200;">(</span>currentTime<span style="color: #002200;">:</span> <span style="color: #400080;">NSTimeInterval</span><span style="color: #002200;">)</span> <span style="color: #002200;">{</span>
  <span style="color: #11740a; font-style: italic;">// Calculate player y offset</span>
  <span style="color: #a61390;">if</span> player.position.y > <span style="color: #2400d9;">200.0</span> <span style="color: #002200;">{</span>
    backgroundNode.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: #2400d9;">0.0</span>, y<span style="color: #002200;">:</span> <span style="color: #002200;">-</span><span style="color: #002200;">(</span><span style="color: #002200;">(</span>player.position.y <span style="color: #002200;">-</span> <span style="color: #2400d9;">200.0</span><span style="color: #002200;">)</span><span style="color: #002200;">/</span><span style="color: #2400d9;">10</span><span style="color: #002200;">)</span><span style="color: #002200;">)</span>
    midgroundNode.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: #2400d9;">0.0</span>, y<span style="color: #002200;">:</span> <span style="color: #002200;">-</span><span style="color: #002200;">(</span><span style="color: #002200;">(</span>player.position.y <span style="color: #002200;">-</span> <span style="color: #2400d9;">200.0</span><span style="color: #002200;">)</span><span style="color: #002200;">/</span><span style="color: #2400d9;">4</span><span style="color: #002200;">)</span><span style="color: #002200;">)</span>
    foregroundNode.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: #2400d9;">0.0</span>, y<span style="color: #002200;">:</span> <span style="color: #002200;">-</span><span style="color: #002200;">(</span>player.position.y <span style="color: #002200;">-</span> <span style="color: #2400d9;">200.0</span><span style="color: #002200;">)</span><span style="color: #002200;">)</span>
  <span style="color: #002200;">}</span>
<span style="color: #002200;">}</span>

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

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

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

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

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

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

Отличная работа! Но ещё рано почивать на лаврах. Для того, чтобы допрыгнуть до звёзд, нужно сначала порыть землю.

Движение с акселерометром

Пришло время задействовать акселерометр. Движение вдоль вертикальной оси у нас отлажено хорошо, но что насчёт движения по горизонтальной оси? Так же, как в Mega Jump, пользователь будет управлять своим «ультра прыгуном» с помощью акселерометра.

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

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

<span style="color: #a61390;">import</span> CoreMotion

Затем добавьте следующие свойства к классу GameScene:

<span style="color: #11740a; font-style: italic;">// Motion manager for accelerometer</span>
<span style="color: #a61390;">let</span> motionManager<span style="color: #002200;">:</span> CMMotionManager <span style="color: #002200;">=</span> CMMotionManager<span style="color: #002200;">(</span><span style="color: #002200;">)</span>
 
<span style="color: #11740a; font-style: italic;">// Acceleration value from accelerometer</span>
<span style="color: #a61390;">var</span> xAcceleration<span style="color: #002200;">:</span> <span style="color: #400080;">CGFloat</span> <span style="color: #002200;">=</span> <span style="color: #2400d9;">0.0</span>

Мы собираемся использовать MotionManager для доступа к данным акселерометра устройства, а также будем хранить последнее расчетное значение ускорения в переменной xAcceleration, которая нам понадобится позже при задании скорость узла героя по оси X.

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

<span style="color: #11740a; font-style: italic;">// CoreMotion</span>
<span style="color: #11740a; font-style: italic;">// 1</span>
motionManager.accelerometerUpdateInterval <span style="color: #002200;">=</span> <span style="color: #2400d9;">0.2</span>
<span style="color: #11740a; font-style: italic;">// 2</span>
motionManager.startAccelerometerUpdatesToQueue<span style="color: #002200;">(</span><span style="color: #400080;">NSOperationQueue</span>.currentQueue<span style="color: #002200;">(</span><span style="color: #002200;">)</span>, withHandler<span style="color: #002200;">:</span> <span style="color: #002200;">{</span>
  <span style="color: #002200;">(</span>accelerometerData<span style="color: #002200;">:</span> CMAccelerometerData<span style="color: #002200;">!</span>, error<span style="color: #002200;">:</span> <span style="color: #400080;">NSError</span><span style="color: #002200;">!</span><span style="color: #002200;">)</span> <span style="color: #a61390;">in</span>
  <span style="color: #11740a; font-style: italic;">// 3</span>
  <span style="color: #a61390;">let</span> acceleration <span style="color: #002200;">=</span> accelerometerData.acceleration
  <span style="color: #11740a; font-style: italic;">// 4</span>
  <span style="color: #a61390;">self</span>.xAcceleration <span style="color: #002200;">=</span> <span style="color: #002200;">(</span><span style="color: #400080;">CGFloat</span><span style="color: #002200;">(</span>acceleration.x<span style="color: #002200;">)</span> <span style="color: #002200;">*</span> <span style="color: #2400d9;">0.75</span><span style="color: #002200;">)</span> <span style="color: #002200;">+</span> <span style="color: #002200;">(</span><span style="color: #a61390;">self</span>.xAcceleration <span style="color: #002200;">*</span> <span style="color: #2400d9;">0.25</span><span style="color: #002200;">)</span>
<span style="color: #002200;">}</span><span style="color: #002200;">)</span>

Здесь очень много всего происходит, поэтому давайте окунёмся поглубже:

1. Свойство accelerometerUpdateInterval определяет количество секунд между обновлениями значений акселерометра. Значение 0,2 обеспечивает наиболее плавную частоту обновления.

2. Мы запускаем акселерометр и задаём блок кода для выполнения во время каждого обновления значений акселерометра.

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

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

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

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

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

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

<span style="color: #a61390;">override</span> <span style="color: #a61390;">func</span> didSimulatePhysics<span style="color: #002200;">(</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;">// Set velocity based on x-axis acceleration</span>
  player.physicsBody?.velocity <span style="color: #002200;">=</span> CGVector<span style="color: #002200;">(</span>dx<span style="color: #002200;">:</span> xAcceleration <span style="color: #002200;">*</span> <span style="color: #2400d9;">400.0</span>, dy<span style="color: #002200;">:</span> player.physicsBody<span style="color: #002200;">!</span>.velocity.dy<span style="color: #002200;">)</span>
  <span style="color: #11740a; font-style: italic;">// 2</span>
  <span style="color: #11740a; font-style: italic;">// Check x bounds</span>
  <span style="color: #a61390;">if</span> player.position.x < <span style="color: #002200;">-</span><span style="color: #2400d9;">20.0</span> <span style="color: #002200;">{</span>
    player.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;">20.0</span>, y<span style="color: #002200;">:</span> player.position.y<span style="color: #002200;">)</span>
  <span style="color: #002200;">}</span> <span style="color: #a61390;">else</span> <span style="color: #a61390;">if</span> <span style="color: #002200;">(</span>player.position.x > <span style="color: #a61390;">self</span>.size.width <span style="color: #002200;">+</span> <span style="color: #2400d9;">20.0</span><span style="color: #002200;">)</span> <span style="color: #002200;">{</span>
    player.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: #002200;">-</span><span style="color: #2400d9;">20.0</span>, y<span style="color: #002200;">:</span> player.position.y<span style="color: #002200;">)</span>
  <span style="color: #002200;">}</span>
<span style="color: #002200;">}</span>

Здесь происходит несколько вещей:

1. Мы изменяем горизонтальную составляющую значения скорости узла героя, используя значение xAcceleration. Далее умножаем её на 400, потому что масштаб акселерометра не соответствует масштабу реальной физики и, увеличивая значение, мы получаем более реалистичный эффект. Значение скорости по оси Y мы оставляем без изменений, потому что акселерометр на него никоем образом не влияет.

2. В Mega Jump, когда герой выходит за пределы экрана слева или справа, он потом возвращается с противоположной стороны. Мы используем то же поведение здесь, проверяя границы экрана и оставляя границу в 20 точек за пределами экрана.

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

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

Система подсчета очков

В целом наша игра Uber Jump будет включать в себя три вида информации, имеющей отношение к герою:

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

2. Наивысший балл. В конце каждого цикла игры всегда будут финальные очки. Uber Jump будет записывать самые высокие значения в файл с настройками пользователя (user defaults), для чтобы игрок знал рекорды, которые нужно побить.

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

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

Создайте новый iOS/Source/Swift File с именем GameState. Добавьте следующее описание класса и свойства в GameState.swift:

<span style="color: #a61390;">class</span> GameState <span style="color: #002200;">{</span>
  <span style="color: #a61390;">var</span> score<span style="color: #002200;">:</span> <span style="color: #a61390;">Int</span>
  <span style="color: #a61390;">var</span> highScore<span style="color: #002200;">:</span> <span style="color: #a61390;">Int</span>
  <span style="color: #a61390;">var</span> stars<span style="color: #002200;">:</span> <span style="color: #a61390;">Int</span>
 
  <span style="color: #a61390;">class</span> <span style="color: #a61390;">var</span> sharedInstance<span style="color: #002200;">:</span> GameState <span style="color: #002200;">{</span>
    <span style="color: #a61390;">struct</span> Singleton <span style="color: #002200;">{</span>
      <span style="color: #a61390;">static</span> <span style="color: #a61390;">let</span> instance <span style="color: #002200;">=</span> GameState<span style="color: #002200;">(</span><span style="color: #002200;">)</span>
    <span style="color: #002200;">}</span>
 
    <span style="color: #a61390;">return</span> Singleton.instance
  <span style="color: #002200;">}</span>
<span style="color: #002200;">}</span>

Заданные три свойства будут предоставлять доступ к текущему счету, наивысшему баллу и к количеству звезд. Переменная класса sharedInstance будет давать доступ к единственному экземпляру класса GameState.

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

Добавьте следующий метод инициализации для GameState.swift:

<span style="color: #a61390;">init</span><span style="color: #002200;">(</span><span style="color: #002200;">)</span> <span style="color: #002200;">{</span>
  <span style="color: #11740a; font-style: italic;">// Init</span>
  score <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>
  highScore <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>
  stars <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>
 
  <span style="color: #11740a; font-style: italic;">// Load game state</span>
  <span style="color: #a61390;">let</span> defaults <span style="color: #002200;">=</span> <span style="color: #400080;">NSUserDefaults</span>.standardUserDefaults<span style="color: #002200;">(</span><span style="color: #002200;">)</span>
 
  highScore <span style="color: #002200;">=</span> defaults.integerForKey<span style="color: #002200;">(</span><span style="color: #bf1d1a;">"highScore"</span><span style="color: #002200;">)</span>
  stars <span style="color: #002200;">=</span> defaults.integerForKey<span style="color: #002200;">(</span><span style="color: #bf1d1a;">"stars"</span><span style="color: #002200;">)</span>
<span style="color: #002200;">}</span>

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

Примечание: Для получения более подробной информации по хранению данных игры, посмотрите урок How to Save your Game Data.

Для хранения заданных значений нам понадобится метод в GameState. Добавьте следующий метод в GameState.swift:

<span style="color: #a61390;">func</span> saveState<span style="color: #002200;">(</span><span style="color: #002200;">)</span> <span style="color: #002200;">{</span>
  <span style="color: #11740a; font-style: italic;">// Update highScore if the current score is greater</span>
  highScore <span style="color: #002200;">=</span> <span style="color: #a61390;">max</span><span style="color: #002200;">(</span>score, highScore<span style="color: #002200;">)</span>
 
  <span style="color: #11740a; font-style: italic;">// Store in user defaults</span>
  <span style="color: #a61390;">let</span> defaults <span style="color: #002200;">=</span> <span style="color: #400080;">NSUserDefaults</span>.standardUserDefaults<span style="color: #002200;">(</span><span style="color: #002200;">)</span>
  defaults.setInteger<span style="color: #002200;">(</span>highScore, forKey<span style="color: #002200;">:</span> <span style="color: #bf1d1a;">"highScore"</span><span style="color: #002200;">)</span>
  defaults.setInteger<span style="color: #002200;">(</span>stars, forKey<span style="color: #002200;">:</span> <span style="color: #bf1d1a;">"stars"</span><span style="color: #002200;">)</span>
  <span style="color: #400080;">NSUserDefaults</span>.standardUserDefaults<span style="color: #002200;">(</span><span style="color: #002200;">)</span>.synchronize<span style="color: #002200;">(</span><span style="color: #002200;">)</span>
<span style="color: #002200;">}</span>

Вот класс GameState, который синхронизируется с хранилищем на устройстве.

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

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

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

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

End of content

No more pages to load

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