SpriteKit — это 2D игровой движок Apple — движок рендеринга, построенный на основе OpenGL. Он был представлен с iOS 7, и каждый последующий выпуск вносил отличные дополнения в фреймворк. Используя текстурированные спрайты, встроенный физический движок и очень мощный класс SKAction , вы можете очень быстро создавать функциональные 2D-игры.
SpriteKit имеет встроенные редакторы для сцен и частиц, узел камеры с момента выпуска iOS9 и встроенную поддержку наборов плиток с момента выпуска iOS 10. Благодаря этим новым дополнениям SpriteKit быстро становится локомотивом для создания 2D-игр.
Чтобы следовать этому уроку, просто загрузите прилагаемое репозиторий GitHub . В нем есть папка с именем ExampleProject Starter . Откройте проект в этой папке в Xcode, и вы готовы к работе!
Вершины
Узлы — это фундаментальные строительные блоки SpriteKit, а SKNode — базовый класс всех узлов. Все ваши экранные активы будут SKNode или его подклассом. SKNode сами по себе SKNode не предоставляют визуального контента. Весь визуальный контент рисуется с использованием одного из нескольких предопределенных подклассов SKNode . SKNode и его подклассы имеют несколько общих свойств, которые вы можете изменить. Некоторые из наиболее важных из них следующие.
-
position(CGPoint): позиция узла в системе координат его родителя -
xScale(CGFloat): масштабирует ширину узла множителем -
yScale(CGFloat): масштабирует высоту узла множителем -
alpha(CGFloat): прозрачность узла -
zRotation(CGFloat): вращение Эйлера вокруг оси z (в радианах)
Одним из наиболее важных SKNode является SKScene . Это корневой узел, к которому добавляются все остальные узлы. Сам по себе SKScene не предоставляет никаких визуальных элементов, но отображает добавленные к нему узлы.
Узлы сцены
SKScenes — это корневые узлы, к которым добавляются все остальные узлы. Сцена анимирует и визуализирует контент из своих дочерних узлов. Чтобы отобразить сцену, вы добавляете ее в SKView (который является подклассом UIView и поэтому имеет многие из тех же свойств, что и UIView ).
В начальном проекте SpriteKit начальная сцена отображается при загрузке проекта. На данный момент это просто пустой черный экран. Это показано, когда GameViewController вызывает GameViewController presentScene(_:) для экземпляра представления, передавая сцену в качестве параметра:
|
01
02
03
04
05
06
07
08
09
10
|
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene(size:CGSize(width: 768, height: 1024))
let skView = self.view as!
skView.showsFPS = false
skView.showsNodeCount = false
skView.ignoresSiblingOrder = false
scene.scaleMode = .aspectFill
skView.presentScene(scene) // Present the Scene
}
|
Не беспокойтесь о других вариантах сейчас; Я объясню их позже в этой серии.
Создание сцены
Многие игры имеют более одного экрана или сцены, поэтому мы создадим новую сцену с нуля, а затем покажем ее из нашей начальной сцены.
Выберите « Файл» > « Создать» > « Файл» в меню XCode и выберите « Какао-класс касания» .

Убедитесь, что для Class установлено значение NewScene, а для Subclass — SKScene . Нажмите Далее, а затем Создать , убедившись, что основная цель отмечена. Ниже приведен код для NewScene.swift .
|
1
2
3
4
5
6
|
import UIKit
import SpriteKit
class NewScene: SKScene {
}
|
Теперь у нас есть две сцены в нашем проекте, и ни одна не имеет визуального контента. Давайте добавим SKLabelNode (как и все узлы, это подкласс SKNode ). Единственной целью SKLabelNode является отображение текстовой метки.
Узлы меток
Узлы меток, реализованные в классе SKLabelNode , используются для отображения текста в вашей игре. Вы можете использовать пользовательские шрифты, если хотите, но для наших целей мы просто придерживаемся значения по умолчанию, которое отображает белый текст и имеет значение Helvetica Neue Ultra Light, 32 пункта .
Добавьте следующее в метод didMove(to:) в GameScene.swift . Этот метод вызывается сразу после представления сцены представлением. Как правило, это то место, где вы можете настроить любые игровые ресурсы и добавить их на сцену.
|
1
2
3
4
|
override func didMove(to view: SKView) {
let startGameLabel = SKLabelNode(text: «Start Game»)
}
|
Здесь мы создаем SKLabelNode используя удобный инициализатор init(text:) , который принимает в качестве параметра строку текста.
Добавление и удаление узлов
Простая инициализация узлов не покажет их в сцене. Чтобы отобразить узлы, вы должны вызвать метод addChild(_:) на принимающем узле, передавая SKNode который вы хотите добавить в качестве параметра.
Добавьте следующее в метод didMove(to:) .
|
1
2
3
4
|
override func didMove(to view: SKView) {
let startGameLabel = SKLabelNode(text: «Start Game»)
addChild(startGameLabel)
}
|
Метод addChild(_:) не является эксклюзивным для SKScene , но является методом SKNode . Это позволяет построить сложную иерархию узлов, известную как «дерево узлов». Например, предположим, что у вас есть игровой персонаж, и вы хотели раздвинуть его руки и ноги отдельно. Вы можете создать экземпляр SKNode а затем добавить каждую отдельную деталь в качестве дочернего элемента этого SKNode (содержащий узел известен как родительский узел). Это даст вам возможность перемещать персонажа как единое целое, перемещая родительский SKNode , но также позволит вам перемещать каждую отдельную часть по отдельности.
Другим важным методом добавления узлов является метод insertChild(_:at:) , который вставляет дочерний элемент в определенную позицию в списке дочерних узлов insertChild(_:at:) узла. Когда вы добавляете дочерний элемент к узлу, он поддерживает упорядоченный список дочерних элементов, на который ссылается чтение свойства дочернего элемента. Это важно при добавлении нескольких узлов к родительскому узлу, так как порядок добавления узлов влияет на некоторые аспекты обработки сцены, включая тестирование попаданий и рендеринг.
Чтобы удалить узел, вы вызываете метод removeFromParent() на узле, который хотите удалить.
Теперь, когда мы рассмотрели добавление и удаление узлов, мы можем перенести фокус обратно на пример проекта. Если вы помните, мы только что добавили SKLabelNode в GameScene . Если вы протестируете сейчас, вы увидите только половину текста в левом нижнем углу экрана.

