SKScene — Построение сцены, пишем игру Happy Airjet — Часть 8

Итак, вы уже многое узнали о работе сцен. Подитожим главные факты:
— Сцены (объекты SKScene) используются для отображения вашего контента в SKView.
— Контент сцены создается в виде дерева объектов узлов. Сцена является корневым узлом.
— Во время отображения сцены работают действия (SKAction) и имитируется физика, для всего дерева узлов.
— Вы создаете собственные сцены наследованием класса SKScene.

Узел и система координат
Когда узел помещают в дерево узлов, его свойство position помещает его в положение в системе координат, представленной родительским узлом. Sprite Kit использует такую же систему координат как iOS и OS X.

cartesian_coords_2x

Значения координат измеряются в пунктах, как в UIKit или AppKit; где это необходимо, точки преобразуются в пиксели, когда сцена визуализируется.

Sprite Kit также имеет стандартную систему вращения. Угол 0 в радианах задает положительная ось х. Положительный угол дается в направлении против часовой стрелки. На рисунке ниже показана система вращения
polar_coords_2x

Happy Airjet
icon114
Наверняка все играли в игры типа Flappy Bird? Если нет, то загуглите что это. На досуге я написал штуку похожую на Flappy Bird, но все таки другую. Вот ссылка на апп стор. Само собой игруха бесплатна. Скачайте и попробуйте поиграть. А теперь предлагаю написать лайт-версию этой лайт-игры, т.е. без меню и без геймцентра. Просто игровой процесс. Написав его вы поймете как сделать простую анимацию спрайтов и как отслеживать коллизии объектов. Поехали:

Шаг 1.
Создайте проект из шаблона Single View Application. Уберите ориентации Landscape, оставте только Portrait. Добавьте во фреймворки SpriteKit. Сразу добавьте картинки из этого архива в Images.xcassets. Не забудь пренести все картинки в х2. Вот мы и подготовили проект. Все, что описано в этом шаге надеюсь делать вы умеете. Если что то не понятно, не стесняйтесь и спрашивайте в комментах, опишу подробнее. Запустите проект и проверьте, все ли ок

Шаг 2.
Создадим сцену. Итак у нас есть один ViewController на который мы должны положить сцену. Создавайте класс Scene, наследник класса SKScene.
Scene
Далее зайдите в storyboard, выделите на вьюконтроллере UIView и поменяйте в свойствах его класс на SKView. Запустите. Если ругается на SKScene, добавьте в Scene.h

#import <SpriteKit/SpriteKit.h>

Теперь надо инициировать сцену и отобразить. Для этого вставьте во ViewController.h

#import "Scene.h"

а во ViewController.m, в обработчик ViewDidLoad

    SKView * skView = (SKView *)self.view;
    skView.showsFPS = YES;
    skView.showsNodeCount = YES;
    
    Scene *scene=[Scene sceneWithSize:skView.bounds.size];
    scene.scaleMode = SKSceneScaleModeAspectFill;
         
    [skView presentScene:scene];

Тут все просто — инициализируем нашу сцену и показываем ее. Она пока пустая. Но на ней уже есть служебная информация FPS и счетчик узлов. Запускайте и проверяйте.

Шаг 3.
Теперь наполняем сцену. В Scene.h добавьте переменные

BOOL createScene; //флаг создания сцены
int score; //Счет
SKLabelNode *scoreLabel; //Надпись с текущим счетом

Далее идем в Scene.m и прописываем метод didMoveToView, он запускается во время показа сцены на экране. И еще пустые шаблоны (они внизу).

-(void)didMoveToView:(SKView *)view
{
    if (!createScene) {   //Защита от повторного создания сцены. Есть более умные способы, но этот самый наглядный
        score=0;          //Обнуляем очки
        
        [self removeAllActions];   //Удаляем со сцены все экшены
        [self removeAllChildren];  //Удаляем со сцены все узлы
        
        [self createBackground];  //Делаем фон
        [self createScoreLab];    //Делаем надпись с очками
        [self createShip];        //Делаем корабль плывущий внизу
        [self createSubmarine];   //Делаем подводную лодку
        [self createPlane];       //Делаем самолет

        createScene=YES;  //Флаг создания сцены
    }
}


-(void)createBackground{
    
}


