Сделать мега популярную игру под iOS совсем несложно, и мы это докажем в этой серии уроков :). Этой статьей мы начинаем серию материалов по созданию игры для iPhone. Мы будем использовать Sprite Kit и Swift на любительском уровне, чтобы создать что-то подобное Mega Jump. Это довольно популярная игра, выполненная в стиле аркады с вертикальными прыжками. Аналогом ее также можно назвать Doodle Jump. На видео ниже можно посмотреть, как игровой процесс выглядит на устройстве...
По мере того как мы будем создавать игру, вы научитесь использовать возможности Sprite Kit, такие как: обнаружение столкновений, управление акселерометром, переходы между сценами. Если вы совсем не знакомы с движком Sprite Kit, то можете ознакомится с ним в статье Рэя Sprite Kit Tutorial for Beginners, а потом приступить к этой серии уроков. Но если вы уверенны в себе, тогда вперёд! С оригиналом статьи на английском, можно ознакомиться здесь. Примечание: Для игры вам понадобится платный аккаунт разработчика. И если вы намерены использовать этот урок в полной мере, то и устройство для тестирования.

Начало

Мы собираемся создать игру подобную Mega Jump. Как гласит известная пословица: "как Вы яхту назовёте, так она и поплывет". Чтобы игра стала по-настоящему яркой, она должна иметь яркое, запоминающееся название. "Uber" - это ещё лучше, чем "mega", поэтому давайте назовём ее "Uber Jump" :). По сути игра в духе Mega Jump является физической игрой: главный герой подбрасывается вверх экрана и с этого момента он должен бороться с силой тяжести, чтобы продержаться максимально долго. По мере того, как герой собирает монеты, он поднимается все выше, а платформы обеспечивают временную передышку на его пути. Наша игра Uber Jump будет иметь такую же модель: сначала мы подбросим нашего персонажа вверх, а затем будем применять к нему постоянную силу тяжести. На своём пути он будет собирать звезды, которые помогут ему подниматься вверх, отвечая за движение вдоль ось Y. Если пропустить слишком много звезд, то герой неизбежно упадете вниз. Акселерометр будет управлять движением игрока вдоль ось X. Наклоняя устройства в право или влево можно задать направление движение героя по горизонтали. Результат будет состоять из динамической составляющей (так как вы будете прикладывать две силы к игроку), а также из кинематической (так как вы непосредственно будете изменять его скорость). Так что скучать не придется! Для начала Вам нужно создать новый Xcode проект с движком Sprite Kit. Запустите Xcode, выберите File \ New \ Project, затем IOS \ Application \ SpriteKit Game шаблон и нажмите кнопку "Далее" (Next). В поле с названием продукта введите UberJump, в качестве языка выберите Swift, во вкладке с устройствами укажите iPhone, а затем нажмите кнопку "Далее". Выберите место для хранения проекта и нажмите кнопку "Создать". Перед тем, как приступить к делу, нужно сделать некоторые предварительные настройки. Найдите файл GameScene.sks в навигаторе. Он нам не понадобится, поэтому выделите его и нажмите кнопку "Удалить". Когда появится окно оповещения, нажмите "Переместить в корзину". Теперь откройте GameViewController.swift и удалите расширение unarchiveFromFile SKNode в верхней части файла, так как нам не понадобится никакой посторонний код. Затем замените метод viewDidLoad следующим :
override func viewDidLoad() {
  super.viewDidLoad()
 
  let skView = self.view as SKView
 
  skView.showsFPS = true
  skView.showsNodeCount = true
 
  let scene = GameScene(size: skView.bounds.size)
  scene.scaleMode = .AspectFit
 
  skView.presentScene(scene)
}
Нажмите Build, затем Run, чтобы убедится, что на данный момент всё работает. Не беспокойтесь о том, что текстовый лейбл "Hello, World" усечён. Вы сможете найти его в файле GameScene.swift и заменить на то, что нужно. Но сперва необходимо сделать последнюю вещь по настройке. Так как наша игра будет использовать акселерометр, мы должны быть уверены, что поворот устройства не переключит приложение в ландшафтный режим. В навигаторе Xcode (там , где сейчас выбран Ваш проект) выберите пункт UberJump, перейдите на вкладку General в настройках и найдите раздел Deployment Info. Убедитесь, что портретная ориентация является единственной доступной для устройства. Мы подготовили проект к тому, чтобы начать добавление компонентов игры. Начнем с картинок.

Импортирование графики

Скачайте графические ресурсы (ссылка на скачивание в конце статьи) для этого проекта и перетащите их мышкой в Xcode. Убедитесь, что выбрана опция "Destination: Copy items if needed" и пункт UberJump. Иллюстрации разделены на фоновые (папка Backgrounds) и игровые (папка Assets.atlas). Фоновые представляют собой плитки, которые будут перелистываться вверх и вниз по экрану во время движения героя: Игровые включают все спрайты, в частности персонаж, плиты и звезды. В целях повышения эффективности, игровые ресурсы должны хранится в атласе текстур. Вы можете узнать поподробнее об атласах текстур в Sprite Kit в этом уроке.