Почему только половина текста отображается? Сейчас самое время поговорить о системе координат и позиционирования SpriteKit.
Позиционирование и координаты
По умолчанию система координат SpriteKit размещает ( 0,0 ) в левом нижнем углу экрана. Также по умолчанию SpriteKit размещает узлы таким образом, чтобы они располагались в ( 0,0 ). И все же, хотя … почему мы видим только половину текста? Это связано с тем, что по умолчанию текстовая метка центрируется по горизонтали относительно источника узла метки, что составляет ( 0,0 ). Ниже изображение, которое показывает, как работает система координат узла.

Начало узла находится в точке ( 0,0 ), положительная координата x перемещается вправо, а положительная координата y поднимается по экрану. Помните, что SKScene является узлом, и, следовательно, его источник также ( 0,0 ).
Установка позиции узла
Теперь, когда мы узнали, что система координат SpriteKit работает и как она размещает узлы, мы можем переместить SKLabelNode в другое положение, чтобы мы могли видеть весь текст. Добавьте следующее в метод didMove(to:) в GameScene.swift .
|
1
2
3
4
5
|
override func didMove(to view: SKView) {
let startGameLabel = SKLabelNode(text: «Start Game»)
startGameLabel.position = CGPoint(x: size.width/2, y: size.height/2)
addChild(startGameLabel)
}
|
Здесь мы размещаем метку в центре сцены. Свойство position имеет тип CGPoint , у которого есть значения x и y, которые представляют одну точку в сцене.
Если вы тестируете сейчас, вы должны увидеть, что метка была расположена в центре сцены.

