Lesson3:Scene and menus(场景和菜单)
在这篇教程中,你将会学习怎样使用"scenes"来安排(划分场景)你的游戏,并且怎样在scene和scene之间转换。
这些东西比较简单,我们也会涉及CCMenu及相关的一些类,用这些可以很简单地创建菜单scene。
这篇教程假设你已经学习过了Lesson1.Install & Test 和 Lesson2.Your First Game
- Scenes(场景)
Cocos2d中的"scene"就是指定类型的node(CCNode的子类),它可以作为基本的父容器用来盛放其他可见的node。
你可以根据你的喜好使用它,但是典型的用法一般是用一个scene作为你真正游戏的场景,用另一个scene显示游戏标题,
最高分列表,选项,等等。用CCScene类来描述一个scene。
一个scene如果是可见的那么说明它正在运行,可以执行actions等等。在同一时刻只能有一个scene运行。然而有时,
可能需要运行另一个scene,那就暂停当前的scene;过后在恢复当前的scene。(原文说得比较麻烦:将另一个新的
scene入栈,它就会在原来那个scene的上面,然后暂停当前的scene,运行新的scene,过后在将运行的scene出栈,
恢复运行原来的那个scene)或者,你可以使用一个scene替代全部其他的scene(这种方法不错,因为它占用的内存更少)
这种入栈/出栈或者替换scene的事情都是由导演(CCDirector)做的。如果你浏览过工程模板的话,你应该见过入下得代
码。在应用代理appDelegate中(例如:在Lesson2AppDelegate.m或者其他类似的文件名)的函数
applicationDidFinishLaunching的末尾处,你将会发现如下的代码:
[[CCDirector sharedDirector] runWithScene: [HelloWorld scene]];
这行代码就是告诉导演开始运行给定的scene(HelloWorld)
用其他的scene替换当前正在运行的scene(例如:当用户点屏幕上的菜单“Play”按钮时,或者当游戏结束后你想回到主菜单时)
只需要调用导演的replaceScene函数:
[[CCDirector sharedDirector] replaceScene: [SomeOtherScene scene]];
这行代码就是结束当前的scene,并且运行指定的scene。你也可以通过再次调用replaceScene,重新运行原来的那个scene。
如果你仅仅是想暂时停止当前的scene,使用pushScene代替replaceScene。随后你可以通过调用popScene结束新的scene,
恢复原来的那个scene。但是要尽量少使用,因为iPhone的内存容量是有限的,并且所有的scene的堆栈都是在内存中的。
导演还能做许多事,像暂停/恢复当前的scene 。想要详细了解CCDirector 去官方网站查看CCDirector reference
- Fancy Transitions (转场特效)
场景和场景之间的转场特效支持由CCTransition*类支持
[[CCDirector sharedDirector] replaceScene: [CCTransitionFade transitionWithDuration:0.5f scene:[SomeOhterScene scene]];
一些常用的转场类:
- CCTransitionFade (逐渐变淡)
- CCTransitionFlipAngular (对角立体旋转)
- CCTransitionShrinkGrow (缩放效果)
- CCTransitionMoveInB (从下方出现)
- CCTransitionMoveInT (从上方出现)
- CCTransitionMoveInL (从左边出现)
- CCTransitionMoveInR (从右方出现)
- CCTransitionFadeTR (从左下方向右上方碎片化变淡)
- CCTransitionFadeUp (从下向上横栏翻转式变淡)
- CCTransitionFlipX (左右立体翻转)
- CCTransitionFlipY (上下立体翻转)
- CCTransitionPageTurn (翻页效果)
- CCTransitionCrossFade (逐渐变淡)
想知道更多地转场类可以查看API文档
- Menu
菜单提供了一条用户和游戏交互的途径,这里使用了一个GUI(图形用户界面)概念:“按钮”。人们常常通过菜单告诉导演
切换scene,但是菜单也是一种方便灵活的控制游戏的方法。
创建菜单,仅需创建一个CCMenu的实例:
CCMenu * myMenu = [CCMenu menuWithItems:nil];
这段代码创建一个空的菜单,没有菜单项(按钮)。为了使菜单能用,你需要添加一些菜单项。
这里有几种菜单项可供选择:
- CCMenuItemAtlasFont
- CCMenuItemFont
- CCMenuItemImage
- CCMenuItemLabel
- CCMenuItemSprite
- CCMenuItemToggle
这些菜单项之间有细微的不同,但是从本质来说,它们都允许你指定一个目标对象(target)和一个selector
(当按钮被触摸的时候会调用)。
其中最简单的一个创建菜单项的方法就是用CCMenuItemImage,只需使用一个指定的图片作为菜单项的“触摸区域”。
CCMenuItemImage *menuItem1 = [CCMenuItemImage itemFromNormalImage:@"myFirstButton.png"
selectedImage: @"myFirstButton_selected.png"
target:self
selector:@selector(doSomething:)];
- itemFromNormalImage就是你想当做菜单项的图片
- selectedImage就是当按钮被触摸时的按钮图片
- target指定了哪个对象处理菜单项的触摸事件(此处使用self,代表了菜单所处的那个scene)
- selector就是要被调用的目标函数
注意:@selector(doSomething)和@selector(doSomething:)是不一样的(注意那个多出来的冒号)。带着那个冒号,这个菜单项
会作为参数传递过去。这个函数被调用时,你给它传过去这个附加的数据是非常有用的。例如,你可以使用菜单项的'tag'属性决定
怎样开始。你甚至可以选择用子类扩充添加一些额外的信息,然后在被调用的函数中访问。
一旦你创建了你的菜单项,你可以添加它们到菜单中,添加的方法和添加“东西”到CCNode中是一样的。
[myMenu addChild:menuItem1];
二选一就行,你还可以一次性创建几个菜单项并且添加它们到菜单中,用那个创建菜单时用到的函数menuWithItem构造函数,
就像这样。。。(在这个例子中,记住一点构造函数参数列表要以nil结尾)
CCMenu *myMenu = [CCMenu menuWithItems:menuItem1, menuItem2, menuItem3, nil];
CCMenu当然也提供了一些方便的函数来布局你的菜单,就像“alignItemsVertically” 和“alignItemsInRows”,可以这样用:
[myMenu alignItemsVertically];
如果放在一起做的话,看起来应该像下面的样子:
// Create some menu items(创建一些菜单项) CCMenuItemImage * menuItem1 = [CCMenuItemImage itemFromNormalImage:@"myfirstbutton.png" selectedImage: @"myfirstbutton_selected.png" target:self selector:@selector(doSomethingOne:)]; CCMenuItemImage * menuItem2 = [CCMenuItemImage itemFromNormalImage:@"mysecondbutton.png" selectedImage: @"mysecondbutton_selected.png" target:self selector:@selector(doSomethingTwo:)]; CCMenuItemImage * menuItem3 = [CCMenuItemImage itemFromNormalImage:@"mythirdbutton.png" selectedImage: @"mythirdbutton_selected.png" target:self selector:@selector(doSomethingThree:)]; // Create a menu and add your menu items to it(创建菜单,并将菜单项添加到菜单中) CCMenu * myMenu = [CCMenu menuWithItems:menuItem1, menuItem2, menuItem3, nil]; // Arrange the menu items vertically(安排菜单项的位置竖直排列) [myMenu alignItemsVertically]; // add the menu to your scene(添加菜单到你的scene中) [self addChild:myMenu];
这里有6个图片供你测试显示的结果
为了使用这些图片你需要下载它们,并且把它们放在你工程得根目录下,然后添加它们到你Xcode中的资源文件夹中。
你会注意到每一个菜单项都有一个单独的回调函数,所以你需要添加上它们:
- (void) doSomethingOne: (CCMenuItem *) menuItem { NSLog(@"The first menu was called"); } - (void) doSomethingTwo: (CCMenuItem *) menuItem { NSLog(@"The second menu was called"); } - (void) doSomethingThree: (CCMenuItem *) menuItem { NSLog(@"The third menu was called"); }
查询更多关于menus的用法,请通读CCMenu文档。
完成最初的三篇教程你的代码可能看起来和下面的类似,注意下面创建按钮被分开了。
// HelloWorldLayer.m // Lesson1 // // Import the interfaces #import "HelloWorldScene.h" #import "CCTouchDispatcher.h" CCSprite *seeker1; CCSprite *cocosGuy; // HelloWorld implementation @implementation HelloWorld +(id) scene { // 'scene' is an autorelease object. CCScene *scene = [CCScene node]; // 'layer' is an autorelease object. HelloWorld *layer = [HelloWorld node]; // add layer as a child to scene [scene addChild: layer]; // return the scene return scene; } // set up the Menus -(void) setUpMenus { // Create some menu items CCMenuItemImage * menuItem1 = [CCMenuItemImage itemFromNormalImage:@"myfirstbutton.png" selectedImage:@"myfirstbutton_selected.png" target:self selector:@selector(doSomethingOne:)]; CCMenuItemImage * menuItem2 = [CCMenuItemImage itemFromNormalImage:@"mysecondbutton.png" selectedImage: @"mysecondbutton_selected.png" target:self selector:@selector(doSomethingTwo:)]; CCMenuItemImage * menuItem3 = [CCMenuItemImage itemFromNormalImage:@"mythirdbutton.png" selectedImage:@"mythirdbutton_selected.png" target:self selector:@selector(doSomethingThree:)]; // Create a menu and add your menu items to it CCMenu * myMenu = [CCMenu menuWithItems:menuItem1, menuItem2, menuItem3, nil]; // Arrange the menu items vertically [myMenu alignItemsVertically]; // add the menu to your scene [self addChild:myMenu]; } // on "init" you need to initialize your instance -(id) init { // always call "super" init // Apple recommends to re-assign "self" with the "super" return value if( (self=[super init] )) { // create and initialize our seeker sprite, and add it to this layer seeker1 = [CCSprite spriteWithFile: @"seeker.png"]; seeker1.position = ccp( 50, 100 ); [self addChild:seeker1]; // do the same for our cocos2d guy, reusing the app icon as its image cocosGuy = [CCSprite spriteWithFile: @"Icon.png"]; cocosGuy.position = ccp( 200, 300 ); [self addChild:cocosGuy]; // schedule a repeating callback on every frame [self schedule:@selector(nextFrame:)]; [self setUpMenus]; // register to receive targeted touch events [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES]; } return self; } // on "dealloc" you need to release all your retained objects - (void) dealloc { // in case you have something to dealloc, do it in this method // in this particular example nothing needs to be released. // cocos2d will automatically release all the children (Label) // don't forget to call "super dealloc" [super dealloc]; } - (void) nextFrame:(ccTime)dt { seeker1.position = ccp( seeker1.position.x + 100*dt, seeker1.position.y ); if (seeker1.position.x > 480+32) { seeker1.position = ccp( -32, seeker1.position.y ); } } - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { return YES; } - (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event { CGPoint location = [touch locationInView: [touch view]]; CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:location]; [cocosGuy stopAllActions]; [cocosGuy runAction: [CCMoveTo actionWithDuration:1 position:convertedLocation]]; } - (void) doSomethingOne: (CCMenuItem *) menuItem { NSLog(@"The first menu was called"); } - (void) doSomethingTwo: (CCMenuItem *) menuItem { NSLog(@"The second menu was called"); } - (void) doSomethingThree: (CCMenuItem *) menuItem { NSLog(@"The third menu was called"); } @end
菜单项也可以使用block语法替代selector语法
// Create some menu items CCMenuItemLabel *mi1 = [CCMenuItemLabel itemWithLabel:someLabel block:^(id sender) { [theDirector pushScene:[SceneGame node]; } ];
你可以在这里找到Objective-C的blocks语法。Blocks语法帮助消除那些只使用一次的函数。