SpriteKit框架的工程项目和添加内容到视图中。
那么下面的内容主要介绍的是怎样切换场景
Step I.使用动作让场景内的内容动起来
静态的文字视图很友好,但是如果文字视图可以动起来的话,它会使整个程序变得更加有趣。
我们在SpriteKit框架中,通常是通过执行动作(action)来移动场景内的元素。
1、创建action对象来描述你想要完成的动画,然后告诉一个元素来运行它。
2、当渲染场景的时候,动作会被执行,在多个帧上发生变化知道完成为止。
我们要完成一个任务,当用户触摸场景内容的时候,文字会放大缩小发生变化。
当用户再次触摸场景内容的时候,文字会停止动作。
详细代码
HelloScene.m
- (instancetype)initWithSize:(CGSize)size
{
self = [superinitWithSize:size];
if (self) {
SKLabelNode *labelNode = [SKLabelNodelabelNodeWithFontNamed:@"Avenir"];
labelNode.fontSize = 13.0f;
labelNode.text = @"Hello Lanou!";
labelNode.position = CGPointMake(size.width / 2.0f, size.height / 2.0f);
[selfaddChild:labelNode];
labelNode.name = @"hello";
}
returnself;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
SKNode *helloNode = [selfchildNodeWithName:@"hello"];
//如果程序内的helloNode标签有动作的话,就移除动作
if (helloNode.hasActions) {
[helloNode removeAllActions];
return;
}
//添加动作
if(helloNode != nil)
{
SKAction *scaleAction_1 = [SKActionscaleTo:2.0fduration:0.25f];
SKAction *scaleAction_2 = [SKActionscaleTo:1.0fduration:0.25f];
SKAction *scaleSequence = [SKActionsequence:@[scaleAction_1,scaleAction_2]];
SKAction *scaleRepeat = [SKActionrepeatActionForever:scaleSequence];
[helloNode runAction:scaleRepeat];
}
}
HelloScene.m代
我们来详细看一下SKAction这个类,到底都包含方法
@interface SKAction (SKActions)
+ (SKAction *)moveByX:(CGFloat)deltaX y:(CGFloat)deltaY duration:(NSTimeInterval)sec;
+ (SKAction *)moveBY:(CGVector)delta duration:(NSTimeInterval)sec;
+ (SKAction *)moveTo:(CGPoint)location duration:(NSTimeInterval)sec;
+ (SKAction *)moveToX:(CGFloat)x duration:(NSTimeInterval)sec;
+ (SKAction *)moveToY:(CGFloat)y duration:(NSTimeInterval)sec;
+ (SKAction *)rotateByAngle:(CGFloat)radians duration:(NSTimeInterval)sec;
+ (SKAction *)rotateToAngle:(CGFloat)radians duration:(NSTimeInterval)sec;
+ (SKAction *)rotateToAngle:(CGFloat)radians duration:(NSTimeInterval)sec shortestUnitArc:(BOOL)shortestUnitArc;
+ (SKAction *)resizeByWidth:(CGFloat)width height:(CGFloat)height duration:(NSTimeInterval)duration;
+ (SKAction *)resizeToWidth:(CGFloat)width height:(CGFloat)height duration:(NSTimeInterval)duration;
+ (SKAction *)resizeToWidth:(CGFloat)width duration:(NSTimeInterval)duration;
+ (SKAction *)resizeToHeight:(CGFloat)height duration:(NSTimeInterval)duration;
+ (SKAction *)scaleBy:(CGFloat)scale duration:(NSTimeInterval)sec;
+ (SKAction *)scaleXBy:(CGFloat)xScale y:(CGFloat)yScale duration:(NSTimeInterval)sec;
+ (SKAction *)scaleTo:(CGFloat)scale duration:(NSTimeInterval)sec;
+ (SKAction *)scaleXTo:(CGFloat)xScale y:(CGFloat)yScale duration:(NSTimeInterval)sec;
+ (SKAction *)scaleXTo:(CGFloat)scale duration:(NSTimeInterval)sec;
+ (SKAction *)scaleYTo:(CGFloat)scale duration:(NSTimeInterval)sec;
+ (SKAction *)sequence:(NSArray *)actions;
+ (SKAction *)group:(NSArray *)actions;
+ (SKAction *)repeatAction:(SKAction *)action count:(NSUInteger)count;
+ (SKAction*)repeatActionForever:(SKAction *)action;
+ (SKAction *)fadeInWithDuration:(NSTimeInterval)sec;
(SKAction *)fadeInWithDuration:(NSTimeInterval)sec;
+ (SKAction*)fadeOutWithDuration:(NSTimeInterval)sec;
+ (SKAction *)fadeAlphaBy:(CGFloat)factor duration:(NSTimeInterval)sec;
+ (SKAction *)fadeAlphaTo:(CGFloat)alpha duration:(NSTimeInterval)sec;
+ (SKAction *)setTexture:(SKTexture *)texture;
+ (SKAction*)animateWithTextures:(NSArray *)textures timePerFrame:(NSTimeInterval)sec;
+ (SKAction*)animateWithTextures:(NSArray *)textures timePerFrame:(NSTimeInterval)sec resize:(BOOL)resize
restore: (BOOL)restore;
/*name must be the name or path of a file of a platform supported audio file format. Use a LinearPCM format audio file with 8 or 16 bits per channel for best performance */
+ (SKAction*)playSoundFileNamed:(NSString*)soundFile waitForCompletion:(BOOL)wait;
+ (SKAction*)colorizeWithColor:(SKColor *)color colorBlendFactor:(CGFloat)colorBlendFactor duration:(NSTimeInterval)sec;
+ (SKAction*)colorizeWithColorBlendFactor:(CGFloat)colorBlendFactor duration:(NSTimeInterval)sec;
+ (SKAction *)followPath:(CGPathRef)path duration:(NSTimeInterval)sec;
+ (SKAction *)followPath:(CGPathRef)path asOffset:(BOOL)offset orientToPath:(BOOL)orient duration:(NSTimeInterval)sec;
+ (SKAction *)speedBy:(CGFloat)speed duration:(NSTimeInterval)sec;
+ (SKAction *)speedTo:(CGFloat)speed duration:(NSTimeInterval)sec;
+ (SKAction*)waitForDuration:(NSTimeInterval)sec;
+ (SKAction*)waitForDuration:(NSTimeInterval)sec withRange:(NSTimeInterval)durationRange;
+ (SKAction*)removeFromParent;
+ (SKAction*)performSelector:(SEL)selector onTarget:(id)target;
+ (SKAction *)runBlock:(dispatch_block_t)block;
+ (SKAction *)runBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue;
+ (SKAction *)runAction:(SKAction *)action onChildWithName:(NSString*)name;
+ (SKAction*)customActionWithDuration:(NSTimeInterval)seconds actionBlock:(void (^)(SKNode *node, CGFloat elapsedTime))block;
@end
在SKActions这个类目中,我们能够看到大量的动作,例如scaleBy:duration:和scaleTo:duration:,这两个到底有什么区别.
其中scaleBy:duration:这个方法返回的SKAction对象能够返回一个reversedAction对象,这两个对象是成完全相反的方向的。而scaleTo:duration:这个方法同样也会返回一个reversedAction对象,但是返回的这个对象却是没有动作内容的。
所以上述的touchesBegan:withEvent:方法中if结构可以用以下实现代替。
//添加动作
if(helloNode != nil)
{
SKAction *scaleAction = [SKActionscaleBy:2.0fduration:0.25f];
SKAction *scaleSequence = [SKActionsequence:@[scaleAction,scaleAction.reversedAction]];
SKAction *scaleRepeat = [SKActionrepeatActionForever:scaleSequence];
[helloNode runAction:scaleRepeat];
}
- 场景间的切换
SpriteKit框架在实现场景切换时非常容易。场景在切换过渡的过程中,你可以保留他们,也可以清除他们。
接下来,我们要完成的任务是,当用户触摸HelloScene场景后,切换到下一个场景,并且清除HelloScene场景。
1、创建新的场景名称GameScene。
2、在HelloScene导入GameScene的头文件。
3、创建新的场景管理单例对象。
4、创建一个按钮元素类。
-
详细代码
GameScene.m
#import "SKSceneManager.h"
@interfaceGameScene()
@propertyBOOL contentCreated;
@end
@implementation GameScene
@synthesize contentCreated = _contentCreated;
- (instancetype)initWithSize:(CGSize)size
{
self = [superinitWithSize:size];
if (self) {
}
returnself;
}
- (void)didMoveToView:(SKView *)view
{
[superdidMoveToView:view];
if (!self.contentCreated) {
[selfcreateSceneContents];
self.contentCreated = YES;
}
}
- (void)createSceneContents
{
self.backgroundColor = [SKColorblackColor];
self.scaleMode = SKSceneScaleModeAspectFit;
//按钮的摆放位置
CGPoint position = CGPointZero;
#ifdef __IPHONE_7_0
position.x = 60;
position.y = 568 - 60;
#else
position.x = 60;
position.y = 480 - 60;
#endif
SKButton *button = [SKButtonbuttonWithSize:CGSizeMake(60, 30) andTitle:@"Back"];
button.target = self;
button.selector = @selector(didClickButton);
button.position = position;
[selfaddChild:button];
}
- (void)didClickButton
{
SKScene *scene = [[SKSceneManagerinstance] sceneForKey:@"HelloScene"withSize:self.size];
[self.viewpresentScene:scene transition:[SKTransitiondoorsCloseVerticalWithDuration:1.0f]];
}
@end
在游戏场景中添加了自定义按钮SKButton的实例对象以及该对象的回调方法didClickButton。
接下来,我们看一下SKButton按钮类的封装和SKSceneManager场景管理类的封装。
SKButton.h
@interface SKButton : SKShapeNode
//回调对象
@property (nonatomic ,assign) id target;
//回调方法
@property (nonatomic ,assign) SEL selector;
//便利构造器
+ (SKButton *)buttonWithSize:(CGSize)size andTitle:(NSString *)title;
@end
SKButton.m
@implementation SKButton
@synthesize target = _target;
@synthesize selector = _selector;
+ (SKButton *)buttonWithSize:(CGSize)size andTitle:(NSString *)title
{
CGRect rect = CGRectMake(0, 0, size.width, size.height);
SKButton *result = [[SKButtonalloc] init];
//创建路径
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRoundedRect(path, NULL, rect, 5.0f, 5.0f);
//设置路径及相关属性
result.path = path;
result.lineWidth = 0.5f;
result.strokeColor = [SKColoryellowColor];
result.userInteractionEnabled = YES;
result.antialiased = YES;
//释放路径
CFRelease(path);
//创建文本标签
SKLabelNode *label = [[SKLabelNodealloc] initWithFontNamed:@"Avenir"];
[label setName:@"label"];
[label setFontSize:22];
[label setText:title];
[label setFontColor:[SKColoryellowColor]];
[label setPosition:CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect) - CGRectGetMidY(label.frame))];
[result addChild:label];
return result;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//接收到用户触摸后,将SEL对象发送给id对象
[self.targetperformSelector:self.selectorwithObject:nil];
}
@end
SKSceneManager.h
#import <SpriteKit/SpriteKit.h>
@interface SKSceneManager : NSObject
+ (SKSceneManager *)instance;
//key是场景类的类名
//size是场景获得的场景对象的尺寸大小
- (SKScene *)sceneForKey:(NSString *)key withSize:(CGSize) size;
//移除对应场景类类名的场景
- (void)removeSceneForKey:(NSString *)key;
@end
SKSceneManager.m
@interfaceSKSceneManager()
@property (nonatomic ,readonly) NSMutableDictionary *sceneInfo;
@end
@implementation SKSceneManager
@synthesize sceneInfo = _sceneInfo;
staticSKSceneManager *s_SKSceneManager = nil;
+ (SKSceneManager *)instance
{
staticdispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (s_SKSceneManager == nil) {
s_SKSceneManager = [[SKSceneManageralloc] init];
}
});
returns_SKSceneManager;
}
- (id)init
{
self = [superinit];
if (self) {
_sceneInfo = [[NSMutableDictionaryalloc] init];
}
returnself;
}
- (SKScene *)sceneForKey:(NSString *)key withSize:(CGSize) size
{
SKScene *scene = [self.sceneInfoobjectForKey:key];
if (scene == nil)
{
scene = [[NSClassFromString(key) alloc] initWithSize:size];
[self.sceneInfosetObject:scene forKey:key];
}
scene.size = size;
return sc
ene;
}
- (void)removeSceneForKey:(NSString *)key
{
[self.sceneInforemoveObjectForKey:key];
}
@end
接下来需要修改一下HelloScene场景中的点击方法。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
SKNode *helloNode = [selfchildNodeWithName:@"hello"];
//如果程序内的helloNode标签有动作的话,就移除动作
if (helloNode.hasActions) {
[helloNode removeAllActions];
return;
}
//添加动作
if(helloNode != nil)
{
SKAction *scaleAction = [SKActionscaleBy:2.0fduration:0.25f];
SKAction *scaleSequence = [SKActionsequence:@[scaleAction,scaleAction.reversedAction]];
SKAction *scaleRepeat = [SKActionrepeatActionForever:scaleSequence];
[helloNode runAction:scaleRepeat];
}
__blockHelloScene *b_self = self;
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void)
{
//获取游戏场景
SKScene *scene = [[SKSceneManagerinstance] sceneForKey:@"GameScene"withSize:self.size];
//创建场景切换对象
SKTransition *doors= [SKTransitiondoorsOpenVerticalWithDuration:1.0f];
//开始切换场景
[b_self.viewpresentScene:scene transition:doors];
});
}
注意到我们这里使用了GCD提供的延时机制!!
接下来我们需要看一下SKTransition场景切换对象的内容。
上边的枚举定义了切换动画的4个方向。
typedefNS_ENUM(NSInteger, SKTransitionDirection) {
SKTransitionDirectionUp,
SKTransitionDirectionDown,
SKTransitionDirectionRight,
SKTransitionDirectionLeft
} NS_ENUM_AVAILABLE(10_9, 7_0);
SK_EXPORT@interface SKTransition : NSObject
+ (SKTransition*)crossFadeWithDuration:(NSTimeInterval)sec;
+ (SKTransition*)fadeWithDuration:(NSTimeInterval)sec;
+ (SKTransition *)fadeWithColor:(SKColor *)color duration:(NSTimeInterval)sec;
+ (SKTransition*)flipHorizontalWithDuration:(NSTimeInterval)sec;
+ (SKTransition*)flipVerticalWithDuration:(NSTimeInterval)sec;
+ (SKTransition*)revealWithDirection:(SKTransitionDirection)direction duration:(NSTimeInterval)sec;
+ (SKTransition*)moveInWithDirection:(SKTransitionDirection)direction duration:(NSTimeInterval)sec;
+ (SKTransition*)pushWithDirection:(SKTransitionDirection)direction duration:(NSTimeInterval)sec;
+ (SKTransition*)doorsOpenHorizontalWithDuration:(NSTimeInterval)sec;
+ (SKTransition*)doorsOpenVerticalWithDuration:(NSTimeInterval)sec;
+ (SKTransition*)doorsCloseHorizontalWithDuration:(NSTimeInterval)sec;
+ (SKTransition*)doorsCloseVerticalWithDuration:(NSTimeInterval)sec;
+ (SKTransition*)doorwayWithDuration:(NSTimeInterval)sec;
/* Create a transition with a CIFilter. The filter must be a transition filter which requires only two images (inputImage, inputTargetImage) and generates a single image (outputImage). SpriteKit sets the inputImage, inputTargetImage, and inputTime properties when rendering, all others must be setup beforehand. */
+ (SKTransition*)transitionWithCIFilter:(CIFilter*)filter duration:(NSTimeInterval)sec;
/**
Pause the incoming Scene during the transition, defaults to YES.
*/
@property (SK_NONATOMIC_IOSONLY) BOOL pausesIncomingScene;
/**
Pause the outgoing Scene during the transition, defaults to YES.
*/
@property (SK_NONATOMIC_IOSONLY) BOOL pausesOutgoingScene;
@end
在SKTransition这个封装类的API中,我们能够看到系统封装了大量的切换效果,例如淡入淡出、水滴效果、移除、推进、开门关门,甚至我们可以使用CoreImage框架中的CIFilter过滤器完成场景转换。