首先,spriteKit是苹果用来开发游戏的一个框架。他的优点就是制作简单,流畅度高。比起一般的view动画有明显的优势。但是,目前来说,他并不能在一般的app应用中大量使用,比较的惋惜。原因在于其层面布局是自成一体SK类型,没办法插入过多的UI类型。
但是,还是非常期待他的进一步改进。那么,我们先来看看,到底他是怎么用的:
首先我们要倒入spriteKit.framework,然后我们可以在任何viewController中加入头文件"GameScene.h",然后实现一下代码:
其中,非常重要的是坐标系。 scene.scaleMode = SKSceneScaleModeResizeFill;我尝试过其他的形式,认为这个模式的坐标系是最为正常,其起始点是左下角(0,0)size是(1024,768),其他的模式你们可以尝试,起始点不在一般的iphone屏幕内,而是在外部,所以这里推荐用这个模式。
这里面涉及到层面问题,也就一并说明吧,如下图,我们首先是在view里面加载一个SKView,然后在SKView中又加载一个
SKScene。SKScene是一个默认为黑色背景的屏幕,不透明,我们的精灵(对象)将直接加载在scene上面,而SKView是一个UIView的拓展类,他可以认为是盖在scene上的一个透明层,我们可以加载UI在SKView,加载SK(精灵)在scene上,但是UI将永远在scene的前面遮挡scene上的精灵,也是因为这个原因,这个框架不能用于一般的应用界面(如果有能解决这个问题的方法,请告诉我)。当然SKView也只是我们controller里面的一个view而已。
-(void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
// Configure the view.
SKView *gameSceneView = [[SKView alloc]init];
gameSceneView.frame = CGRectMake(0, 0, 320, 568);
gameSceneView.backgroundColor = [UIColor clearColor];
[self.view addSubview:gameSceneView];
NSLog(@"%f,%f,%f,%f",self.view.frame.origin.x,self.view.frame.origin.y,self.view.frame.size.width,self.view.frame.size.height);
skView.showsFPS = YES;
skView.showsNodeCount = YES;
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = YES;
// Create and configure the scene.
GameScene *scene = [GameScene unarchiveFromFile:@"GameScene"];
scene.scaleMode = SKSceneScaleModeResizeFill;
// Present the scene.
[skView presentScene:scene];
}首先,我们可以给这个世界设置一个边界。让屏幕里面的精灵不要跑出去,同时也可以证明,我们的坐标是没有问题的。创建一个
SKScene类型GameScene,也就是上文引用加载的scene, 这里我们设置边界SKPhysicsBody *body = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, 320, 568)];他具有物理效果,一切有物理遇到碰到他就是碰到墙了。这里还涉及到一些其他的物理特性设置,暂且就不详细说明了。
-(void)setEgde{
self.backgroundColor = [SKColor blueColor];
self.alpha = 0.5;
self.userInteractionEnabled = YES;
/*
SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
myLabel.text = @"Hello, World!";
myLabel.fontSize = 30;
myLabel.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
[self addChild:myLabel];
*/
SKPhysicsBody *body = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, 320, 568 + 40)];
self.scene.name = @"boundary";
self.physicsBody = body;
self.physicsBody.categoryBitMask = 0x00000001;
self.physicsBody.collisionBitMask = 0x00000001;
self.physicsBody.contactTestBitMask = 0x00000001;
// self.friction = 0.2;//默认的摩擦力的值
self.physicsWorld.contactDelegate = self;
NSLog(@"%f,%f,%f,%f",self.frame.origin.x,self.frame.origin.y,self.frame.size.width,self.frame.size.height);
}然后我们看看一些简单的效果动画,看看是不是真的简单有效。这里展示了一个要消失的动画和移动的动画
-(void)hideMyLabel{
SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
myLabel.text = @"start!";
myLabel.fontSize = 65;
myLabel.position = CGPointMake(320/2,568/2);
[self addChild:myLabel];
[self performSelector:@selector(gameStart) withObject:nil afterDelay:0.25];
if (myLabel != nil) {
SKAction *fadeAway = [SKAction fadeOutWithDuration:0.25];
SKAction *remove = [SKAction removeFromParent];
SKAction *combine = [SKAction sequence:@[fadeAway,remove]];
[myLabel runAction:combine];
}
}创建一个 SKLabelNode, 然后给它加载动画,sequence是异步连接动画,还有一个group的方法,是同步连接动画。runAction运行动画,我们就可以看到效果了。
-(void)gameStart{
SKSpriteNode *headerSprite = [[SKSpriteNode alloc]initWithColor:[SKColor yellowColor] size:CGSizeMake(320, 20)];
headerSprite.name = @"headerSprite";
headerSprite.position = CGPointMake(160, 568);
[self addChild:headerSprite];
if (headerSprite != nil) {
SKAction *resize = [SKAction resizeByWidth:-(240) height:0 duration:0.5];
SKAction *moveTo = [SKAction moveTo:CGPointMake(120, 568) duration:0.5];
//异步动画
// SKAction *combine = [SKAction sequence:@[resize,moveTo]];
//同步动画
SKAction *combine2 = [SKAction group:@[resize,moveTo]];
[headerSprite runAction:combine2]; }
}这里是一个方块下落的动画, 其中我们看到有一个name,那个相当于我们button里面的tag,只要有name,我们就能在世界里面找回这个精灵。总的来说,是不是特别的简便。
然后我们看看一些有用的方法
/*
每一帧都会跑到的地方
*/
-(void)update:(CFTimeInterval)currentTime {
/*
给予名称为dropSprite精灵一个外力
*/
[self enumerateChildNodesWithName:@"dropSprite" usingBlock:^(SKNode *node, BOOL *stop) {
[node.physicsBody applyForce:CGVectorMake(0, -100)];
// [node.physicsBody applyForce:CGVectorMake(-100, 0)];
}];
/* Called before each frame is rendered */
}
这个方法是每一帧都会跑,你们可以理解为每秒都会跑就行(一帧是一个循环,其速度取决于这个精灵世界处理的效率),我们可以在这里做一些操作,比方说,给我们一个名字叫@“ dropSprite”的精灵一个向下的推力。//碰撞处理函数
- (void)didBeginContact:(SKPhysicsContact *)contact{
NSLog(@"%@, %@", contact.bodyA.node.name, contact.bodyB.node.name);
if ([contact.bodyA.node.name isEqual: @"boundary"]) {
/*
dynamic在碰撞的时候是否静态,决定碰撞时是否有力反弹的效果
*/
contact.bodyA.dynamic = NO;
contact.bodyB.dynamic = NO;
SKLabelNode *label = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
label.fontSize = 24;
label.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
label.text = @"到边界啦啦啦!!!!!";
[self addChild:label];
SKAction *wait = [SKAction waitForDuration:2];
SKAction *clear = [SKAction removeFromParent];
[label runAction:[SKAction sequence:@[wait, clear]]];
}
}
这个方法也非常有用,每当精灵碰撞(当然会有条件,默认触碰到的精灵都会触发这个事件)会跑进来,那么,我们就可以操作碰撞的事件了。这里,我们就是在碰到名字为@"boundary"时(边界)显示一个SKLabel。这里面有一个属性也很有用dynamic,这个属性的设置决定了是否在碰撞的时候产生物理反应。这个在spriteKit中还是做的很逼真的。
到这里,简单的介绍了一下spriteKit框架,有时间在详细聊吧。