Kingyeung Chan

THINK GREAT THOUGHTS AND YOU WILL BE GREAT

大家好,我系Monster.Chan,一名来自中国的 iOS / AWS / Unity3D 开发者,就职于SAINSTORE。在不断修炼,努力提升自己


结合工作经验,目前写了《Getting Started with AWS》、《Websites & Web Apps on AWS》,欢迎试读或者购买

SpriteKit-动画与纹理图集

这次我们使用SpriteKit框架创建一个简单的动画:在屏幕上行走的熊。

还会使用到纹理图集来制作动画效果,通过在屏幕上点击,触发事件让熊移动,并且改变熊的运动方向。

创建一个工程

我们先来创建一个SpriteKit的默认工程,如下图:

并将工程命名为AnimatedBear,将Devices选为iPad为了让熊有更多的空间行走。

现在我们直接编译运行,点击屏幕你会看见一个自由旋转的飞机在屏幕上:

好的,我们创建的工程没有问题,现在我们开始大改造,首先我们先下载 熊的动画资源

解压后将BearImages.atlas文件架直接拖到工程里面。

纹理图集

如果之前你没使用过纹理图集,那你可以把它想象为一副很大的图片,其中包括动画中需要使用到的各种图片。 这个图集可以看做是一个文件,它指定了每个sprite的边界范围,当在代码中需要使用时,可以将这些sprite取出来。

你可能会问为什么要使用图集,因为使用纹理图集是因为Sprite Kit和图形引擎会对其做相应的优化处理。

为纹理图集创建一个文件夹,并将图片文件放置到该文件夹中,然后在文件夹名称尾部添加.atlas。 这样Xcode就能识别出.atlas扩展名,进而自动的将图片合并为一个纹理图集。

实现一个简单的动画

下面我们来修改MyScene.m

#import "MyScene.h"
@implementation MyScene {
    SKSpriteNode *_bear;
    NSArray *_bearWalkingFrames;
}
-(id)initWithSize:(CGSize)size {
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
        self.backgroundColor = [SKColor blackColor];
        //用于保存行走帧
        NSMutableArray *walkFrames = [NSMutableArray array];
        //加载纹理图集
        SKTextureAtlas *bearAnimationAtlas = [SKTextureAtlas atlasNamed:@"BearImages.atlas"];
        int numImages = [bearAnimationAtlas .textureNames count];
        for (int i = 0; i < numImages / 2; i++) {
            NSString *textureName = [NSString stringWithFormat:@"bear%d", i];
            SKTexture *tmp = [bearAnimationAtlas textureNamed:textureName];
            [walkFrames addObject:tmp];
        }
        _bearWalkingFrames = walkFrames;
        //TODO
    }
    return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
}
-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
}
@end

也许你已经注意到numImages这个变量作除以2处理,因为纹理图集包含了所有分辨率的图片文件(non-retina和retina), 共有16个文件,每种分辨率有8个文件。

好的,接下来继续coding,在场景添加一个熊,并让它不知疲倦永远行走。 在-initWithSize:TODO处加入如下代码:

SKTexture *tmp = _bearWalkingFrames[0];
_bear = [SKSpriteNode spriteNodeWithTexture:tmp];
_bear.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:_bear];
[self walkingBear];

利用帧列表的第一帧构建一个sprite,然后将其放置到屏幕正中间。最后调用walkingBear方法,让熊开始走动。

添加-walkingBear方法:

- (void)walkingBear {
    [_bear runAction:[SKAction repeatActionForever:
                      [SKAction animateWithTextures:_bearWalkingFrames
                                       timePerFrame:0.1f
                                             resize:YES
                                            restore:YES]] withKey:@"walkingInPlaceBear"];
}

上面这个action会以0.1秒的间隔开始播放各帧,如果你的代码再次调用这个方法使动画重新开始的话, walkingInPlaceBear这个key会强制移除动画,这对于确保动画不相互干扰非常重要。 这个action是永久重复的,内部的actionanimateWithTextures会按顺序动画播放帧列表中的图片。

编译并运行,可以看见一只不断原地踏步的熊:

改变运动方向

下面让我们添加代码,通过点击屏幕的点改变熊的运行方向,在MyScene.m文件中作以下修改:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    for(UITouch *touch in touches) {
        CGPoint location = [touch locationInNode:self];
        CGFloat multiplierForDirection;
        if (location.x <= CGRectGetMidX(self.frame)) {
            //左边
            multiplierForDirection = 1;
        } else {
            //右边
            multiplierForDirection = -1;
        }
        _bear.xScale = fabs(_bear.xScale) * multiplierForDirection;
        [self walkingBear];
    }
}