Построение сцены

Вы наверное заметили, что в шаблоне проекта уже создан класс GameScene. Он представляет собой сцену Sprite Kit, которая на данный момент отображает надпись "Hello, World!" при запуске игры. Большинство действий игры Uber Jump будут происходить здесь, поэтому откройте класс GameScene.swift и замените содержимое следующим:
import SpriteKit
 
class GameScene: SKScene {
 
  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }
 
  override init(size: CGSize) {
    super.init(size: size)     
    backgroundColor = SKColor.whiteColor()
  }
}
Нажмите Build и Run. GameScene теперь отображает ничто иное, как белый экран. Это чистый холст, на который мы будем добавлять основные объекты игры. Mega Jump использует эффект параллакса слоев, чтобы произвести реалистичную визуализацию объема. Например, объекты на переднем двигаются быстрее, чем фоновые объекты. В Uber Jump мы собираемся сделать тот же эффект, создав следующие слои в сцене: - Background: слой с медленным движением, отображающий далекий пейзаж. - Midground: более быстрая декорация из веток деревьев. - Foreground: быстро движущийся слой, содержащий персонаж, звезды и платформы, которые составляют ядро геймплея. - HUD: верхний слой, который не двигаться и отображает лейблы подсчета очков. Фоновый узел - это первый, который нужно добавить. Откройте GameScene.swift и добавьте следующий метод:
func createBackgroundNode() -> SKNode {
  // 1
  // Create the node
  let backgroundNode = SKNode()
  let ySpacing = 64.0 * scaleFactor
 
  // 2
  // Go through images until the entire background is built
  for index in 0...19 {
    // 3
    let node = SKSpriteNode(imageNamed:String(format: "Background%02d", index + 1))
    // 4
    node.setScale(scaleFactor)
    node.anchorPoint = CGPoint(x: 0.5, y: 0.0)
    node.position = CGPoint(x: self.size.width / 2, y: ySpacing * CGFloat(index))
    //5
    backgroundNode.addChild(node)
  }
 
  // 6
  // Return the completed background node
  return backgroundNode
}
Давайте подробно разберем, что мы здесь делаем: 1. Во - первых, мы создаем новый экземпляр SKNode. Он не имеет никакого визуального содержимого, но располагается на сцене. Это означает, что мы можем перемещать его в любое место и вместе с ним будут двигаться его дочерние узлы. 2. У нас есть 20 фоновых изображений, которые нужно расположить в ряд, чтобы заполнить задний план. 3. Каждый дочерний узел унаследован от SKSpriteNode с добавлением соответствующего фонового изображения, загруженного из наших ресурсов. 4. Смещение якорной точки каждого узла в его нижний центр позволяет легко расположить узлы друг над другом. 5. Мы добавляем каждый дочерний узел в фоновой узел. 6. Наконец, Мы возвращаем фоновый узел. Теперь самое время поместить фоновый узел на сцену. В GameScene.swift добавьте следующие свойства:
// Layered Nodes
let backgroundNode = SKNode()
let midgroundNode = SKNode()
let foregroundNode = SKNode()
let hudNode = SKNode()
 
// To Accommodate iPhone 6
let scaleFactor: CGFloat = 0.0
Мы добавляем свойства для каждого из узлов, которые будет использоваться в игре. На данный момент нам нужен только фоновый узел, но не помешает заодно объявить и другие узлы. Мы также можем добавить свойство scaleFactor. Это нужно для того, чтобы графика правильно масштабировалась и позиционировалась на всех моделях iPhone. Чтобы добавить фон к сцене, вставьте следующий код в метод init(size:) класса GameScene.swift сразу после строки, которая устанавливает цвет фона:
scaleFactor = self.size.width / 320.0
 
// Create the game nodes
// Background
backgroundNode = createBackgroundNode()
addChild(backgroundNode)
Графика рассчитана на стандартный экран, шириной в 320 точек, который имеют большинство моделей iPhone. Поэтому здесь используется масштабный коэффициент для адаптации под другие размеры экрана. Все, что нам нужно сделать после инициализации фонового узла, это добавить его в качестве дочернего к текущей сцене. Нажмите Build и Run, чтобы удостовериться в том, что наш фоновый узел отображается на сцене так, как показано ниже: Примечание: несмотря на то, что в фоновый узел мы добавили 20 дочерних, вы увидите, что при запуске приложения на устройстве с 4- х дюймовым экраном, на сцене отображаются только девять узлов. А с экраном в 3,5 дюйма - всего восемь. Sprite Kit достаточно продвинут, и отображает только те узлы, которые должны быть видны в игре на данный момент.

Добавление узла игрока (Node)

