How to Use Animations and Sprite Sheets in Cocos2D 2.X

http://www.raywenderlich.com/32045/how-to-use-animations-and-sprite-sheets-in-cocos2d-2-x


This post is also available in: Japanese

Smoky says: Only you can start this bear!

Smoky says: Only you can start this bear!

Update 3/12/2013: Fully updated for Cocos2D 2.1-rc0a, Texture Packer 3.07, ARC, Retina Displays, and Modern Objective-C style (original post by Ray Wenderlich, update by Tony Dahbura).

I’ve gotten a ton of requests from readers of this blog to make a tutorial on how to use animations and sprite sheets in Cocos2D 2.x. You asked for it, you got it!

In this tutorial, you will learn how to create a simple animation of a bear walking in Cocos2D. You’ll also learn how to make them efficient by using sprite sheets, how to make your bear move in response to touch events, and how to change the direction the bear faces based on where the bear is moving.

If you are new to Cocos2D, you may wish to go through the tutorial series on How To Make A Simple iPhone Game With Cocos2D first, but this is not required!

Getting Started

Before getting started be sure to install the latest “unstable” Cocos2D 2.x libraries by downloading them from the official Cocos2D-iPhone home page. The How To Make A Simple iPhone Game tutorial has detailed installation instructions if you need them.

Let’s start by creating a Xcode skeleton for our project – create a new project with the Cocos2D 2.x iOS project template and name it AnimBear.

We are going to use ARC in this project, but by default the template isn’t set up to use ARC. Luckily, fixing it is really easy. Just go to Edit\Refactor\Convert to Objective-C ARC. Expand the dropdown and select only the last four files (main.mAppDelegate.m,HelloWorldLayer.m, and IntroLayer.m), then click Check and finish the steps of the wizard.

Choose files to convert to ARC

Next, go ahead and download some images of an animated bear made by my lovely wife.

When you unzip the file, take a look at the images – they are just individual frames of a bear that when you put them together, look like the bear is moving.

Examples of the bear images you will be using.

These images are saved in the maximum required resolution – for an iPad with a retina display (4X). In this tutorial, you will create the iPhone retina (2X) and iPhone non-retina (1X) images from these.

You could just add these directly to your Cocos2D 2.x project at this point and create an animation based on these individual images. However, there’s another way to create animations in Cocos2D that is more efficient – by using sprite sheets.

Sprite Sheets and Bears, Oh My!

If you haven’t used sprite sheets yet, think of them as gigantic images that you put your sprites within. They come with a file that specifies the boundaries for each individual sprite so you can pull them out when you need them within the code.

The reason why these are such a good idea to use is because Cocos2D is optimized for them. If you use sprites within a sprite sheet properly, rather than making one OpenGL ES draw call per sprite it just makes one per sprite sheet.

In short – it’s faster, especially when you have a lot of sprites!

Note: Want to learn more? Check out this polished and amusing video all about Sprite Sheets!

As for sprite sheets, you could actually create them yourself manually with your image editor and create the file that specifies the boundaries yourself by hand as well.

However, this would be crazy talk because Andreas Loew has developed an amazing application called Texture Packer that does this automatically for you!

Texture Packer To The Rescue!

If you don’t have it already, go ahead and download a copy of Texture Packer. There is a lite version that will work for this tutorial but you will quickly see the advantages of this great tool and want to use the full featured version.

Once you have the app installed, go to File\New and you will see a blank window appear. Just drag the folder with the images into the Texture Packer window and it will automatically read all the files.

When you do that, you’ll notice that Texture Packer will size the Sprite Sheet automatically. By default, however, all the sprites don’t fit – so change the Max Size to 4096×4096. Now everything should fit OK:

Adding bear images

You’re almost there – but notice how some of the bear images are wider than others. If you look at the original images, that isn’t the way they were made – but by default Texture Packer sets the Trim mode to remove the transparency around a sprite.

For these images, this isn’t what you want because it would mess up the positioning of the bear for the animations. Luckily, this is easy to fix – just set the Trim mode to “None” in the Layout section of the TextureSettings. The view on the right will redraw to show this.

At this point, your window should look similar to the following:

Texture Packer 2

And that’s it! So let’s save the spritesheet image and definitions so you can use them in your app.

In the panel on the left, under Output make sure Data Format says cocos2d. Then click the button with the  next to Data File and locate your Cocos2d 2.x project’s resources folder and save the file as AnimBear-ipadhd. TexturePacker will automatically fill in the Texture File name for you.

Next, click the gear icon next to AutoSD. In the Presets dropdown, select cocos2d ipad/hd/sd and click Apply. This makes TexturePacker scale down the artwork for the iPhone retina (2x) and iPhone non-retina (1x) displays automatically.

Using AutoSD option in Texture Packer

Finally, close the popup and click Publish. TexturePacker will automatically create the sprite sheets for you – in all resolutions.

Now go back to XCode and add these to your project. Right click on the Resources folder of your project, click Add Files to AnimBear…, select all files that begin with AnimBear in the Resources folder, and add them to your project. At this point your Resources group should look like this:

Adding the sprite sheets to your project

While you’re at it, click on AnimBear.plist in XCode to see what Texture Packer did for you. You’ll see that it’s just a property list with two sections – frames and metadata. In the frames section, there is an entry for each of the images in the spritesheet, with properties inside that give the bounding box for the image within the spritesheet. Cool eh? Sure beats doing this by hand:]

Screenshot of sprite sheet plist generated by Texture Packer

But what would be even cooler is an animated bear! So let’s get to it!

A Simple Animation

You’re going to start just by plopping the bear in the middle of the screen and looping the animation so he moves forever, just to make sure things are working.

So let’s start by cleaning up some of the prebuilt code that the Cocos2d 2.x template inserted. Replace HelloWorldLayer.h with the following:

#import "cocos2d.h"
 
@interface HelloWorldLayer : CCLayer
{
}
 
+(CCScene *) scene;
 
@end

Because you are going to use modern Objective-C capabilities the rest of your work will be in HelloWorldLayer.m. So switch over to HelloWorldLayer.m and replace the contents with the following:

#import "HelloWorldLayer.h"
 
@interface HelloWorldLayer ()
{
    BOOL bearMoving;
}
 
@property (nonatomic, strong) CCSprite *bear;
@property (nonatomic, strong) CCAction *walkAction;
@property (nonatomic, strong) CCAction *moveAction;
 
@end
 
@implementation HelloWorldLayer
 
+(CCScene *) scene
{
	CCScene *scene = [CCScene node];
	HelloWorldLayer *layer = [HelloWorldLayer node];
	[scene addChild: layer];
	return scene;
}
 
-(id) init {
    if((self = [super init])) {
        // TODO...
    }
    return self;
}
 
@end

At this point you’ve just emptied out the project template to create a nice blank slate (and defined a few variables you’ll need later). Build and run to make sure everything builds OK – you should see a blank screen.

There are 5 steps you will need to take to get this animation to work, so let’s cover them one at a time. Add each of these snippets to your init method in the “TODO” area shown by the comment.

1) Cache the sprite frames and texture

[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"AnimBear.plist"];

First, you make a call to the shared CCSpriteFrameCache’s addSpriteFramesWithFile method, and pass in the name of the property list that Texture Packer generated for you. This method does the following:

  • Looks for an image with the same name as the passed-in property list, but ending with “.png” instead, and loads that file into the shared CCTextureCache (in our case, AnimBear.png).
  • Parses the property list file and keeps track of where all of the sprites are, using CCSpriteFrame objects internally to keep track of this information.

Note that Cocos2D will automatically look for the right file based on the resolution of the device – for example, if you’re running on an iPad with a Retina display, it will load AnimBear-ipadhd.png and AnimBear-ipadhd.plist instead.

2) Create a sprite batch node

CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:@"AnimBear.png"];
[self addChild:spriteSheet];

Next, you create a CCSpriteBatchNode object, passing in the image of your sprite sheet. The way sprite sheets work in Cocos2D 2.x is the following:

  • You create a CCSpriteBatchNode object passing in the image file containing all of the sprites, as you did here, and add that to your scene.
  • Now, any time you create a sprite that comes from that sprite sheet, you should add the sprite as a child of the CCSpriteBatchNode. As long as the sprite comes from the sprite sheet it will work, otherwise you’ll get an error.
  • The CCSpriteBatchNode code has the smarts to look through its CCSprite children and draw them in a single OpenGL ES call rather than multiple calls, which again is much faster.

3) Gather the list of frames

NSMutableArray *walkAnimFrames = [NSMutableArray array];
for (int i=1; i<=8; i++) {
    [walkAnimFrames addObject:
        [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
            [NSString stringWithFormat:@"bear%d.png",i]]];
}

To create the list of frames, you simply loop through your image’s names (they are named with a convention of Bear1.png -> Bear8.png) and try to find a sprite frame by that name in the shared CCSpriteFrameCache. Remember, these should already be in the cache because you called addSpriteFramesWithFile earlier.

4) Create the animation object

CCAnimation *walkAnim = [CCAnimation 
    animationWithSpriteFrames:walkAnimFrames delay:0.1f];

Next, you create a CCAnimation by passing in the list of sprite frames, and specifying how fast the animation should play. You are using a 0.1 second delay between frames here.

5) Create the sprite and run the animation action

CGSize winSize = [[CCDirector sharedDirector] winSize];
self.bear = [CCSprite spriteWithSpriteFrameName:@"bear1.png"];
self.bear.position = ccp(winSize.width/2, winSize.height/2);
self.walkAction = [CCRepeatForever actionWithAction:
    [CCAnimate actionWithAnimation:walkAnim]];
[self.bear runAction:self.walkAction];
[spriteSheet addChild:self.bear];

You then create a sprite for your bear passing in a frame to start with, and center it in the middle of the screen. Next, set up a CCAnimateAction telling it the name of the CCAnimation to use, and tell the bear to run it!

