Статьи

Работа с SKTransition

Из этого туториала вы узнаете, как комбинировать представления UIKit и класс SKTransition для создания красивых, пользовательских переходов между различными SKScenes . Читай дальше!


Рисунок 1: Окончательный результат!
Иллюстрация окончательного результата.

Сцены — это интерфейсы, которые содержат несколько объектов просмотра. Обычно вы создаете несколько сцен для каждой части вашей игры, а затем при необходимости используете переходы между сценами. Например, вы можете создать разные классы сцены для представления любого или всех следующих понятий:

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

Как видите, сцена может быть чем угодно, что программист хотел бы создать. Как правило, вы переходите к новой сцене на основе игровых целей или прямого пользовательского ввода. Например, если таймер заканчивается, может быть представлена ​​новая сцена, представляющая «окончание игры». Кроме того, если пользователь нажимает кнопку параметров, вы можете перейти к новой сцене, чтобы настроить параметры игры. Обратите внимание, что на этапе перехода свойство сцены немедленно обновляется, чтобы указывать на новую сцену. После этого происходит переход.

Переход также может иметь объект перехода (эффект или анимацию). Этот объект создаст динамичное, красивое представление, когда произойдет переход. Существует несколько объектов, и для полной и официальной справки вам следует обратиться к справке по классу SKTransition .


Класс SKTransition представляет несколько методов и два свойства (подробнее об этом позже). Цель всех методов — создать блестящий и динамичный переход между сценами. Методы класса можно разделить на четыре основных раздела:

  • С длительностью: переходы, которые будут происходить в течение определенного периода времени.
  • С цветом: переходы, которые будут использовать объект UIColor чтобы UIColor присущий переход.
  • С Направлением: Переходы, которые будут сделаны с указанного направления.
  • С CIFilter: переходы, которые будут использовать пользовательский фильтр для создания визуального эффекта при переходе.

Duration и Color являются простыми объектами, но не Direction и CIFilter .

Как следует из названия, свойство direction означает, что переход будет происходить в определенном направлении. Свойство direction может иметь одну из четырех констант. Эти константы объявлены как NS_ENUM , например так:

1
2
3
4
5
6
7
typedef NS_ENUM(NSInteger,
   SKTransitionDirection) {
   SKTransitionDirectionUp,
   SKTransitionDirectionDown,
   SKTransitionDirectionRight,
   SKTransitionDirectionLeft,
};

CIFilter даже более надежен, чем Direction поскольку он также является ссылочным классом с методами класса, методами экземпляра и свойствами. В этом руководстве не будет CIFilter класс CIFilter , но будет представлен пример того, как его использовать для создания пользовательского фильтра и SKTransition . Дополнительное примечание относительно класса CIFilter : он поддерживает десятки эффектов, но не все поддерживаются последней версией iOS. Вам следует обратиться к Справочнику по фильтру основных изображений, чтобы увидеть список совместимости.

Обратите внимание, что вы можете использовать несколько «сгруппированных методов» для создания SKTransiton . Но как узнать, какие из них можно комбинировать? Для этого вы должны обратиться к сигнатуре метода и определить свойства, принятые каждым методом.

Например, давайте проанализируем три метода, чтобы увидеть, что они могут обрабатывать:

  • doorsOpenVerticalWithDuration:
  • fadeWithColor:duration:
  • revealWithDirection:duration:

Как уже говорилось ранее, имена методов позволяют вам быстро понять, что может делать каждый метод. Для уточнения, метод doorsOpenVerticalWithDuration: будет учитывать только продолжительность. Метод fadeWithColor:duration: использует как цвет, так и продолжительность. Сначала вы должны определить объект UIColor а затем время продолжительности. Метод revealWithDirection:duration: будет использовать только направление, которое, в свою очередь, может быть одним из четырех свойств. Обратите внимание, что вы также можете расширить класс SKTransition для создания пользовательских переходов и объединения Duration , Color , Direction и CIFilter .

Рисунок 2: showWithDirection
Иллюстрация раскрытия с направлением (UP)

Класс SKTransition имеет только два свойства: pausesIncomingScene и pausesOutgoingScene . Оба настраивают, воспроизводятся ли анимации во время перехода, и оба свойства являются Boolean значениями. Разница заключается в следующем:

  • pausesIncomingScene определяет, будет ли входящая сцена приостановлена ​​во время перехода.
  • pausesOutgoingScene определяет, будет ли исходящая сцена приостановлена ​​во время перехода.

