Лучший способ изучить Sprite Kit это увидеть его в действии — Часть 3

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

1. Создайте новый класс Spaceship и установите subclass SKScene.

2. Добавьте в Spaceship.h свойство contentCreated

@property BOOL contentCreated;

3. Добавьте в Spaceship.m методы didMoveToView: и createSceneContents

- (void)didMoveToView:(SKView *)view
{
    if (!self.contentCreated)
    {
        [self createSceneContents];
        self.contentCreated = YES;
    }
}
- (void)createSceneContents
{
    self.backgroundColor = [SKColor whiteColor];
    self.scaleMode = SKSceneScaleModeAspectFit;
}

4. Добавьте импорт Spaceship.h в HelloScene.m файл

#import "Spaceship.h"

5.В классе HelloScene, в методе touchesBegan:withEvent: замените runAction: на новый runAction

[helloNode runAction: moveSequence completion:^{
            SKScene *spaceshipScene  = [[Spaceship alloc] initWithSize:self.size];
            SKTransition *doors = [SKTransition doorsOpenVerticalWithDuration:0.5];
            [self.view presentScene:spaceshipScene transition:doors];
        }];

6. Запустите проект и тапните по экрану. После окончания анимации должен произойти переход к новой сцене.

Сделаем космический корабль во второй сцене
Наша новая сцена еще совсем пустая, поэтому мы должны добавить на нее космический корабль. Чтобы построить космический корабль, вам нужно использовать несколько объектов SKSpriteNode, один чтобы создать космический корабль и несколько для огней на его поверхности. Каждый из спрайтов будет выполнять действия.
SKSpriteNode является наиболее распространенным классом, используемым для создания контента. SKSpriteNode могут быть как с текстурами так и без них. Сейчас мы не будем использовать текстуры, но в дальнейшем вы сможете легко заменить эти спрайты, на спрайты с текстурами и при этом поведение спрайта не изменится. Для начала сделаем спрайт-корпус космического корабля, обычный прямоугольник. Потом разместим мигающие огни на корпусе. Так как огни должны двигаться вместе с кораблем, то разместим их в корабле (т.е. корабль будет родительским узлом для огней). Координаты огней будут указываться относительно

Позднее эти заполнители могут быть легко заменены текстурированных спрайтов без изменения их поведения. В реальной игре , вам может потребоваться десятки или сотни узлов , чтобы создать визуальное содержимое вашей игре. Но , в сущности, те спрайты собираетесь использовать те же методы, как этого простого примера.
Хотя вы можете добавлять все три спрайты прямо на сцене , это не комплект способ Sprite. Мигающие огни являются частью космического корабля! Если корабль движется , огни должны двигаться вместе с ним. Решение сделать космический корабль узел их родителей, так же, что сцена собирается быть родителем корабля . Координаты огни будут указаны относительно позиции родительского узла , который находится в центре спрайта изображения.
Чтобы добавить космический корабль
1. В SpaceshipScene.m, добавьте в метод createSceneContents код

    SKSpriteNode *spaceship = [self newSpaceship];
    spaceship.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame)-150);
    [self addChild:spaceship];

2. Также добавьте метод newSpaceship

- (SKSpriteNode *)newSpaceship
{
    SKSpriteNode *hull = [[SKSpriteNode alloc] initWithColor:[SKColor redColor] size:CGSizeMake(64,32)];
    SKAction *hover = [SKAction sequence:@[
                                           [SKAction waitForDuration:1.0],
                                           [SKAction moveByX:100 y:0.0 duration:1.0],
                                           [SKAction waitForDuration:1.0],
                                           [SKAction moveByX:0 y:300.0 duration:1.0],

                                           [SKAction waitForDuration:1.0],
                                           [SKAction moveByX:-100 y:0.0 duration:1.0],
                                           [SKAction waitForDuration:1.0],
                                           [SKAction moveByX:0 y:-300 duration:1.0]]];
    [hull runAction: [SKAction repeatActionForever:hover]];
    return hull;
}

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

3. Запустите проект, сделайте тап на экране, и на второй сцене вы увидите красный прямоугольник (корпус корабля) который двигается по заданной нами траектории.

