导航

游戏必然涉及到场景的切换(蜘蛛人不在此列)。

如果新场景在init的时候需要加载大量的资源,在这段时间中用户界面将会失去响应。这对于玩家而言是一个非常糟糕的体验,他可能觉得你把他搞死机了 :)

我们在WOW中进入副本的瞬间其实就涉及到场景的切换,很明显的是暴风雪的程序员并没有让我们体验这种糟糕的感觉,他们会设计一个过场场景来告诉我们加载了百分之多少了。

记住:等待比不知所措要好得多。

先看展示动画:展示动画

由于动画中看不到鼠标点击的操作,所以你可能会觉得有点不知所措。听我来解释一下。

本例涉及到3个场景,源场景,目标场景和过渡场景。

在源场景和目标场景的init方法中都有复杂的浮点数除法运算,这可能会需要3秒钟的时间(模拟器环境下,真机可能更长)。

当源场景被触摸以后,直接使用导演的replaceScene切换到目标场景,你会发现显示帧率的区域停止了跳动,游戏此时进入"失去响应状态"。

在目标场景中我们再次触摸屏幕,此时借助过渡场景来replaceScene到目标场景,loading界面出现,相同的时间后顺利切换到源场景。

 

来看实现过程,代码结构如下图:

源场景和目标场景其实都是用层来构造的,过程场景是一个CCSecen。

先看FirstLayer中的内容:

 

//
// FirstLayer.m
// CH05
//
// Created by phc on 11-12-12.
// Copyright 2011年 hxsoft. All rights reserved.
//

#import "FirstLayer.h"
#import "NextLayer.h"

@implementation FirstLayer
-(void)dealloc
{
[super dealloc];
}
+(id)scene
{
CCScene *sc = [CCScene node];
[sc addChild:[FirstLayer node]];
return sc;
}
//用浮点数除法折腾
-(void)zheteng
{
float a = 2423;
float b = 3432;
for (int i = 0; i < 300000000; i++) {
a /= b;
}
}
-(id)init
{
if (self = [super init]) {
CCLabelTTF *lbl = [CCLabelTTF labelWithString:@"点击屏幕切换场景(没有loading)" fontName:@"Arial" fontSize:18];
[self addChild:lbl];
CGSize sizeOfWin = [[CCDirector sharedDirector] winSize];
lbl.position = CGPointMake(sizeOfWin.width * 0.5, sizeOfWin.height * 0.5);
[self zheteng];

[self setIsTouchEnabled:YES];
}
return self;
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[[CCDirector sharedDirector] replaceScene:[NextLayer scene]];

}

@end


使用静态方法scene来构造场景并返回。

 

zheteng方法负责用三亿个浮点数除法运算来模拟加载大量资源。

在屏幕的触摸中,直接使用导演的replaceScene方法切换到目标场景。

 

看NextLayer的代码:

 

//
// NextLayer.m
// CH05
//
// Created by phc on 11-12-12.
// Copyright 2011年 hxsoft. All rights reserved.
//

#import "NextLayer.h"
#import "LoadingScene.h"

@implementation NextLayer
+(id)scene
{
CCScene *sc = [CCScene node];
[sc addChild:[NextLayer node]];
return sc;
}
//用浮点数除法折腾
-(void)zheteng
{
float a = 2423;
float b = 3432;
for (int i = 0; i < 300000000; i++) {
a /= b;
}
}
-(id)init
{
if (self = [super init]) {
[self zheteng];
CCLabelTTF *lbl = [CCLabelTTF labelWithString:@"目标场景,点击屏幕切换场景(有loading)" fontName:@"Arial" fontSize:16];
[self addChild:lbl];
CGSize sizeOfWin = [[CCDirector sharedDirector] winSize];
lbl.position = CGPointMake(sizeOfWin.width * 0.5, sizeOfWin.height * 0.5);

[self setIsTouchEnabled:YES];
}
return self;
}

-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[[CCDirector sharedDirector] replaceScene:[LoadingScene initWithTargetScene:FIRSTLAYERTARGET]];
}
@end


和FirstLayer唯一不同的就是,本层使用replaceScene切换到了LoadingScene场景,注意initWithTargetScene这个方法。他出现在LoadingScene类中:

 

 

//
// LoadingScene.m
// CH05
//
// Created by phc on 11-12-12.
// Copyright 2011年 hxsoft. All rights reserved.
//

#import "LoadingScene.h"
#import "FirstLayer.h"
#import "NextLayer.h"

@implementation LoadingScene
-(void)dealloc
{
[super dealloc];
}
+(id)initWithTargetScene:(targetScene)targetSc
{

return [[[self alloc] initScene:targetSc] autorelease];

}
-(id)initScene:(targetScene)targetSc
{
_targetsc = targetSc;
if (self = [super init]) {
CCLabelTTF *lbl = [CCLabelTTF labelWithString:@"Loading..." fontName:@"Marker Felt" fontSize:64];
[self addChild:lbl];
CGSize sizeOfWin = [[CCDirector sharedDirector] winSize];
lbl.position = CGPointMake(sizeOfWin.width * 0.5, sizeOfWin.height * 0.5);


[self schedule:@selector(waitLoad)];
}
return self;
}
-(void)waitLoad
{
switch (_targetsc) {
case FIRSTLAYERTARGET:
[[CCDirector sharedDirector] replaceScene:[FirstLayer scene]];
break;
case NEXTLAYERTARGET:
[[CCDirector sharedDirector] replaceScene:[NextLayer scene]];
break;

default:
break;
}
}
@end


initWithTargetScene类负责构造自身的示例,同时接收一个targetScene类型的参数来指向过渡到的目标场景,这是为了以后多场景扩展而设计的代码。

 

当然,targetScene不是系统提供的,他其实是我们自定义的一个枚举类型,他出现在LoadingScene.h中:

 

typedef enum
{
INVALIDTARGET = 0,
FIRSTLAYERTARGET = 1,
NEXTLAYERTARGET = 2
}targetScene;


 

 

我们在schedule回调的方法waitLoad中切换到目标场景。,有的看官可能要问了,为什么不在init方法中直接使用replaceScene方法呢?这里有两个准则要遵守:

规则一:

永远不要在一个节点的init方法中调用CCDirector的replaceScene方法。

规则二:

请遵守规则一。哈哈,不遵守规则的后果是程序崩溃。Director无法容忍一个节点在初始化的同时进行场景替换。

 

其实这并没有从根本上解决问题,我们仅仅是做了一次中转而已。

甚至从效率上讲,因为多了一次场景切换,反而增加了系统负担。

上述粗体的字听起来似乎有道理,其实不是这样的。世上总有很有多类似这样的看似有道理的谎言。

虽然我们增加了一个场景,但是从内存管理上而言,反而节省了开支,因为在切换场景的时候,会有一个致命瞬间,在这个瞬间两个场景同时存在于内存中,很明显的是loading场景只会占用很少的资源,你懂得。而且从感官上而言,玩家可能会觉得更容易接受使用loading。

代码下载