Статьи

iOS 8: что нового в SpriteKit, часть 2

В этом руководстве представлен обзор новых функций платформы SpriteKit, представленных в iOS 8. Новые функции призваны упростить поддержку расширенных игровых эффектов и включают поддержку пользовательских шейдеров фрагментов OpenGL ES, освещения, теней, новых расширенных возможностей. физические эффекты и анимация, а также интеграция со SceneKit. В этом руководстве вы узнаете, как реализовать эти новые функции.

Эта серия разделена на два руководства и охватывает самые важные новые функции платформы SpriteKit. В первой части мы рассмотрим шейдеры, освещение и тени. Во второй части я расскажу об интеграции физики и SceneKit.

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

Загрузите проект Xcode, который мы создали в предыдущей статье, с GitHub, если вы хотите подписаться на него.

В iOS 8 SpriteKit представил новые физические функции, такие как физика на пиксель, ограничения, обратная кинематика и физика.

Кинематика — это процесс расчета трехмерного положения конца связанной структуры с учетом углов всех соединений. Обратная кинематика (IK) делает противоположное. С учетом конечной точки конструкции, какие углы должны быть выполнены соединениями для достижения этой конечной точки? Следующее изображение разъясняет эти понятия.

Обратная кинематика

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

Откройте PhysicsSceneEditor и добавьте спрайт croquette-o.png в желтый прямоугольник. Выберите спрайт и измените Имя в Инспекторе SKNode на Корень . Установите для параметра « Тип тела определения физики» значение « Нет» .

Добавьте второй спрайт, wood.png и измените его Имя на FirstNode . Измените родительское поле на Root. Переместите FirstNode , поместив его справа от Root, и измените его размер, чтобы создать прямоугольник, как показано ниже. Установите для параметра « Тип тела определения физики» значение « Нет» .

Результат должен выглядеть примерно так:

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

Якорная точка

Выполните предыдущие шаги и добавьте еще два спрайта.

  • Добавьте еще один спрайт croquette-o.png .
  • Измените его имя SecondNode .
  • Измените его Родителя на FirstNode .
  • Разместите его справа от FirstNode .
  • Измените тип тела определения физики на None .
  • Добавьте еще один спрайт wood.png .
  • Измените его поле имени на ThirdNode .
  • Измените его Parent на SecondNode .
  • Расположите его справа от второго узла .
  • Измените его размер, чтобы создать прямоугольник.
  • Измените тип тела определения физики на None .

Результат должен выглядеть примерно так:

Результирующее изображение

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

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

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

Режим симуляции
Режим редактирования

Измените режим для имитации и выберите спрайты FirstNode, SecondNode и ThirdNode. Вы можете выбрать несколько спрайтов, нажав Command .

Затем нажмите Shift-Control-Click и переместите спрайты по сцене. В результате узлы спрайтов анимируются и вращаются. Тем не менее, вращение странно и должно быть исправлено.

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

Выберите узлы спрайтов Root и SecondNode и установите максимальный угол ограничения IK равным 0 . Выберите узлы спрайтов FirstNode и ThirdNode и установите для точки привязки X значение 0 а для максимального угла IK90 .

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

На скриншоте ниже показана правильная конфигурация ограничений.

Ограничения в действии

Магнитные поля также являются новыми в SpriteKit. Давайте посмотрим, как это работает, добавив магнитное поле на физическую сцену. Откройте PhysicsScene.m и переменную экземпляра с именем SKFieldNode типа SKFieldNode .

1
2
3
@implementation PhysicsScene {
    SKFieldNode *magneticFieldNode;
}

В didMoveToView: метод, мы сначала конфигурируем сцену, создавая экземпляр SKPhysicsBody для сцены и добавив гравитационную силу. Это означает, что любые узлы в сцене будут вытянуты вниз.

1
2
3
SKPhysicsBody *physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
[self.physicsWorld setGravity:CGVectorMake(0, -9)];
[self setPhysicsBody:physicsBody];

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