上面的代码会根据tap的位置,让touchesBegan方法判断tap处于屏幕正中间的左边还是右边, 通过该方法,决定熊的朝向。熊的方向是通过Sprite Kit来改变的(通过负值乘以xScale就可以让熊朝向左边。)

让熊真正行走

现在让我们修改MyScene.m代码,实现通过点击屏幕,熊移动到相应位置。

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    //Called when a touch begins
    for(UITouch *touch in touches) {
        CGPoint location = [touch locationInNode:self];
        CGFloat multiplierForDirection;
        //设定熊的移动速度
        CGSize winSize = self.size;
        //当熊移动距离为屏幕宽度时需要3秒
        float bearVelocity = winSize.width / 3.0;
        //计算熊的移动量
        CGPoint moveDifference = CGPointMake(location.x - _bear.position.x, location.y - _bear.position.y);
        //计算实际移动距离
        float moveDistance = sqrtf(moveDifference.x * moveDifference.x + moveDifference.y * moveDifference.y);
        //计算实际移动所需时间
        float moveDuration = moveDistance / bearVelocity;
        if (moveDifference.x < 0) {
            multiplierForDirection = 1;
        } else {
            multiplierForDirection = -1;
        }
        _bear.xScale = fabs(_bear.xScale) * multiplierForDirection;
        //[self walkingBear];
        //1
        if ([_bear actionForKey:@"bearMoving"]) {
            [_bear removeActionForKey:@"bearMoving"];
        }
        //2
        if (! [_bear actionForKey:@"walkingInPlaceBear"]) {
            [self walkingBear];
        }
        //3
        SKAction *moveAction = [SKAction moveTo:location duration:moveDuration];
        //4
        SKAction *doneAction = [SKAction runBlock:^{
            [self bearMoveEnded];
        }];
        //5
        SKAction *moveActionWithDone = [SKAction sequence:@[moveAction, doneAction]];
        //6
        [_bear runAction:moveActionWithDone withKey:@"bearMoving"];
    }
}
- (void)bearMoveEnded {
    [_bear removeAllActions];
}
  1. 停止已有的移动Action(因为你准备要熊移动到其他地方),通过使用key可以开始和停止以此命名的动画的运行。

  2. 判断熊的动画序列是否执行,让我们视觉认为熊是真的走动到那里。

  3. 创建一个移动Action,让熊在指定时间一定到指定位置。

  4. 创建一个doneAction,让熊到达指定位置后,停止动画序列。

  5. 创建一个顺序Action,让上面两个Action按顺序执行。

  6. 让熊开始运行action,并制定一个key为:”bearMoving”。记住,这里的key用来判断熊是否需要移动到新的位置。

注意:Sprite Kit支持两种action:sequencegroup。sequence action表示action按照顺序执行。 如果想要action同时运行,那么就使用group。

一切就绪,现在熊可以很好实现指向性移动了。

代码下载

最近的文章

SpriteKit-拖放Sprites

让我们快速开始吧在实现触摸处理之前,我们先来创建一个基本的Sprite Kit工程,并在scene中显示出一些sprite(动物)和背景。至于怎么创建工程这里就不再重复讲述了,如确实有问题建议回到SpriteKit快速入门。将工程命名为DragDrop,devices选择iPhone,跟SpriteKit快速入门一样,我们希望这个程序只支持横屏显示(landscape)。所以在Project Navigator中选中DragDrop工程,然后选择DragDrop target,在弹出的画面...…

继续阅读
更早的文章

SpriteKit快速入门

什么是SpriteKit首先要知道什么是Sprite。顾名思义Sprite就是精灵,在游戏开发中,精灵指的是以图像方式呈现在屏幕上的一个图像。这个图像也许可以移动,用户可以与其交互,也有可能仅只是游戏的一个静止的背景图。总体来说精灵构成了游戏的绝大部分主体视觉内容,一个2D引擎的主要工作,就是高效地组织,管理和渲染这些精灵。 Hello SpriteKit!下面直接上SpriteKit的基本用法。我们用Xocde5内置的SpriteKit模板来构建一个简单的Hello World工程。至...…

继续阅读