Так как оба являются Boolean значениями, определение легко и может быть понято в следующем фрагменте:

1
2
3
4
5
// other code …
transitionCrossFade = [SKTransition crossFadeWithDuration:1];
transitionCrossFade.pausesIncomingScene = TRUE;
transitionDoorsCloseHorizontal = [SKTransition doorsCloseHorizontalWithDuration:2];
transitionDoorsCloseHorizontal.pausesOutgoingScene = FALSE;

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


Теперь, когда вы знаете основы класса SKTransition , вы можете начать фазу программирования.

Первый шаг — открыть Xcode и запустить новый проект SpriteKit . Затем вы должны добавить еще один класс Objective-C именем TransitionResult и суперкласс SKScene .

Целью этого проекта является создание двух классов, которые будут поменяться местами между ними. Первый ( MyScene уже определенный Xcode) будет содержать UITableView который будет содержать ссылку на каждый SKTransition . Второй ( TransitionResult ) будет сценой назначения после перехода.

Когда пользователь нажимает на экран после перехода, он снова переносится в MyScene .

В MyScene.h вы объявите 3 объекта: UITableView , UISlider и NSArray . UITableView будет отображать имена каждого перехода, UISlider будет определять Duration этого перехода, а NSArray будет содержать имена каждого SKTransition . Окончательный код MyScene.h будет похож на этот фрагмент:

1
2
3
@property (retain, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic, retain) IBOutlet UISlider *sliderTimer;
@property (strong, nonatomic) NSArray *transitionsArray;

Теперь сосредоточьтесь на MyScene реализации MyScene .

Первым шагом является использование метода -(id)initWithSize:(CGSize)size для инициализации вышеупомянутых объектов. Одна из возможных настроек конфигурации:

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
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(CGRectGetMinX(self.frame), CGRectGetMinY(self.frame)+20, CGRectGetMaxX(self.frame), CGRectGetMaxY(self.frame)-80)];
_tableView.dataSource = self;
_tableView.delegate = self;
 
_sliderTimer = [[UISlider alloc] initWithFrame:CGRectMake(CGRectGetMidX(self.frame)-70, CGRectGetMaxY(self.frame)-40, 140, 3)];
[_sliderTimer addTarget:self action:@selector(sliderAction) forControlEvents:UIControlEventValueChanged];
[_sliderTimer setBackgroundColor:[UIColor clearColor]];
_sliderTimer.minimumValue = 1;
_sliderTimer.maximumValue = 4;
_sliderTimer.continuous = YES;
_sliderTimer.value = 1;
 
_transitionsArray = [[NSArray alloc] initWithObjects:
                           @»crossFadeWithDuration»,
                           @»doorsCloseHorizontalWithDuration»,
                           @»doorsCloseVerticalWithDuration»,
                           @»doorsOpenHorizontalWithDuration»,
                           @»doorsOpenVerticalWithDuration»,
                           @»doorwayWithDuration»,
                           @»fadeWithColor:duration»,
                           @»fadeWithDuration»,
                           @»flipHorizontalWithDuration»,
                           @»flipVerticalWithDuration»,
                           @»moveInWithDirectionDown:duration»,
                           @»moveInWithDirectionUp:duration»,
                           @»moveInWithDirectionLeft:duration»,
                           @»moveInWithDirectionRight:duration»,
                           @»pushWithDirection:duration»,
                           @»revealWithDirection:duration»,
                           @»transitionWithCIFilter:duration»,
                           nil];

Тем не менее, предупреждение отображается, так как действие sliderAction отсутствует. Метод будет обновлять в режиме реального времени transitionTimerText с учетом значения UISlider .

1
2
3
-(void)sliderAction{
    transitionTimerText.text = [[NSString alloc] initWithFormat:@»Transition Duration: %f», _sliderTimer.value];
}

Обратите внимание, что виды, расположение, конфигурация и макет полностью настраиваются. Если вы хотите, вы можете настроить это в ваших интересах. Кроме того, вы добавите SKLabelNode для хранения и отображения значения UISlider . Это значение будет представлять SKTransition Duration . Добавьте SKLabelNode* transitionTimerText в ваш файл реализации, и соответствующая инициализация будет:

1
2
3
4
5
transitionTimerText = [SKLabelNode labelNodeWithFontNamed:@»Chalkduster»];
transitionTimerText.text = [[NSString alloc] initWithFormat:@»Transition Duration: %f», _sliderTimer.value];
transitionTimerText.fontSize = 10;
transitionTimerText.color = [SKColor colorWithRed:0 green:0 blue:0 alpha:1];
transitionTimerText.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMinY(self.frame)+45);

Теперь, когда у вас есть настроенные объекты, вам просто нужно добавить их на сцену. Для этого вы будете использовать -(void) didMoveToView:(SKView *)view метод представления. Добавьте его в свой файл и внутри добавьте вышеупомянутые виды к основному виду:

1
2
3
4
5
-(void) didMoveToView:(SKView *)view{
    [self addChild:transitionTimerText];
    [self.scene.view addSubview:_sliderTimer];
    [self.scene.view addSubview:_tableView];
}

Если вы запустите проект сейчас, вы увидите два объекта на экране: UISlider и SKLabelNode .

Следующим шагом является отображение методов SKTransition в UITableView . Для этого вам нужно изменить файл MyScene.h и расширить свой класс протоколами <UITableViewDataSource, UITableViewDelegate> . Окончательный MyScene.h должен выглядеть так:

1
@interface MyScene : SKScene <UITableViewDataSource, UITableViewDelegate>

Вернитесь к файлу реализации, и вы получите предупреждение. Это предупреждение говорит о том, что вам необходимо реализовать дополнительные методы, присущие UITableView . Необходимые методы: -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section и -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath . Первый вернет количество строк в таблице, а второй будет обрабатывать логику для каждой ячейки внутри таблицы. Для дополнительных примечаний относительно класса UITableView вы должны обратиться к официальному справочному классу.

Первый способ прост и состоит из одной строки:

1
2
3
4
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [_transitionsArray count];
}

Второй метод является более сложным, так как мы должны определить свойства и конфигурации ячейки таблицы (содержимое ячейки). В этом примере вы будете использовать простой UITableViewCellStyleSubtitle . Если у вас возникли проблемы при написании метода, полная версия представлена ​​ниже:

01
02
03
04
05
06
07
08
09
10
11
12
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    NSString *transitions = [_transitionsArray objectAtIndex:indexPath.row];
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@»Identifier»];
 
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@»Identifier»];
    }
 
    [cell.textLabel setText:transitions];
    return cell;
}

Запустите ваш код сейчас, и вы должны увидеть каждую клеточную строку с уникальным именем. Каждое имя представляет собой SKTransiton используемый, если пользователь SKTransiton этой ячейки. Вы также заметите, что UITableView не имеет заголовка. Давайте это исправим!

Добавьте следующий метод: - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section . В этом методе добавьте следующий код:

01
02
03
04
05
06
07
08
09
10
11
NSString *sectionName;
    switch (section)
    {
        case 0:
            sectionName = NSLocalizedString(@»SKTransition List», @»SKTransition List»);
            break;
        default:
            sectionName = @»»;
            break;
    }
    return sectionName;

Этот метод может иметь несколько заголовков для нескольких разделов UITableView . Однако у нас будет только один раздел, поэтому заголовок будет «Список SKTransition» (или любой другой по вашему выбору).

На этом этапе вам нужно добавить взаимодействие с пользователем в ячейки. Для этого нужен еще один дополнительный метод. На этот раз должен быть вызван метод -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath . Этот метод длинный, но его легко понять. Вы будете выделять необходимые ресурсы для каждой SKTransition и, основываясь на SKTransition ячейке, представите еще одну SKScene .

Первым шагом является импорт файла заголовка TransitionResult , а затем определение объекта TransitionResult для результирующего SKScene . Посмотрите на следующее, чтобы увидеть это в действии:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
TransitionResult* transitionResult;
 
SKTransition* transitionCrossFade;
SKTransition* transitionDoorsCloseHorizontal;
SKTransition* transitionDoorsCloseVertical;
SKTransition* transitiondoorsOpenHorizontal;
SKTransition* transitionDoorsOpenVertical;
SKTransition* transitionDoorway;
SKTransition* transitionFadeWithColor;
SKTransition* transitionFadeWithDuration;
SKTransition* transitionFlipHorizontal;
SKTransition* transitionFlipVertical;
SKTransition* transitionMoveInWithDirectionDown;
SKTransition* transitionMoveInWithDirectionUp;
SKTransition* transitionMoveInWithDirectionLeft;
SKTransition* transitionMoveInWithDirectionRight;
SKTransition* transitionPushWithDirection;
SKTransition* transitionRevealWithDirectionUp;
SKTransition* transitionWithCIFilter;