Настало время выпустить на сцену нашего "Супер Прыгуна". В GameScene.swift, добавьте следующий метод:
func createPlayer() -> SKNode {
  let playerNode = SKNode()
  playerNode.position = CGPoint(x: self.size.width / 2, y: 80.0)
 
  let sprite = SKSpriteNode(imageNamed: "Player")
  playerNode.addChild(sprite)
 
  return playerNode
}
По такому же принципу, как мы добавляли фоновый узел, создается SKNode и в него в качестве дочернего добавляется узел SKSpriteNode, содержащий спрайт игрока. Расположим узел игрока таким образом, чтоб он был в центре экрана по горизонтали и чуть выше нижней части сцены. Перед тем как добавить узел игрока на сцену, нужно создать передний план. Как уже говорилось выше, узел переднего плана будет содержать игрока, звезды и платформы. Это означает то, что, когда мы будем его перемещать, то вместе с ним будут двигаться все игровые элементы. Вставьте следующий код в метод init(size:) сразу после строки, которая добавляет backgroundNode на сцену:
// Foreground
foregroundNode = SKNode()
addChild(foregroundNode)
Теперь, когда для элементов геймплея есть узел переднего плана, мы можем добавить к нему узел игрока. В верхней части GameScene.swift объявите следующее свойство:
// Player
let player = SKNode()
Теперь поместите узел игрока на сцену, вставив следующий код в init(size:) сразу после строки, которая добавляет на сцену foregroundNode:
// Add the player
player = createPlayer()
foregroundNode.addChild(player)
Постройте и запустите (Build и Run), чтобы увидеть Супер Прыгуна, который готов началу приключения:

Добавление гравитации и физического тела

Наш герой выглядит слишком расслабленным. Было бы неплохо добавить физику в игру и посмотреть, что из этого получится. Начнём с того, что физика нашей игры не мыслима без гравитации. В GameScene.swift, добавьте следующую строку в init(size:) сразу после строки, которая устанавливает цвет фона:
// Add some gravity
physicsWorld.gravity = CGVector(dx: 0.0, dy: -2.0)
Этим самым вы добавляете вектор силы тяжести в физический мир, основываясь на сюжете игры. Сила тяжести не имеет проекции на ось X, она производит усилие, направленное вниз вдоль оси Y. Постройте и запустите, чтобы посмотреть, как гравитация будет влиять на узел игрока. Хм ... ничего не произошло. Почему гравитация Вашему герою нипочем? Попробуйте выяснить это сами, прежде чем посмотреть ответ. Ответ: Сила, создаваемая гравитацией оказывает динамический эффект только на физические тела сцены. А такие объекты класса SKNode, как игрок узел нашего игрока, не имеют физических тел, заданных по умолчанию. Поэтому нам нужно задать физическое тело узлу игрока. В конце метода createPlayer добавьте следующий код перед оператором return:
// 1
playerNode.physicsBody = SKPhysicsBody(circleOfRadius: sprite.size.width / 2)
// 2
playerNode.physicsBody?.dynamic = true
// 3
playerNode.physicsBody?.allowsRotation = false
// 4
playerNode.physicsBody?.restitution = 1.0
playerNode.physicsBody?.friction = 0.0
playerNode.physicsBody?.angularDamping = 0.0
playerNode.physicsBody?.linearDamping = 0.0
Приведенный выше код задает физическое тело для узла игрока. Давайте рассмотрим его детально: 1. Каждое физическое тело должно иметь форму, которую физический движок может использовать для обнаружения столкновений. Наиболее удобная форма тела для использования в обнаружении столкновений - круг (легко определить, когда круги перекрывают друг друга) и , к тому же, круг очень хорошо подходит для узла Вашего игрока. Его радиус составляет половину ширины спрайта. 2. Физические тела подразделяются на статические и динамические. Динамические тела находятся под воздействием физического движка и поэтому зависят от сил и импульсов. Статические тела не попадают под это воздействие, но мы все равно можем использовать их для обнаружения столкновений. Например, такое статическое тело, как стена или твердая платформа никогда не будет двигаться, но в него могут врезаться другие объекты. Поскольку мы хотим, чтобы узел нашего игрока был под воздействием силы тяжести, установите его свойству dynamic значение true. 3. Нужно, чтобы узел нашего игрока оставаться всегда в вертикальном положении, поэтому отключаем вращение узла. 4. Поскольку мы самостоятельно обрабатываем столкновения в этой игре, то устанавливаем параметры физического тела узла игрока таким образом, чтобы не было ни трения ни демпфирования. Однако, мы устанавливаем его свойству restitution значение 1, чтобы физическое тело не потеряло часть своего импульса во время столкновений. Постройте и запустите, чтобы увидеть как спрайт игрока падает в нижнюю часть экрана, поскольку гравитация безжалостно тянет его к центру Земли. Возможно, это звучит немного мелодраматически, но как наш герой собирается найти своё место в этом мире? Продолжение через неделю.... Скачать графические ресурсы для урока.