1
2
3
4
5
magneticFieldNode = [SKFieldNode magneticField];
[magneticFieldNode setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:80]];
[magneticFieldNode setPosition:CGPointMake(100, 100)];
[magneticFieldNode setStrength:3];
[self addChild:magneticFieldNode];

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

01
02
03
04
05
06
07
08
09
10
11
12
for (int i = 0; i < 300; i++) {
         
    SKSpriteNode *node4 = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImageNamed:@»wood.png»] size:CGSizeMake(25, 25)];
    [node4 setPhysicsBody:[SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(25, 25)]];
    [node4 setPosition:CGPointMake(arc4random()%640, arc4random()%950)];
    [node4.physicsBody setDynamic:YES];
    [node4.physicsBody setAffectedByGravity:YES];
    [node4.physicsBody setAllowsRotation:true];
    [node4.physicsBody setMass:0.9];
         
    [self addChild:node4];
}

Завершенный didMoveToView: метод должен выглядеть следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-(void)didMoveToView:(SKView *)view {
    SKPhysicsBody *physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
    [self.physicsWorld setGravity:CGVectorMake(0, -9)];
    [self setPhysicsBody:physicsBody];
     
    magneticFieldNode = [SKFieldNode magneticField];
    [magneticFieldNode setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:80]];
    [magneticFieldNode setPosition:CGPointMake(100, 100)];
    [magneticFieldNode setStrength:3];
    [self addChild:magneticFieldNode];
     
    for (int i = 0; i < 300; i++) {
        SKSpriteNode *node4 = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImageNamed:@»wood.png»] size:CGSizeMake(25, 25)];
        [node4 setPhysicsBody:[SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(25, 25)]];
        [node4 setPosition:CGPointMake(arc4random()%640, arc4random()%950)];
        [node4.physicsBody setDynamic:YES];
        [node4.physicsBody setAffectedByGravity:YES];
        [node4.physicsBody setAllowsRotation:true];
        [node4.physicsBody setMass:0.9];
         
        [self addChild:node4];
    }
}

Перед созданием и запуском приложения мы touchesMoved:withEvent: метод touchesMoved:withEvent: чтобы вы могли перемещать узел магнитного поля, touchesMoved:withEvent: пальцем.

1
2
3
4
5
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch *touch in touches) {
        [magneticFieldNode setPosition:[touch locationInNode:self]];
    }
}

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

SceneKit — это высокоуровневая среда Objective-C для создания приложений и игр, использующих трехмерную графику. Он поддерживает импорт, манипулирование и рендеринг трехмерных активов. Алгоритм рендеринга требует только описания содержимого вашей сцены, анимации и действий, которые вы хотите выполнить.

Благодаря SceneKit вы теперь можете создавать и доставлять 3D-контент, используя SpriteKit. SceneKit имеет древовидную структуру и может использоваться двумя способами:

  • автономная среда SceneKit
  • интегрирован в SpriteKit

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

Иерархия SceneKit T

Когда вы интегрируете SceneKit в приложение на основе SpriteKit, вам нужно определить объект SK3DNode в качестве корневого объекта для вашей сцены. Это означает, что основная иерархия SceneKit изменяется на следующее:

SceneKit иерархия SpriteKit

Обратите внимание, что не каждый дочерний узел на приведенной выше схеме является обязательным. Вы только определяете и настраиваете узлы, которые соответствуют вашим потребностям. Например, вы можете добавить узел SCNLight для освещения сцены, даже если вы не включили узел SCNCamera в сцену.

SpriteKit и SceneKit поддерживают несколько форматов файлов для импорта моделей. Вы можете просмотреть эти модели в режиме реального времени в Xcode. Внутри папки « Текстуры » в вашем проекте ( Ресурсы> Текстуры ), есть файл с именем ship.dae . Когда вы выбираете этот файл, вы получаете новый пользовательский интерфейс, как показано ниже.

Предварительный просмотр Xcode

Слева от редактора вы видите две группы:

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

Чтобы использовать SceneKit в сочетании со SpriteKit, необходимо импортировать библиотеку SceneKit из инфраструктуры SceneKit. Откройте SceneKitScene.m и включите его, как показано ниже.