Теперь в -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath настало время выделить необходимые ресурсы. Полный код:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
transitionCrossFade = [SKTransition crossFadeWithDuration:_sliderTimer.value];
    transitionCrossFade.pausesIncomingScene = TRUE;
    transitionDoorsCloseHorizontal = [SKTransition doorsCloseHorizontalWithDuration:_sliderTimer.value];
    transitionDoorsCloseHorizontal.pausesOutgoingScene = FALSE;
    transitionDoorsCloseVertical = [SKTransition doorsCloseVerticalWithDuration:_sliderTimer.value];
    transitiondoorsOpenHorizontal = [SKTransition doorsOpenHorizontalWithDuration:_sliderTimer.value];
    transitionDoorsOpenVertical = [SKTransition doorsOpenVerticalWithDuration:_sliderTimer.value];
    transitionDoorway = [SKTransition doorwayWithDuration:_sliderTimer.value];
    transitionFadeWithColor = [SKTransition fadeWithColor:[UIColor yellowColor] duration:_sliderTimer.value];
    transitionFadeWithDuration = [SKTransition fadeWithDuration:_sliderTimer.value];
    transitionFlipHorizontal = [SKTransition flipHorizontalWithDuration:_sliderTimer.value];
    transitionFlipVertical = [SKTransition flipVerticalWithDuration:_sliderTimer.value];
    transitionMoveInWithDirectionDown = [SKTransition moveInWithDirection:SKTransitionDirectionDown duration:_sliderTimer.value];
    transitionMoveInWithDirectionUp = [SKTransition moveInWithDirection:SKTransitionDirectionUp duration:_sliderTimer.value];
    transitionMoveInWithDirectionLeft = [SKTransition moveInWithDirection:SKTransitionDirectionLeft duration:_sliderTimer.value];
    transitionMoveInWithDirectionRight = [SKTransition moveInWithDirection:SKTransitionDirectionRight duration:_sliderTimer.value];
    transitionPushWithDirection = [SKTransition pushWithDirection:SKTransitionDirectionDown duration:_sliderTimer.value];
    transitionRevealWithDirectionUp = [SKTransition revealWithDirection:SKTransitionDirectionUp duration:_sliderTimer.value];
 
    CGRect screenRect = [[UIScreen mainScreen] bounds];
    CIVector *extent = [CIVector vectorWithX:0 Y:0 Z:screenRect.size.width W:screenRect.size.height];
    transitionWithCIFilter = [SKTransition transitionWithCIFilter:[CIFilter filterWithName: @»CIFlashTransition»
                                                                             keysAndValues: @»inputExtent», extent,
                                                                   @»inputCenter»,[CIVector vectorWithX:0.3*screenRect.size.width Y:0.7*screenRect.size.height],
                                                                   @»inputColor», [CIColor colorWithRed:1.0 green:0.8 blue:0.6 alpha:1],
                                                                   @»inputMaxStriationRadius», @2.5,
                                                                   @»inputStriationStrength», @0.5,
                                                                   @»inputStriationContrast», @1.37,
                                                                   @»inputFadeThreshold», @0.85, nil] duration:_sliderTimer.value];
 
    transitionResult = [[TransitionResult alloc] initWithSize:CGSizeMake(CGRectGetMaxX(self.frame), CGRectGetMaxY(self.frame))];
 
    switch (indexPath.row) {
        case 0:
            [self.scene.view presentScene:transitionResult transition:transitionCrossFade];
            [self removeUIKitViews];
            break;
        case 1:
            [self.scene.view presentScene:transitionResult transition:transitionDoorsCloseHorizontal];
            [self removeUIKitViews];
            break;
        case 2:
            [self.scene.view presentScene:transitionResult transition:transitionDoorsCloseVertical];
            [self removeUIKitViews];
            break;
        case 3:
            [self.scene.view presentScene:transitionResult transition:transitiondoorsOpenHorizontal];
            [self removeUIKitViews];
            break;
        case 4:
            [self.scene.view presentScene:transitionResult transition:transitionDoorsOpenVertical];
            [self removeUIKitViews];
            break;
        case 5:
            [self.scene.view presentScene:transitionResult transition:transitionDoorway];
            [self removeUIKitViews];
            break;
        case 6:
            [self.scene.view presentScene:transitionResult transition:transitionFadeWithColor];
            [self removeUIKitViews];
            break;
        case 7:
            [self.scene.view presentScene:transitionResult transition:transitionFadeWithDuration];
            [self removeUIKitViews];
            break;
        case 8:
            [self.scene.view presentScene:transitionResult transition:transitionFlipHorizontal];
            [self removeUIKitViews];
            break;
        case 9:
            [self.scene.view presentScene:transitionResult transition:transitionFlipVertical];
            [self removeUIKitViews];
            break;
        case 10:
            [self.scene.view presentScene:transitionResult transition:transitionMoveInWithDirectionDown];
            [self removeUIKitViews];
            break;
        case 11:
            [self.scene.view presentScene:transitionResult transition:transitionMoveInWithDirectionUp];
            [self removeUIKitViews];
            break;
        case 12:
            [self.scene.view presentScene:transitionResult transition:transitionMoveInWithDirectionLeft];
            [self removeUIKitViews];
            break;
        case 13:
            [self.scene.view presentScene:transitionResult transition:transitionMoveInWithDirectionRight];
            [self removeUIKitViews];
            break;
        case 14:
            [self.scene.view presentScene:transitionResult transition:transitionPushWithDirection];
            [self removeUIKitViews];
            break;
        case 15:
            [self.scene.view presentScene:transitionResult transition:transitionRevealWithDirectionUp];
            [self removeUIKitViews];
            break;
        case 16:
            [self.scene.view presentScene:transitionResult transition:transitionWithCIFilter];
            [self removeUIKitViews];
            break;
        default:
            break;
    }

