iOS2D游戏引擎框架SpriteKit入门
    最近用闲暇时间了解了下iOS 7.0 SDK就提供的一个2D游戏引擎框架SpriteKit,用此实现了一个之前比较流行的游戏“保卫萝卜”中的一个小场景,毕竟有具体需求的实践能提高学习效率,在此做个记录总结。
    SpriteKit主要用来做2D游戏,将图形渲染和动画用于任意一个精灵,游戏中的任意一个图像元素都可以理解为一个精灵,我们要做的就是通过SpriteKit来管理这些精灵,让它们像我们预想的方向展现或变化。
    同样SDK还提供了3D渲染引擎SceneKit,和GPU优化的底层接口来渲染3D图形的API Metal,这些之后再来学习。
    先预览一下实现后的小游戏。
保卫萝卜
一、工程搭建
    游戏的工程搭建我们可以直接创建一个XCode提供的Game Project模板,也可以直接在已有的工程中使用。
1、Game 模板
    在创建工程处选择Game模板,路径File->New->Project->Game,样式见下图。
Game模板
Language选择你喜欢用的Objective-C或Swift,Game Technology这里选择SpriteKit。创建完成后目录结构如下。
模板目录结构
    工程会默认生成一个“Hello,World”的Demo,运行起来可以直接看到效果,按下、抬起、移动手指的交互动作都做了动画,可以感受下。
    根视图GameViewController中,调用了GameScene,Hello Wold所有的精灵展现及动画都是在GameScene中处理的。所以想要开始开发游戏之前,需要先将这个Hello World De
mo相关内容清理掉。视图文件GameScene.sks、动画文件Actions.sks、源码文件GameScene.h、GameScene.m都删除,并将GameViewController中的viewDidLoad函数中创建并使用GameScene的代码也相应删除掉。这样就可以在这个GameViewController中创建并使用我们要做的Scene了。
2、已有的工程中开发游戏 
    在想要实现游戏的界面,将Storyboard或Xib中ViewController的根View,Class类由UIView改成SKView,因为游戏框架的元素都要使用SpriteKit提供的类来实现,方便管理精灵。这样就跟Game模型中的GameViewController一样了。
二、开始使用
1、创建Scene文件并在ViewController中调用
创建MyScene文件,继承SKScene,后续游戏的内容都将在这个页面开发。
# MyScene.h
import <SpriteKit/SpriteKit.h>
# MyScene.m
#import "GameScene.h"
在游戏页面的ViewController中,创建一个SKView,用于展现才刚创建的MyScene。
IBOutlet SKView *_sceneView;
在Xib或Storyboard中添加UIView,将类改为SKView,关联即可。
在ViewController中的viewDidLoad中调用MyScene。
// Create and configure the scene. 
CGSize size = CGSizeMake(_sceneView.bounds.size.height, _sceneView.bounds.size.width); 
MyScene * scene = [MyScene sceneWithSize:size]; 
scene.scaleMode = SKSceneScaleModeAspectFill;       
// Present the scene. 
[_sceneView presentScene:scene];
MyScene已经调用完成,后续精灵的展现、动画都在MyScene文件里添加。
2、在MyScene中添加精灵
// 添加背景
- (void)addBackground
    background.size = CGSizeMake(self.size.width, self.size.height);
    background.position = CGPointMake(background.size.width/2, background.size.height/2);
    [self addChild:background];
}
基本图片类的精灵都是通过SKSpriteNode对象来创建,通过spriteNodeWithImageNamed:函数可以将图片做成精灵对象,通过name属性为其命名,用户点击等操作可以通过名字判断出是哪个精灵,后面会介绍到。通过size和postion属性可以指定精灵的宽高和位置。
同样的方法将游戏中用到的一些静态精灵都摆放上去。
- (id)initWithSize:(CGSize)size{ 
    if (self = [super initWithSize:size]) {     
        self.backgroundColor = [SKColor whiteColor];     
        [self addBackground];    // 背景     
        [self addFlag];                // 起点     
        [self addCarrot];            // 终点     
        [self addStaticObject];    // 其他的羊头、石头、宝箱等装饰物 
    } 
    return self;
}
xcode入门SKColor在iOS上就是UIColor,可以看下定义,是为了在MacOS上和iOS上统一使用,也方便移植。
#if TARGET_OS_IPHONE
#define SKColor UIColor
#else
#define SKColor NSColor
#endif
添加完这些精灵的样式如图。
九个精灵
3、为精灵添加动画
这里要涉及到SKAction,简单介绍一下,因为游戏的大部分动作行为都是它来实现的。
SKAction可以实现的动作有很多,列举几个常用的
1、相对位移或绝对位移
2、旋转到指定角度或旋转指定角度
3、改变尺寸或缩放
4、隐藏、显示、渐隐、渐现、指定透明度
5、添加一个或一系列纹理图片
6、加减速、等待
7、播放简单的声音等等
以上是单个动作的SKAction,同样可以通过下面两个函数将多个单一动作整合成复合动作SKAction
+ (SKAction *)sequence:(NSArray *)actions;
+ (SKAction *)group:(NSArray *)actions;
入参是N多个Action,sequence:函数是串行的来执行数组中的所有Action,group:函数是并行的来执行。通过以上这些我们就可以做各种复杂的动画了。
1、首先通过SKAction来为仙人掌加一个张嘴呼吸的动画。
// cactus为仙人掌的SKSpriteNode对象
[cactus runAction:[SKAction repeatActionForever:animation]];
2、再来给终点的萝卜增加点击交互,点击后抖动和叫声。
因为SKScene继承自UIResponder,所以我们可以使用touchesBegan:withEvent:函数来实现点击
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
        for (UITouch *touch in touches)
                CGPoint touchLocation = [touch locationInNode:self];
                SKNode *node = [self nodeAtPoint:touchLocation];
                                [self carrotAnimation];
        }
    }
}
上面代码用来判断点击位置是否为萝卜精灵,如果是并且没有在做动作,就执行下面的Acti
on。
- (void)carrotAnimation
    // 播放一组图片
    // 随机播放一个声音
    int random = arc4random(;
        [self.carrot runAction:groupAction];
}
为萝卜做了一个GIF和一个随机声音动作,通过group合成一个Action,让萝卜执行。
3、小怪物的出现和行走路径
在起始位置创建一个小怪物,并伴随着一个叫声。
// 小怪物
    character.position = CGPointMake(60 + character.size.width/2, self.size.height - character.size.height/2);
    [self addChild:character];
// 叫声
    [character runAction:soundAction];
同时出现一个漩涡,漩涡旋转两圈并消失。
// 出现漩涡
    wheel.position = CGPointMake(character.position.x, character.position.y - 10);
    [self addChild:wheel];
// 旋转两圈后消失
SKAction *rotateAction = [SKAction rotateByAngle:4 * M_PI duration:0.4];
            [wheel removeFromParent];