Переключение между сценами
В настоящее время NewScene — это просто пустая сцена. Давайте также добавим к нему метку, и тогда мы сможем научиться переключаться между сценами. Перед вами стоит задача: прежде чем читать дальше, попробуйте добавить в NewScene ярлык с NewScene « Вернуться назад ». Мое решение ниже.
Первое, что нам нужно сделать, это добавить метод didMove(to:) . Добавьте следующее в NewScene.swift .
|
1
2
3
4
5
6
7
|
class NewScene: SKScene {
override func didMove(to view: SKView) {
}
}
|
Далее нам нужно добавить метку. Добавьте следующее в метод didMove(to:) который вы добавили выше.
|
1
2
3
4
5
|
override func didMove(to view: SKView) {
let goBackLabel = SKLabelNode(text: «Go Back»)
goBackLabel.position = CGPoint(x: size.width/2, y: size.height/2)
addChild(goBackLabel)
}
|
Это добавляет метку в NewScene с текстом « Вернуться ». Далее мы реализуем функциональность, которую предлагает этот ярлык, — мы будем реагировать на сенсорные события, переключая сцены.
Отвечая на прикосновение
Почти все мобильные игры будут взаимодействовать с помощью сенсорного. На этом этапе вы узнаете, как реагировать на сенсорные события в вашей игре.
Чтобы зарегистрировать обработчики сенсорных событий в вашей игре, вы должны реализовать представление touchesBegan(_:with:) . Добавьте следующее в GameScene.swift:
|
1
2
3
|
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print(«YOU TOUCHED»)
}
|
Если вы хотите проверить это сейчас, вы увидите, что ВЫ прикоснулись к консоли, когда дотронетесь до экрана. Однако обычно нам нужно знать, когда был затронут конкретный узел. Для этого нам нужен способ найти и идентифицировать узлы. Мы узнаем, как этого добиться, а затем вернемся и закончим метод touchesBegan(_:with:) .
Поиск дерева узлов
Чтобы иметь возможность идентифицировать узел, вы используете свойство name узла и ищите в дереве узлов узел с этим именем. Свойство name узла принимает буквенно-цифровую строку без пунктуации.
Есть несколько способов поиска узла по его свойству name . Если у вас уже есть ссылка на узел, вы можете просто проверить его свойство name напрямую, что мы и сделаем в методе touchesBegan(_:with:) . Однако важно знать, как искать дерево узлов для определенного узла по имени или искать группу узлов с тем же именем.
Метод childNode(withName:) ищет дочерние узлы для конкретного имени, переданного в качестве параметра.
Метод enumerateChildNodes(withName:using:) ищет дочерние узлы и вызывает блок один раз для каждого соответствующего узла, который он находит. Этот метод используется, когда вы хотите найти все узлы с одинаковым именем.
subscript(_:) метод subscript(_:) возвращает массив узлов, которые соответствуют параметру name.
Вы также можете искать узлы, используя синтаксис расширенного поиска, который позволяет вам искать по всему дереву сцены или искать, например, шаблон, а не точное имя. Эта расширенная возможность поиска выходит за рамки данного руководства. Однако, если вы хотите узнать больше, вы можете прочитать об этом в SKNode программированию SKNode .
Теперь, когда мы знаем, как искать узлы в дереве узлов, давайте дадим нашим меткам имя.
Добавьте следующее в метод didMove(to:) в GameScene.swift .
|
1
2
3
4
5
6
|
override func didMove(to view: SKView) {
let startGameLabel = SKLabelNode(text: «Start Game»)
startGameLabel.name = «startgame»
startGameLabel.position = CGPoint(x: size.width/2, y: size.height/2)
addChild(startGameLabel)
}
|
Здесь мы устанавливаем startGameLabel name startgame в startgame .
Нам также нужно установить имя метки в NewScene . Добавьте следующее с помощью didMove(to:) в NewScene.swift .
|
1
2
3
4
5
6
|
override func didMove(to view: SKView) {
let goBackLabel = SKLabelNode(text: «Go Back»)
goBackLabel.name = «goback»
goBackLabel.position = CGPoint(x: size.width/2, y: size.height/2)
addChild(goBackLabel)
}
|
Мы устанавливаем для свойства name значение goback .
Определение того, какой узел касается
Добавьте следующее в метод touchesBegan(_:with:) в GameScene.swift .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
let touchLocation = touch.location(in: self)
let touchedNode = self.atPoint(touchLocation)
if(touchedNode.name == «startgame»){
let newScene = NewScene(size: size)
newScene.scaleMode = scaleMode
let doorsClose = SKTransition.doorsCloseVertical(withDuration: 2.0)
view?.presentScene(newScene, transition: doorsClose)
}
}
|
Свойство multiTouchEnabled вида сцены по умолчанию имеет значение false , что означает, что вид получает только первое касание последовательности мультитач. Если это свойство отключено, вы можете получить касание, используя first вычисленное свойство из набора касаний, поскольку в наборе есть только один объект.
Мы можем получить touchLocation в сцене из свойства location касания. Затем мы можем выяснить, какой узел был затронут, вызывая atPoint(_:) и передавая touchLocation .
Мы проверяем, touchedNode ли touchedNode имени touchedNode с именем "startgame" , и если это так, мы знаем, что пользователь коснулся метки. Затем мы создаем экземпляр NewScene и устанавливаем его свойство scalemode таким же, как у текущей сцены — это гарантирует, что сцена действует одинаково на разных устройствах. Наконец, мы создаем SKTransition и вызываем метод presentScene(_:transition:) , который представляет сцену вместе с переходом.
В классе SKTransition есть много методов класса, которые вы можете вызывать для показа различных переходов между сценами вместо немедленного показа сцены. Это обеспечивает некоторую «конфету» для конечного пользователя и делает показ новой сцены менее резким. Чтобы увидеть все доступные типы переходов, проверьте класс SKTransition в справочном руководстве .
Я не собираюсь реализовывать метод touchesBegan(_:with:) в NewScene . Почему бы вам не попробовать сделать это самостоятельно и вернуть метку обратно в GameScene используя другой тип перехода? Код будет сильно напоминать то, что мы имеем выше, только помните, что мы назвали SKLabelNode "goback" .
Вывод
До сих пор мы узнали много нового об узлах, используя сцены, и вы увидели, как использовать узел метки в качестве общего примера для изучения некоторых характеристик узлов. Мы изучили их системы координат, как найти их в дереве узлов, как расположить их и как реагировать на сенсорные события.
Доступно несколько других типов узлов, и мы рассмотрим их в следующем руководстве — начиная с SKSpriteNode !
Чтобы узнать больше о том, как начать использовать SpriteKit, вам также следует ознакомиться с публикацией Дэвиса Элли здесь на Envato Tuts +
Кроме того, ознакомьтесь с нашими курсами SpriteKit! Они проведут вас через все этапы создания вашей первой игры SpriteKit для iOS, даже если вы никогда не программировали SpriteKit раньше.