Вы получите предупреждение о том, что метод ( removeUIKitViews ) отсутствует. Этот метод является простым вызовом для удаления некоторых представлений из родительского и суперпредставления. Хотя это и упрощенно, необходимый код:

1
2
3
4
5
6
-(void) removeUIKitViews
{
    [transitionTimerText removeFromParent];
    [_tableView removeFromSuperview];
    [_sliderTimer removeFromSuperview];
}

Теперь несколько замечаний относительно -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath метод -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath .

  • SKTransition перехода SKTransition аналогична всем переходам.
  • Определенный фильтр является настроенным. Как указано выше, вы можете перенастроить его или определить совершенно новый фильтр.
  • Время Duration определяется значением UISlider .

Чтобы запустить код и проверить переходы, вам нужно заполнить класс TransitionResult . Переместите этот класс и добавьте метод -(id)initWithSize:(CGSize)size . Это похоже на метод MyScene.m . Вы можете попробовать написать это самостоятельно. Скопируйте и вставьте из другого класса, чтобы он выглядел следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
-(id)initWithSize:(CGSize)size {
    if (self = [super initWithSize:size]) {
 
        self.backgroundColor = [SKColor colorWithRed:0.35 green:0.45 blue:0.23 alpha:1.0];
 
        SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@»Chalkduster»];
 
        myLabel.text = @»Tap go back»;
        myLabel.fontSize = 15;
        myLabel.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
 
        [self addChild:myLabel];
    }
    return self;
}

Теперь вы можете запустить код и проверить переходы. Иди и попробуй их!

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

Вам понадобится метод -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event и вам нужно будет импортировать класс MyScene.h . Итак, последний шаг — выделить и инициировать объект класса и поменять местами сцены. Следующий фрагмент поможет вам сделать это:

1
2
3
4
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        MyScene* home = [[MyScene alloc] initWithSize:CGSizeMake(CGRectGetMaxX(self.frame), CGRectGetMaxY(self.frame))];
        [self.scene.view presentScene:home];
}

Наконец, запустите вашу программу и протестируйте все SKTransitions . Следующее изображение представляет один из переходов:

Рисунок 3: SKTRansition
Еще одна иллюстрация эффекта SKTRansition

В этом уроке по SKTransition мы рассмотрели следующее:

  • Полный обзор класса SKTransition .
  • Как создать и настроить все параметры SKTransition .
  • Работа со свойствами SKTransition .
  • Как создать и настроить UITableView и UISlider и использовать их параллельно со Sprite Kit.

Если у вас есть какие-либо вопросы или комментарии, пожалуйста, оставляйте их ниже!