Finally, you add the bear to the scene – by adding it as a child of the sprite sheet! Note that if you did not add it as a child of the spritesheet and instead added it as a child of the layer, you would not get the performance benefits (such as if you had several bears).

Done!

And that’s it! So build and run the project, and if all goes well you should see your bear happily strolling on the screen!

Screenshot of a simple animation with Cocos2D

Changing Animation Facing Direction Based on Movement

Things are looking good – except you don’t want this bear meandering about on its own, that would be dangerous! Would be much better if you could control its movement by touching the screen to tell it where to go.

So make the following changes to HelloWorldLayer.m:

// Comment out the runAction method in the init method:
//[self.bear runAction:self.walkAction];
 
// And add this to the init method after [spriteSheet addChild:self.bear]; line
self.touchEnabled = YES;
 
// Add these new methods
- (void)registerWithTouchDispatcher
{
    [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:
           self priority:0 swallowsTouches:YES];
}
 
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
    return YES;
}
 
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
    //Stuff from below!
}
- (void)bearMoveEnded
{
    [self.bear stopAction:self.walkAction];
    bearMoving = NO;
}

Starting at the beginning, you are commenting out running the walk action in the init method, because you don’t want our bear moving until you tell him to!

You also set the layer as touch enabled, and implement registerWithTouchDispatcher and ccTouchBegan. If you are curious as to the advantages of using this method rather than plain ccTouchesBegan, check out an explanation in the How To Make a Tile Based Game with Cocos2D 2.X Tutorial.

When the bearMoveEnded method is called, you want to stop any running animation and mark that you’re no longer moving.

As for the ccTouchEnded function, this is where the meat of your code will go. There’s a lot of stuff here, so let’s break it into steps like before!

1) Determine the touch location

CGPoint touchLocation = [self convertTouchToNodeSpace:touch];

Nothing new here – you just start by converting the touch point into local node coordinates using the usual method.

2) Set the desired velocity

CGSize screenSize = [[CCDirector sharedDirector] winSize];
float bearVelocity = screenSize.width / 3.0;

Here you set up a velocity for the bear to move. You will estimate that it should take about 3 seconds for the bear to move the width of the iPhone screen. Since the new models are out you need to account for the width by asking the screen for its width (480 or 568 pixels), so since velocity is distance over time it would be the width pixels / 3 seconds.

3) Figure out the amount moved in X and Y

CGPoint moveDifference = ccpSub(touchLocation, self.bear.position);

Next you need to figure out how far the bear is moving along both the x and y axis. You can do this by simply subtracting the bear’s position from the touch location. There is a convenient helper function Cocos2D provides called ccpSub to do this.

4) Figure out the actual length moved

float distanceToMove = ccpLength(moveDifference);

You then need to calculate the distance that the bear actually moves along a straight line (the hypotenuse of the triangle). Cocos2D also has a helper function to figure this out based on the offset moved: ccpLength!

5) Figure out how long it will take to move

float moveDuration = distanceToMove / bearVelocity;

Finally, you need to calculate how long it should take the bear to move this length, so you simply divide the length moved by the velocity to get that.

6) Flip the animation if necessary

if (moveDifference.x < 0) {
   self.bear.flipX = NO;
} else {
   self.bear.flipX = YES;
}

Next, you look to see if the bear is moving to the right or to the left by looking at the move difference. If it’s less than 0, you’re moving to the left and you can play the animation as-is. However, if it’s moving to the right you need to flip your animation to the other way!

First instinct might be to run to your image editor and create new images for the bear facing the other direction, and use those. However Cocos2D has a much easier (and more efficient) way – you can simply flip the existing images!

The way it works, you actually set a flip value on the sprite the animation is run on, and it will cause any animation frames that is run on the sprite to be flipped as well. So in the case you are moving the bear to the right, you set flipX to YES.

7) Run the appropriate actions

[self.bear stopAction:self.moveAction];
 
if (!bearMoving) {
    [self.bear runAction:self.walkAction];
}
 
self.moveAction = [CCSequence actions:
    [CCMoveTo actionWithDuration:moveDuration position:touchLocation],
    [CCCallFunc actionWithTarget:self selector:@selector(bearMoveEnded)],
    nil];
 
[self.bear runAction:self.moveAction];
bearMoving = YES;

Next, you stop any existing move action (because you’re about to override any existing command to tell the bear to go somewhere else!) Also, if you’re not moving, you stop any running animation action. If you are already moving, you want to let the animation continue so as to not interrupt its flow.

Finally, you create the move action itself, specifying where to move, how long it should take, and having a callback to run when it’s done. You also record that we’re moving at this point by setting our instance variable bearMoving=YES.

Done!

A lot of code – but was it worth it? Build and run to see! If all works well you should be able to tap the screen to move your bear all around.

Screenshot of our updated bear animation - with touch to move and flip support!

Where To Go From Here?

Here is a sample project with all of the code you’ve developed in the above tutorial.

At this point, you should know how to use animations in your projects. You should have some fun and experiment by creating your own animations and seeing what you can do!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值