-(void)createScoreLab{
    
}


-(void)createShip{
    
}


-(void)createSubmarine{
    
}


-(void)createPlane{
    
}

Набросали шаблонов пустых пока. Запустите и проверьте на ошибки. Если все ок, начнем заполнять шаблоны. Сначала createBackground (создаем фон).

-(void)createBackground
{
    SKSpriteNode *bg = [SKSpriteNode spriteNodeWithImageNamed:@"background"];
    bg.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
    [self addChild:bg];
}

Запускайте и проверяйте. Должен появится фон. Далее создадим надпись с текущим счетом createScoreLab

-(void)createScoreLab{
    scoreLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
    scoreLabel.text = [NSString stringWithFormat:@"Score %d", score];
    [scoreLabel setFontColor:[SKColor yellowColor]];
    scoreLabel.fontSize = 20;
    scoreLabel.position = CGPointMake(10,self.size.height-40);
    [scoreLabel setZPosition:1];
    [scoreLabel setName:@"SCORELAB"];
    [scoreLabel setHorizontalAlignmentMode:SKLabelHorizontalAlignmentModeLeft];
    [self addChild:scoreLabel];
}

Тут вообщем все понятно вроде, создаем метку SKLabelNode, и настраиваем, позицию, выравнивание, цвет, шрифт и размер. Запускайте и проверяйте.
На сцене должен быть фон и метка со счетом. Далее Корабль внизу createShip

-(void)createShip{
    SKSpriteNode *ship = [SKSpriteNode spriteNodeWithImageNamed:@"ship1"]; //создаем спрайт с кораблем
    [ship setSize:CGSizeMake(34, 28)];  //задаем размер
    ship.position = CGPointMake(-50, 70);  //задаем стартовую позицию
    ship.name = @"SHIP";                   //задаем имя
    [ship setAnchorPoint:CGPointZero];     //задаем якорную точку
    
    SKAction *yo=[SKAction sequence:@[    
                                      [SKAction moveByX:self.frame.size.width+(ship.size.width*2) y:0 duration:5], //корабль плывет 5 секунд
                                      [SKAction moveTo:CGPointMake(-50, 70) duration:0], //возвращаем корабль на стартовуб позицию (за экраном)
                                      [SKAction waitForDuration:3 withRange:3]]]; //ждем от 0 до 3х секунд
    
    [self addChild:ship];  //добавляем корабль на сцену
    
    [ship runAction:[SKAction repeatActionForever:yo]]; //повторяем бесконечно SKAction yo

}

Я все прокомментировал, если не понятно, пишите в комментах. Запускайте и проверяйте. Корабль должен плавать внизу. Далее субмарина. Примено все тоже самое

-(void)createSubmarine
{
    SKSpriteNode *subm = [SKSpriteNode spriteNodeWithImageNamed:@"ship2"];
    subm.position = CGPointMake(self.frame.size.width, 10);
    [subm setAnchorPoint:CGPointZero];
    subm.name = @"SUBMARINE";
    
    SKAction *yo=[SKAction sequence:@[
                                      [SKAction moveByX:-self.frame.size.width-subm.size.width y:0 duration:5],
                                      [SKAction moveTo:CGPointMake(self.frame.size.width, 10) duration:0],
                                      [SKAction waitForDuration:5 withRange:3]]];
    
    
    [self addChild:subm];
    
    [subm runAction:[SKAction repeatActionForever:yo]];

}

scene2
Пока все. В следующей части создадим облака и самолет. И пропишем их физику. С любыми вопросами пишите в комменты.

  • oleg

    «Сразу добавьте картинки из этого архива в Images.xcassets. Не забудь пренести все картинки в х2.»

    Вот это не совсем понятно

    • floMaster

      Чтобы хранить картинки в проекте, эпл придумало структуру images.xcassets. Эта штука находится обычно слева, в менеджере файлов проекта. Так вот когда туда нажмете, надо и картинки туда перенести из архива. У каждой картинки в этой структуре есть два (по умолчанию) представления — для обычных (старых) экранов и для ретиновских дисплеев. Когда перетаскиваете файлы они встают в позицию Обычный экран. А рядом пустая позиция для ретины, x2 подписано. Так вот картинки в x2 надо перенести