1
#include <SceneKit/SceneKit.h>

Мы собираемся использовать модель, хранящуюся в ship.dae, в качестве 3D-сцены. Внутри didMoveToView: метод создайте объект SCNScene который загружает сцену из этого файла.

1
SCNScene *shipScene = [SCNScene sceneNamed:@»ship.dae»];

Помните древовидную иерархию, о которой я упоминал ранее? Чтобы добавить объект shipScene объекту SKScene , необходимо выполнить два шага:

  • создать объект SK3DNode
  • определить сцену SceneKit для визуализации

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

1
2
3
SK3DNode *sk3DNodeFist = [[SK3DNode alloc] initWithViewportSize:CGSizeMake(300, 300)];
[sk3DNodeFist setPosition:CGPointMake(200,300)];
[sk3DNodeFist setScnScene:shipScene];

Наконец, добавьте объект SK3DNode в качестве дочернего узла к объекту SKScene .

1
[self addChild:sk3DNodeFist];

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

1
[self setBackgroundColor:[SKColor greenColor]];

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

01
02
03
04
05
06
07
08
09
10
-(void)didMoveToView:(SKView *)view {
    [self setBackgroundColor:[SKColor greenColor]];
     
    SCNScene *shipScene = [SCNScene sceneNamed:@»ship.dae»];
     
    SK3DNode *sk3DNodeFist = [[SK3DNode alloc] initWithViewportSize:CGSizeMake(300, 300)];
    [sk3DNodeFist setPosition:CGPointMake(200,300)];
    [sk3DNodeFist setScnScene:shipScene];
    [self addChild:sk3DNodeFist];
}

Давайте создадим более сложную сцену, которая содержит несколько объектов SCNNode . Для этой второй сцены нам нужно создать еще SK3DNode объект SK3DNode .

1
2
SK3DNode *sk3DNode = [[SK3DNode alloc] initWithViewportSize:CGSizeMake(400, 400)];
[sk3DNode setPosition:CGPointMake(150,200)];

Затем мы создаем объект SCNScene , который будет содержать дочерние узлы сцены.

1
SCNScene *sceneObject = [SCNScene scene];

Этот sceneObject будет иметь три узла:

  • Камера: этот узел используется для просмотра сцены через заданную позицию.
  • Свет: этот узел позволяет вам видеть различные свойства материала трехмерного объекта. Вы обычно определяете тип света и цвет.
  • 3D-объект: это импортированный или определенный объект в вашем коде. По умолчанию SceneKit позволяет вам определять несколько параметрических трехмерных объектов, то есть тора, прямоугольника, пирамиды, сферы, цилиндра, конуса, трубки, капсулы, пола, трехмерного текста или пользовательской фигуры.

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

  1. Создайте объект SCNCamera и определите его свойства.
  2. Создайте SCNNode которому будет назначен SCNCamera .
  3. Добавьте SCNNode как дочерний узел к объекту SCNScene .

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

1
2
3
4
5
SCNCamera *camera = [SCNCamera camera];
SCNNode *cameraNode = [SCNNode node];
[cameraNode setCamera:camera];
[cameraNode setPosition:SCNVector3Make(0, 0, 40)];
[sceneObject.rootNode addChildNode:cameraNode];

По умолчанию местоположение камеры и 3D-сцена расположены в начале координат (0,0,0) . Используя свойство position, вы можете настроить камеру вдоль трех осей x, y и z, чтобы изменить ее положение.

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

1
2
3
4
5
6
7
8
9
SCNLight *spotLight = [SCNLight light];
[spotLight setType:SCNLightTypeDirectional];
[spotLight setColor:[SKColor redColor]];
 
SCNNode *spotLightNode = [SCNNode node];
[spotLightNode setLight:spotLight];
[spotLightNode setPosition:SCNVector3Make(0, 0, 5)];
[cameraNode addChildNode:spotLightNode];
[sceneObject.rootNode addChildNode:spotLightNode];

