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 раньше.