分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.youkuaiyun.com/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
《Learn IPhone andiPad Cocos2d Game Delevopment》的第5章。
一、使用多场景
很少有游戏只有一个场景。这个例子是这个样子的:
这个Scene中用到了两个Layer,一个Layer位于屏幕上方,标有”Herebe your Game Scores etc“字样的标签,用于模拟游戏菜单。一个Layer位于屏幕下方,一块绿色的草地上有一些随机游动的蜘蛛和怪物,模拟了游戏的场景。
1、加入新场景
一个场景是一个Scene类。加入新场景就是加入更多的Scene类。
有趣的是场景之间的切换。使用[CCDirectorreplaceScene]方法转场时,CCNode有3个方法会被调用:OnEnter、OnExit、 onEnterTransitionDidFinish。
覆盖这3个方法时要牢记,始终要调用super的方法,避免程序的异常(比如内存泄露或场景不响应用户动作)。
-(void)onEnter {
// node的 init方法后调用.
// 如果使用CCTransitionScene方法,在转场开始后调用.
[superonEnter];
}
-(void )onEnterTransitionDidFinish {
// onEnter方法后调用.
// 如果使用CCTransitionScene方法,在转场结束后调用.
[superonEnterTransitionDidFinish];
}
-(void)onExit
{
// node的dealloc 方法前调用.
// 如果使用CCTransitionScene方法,在转场结束时调用.
[superonExit];
}
当场景变化时,有时候需要让某个node干点什么,这时这3个方法就派上用场了。
与在node的init方法和dealloc方法中做同样的事情不同,在onEnter方法执行时,场景已经初始化了;而在onExit方法中,场景的node仍然是存在的。
这样,在进行转场时,你就可以暂停动画或隐藏用户界面元素,一直到转场完成。这些方法调用的先后顺序如下(使用replaceScene 方法):
1. 第2个场景的 scene方法
2. 第2个场景的 init方法
3. 第2个场景的 onEnter方法
4. 转场
5. 第1个场景的 onExit方法
6. 第2个场景的 onEnterTransitionDidFinish方法
7. 第1个场景的 dealloc方法
二、请稍候⋯⋯
切换场景时,如果场景的加载是一个比较耗时的工作,有必要用一个类似“Loading,please waiting…”的场景来过渡一下。用于在转场时过渡的场景是一个“轻量级”的Scene类,可以显示一些简单的提示内容:
typedef enum
{
TargetSceneINVALID = 0,
TargetSceneFirstScene,
TargetSceneOtherScene,
TargetSceneMAX,
} TargetScenes;
@interface LoadingScene : CCScene
{
TargetScenes targetScene_;
}
+(id)sceneWithTargetScene:(TargetScenes)targetScene;
-(id)initWithTargetScene:(TargetScenes)targetScene;
@end
#import "LoadingScene.h"
#import "FirstScene.h"
#import "OtherScene.h"
@interface LoadingScene(PrivateMethods)
-(void) update:(ccTime)delta;
@end
@implementation LoadingScene
+(id)sceneWithTargetScene:(TargetScenes)targetScene;
{
return [[[self alloc]initWithTargetScene:targetScene] autorelease];
}
-(id)initWithTargetScene:(TargetScenes)targetScene
{
if ((self = [super init]))
{
targetScene_ = targetScene;
CCLabel* label = [CCLabellabelWithString:@"Loading ..." fontName:@"Marker Felt" fontSize:64];
CGSize size = [[CCDirectorsharedDirector] winSize];
label.position =CGPointMake(size.width / 2, size.height / 2);
[self addChild:label];
[self scheduleUpdate];
}
return self;
}
-(void) update:(ccTime)delta
{
[selfunscheduleAllSelectors];
switch (targetScene_)
{
case TargetSceneFirstScene:
[[CCDirector sharedDirector] replaceScene:[FirstScene scene]];
break;
case TargetSceneOtherScene:
[[CCDirector sharedDirector] replaceScene:[OtherScene scene]];
break;
default:
// NSStringFromSelector(_cmd) 打印方法名
NSAssert2(nil, @"%@: unsupported TargetScene %i", NSStringFromSelector(_cmd), targetScene_);
break;
}
}
-(void) dealloc
{
CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);
[super dealloc];
}
@end
首先,定义了一个枚举。这个技巧使LoadingScene能用于多个场景的转场,而不是固定地只能在某个场景的切换时使用。继续扩展这个枚举的成员,使LoadingScene能适用与更多目标Scene的转场。
sceneWithTargetScene 方法中返回了一个autorelease的对象。在coco2d自己的类中也是一样的,你要记住在每个静态的初始化方法中使用autorelease。
在 方法中,构造了一个CCLabel,然后调用 scheduleUpdate 方法。 scheduleUpdate 方法会在下一个时间(约一帧)后调用update方法。在update方法中,我们根据 sceneWithTargetScene 方法中指定的枚举参数,切换到另一个scene。在这个scene的加载完成之前,LoadingScene会一直显示并且冻结用户的事件响应。
我们不能直接在初始化方法 initWithTargetScene 中直接切换scene,这会导致程序崩溃。记住,在一个Node还在初始化的时候,千万不要在这个scene上调用CCDirector的replaceScene方法。
LoadingScene的使用很简单,跟一般的scene一样:
CCScene* newScene = [LoadingScene sceneWithTargetScene:TargetSceneFirstScene];
[[CCDirector sharedDirector] replaceScene:newScene];
三、使用Layer
Layer类似Photoshop中层的概念,在一个scene中可以有多个Layer:
typedef enum
{
LayerTagGameLayer,
LayerTagUILayer,
} MultiLayerSceneTags;
typedef enum
{
ActionTagGameLayerMovesBack,
ActionTagGameLayerRotates,
}MultiLayerSceneActionTags;
@class GameLayer;
@class UserInterfaceLayer;
@interface MultiLayerScene :CCLayer
{
bool isTouchForUserInterface;
}
+(MultiLayerScene*) sharedLayer;
@property (readonly) GameLayer* gameLayer;
@property (readonly) UserInterfaceLayer*uiLayer;
+(CGPoint) locationFromTouch:(UITouch*)touch;
+(CGPoint) locationFromTouches:(NSSet *)touches;
+(id) scene;
@end
@implementation MultiLayerScene
static MultiLayerScene* multiLayerSceneInstance;
+(MultiLayerScene*) sharedLayer
{
NSAssert(multiLayerSceneInstance != nil, @"MultiLayerScenenot available!");
return multiLayerSceneInstance;
}
-(GameLayer*) gameLayer
{
CCNode* layer = [self getChildByTag:LayerTagGameLayer];
NSAssert([layer isKindOfClass:[GameLayer class]], @"%@: not aGameLayer!", NSStringFromSelector(_cmd));
return (GameLayer*)layer;
}
-(UserInterfaceLayer*) uiLayer
{
CCNode* layer = [[MultiLayerScene sharedLayer] getChildByTag:LayerTagUILayer];
NSAssert([layer isKindOfClass:[UserInterfaceLayer class]], @"%@: not aUserInterfaceLayer!", NSStringFromSelector(_cmd));
return (UserInterfaceLayer*)layer;
}
+(CGPoint) locationFromTouch:(UITouch*)touch
{
CGPoint touchLocation = [touchlocationInView: [touch view]];
return [[CCDirector sharedDirector] convertToGL:touchLocation];
}
+(CGPoint) locationFromTouches:(NSSet*)touches
{
return [self locationFromTouch:[touches anyObject]];
}
+(id) scene
{
CCScene* scene = [CCScene node];
MultiLayerScene* layer = [MultiLayerScene node];
[scene addChild:layer];
return scene;
}
-(id) init
{
if ((self = [super init]))
{
NSAssert(multiLayerSceneInstance == nil, @"anotherMultiLayerScene is already in use!");
multiLayerSceneInstance = self;
GameLayer* gameLayer = [GameLayer node];
[self addChild:gameLayer z:1 tag:LayerTagGameLayer];
UserInterfaceLayer* uiLayer = [UserInterfaceLayer node];
[self addChild:uiLayer z:2 tag:LayerTagUILayer];
}
return self;
}
-(void) dealloc
{
CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);
[super dealloc];
}
@end
MultiLayerScene 中使用了多个Layer:一个GameLayerh 和一个UserInterfaceLayer 。
MultiLayerScene 使用了静态成员 multiLayerSceneInstance 来实现单例。 MultiLayerScene也是一个Layer,其node方法实际上调用的是实例化方法init——在其中,我们加入了两个Layer,分别用两个枚举LayerTagGameLayer 和LayerTagUILayer 来检索,如属性方法gameLayer和uiLayer所示。
uiLayer是一个UserInterfaceLayer,用来和用户交互,在这里实际上是在屏幕上方放置一个菜单,可以把游戏的一些统计数字比如:积分、生命值放在这里:
typedef enum
{
UILayerTagFrameSprite,
}UserInterfaceLayerTags;
@interface UserInterfaceLayer :CCLayer
{
}
-(bool) isTouchForMe:(CGPoint)touchLocation;
@end
@implementation UserInterfaceLayer
-(id) init
{
if ((self = [super init]))
{
CGSize screenSize = [[CCDirector sharedDirector] winSize];
CCSprite* uiframe = [CCSprite spriteWithFile:@"ui-frame.png"];
uiframe.position = CGPointMake(0, screenSize.height);
uiframe.anchorPoint = CGPointMake(0, 1);
[self addChild:uiframe z:0 tag:UILayerTagFrameSprite];
// 用Label模拟UI控件( 这个Label没有什么作用,仅仅是演示).
CCLabel* label = [CCLabel labelWithString:@"Here be yourGame Scores etc" fontName:@"Courier" fontSize:22];
label.color = ccBLACK;
label.position = CGPointMake(screenSize.width / 2, screenSize.height);
label.anchorPoint = CGPointMake(0.5f, 1);
[self addChild:label];
self.isTouchEnabled = YES;
}
return self;
}
-(void) dealloc
{
CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);
[super dealloc];
}
-(