4. Добавьте код в метод newSpaceship.

    SKSpriteNode *light1 = [self newLight];
    light1.position = CGPointMake(-28.0, 6.0);
    [hull addChild:light1];

    SKSpriteNode *light2 = [self newLight];
    light2.position = CGPointMake(28.0, 6.0);
    [hull addChild:light2];

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

5. Добавьте метод newLight

- (SKSpriteNode *)newLight
{
    SKSpriteNode *light = [[SKSpriteNode alloc] initWithColor:[SKColor greenColor] size:CGSizeMake(8,8)];
    SKAction *blink = [SKAction sequence:@[
                                           [SKAction fadeOutWithDuration:0.25],
                                           [SKAction fadeInWithDuration:0.25]]];
    SKAction *blinkForever = [SKAction repeatActionForever:blink];
    [light runAction: blinkForever];
    return light;
}

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

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

Добавим симуляцию физики на сцену
1. Измените метод newSpaceship, добавьте SKPhysyscsBody к космическому кораблю

hull.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:hull.size];

2. Запустите проект.
Космический корабль падает вниз. Это потому, что сила притяжения применяется космическому кораблю. Несмотря на это, его анимация движения по-прежнему работает.

3. Добавьте в метод newSpaceship небольшое изменение, чтобы предотвратить воздействие на корабль физических сил. Корабль перестанет падать.

hull.physicsBody.dynamic = NO;


4. Добавьте код в метод createSceneContents

 SKAction *makeRocks = [SKAction sequence: @[
                                                [SKAction performSelector:@selector(addRock) onTarget:self],
                                                [SKAction waitForDuration:0.10 withRange:0.15]
                                                ]];
 [self runAction: [SKAction repeatActionForever:makeRocks]];

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

5. Добавьте метод addRock

static inline CGFloat skRandf() {
    return rand() / (CGFloat) RAND_MAX;
}
static inline CGFloat skRand(CGFloat low, CGFloat high) {
    return skRandf() * (high - low) + low;
}

- (void)addRock
{
    SKSpriteNode *rock = [[SKSpriteNode alloc] initWithColor:[SKColor orangeColor] size:CGSizeMake(10,10)];
    rock.position = CGPointMake(skRand(0, self.size.width), self.size.height-50);
    rock.name = @"rock";
    rock.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rock.size];
    rock.physicsBody.usesPreciseCollisionDetection = YES;
    [self addChild:rock];
}

6. Запустите приложение.

Теперь камни будут падать из верхней части сцены. Когда камень попадает на корабль, он отскакивает от корабля. Мы не добавляли для камней ни каких действий, камни падают и отскакивают полностью за счет физики Sprite Kit.
Если вы запустите приложение и немного подождете, частота кадров начинает падать, несмотря на то, что количество узлов в диагностикческой информации остается очень малым. Это происходит потому, информация отображается только по видимым узлам на сцене. Однако, когда камень падает через нижней край сцены, он продолжает существовать в сцене, а это значит, что Sprite Kit продолжает моделировать физику для этого узла. В конце концов обрабатываемых узлов становится очень много и Sprite Kit начинает тормозить.

7. Добавьте метод didSimulatePhysics, он удаляет камни которые уже не видно на экране

-(void)didSimulatePhysics
{
[self enumerateChildNodesWithName:@"rock" usingBlock:^(SKNode *node, BOOL *stop) {
        if (node.position.y < 0)
            [node removeFromParent];
}]; 
}

Ваше первое приложение Sprite Kit готово. Я намеренно не выкладываю исходник, поскольку в сети очень много исходников на эту тему, если вам нужен только он. Если же вы хотите действительно заняться изучением Sprite Kit, то вам не составит труда сделать все как описано в моих статьях и заодно понять как это все работает.
Далее будем рассматривать более детально все свойства и методы.

  • Pavel

    Здравствуйте, а можете объяснить, почему при описании позиции корабля начало координат находится внизу экрана, а при удалении камней уже вверху?

    • floMaster

      Привет! Начало координат при добавлении корабля внизу-слева, и при удалении камней тоже внизу. Вот тут удаляются камни:
      if (node.position.y < 0) //node — это камень в нашем случае )
      [node removeFromParent];

      Если камень при падении уходит ниже 0 (это y), т.е. ниже нижней части экрана, то камень удаляем.