Мы также создадим объект torus, как показано в следующем фрагменте кода.

1
2
3
4
SCNTorus *torus= [SCNTorus torusWithRingRadius:13 pipeRadius:1.5];
SCNNode *torusNode = [SCNNode nodeWithGeometry:torus];
[torusNode setTransform:SCNMatrix4MakeRotation(M_PI / 3, 0, 1, 0)];
[sceneObject.rootNode addChildNode:torusNode];

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

1
2
[sk3DNode setScnScene:sceneObject];
[self addChild:sk3DNode];

Вот как должен выглядеть финальный didMoveToView: метод.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
-(void)didMoveToView:(SKView *)view {
    [self setBackgroundColor:[SKColor greenColor]];
 
    SCNScene *shipScene = [SCNScene sceneNamed:@»ship.dae»];
     
    SK3DNode *sk3DNodeFist = [[SK3DNode alloc] initWithViewportSize:CGSizeMake(300, 300)];
    [sk3DNodeFist setPosition:CGPointMake(200,300)];
    [sk3DNodeFist setScnScene:shipScene];
    [self addChild:sk3DNodeFist];
     
    SK3DNode *sk3DNode = [[SK3DNode alloc] initWithViewportSize:CGSizeMake(400, 400)];
    [sk3DNode setPosition:CGPointMake(150,200)];
     
    SCNScene *sceneObject = [SCNScene scene];
     
    SCNCamera *camera = [SCNCamera camera];
    SCNNode *cameraNode = [SCNNode node];
    [cameraNode setCamera:camera];
    [cameraNode setPosition:SCNVector3Make(0, 0, 40)];
    [sceneObject.rootNode addChildNode:cameraNode];
     
    SCNLight *spotLight = [SCNLight light];
    [spotLight setType:SCNLightTypeDirectional];
    [spotLight setColor:[SKColor redColor]];
     
    SCNNode *spotLightNode = [SCNNode node];
    [spotLightNode setLight:spotLight];
    [spotLightNode setPosition:SCNVector3Make(0, 0, 5)];
    [cameraNode addChildNode:spotLightNode];
    [sceneObject.rootNode addChildNode:spotLightNode];
     
    SCNTorus *torus= [SCNTorus torusWithRingRadius:13 pipeRadius:1.5];
    SCNNode *torusNode = [SCNNode nodeWithGeometry:torus];
    [torusNode setTransform:SCNMatrix4MakeRotation(M_PI / 3, 0, 1, 0)];
    [sceneObject.rootNode addChildNode:torusNode];
     
    [sk3DNode setScnScene:sceneObject];
    [self addChild:sk3DNode];
}

Создайте и запустите приложение. Вы должны увидеть нечто похожее на следующий скриншот.

Конечный результат

Вы можете анимировать сцену, используя класс CABasicAnimation . Вам просто нужно создать экземпляр CABasicAnimation , вызвав animationWithKeyPath: Анимация, которую мы создаем в следующем фрагменте кода, будет бесконечно зацикливаться и будет длиться пять секунд. Добавьте следующий фрагмент кода в метод didMoveToView:

1
2
3
4
5
6
7
CABasicAnimation *torusRotation = [CABasicAnimation animationWithKeyPath:@»rotation»];
torusRotation.byValue = [NSValue valueWithSCNVector4:SCNVector4Make(1, 1, 0, 4.0*M_PI)];
[torusRotation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
[torusRotation setRepeatCount:INFINITY];
[torusRotation setDuration:5.0];
 
[torusNode addAnimation:torusRotation forKey:nil];

Создайте и запустите приложение для проверки анимации.

Если вы хотите узнать больше о SpriteKit, я советую вам прочитать следующие учебные пособия по SpriteKit:

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

На этом завершается второй урок из этой серии из двух частей, посвященной новым функциям платформы SpriteKit, представленным в iOS 8. В этой части вы узнали, как использовать физическое моделирование и интегрировать SceneKit. Если у вас есть какие-либо вопросы или комментарии, не стесняйтесь оставлять комментарии в комментариях.