iOS游戏教程:如何结合使用Cocos2d和UIKit

iOS游戏教程:如何结合使用Cocos2d和UIKit

作者:蓝鸥科技 发布于:2013-2-17 17:14 Sunday 分类:iOS高级-UI


创建一款应用,一部分用Cocos2d,一部分用UIKit来实现! 

Cocos2D是一个很好的框架,但是有时候用UIKit去实现你的游戏会方便点。

例如,通常使用UIKit来设计你的主菜单,设置页面,然后使用Cocos2D来实现游戏的主逻辑会很有效。

你也许还会发现在Cocos2D场景上使用UIKit控件会挺方便的 – 例如text fields,buttons,tab bars和ad views。

在本教程中,你会学习到如何实现它!我们会使用一个简单的Cocos2D工程,在它的基础上修改,使用UIKit来制作主菜单和关于页面,并且覆盖一些UIKit控件在上面。

本教程假设你对Cocos2D和UIKit开发已经有基本的了解。如果你是个Cocos2d和UIKit的新手,可以先看看本网站的其他教程

开始

先下载本教程要使用到的工程文件

运行该工程,你将看到以下画面:

本工程是一款简单的Cocos2D应用,展示了一些由我 可爱的太太Vicki设计的日历。

如果你对如何制作这款应用感兴趣,我们在How To Mask a Sprite with Cocos2D 1.0这篇教程中有详细讲解。然而,我根据本教程的需要,对工程做了一些小改动。

  • 添加了一些由Vicki制作的新墙纸
  • 添加了一些你会在本教程中使用的新美术素材
  • 支持retina display
  • 让墙纸按顺序排列,而不是随意叠放
  • 修改了场景的切换动画

现在预览一遍代码,明确你的方向,确保你理解了所有的工作原理。因为我们要开始使用UIKit了!

添加一个MainWindow.xib文件

 

通常,Cocos2D模版并没有使用MainWindow.xib文件。反而,在 Supporting Files 的main.m文件中,它将AppDelegate类的名字传到UIApplicationMain中:

int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate");

以上代码一开始让UIKit去创建一个AppDelegate类的实例。AppDelegate会在applicationDidFinishLaunching方法的起始位置创建main window:

window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

这是比较好的处理方式,但是如果我们按照这个思路继续下去,想要在开始时以一个不同的view controller作为主菜单,我们就必须以程序的方式创建它了。

个人看来,我更倾向于在任何情况下使用Interface Builder,因为它的快捷和更少的代码量意味着更少的维护成本,所以让我们修改工程,先加载一个XIB文件,该文件称为MainWindow.xib。

先打开Supporting Files的main.m文件,然后使用以下代码替换掉调用UIApplicationMain方法的代码行:

int retVal = UIApplicationMain(argc, argv, nil, nil);

当你将nil以最后的参数传入时,它会通知UIKit去加载Info.plist文件中的NSMainNibFile关键字所指定的XIB文件。我们继续进行下一步!

打开你的Info.plist文件,在空白区域点击鼠标右键,从弹出的菜单栏选择Add Row。你可以从下拉菜单选择“Main nib file base name”(或者输入关键字NSMainNibFile)。

然后双击这个关键字对应的value栏,输入MainWindow。你的界面看起来应该和下面的一样:

下一步是创建MainWindow.xib文件! 在你的MaskedCal组按住Control键然后点击鼠标(control-click),然后选择New File。选择iOS User Interface Empty,然后点击下一步。选择iPhone作为Device Family选项,点击下一步,将文件命名为MainWindow.xib文件,然后点击保存。

点击新创建的MainWindow.xib文件,你会在Interface Builder中看到它 – 基本是空白的!

让我们做些创建工作。首先,我们需要把File’s Owner设置为UIApplication。因为我们在开始时,已经告诉UIApplication给XIB文件了,它把自身的引用传给了我们。所以点击File’s Owner, 然后在右侧栏的Identity Inspector中,设置Class为UIApplication。

接下来,我们需要配置XIB文件,在一开始时创建我们的Application Delegate实例。因此在Object库中,拖动一个Object到Objects面板:

然后,选择新的Object,来到Identity Inspector,然后设置Class为AppDelegate.

最后一步 – 我们必须告诉UIApplication,这个新的AppDelegate类是它的委托!通过鼠标右键点击File’s Owner,然后control-click拖动delegate附近的点到App Delegate Object上:

woot!我们刚完成了以下的初始化步骤:

  1. 在main函数中,UIApplicationMain方法在最后一个参数设定为nil的情况下 运行,
  2. UIKit接着加载了我们在Info.plist文件中指定的XIB文件 – MainWindow.xib。
  3. 当XIB被加载后,它为我们在XIB文件中设定的每个对象创建实例。现在,我们在那里有了AppDelegate类,所以它创建了一个新的AppDelegate类。
  4. 加载XIB文件也将AppDelegate的实例设定为UIApplication的委托。
  5. 所以现在UIApplication会在AppDelegate类中调用applicationDidFinishLaunching的方法!

好的,现在结束了对Xib文件的设定。让我们在XIB文件中创建main window,而不是用程序的方式去实现。打开AppDelegate.h文件,然后修改UIWindow的属性,申明它为IBOutlet:

@property (nonatomic, retain) IBOutlet UIWindow *window;

并且来到AppDelegate.m文件,将对UIWindow的程序实现代码行注释掉:

//window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

来到MainWindow.xib,然后从Object library拖动Window到Objects面板中。接着鼠标右键点击App Delegate对象,然后从window outlet处,control-click拖动一条线到新的Window对象上:

编译运行工程,一切像之前那样运作正常,你现在使用了MainWindow.xib文件,这会让接下来的步骤更容易些。

添加一个Main Menu View Controller

与其现在就开始使用Cocos2D场景(scene),我们先使用另一个View Controller – 它会被设定为主菜单。首先让我们创建一个新的view controller。在Masked 组中control-click,选择New File。选择iOS Cocoa Touch UIViewController 子类,然后点击下一步。输入UIViewController作为继承的父类,确保Target for iPad 没有被选中,并且选上XIB作为user interface,然后点击Next。命名新类为MainMenuViewController.m, 然后点击保存。

点击MainMenuViewController.xib文件,然后点击Objects面板下面的View。在Attributes Inspector中,设置Orientation为Landscape。

接下来拖动一个Image View到view中,然后让它填满整个区域。设置Image为Title_bg.png。

接下来拖动一个Round Rect Button到view中。选择the Size Inspector 然后设置X=173, Y=143, Width=135, 和 Height = 62:

选择Attributes Inspector, 设置Type为Custom,然后在默认项中设置Image为button_view_uns.png。 然后改变State Config为Highlighted, 接着设定Image为button_view_sel.png。

根据以下信息对第二个按钮重复上面的步骤:

  • X=173, Y=213, Width=135, Height=62
  • Custom type
  • Default state image: button_about_uns.png
  • Highlighted state image: button_about_sel.png

接着有一些代码需要编写。打开MainMenuViewController.m文件,然后根据以下内容做修改:

// Replace this method - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{     return UIInterfaceOrientationIsLandscape(interfaceOrientation);} // Add new method - (void) viewWillAppear:(BOOL)animated{     [self.navigationController setNavigationBarHidden:YES animated:animated];
    [super viewWillAppear:animated];}

这会确保我们的view controller只旋转到水平方向,并且在启动时隐藏掉navigation bar。

做得不错,我们会在接下来完成对buttons的设定。现在让我们先把这些内容在开始时展现吧。

选择MainWindow.xib文件,然后拖动一个Navigation Controller到Objects panel中。在Window上control-click,然后从rootViewController中拖动一条线到新的Navigation Controller上:

这里设定了第一个出现的view controller为这个navigation controller。我们这里使用一个navigation controller,让管理view controller栈变得容易些。

接下来,点击Navigation Controller旁边的箭头,在展示出来的选项中选择View Controller。在Attributes Inspector中,设置NIB文件的名字为MainMenuViewController, 然后在Identity Inspector中把Class设定为MainMenuViewController。

这样,在Navigation Controller中的第一项就设定为我们刚才制作的Main Menu View Controller了!

几乎要完成了 – 我们需要注释掉Cocos2D模版为我们设定好的旧代码。

打开AppDelegate.m文件,然后注释掉以下部分:

/* 
#import "HelloWorldLayer.h"
#import "RootViewController.h"
*/ /* 
// Init the View Controller
viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
viewController.wantsFullScreenLayout = YES;
//
// Create the EAGLView manually
//  1. Create a RGB565 format. Alternative: RGBA8
//	2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition
//
//
EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
pixelFormat:kEAGLColorFormatRGB565	// kEAGLColorFormatRGBA8
depthFormat:0						// GL_DEPTH_COMPONENT16_OES
];
// attach the openglView to the director
[director setOpenGLView:glView];
*/ /* 
// make the OpenGLView a child of the view controller
[viewController setView:glView];
// make the View Controller a child of the main window
[window addSubview: viewController.view];
*/ /* 
[[CCDirector sharedDirector] runWithScene: [HelloWorldLayer sceneWithLastCalendar:0]]; 
*/

好的,就是这样!编译运行,你将看到新的主菜单页面了:

接下来,让我们做进一步的编程,让按钮工作吧!

连接上Cocos2D View

首先对RootViewController做些修改,重新添加我们从AppDelegate移除的初始化Cocos2D的代码。打开RootViewController.m文件并作以下修改:

// Add to top of file #import "HelloWorldLayer.h" // Add these new methods - (void)setupCocos2D {     EAGLView *glView = [EAGLView viewWithFrame:self.view.bounds
        pixelFormat:kEAGLColorFormatRGB565	// kEAGLColorFormatRGBA8         depthFormat:0                        // GL_DEPTH_COMPONENT16_OES     ];
    glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [self.view insertSubview:glView atIndex:0];
    [[CCDirector sharedDirector] setOpenGLView:glView];
    CCScene *scene = [HelloWorldLayer sceneWithCalendar:1];
    [[CCDirector sharedDirector] runWithScene:scene];} - (void) viewWillAppear:(BOOL)animated{     [self.navigationController setNavigationBarHidden:YES animated:NO];
    [super viewWillAppear:animated];} - (void)viewDidLoad {     [super viewDidLoad];    
    [self setupCocos2D];} // Add to end of viewDidUnload [[CCDirector sharedDirector] end];

setupCocos2D函数中的代码是我们之前从AppDelegate中移除的。它创建了一个EAGLView,然后把它作为subview添加到view controller的view中(在栈的底部)。它告诉Cocos2D使用新的view,然后让它运行HelloWorldLayer的场景(scene)。

注意到我们在viewDidLoad中调用这些代码,我们需要在viewDidUnload中通过调用[[CCDirector sharedDirector] end]来做清理工作。记住当views不可见时,它们可以在内存较低的情况下被清理掉。

Cocos2D并没有为RootViewController创建一个XIB文件,但是如果有一个的话会方便点,让我们继续创建它吧。在MaskedCal组中Control-click,然后点击New File, 选择 iOS User Interface Empty,然后点击Next。选择iPhone作为Device Family,点击Next,命名新文件为RootViewController.xib,然后点击Save。

现在做初始化的工作会很容易了:

  • 选中File‘s Owner, 然后在Identity Inspector中设置class为RootViewController。
  • 然后从Object Library中拖动一个View到Objects list中。
  • 在Attributes Inspector中,设置Orientation为Landscape。
  • 最后,control-click File’s Owner, 然后从view的outlet中拖动一条线到你刚添加的view上。

好的 – 我们的RootViewController已经设置好了,现在我们需要让主菜单的按钮被点击后,显示该页面!

打开MainMenuViewController.xib文件,然后打开Assistant Editor。确保它被设置为Automatic,然后选中View,让MainMenuViewController.h文件在Assistant Editor中显示。

然后,control-drag View button到@interface下面。设置Connection为Action,设定它的名字为viewTapped, 然后点击Connect:

接下来我们需要为RootViewController创建一个实例变量和property。打开MainMenuViewController.h文件然后根据以下内容做修改:

#import <UIKit/UIKit.h> #import "RootViewController.h" @interface MainMenuViewController : UIViewController {     RootViewController *_rootViewController;} @property (retain) RootViewController *rootViewController;- (IBAction)viewTapped:(id)sender;@end

接下来打开MainMenuViewController.m文件,然后做以下修改:

// Add to top of file @synthesize rootViewController = _rootViewController;// Add new methods - (void)viewWallpapers:(id)arg {     if (_rootViewController == nil) {         self.rootViewController = [[[RootViewController alloc] initWithNibName:nil bundle:nil] autorelease];
    }     [self.navigationController pushViewController:_rootViewController animated:YES];} - (IBAction)viewTapped:(id)sender {     [self viewWallpapers:nil];} - (void)dealloc{     [_rootViewController release];
    _rootViewController = nil;
    [super dealloc];}

相当简单 – 要用Cocos2D的subview来展示RootViewController,我们只需要创建view controller,然后把它推到navigation controller的栈中!注意当我们调用initWithNibName方法时, 我们可以传入nil然后它会寻找一个和view controller有相同名字的nib文件(例如RootViewController.xib)。

编译运行,现在你可以点击View 上的按钮来加载Cocos2D的场景(scene)了!

覆盖上UIKit Views

到目前为止还不错,但是没有办法可以回到主菜单页面!

让我们在screen上面覆盖上一个UIKit按钮,这样你可以点击返回。你可以使用相同的技术,对任何其他需要的控件做处理 – 从UITextFields到UISliders 或者更多。

打开RootViewController.xib文件,然后拖动一个Round Rect 按钮到你的view上。按照以下参数做设定:

  • Button 1: X=0, Y=256, Width=64, Height=64, Custom, Image=Home.png
  • Button 2: X=396, Y=256, Width=64, Height=64, Custom, Image=Message.png

接着,点击main view然后设置背景颜色为黑色,这样按钮在Interface Builder中会显示得更清晰些。到现在,窗口应该是这样子的:

接下来,control-drag Home按钮到你的assistant editor的RootViewController.h文件下面,刚好在@end上面。修改Connection为Action,名字为homeTapped, 然后点击Connect。

然后切换到RootViewController.m文件,在homeTapped方法中添加以下代码:

[self.navigationController popViewControllerAnimated:YES];

编译运行,然后你应该能够点击home按钮返回主菜单了!

添加Gesture Recognizers

在Cocos2D游戏中使用gesture recognizers通常是有效的,比起你自己去检测手势事件,他们可以让事情变得更简单。

让我们添加以下手势到scene中吧:

  • 一个轻拍(tap) gesture recognizer,用于进入下一页。
  • 一个向左滑动(swipe left)gesture recognizer,用于进入下一页。
  • 一个向右滑动(swipe right)gesture recognizer, 用于进入上一页。
  • 一个双击(double tap)手势,我们会在稍后让它隐藏掉UIButton覆盖层(但是现在只输出一段消息)。

首先,让我们移除掉旧的touch操作代码。来到HelloWorldLayer.m文件,然后删除ccTouchBegan方法,registerWithTouchDispatcher方法,还有那行写着isTouchEnabled为YES的代码。

接着切换到HelloWorldLayer.h文件,然后为gesture recognizers添加一些成员变量和properties。

// Add inside @interface UITapGestureRecognizer * _tapRecognizer;
UITapGestureRecognizer * _doubleTapRecognizer;
UISwipeGestureRecognizer * _swipeLeftRecognizer;
UISwipeGestureRecognizer * _swipeRightRecognizer;// Add after @interface @property (retain) UITapGestureRecognizer * tapRecognizer;@property (retain) UITapGestureRecognizer * doubleTapRecognizer;@property (retain) UISwipeGestureRecognizer * swipeLeftRecognizer;@property (retain) UISwipeGestureRecognizer * swipeRightRecognizer;

接下来切换到HelloWorldLayer.m文件,然后做以下修改:

// Add after @implementation @synthesize tapRecognizer = _tapRecognizer;@synthesize doubleTapRecognizer = _doubleTapRecognizer;@synthesize swipeLeftRecognizer = _swipeLeftRecognizer;@synthesize swipeRightRecognizer = _swipeRightRecognizer;// Then add these new methods - (void)onEnter {     self.doubleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)] autorelease];
    _doubleTapRecognizer.numberOfTapsRequired = 2;
    [[[CCDirector sharedDirector] openGLView] addGestureRecognizer:_doubleTapRecognizer];
 
    self.tapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)] autorelease];
    [_tapRecognizer requireGestureRecognizerToFail:_doubleTapRecognizer];
    [[[CCDirector sharedDirector] openGLView] addGestureRecognizer:_tapRecognizer];
 
    self.swipeLeftRecognizer = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleLeftSwipe:)] autorelease];
    _swipeLeftRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
    [[[CCDirector sharedDirector] openGLView] addGestureRecognizer:_swipeLeftRecognizer];    
 
    self.swipeRightRecognizer = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightSwipe:)] autorelease];
    _swipeRightRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
    [[[CCDirector sharedDirector] openGLView] addGestureRecognizer:_swipeRightRecognizer];    } - (void)onExit {     [[[CCDirector sharedDirector] openGLView] removeGestureRecognizer:_tapRecognizer];
    [[[CCDirector sharedDirector] openGLView] removeGestureRecognizer:_doubleTapRecognizer];
    [[[CCDirector sharedDirector] openGLView] removeGestureRecognizer:_swipeLeftRecognizer];
    [[[CCDirector sharedDirector] openGLView] removeGestureRecognizer:_swipeRightRecognizer];} // Add to dealloc [_tapRecognizer release];
_tapRecognizer = nil;[_doubleTapRecognizer release];
_doubleTapRecognizer = nil;[_swipeLeftRecognizer release];
_swipeLeftRecognizer = nil;[_swipeRightRecognizer release];
_swipeRightRecognizer = nil;

当一个场景首次显示的时候,onEnter方法会被调用,然后当它消失时onExit方法会被调用。在这两个方法中,我们分别地设定和移除gesture recognizers,

注意到创建一个gesture recognizer时,你会使用到以下模式:

  1. 创建gesture recognizer,设定它的target来接收callback(这个场景中的)然后callback方法会被调用(我们在接下来会定义这个方法)。
  2. 在gesture recognizers中设定你想要的任何参数,比如numberOfTapsRequired, 或者是swipe direction。
  3. 添加gesture recognizer到view中 – 当前情况是,openGL view由CCDirector管理。

另外还有一点有趣的地方需要注意下 – 当我们设定single tap gesture recognizer时,我们要注意轻拍的次数,double tap gesture recognizer在何时会被取消。如果你没有这样设定,single tap gesture recognizer会在double tap的第一次轻拍的时候调用,我们并不想在应用中这样使用。

接下来添加gesture recognizer的callbacks方法,在onEnter方法的上面:

- (void)handleTap:(UITapGestureRecognizer *)tapRecognizer {     CCLOG(@"Tap!");
    CCScene *scene = [HelloWorldLayer sceneWithCalendar:calendarNum+1];
    [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInR transitionWithDuration:0.25 scene:scene]];} - (void)handleDoubleTap:(UITapGestureRecognizer *)doubletapRecognizer {     CCLOG(@"Double Tap!");    } - (void)handleLeftSwipe:(UISwipeGestureRecognizer *)swipeRecognizer {     CCLOG(@"Swipe Left!");
    CCScene *scene = [HelloWorldLayer sceneWithCalendar:calendarNum+1];
    [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInR transitionWithDuration:0.25 scene:scene]];    } - (void)handleRightSwipe:(UISwipeGestureRecognizer *)swipeRecognizer {     CCLOG(@"Swipe Right!");
    CCScene *scene = [HelloWorldLayer sceneWithCalendar:calendarNum-1];
    [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInL transitionWithDuration:0.25 scene:scene]];    }

正如你看到的,这些都是相当简单的方法,只是在场景(scenes)之间切换。编译运行,你现在应该可以响应轻拍或者滑动的手势了!

另外,尝试双击操作,并且在log中确认它检测到了双击操作事件。我们会继续实现接下来的方法,让它隐藏掉全屏壁纸的HUD页面!

与RootViewController交流

你经常需要有一种方式去从Cocos2D中与RootViewController做交流。例如,你也许想要在一些事件上显示一个UI元素。在我们的需求中,我们想要通过双击操作来显示或者隐藏掉HUD面板。

有多种处理方式,可以避免依赖性,但是最简单的方法是直接向RootViewController传递一个参数到layer上。

打开HelloWorldLayer.h文件,并且做以下修改:

// Add to top of file #import "RootViewController.h" // Add inside @interface RootViewController * _rootViewController;// Replace sceneWithCalendar and initWithCalendar with these + (CCScene *) sceneWithCalendar:(int)lastCalendar rootViewController:(RootViewController *)rootViewController;- (id)initWithCalendar:(int)lastCalendar rootViewController:(RootViewController *)rootViewController;

然后切换到HelloWorldLayer.m文件,并且做以下修改:

// Replace sceneWithCalendar method with the following +(CCScene *) sceneWithCalendar:(int)calendar rootViewController:(RootViewController *)rootViewController {     CCScene *scene = [CCScene node];
    HelloWorldLayer *layer = [[[HelloWorldLayer alloc] 
                               initWithCalendar:calendar rootViewController:rootViewController] autorelease]; // new     [scene addChild: layer];	
    return scene;} // Replace beginning of initWithCalendar with the following -(id) initWithCalendar:(int)calendar rootViewController:(RootViewController *)rootViewController{ 	if( (self=[super init])) {  
        _rootViewController = rootViewController;
        // Rest of method... // Replace touch handler methods with the following - (void)handleTap:(UITapGestureRecognizer *)tapRecognizer {     CCLOG(@"Tap!");
    CCScene *scene = [HelloWorldLayer sceneWithCalendar:calendarNum+1 rootViewController:_rootViewController];
    [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInR transitionWithDuration:0.25 scene:scene]];} - (void)handleDoubleTap:(UITapGestureRecognizer *)doubletapRecognizer {     CCLOG(@"Double Tap!");    
    [_rootViewController toggleUI];} - (void)handleLeftSwipe:(UISwipeGestureRecognizer *)swipeRecognizer {     CCLOG(@"Swipe Left!");
    CCScene *scene = [HelloWorldLayer sceneWithCalendar:calendarNum+1 rootViewController:_rootViewController];
    [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInR transitionWithDuration:0.25 scene:scene]];    } - (void)handleRightSwipe:(UISwipeGestureRecognizer *)swipeRecognizer {     CCLOG(@"Swipe Right!");
    CCScene *scene = [HelloWorldLayer sceneWithCalendar:calendarNum-1 rootViewController:_rootViewController];
    [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInL transitionWithDuration:0.25 scene:scene]];    }

所以基本上我们只是传递一个引用到RootViewController – 相当简单的东西。然后在double tap上,我们调用了一个叫做toggleUI的新方法,我们现在来完成它!

首先,我们需要添加一些outlets给按钮,这样我们可以显示/隐藏掉它们。打开RootViewController.xib文件,然后control-drag home button 到assistant editor的RootViewController.h文件中,就在@interface下面。设定connection为Outlet,名字为homeButton,然后点击connect。

对mail button做重复操作,命名outlet为mailButton.

接着,打开RootViewController.h文件,然后做以下修改:

// Add inside @interface BOOL _showingUI;
CGPoint _mailCenterOrig;
CGPoint _homeCenterOrig;// Add after @interface - (void)toggleUI;

接着打开RootViewController.m文件然后做以下修改:

// Add inside viewDidLoad, BEFORE call to setupCocos2D _showingUI = YES;
_mailCenterOrig = _mailButton.center;
_homeCenterOrig = _homeButton.center;// Modify call to sceneWithCalendar inside setupCocos2D with the following CCScene *scene = [HelloWorldLayer sceneWithCalendar:1 rootViewController:self];// Add after viewDidLoad - (void)toggleUI {     _showingUI = !_showingUI;
 
    [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^(void) {        
        if (_showingUI) {             _mailButton.center = _mailCenterOrig;
            _homeButton.center = _homeCenterOrig;
        } else {             _mailButton.center = CGPointMake(_mailCenterOrig.x+_mailButton.bounds.size.width, _mailCenterOrig.y+_mailButton.bounds.size.height);
            _homeButton.center = CGPointMake(_homeCenterOrig.x-_homeButton.bounds.size.width, _homeCenterOrig.y+_homeButton.bounds.size.height);
        }     } completion:^(BOOL finished) {     }];}

它使用了UIView animation来实现一个美观的动画,让按钮滑动 离开窗口/进入窗口 去展现和隐藏他们。如果你对UIView animation还不熟悉,你可以看下How To Use UIView Animation Tutorial.

就这样 – 编译运行,然后你应该可以双击窗口来让壁纸完美地展示出来的!

跟Layer通信

就像你有时需要从layar那里跟RootViewController通信一样,你也经常需要从RootViewController那里和layar做通信。

我们将要用当前的墙纸作为背景实现点击mail按钮来发送邮件。RootViewController将会包含展示mail composer的代码,但是它需要向layar询问获得当前显示墙纸的UIImage对象。

同样的,有很多种方式可以去实现它,但是我们会采用简单的方式 – 向CCDirector询问获得当前运行的scene。我们只有一个scene/layar,所以我们可以获得scene中的第一层layer,然后把它传给HelloWorldLayer,接着在上面调用任何我们想要的方法。

让我们先准备HelloWorldLayer,创建一个方法提供给我们当前的墙纸image。打开HelloWorldLayer.h文件,然后做以下修改:

// Add inside @interface
UIImage * _curImage;
// Add after @interface
@property (retain) UIImage * curImage;

然后切换到HelloWorldLayer.m文件,并做以下修改:

// Add after @implementation @synthesize curImage = _curImage;// Inside maskedSpriteWithBuffer, comment out these lines: //[textureSprite setBlendFunc:(ccBlendFunc){GL_DST_ALPHA, GL_ZERO}]; //[maskSprite visit]; // Add inside maskedSpriteWithBuffer, right before return retval self.curImage = [rt getUIImageFromBuffer];// Add to dealloc [_curImage release];
_curImage = nil;

这里我们对CCRenderTexture使用一个便捷方法,将当前的buffer转换成一个UIImage对象。我们还取消了对查看mask sprite的调用,因为我们真的不再需要去展示它了,我们只是看全屏的壁纸而已。

接下来,打开RootViewController.xib文件,然后从mail button那里按住control键,鼠标点击(control-drag)拖动到assistant editor的RootViewController.h文件的@interface下面。设置connection类型为Action,命名为mailTapped, 然后点击Connect。

要显示mail composer,我们需要添加MessageUI framework到工程中。所以在 groups & files树中选择MaskedCal工程,选择MaskedCal target,选择Build Phases Tab,展开Link binary with Libraries section选项,然后点击+按钮。

从下拉列表中选择MessageUI.framework,然后点击Add。然后打开RootViewController.h文件,并做以下修改:

// Add to top of file #import <MessageUI/MessageUI.h> #import <MessageUI/MFMailComposeViewController.h> // Mark class as implementing MFMailComposeViewControllerDelegate @interface RootViewController : UIViewController  {

最后,切换到RootViewController.m文件,然后用以下内容替换掉mailTapped方法(加上两个新方法):

- (void)mailData:(NSData *)data {     if (![MFMailComposeViewController canSendMail]) {         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Oops!", @"")                                                         message:NSLocalizedString(@"Your device cannot send mail.", @"") 
                                                       delegate:self 
                                              cancelButtonTitle:NSLocalizedString(@"OK", @"")                                               otherButtonTitles:nil];
        [alert show];
        [alert release];
        return;
    }  
	// Start up mail picker 	MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
 
	UINavigationBar *bar = picker.navigationBar;
	picker.mailComposeDelegate = self;
 
	[picker setSubject:@"Check out this cute wallpaper!"];
	[picker addAttachmentData:data mimeType:@"image/jpg" fileName:@"wallpaper.jpg"];
 
	// Set up the recipients. 	NSArray *toRecipients = [NSArray arrayWithObjects:nil];	
	[picker setToRecipients:toRecipients];
 
	// Fill out the email body text.     NSString *actualBody = @"Check out this cute wallpaper!  You can download the fullscreen version for free from: http://www.vickiwenderlich.com";
	[picker setMessageBody:actualBody isHTML:NO];
 
	// Present the mail composition interface. 	[self presentModalViewController:picker animated:YES];
 
	bar.topItem.title = @"Email Wallpaper";
 
	[picker release]; // Can safely release the controller now. } - (IBAction)mailTapped:(id)sender {  
    CCScene * scene = [[CCDirector sharedDirector] runningScene];
    HelloWorldLayer *layer = [scene.children objectAtIndex:0];
    UIImage *curImage = layer.curImage;
    NSData *data = UIImageJPEGRepresentation(curImage, 0.8); 
 
    [self mailData:data];
 } #pragma mark MFMailComposeViewControllerDelegate - (void)mailComposeController:(MFMailComposeViewController *)controller
		  didFinishWithResult:(MFMailComposeResult)result
						error:(NSError *)error {     [self dismissModalViewControllerAnimated:YES];}

注意mailTapped方法获得了running scene,再从running scene中获取第一个child,并且知道它是一个HelloWorldLayer。然后它可以从layer那里获得curImage。编译运行,现在你可以将你喜欢的墙纸发送给你的一个朋友了!:]

Progress HUDs

如果你已经在设备上运行了代码,你也许会注意到有一段恼人的延迟 – 初始化设置Cocos2D, 然后将图片压缩成JPG格式,以用于邮件发送。

当你有需要长时间运行的operations时, 用户不得不等待,这里如果显示一个指示器,表示有操作正在处理,会让app的交互性显得更好。

一种简单的实现方式是通过使用一个我喜欢的utility class,叫做MBProgressHUD。你可以在这里下载,然后把MBProgressHUD.h 和 MBProgressHUD.m文件从解压缩的文件夹拖动到工程中。确保 “Copy items into destination group’s folder (if needed)”选上,然后点击Finish。

然后来到MainViewController.m文件,并做以下修改:

// Add to top of file #import "MBProgressHUD.h" // Replace viewTapped with the following - (IBAction)viewTapped:(id)sender {    
    if (_rootViewController == nil) {         MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
        hud.labelText = @"Loading...";
        [self performSelector:@selector(viewWallpapers:) withObject:nil afterDelay:0.1];
    } else {         [self viewWallpapers:nil];
    }    } // Add to the beginning of viewWillAppear [MBProgressHUD hideHUDForView:self.view animated:NO];

以上代码只是和我们之前写的viewWallpapers方法(为RootViewController做了初始工作)一样,显示了一个HUD。我们计划让viewWallpapers方法在一段小的延迟后调用,这样HUD在我们运行代码初始化RootViewController之前,就有时间出现了。

接着来到RootViewController.m文件,然后做以下修改:

// Add to top of file #import "MBProgressHUD.h" // Replace mailTapped method with the following - (IBAction)mailTapped:(id)sender {  
    MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hud.labelText = @"Preparing wallpaper...";
 
    CCScene * scene = [[CCDirector sharedDirector] runningScene];
    HelloWorldLayer *layer = [scene.children objectAtIndex:0];
    UIImage *curImage = layer.curImage;
 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {        
        NSData *data = UIImageJPEGRepresentation(curImage, 0.8); 
        dispatch_async(dispatch_get_main_queue(), ^(void) {             [MBProgressHUD hideHUDForView:self.view animated:YES];
            [self mailData:data];
        });
    });
 }

这里在一个background thread中调用了UIImageJPEGRepresentation,伴随着HUD以动画的形式出现。

编译并运行,然后app的交互性应该会变得更好了!

About Screen

App快要完成了,除了一个About页面!如果你已经对如何添加新的view controllers很熟悉,可以跳过以下步骤,但是如果你还想了解更多,就继续下面的步骤吧!

在MaskedCal组Control-click,然后选择New File。选择 iOS Cocoa Touch UIViewController subclass,然后点击Next。输入UIVIewController作为Subclass,确保With XIB for user interface勾选上,然后点击Next,命名类为AboutViewController.m, 然后点击Save。

点击 AboutViewController.xib文件,然后做以下修改:

  • 选择View,然后在Attributes Inspector 设置Orientation为Landscape。
  • 拖动UIImageView,填充整个View然后设定image为About_page_bg.jpg
  • 拖动UILabel到View中,设定X=49, Y=132, Width=382, Height=104, Text Color为White,然后Font设定为Heiti SC Light 16.0,Text Alignment为Center,Lines的#为0(表示没有限制)。
  • 改变text为”Vicki works with her husband to create iPhone and iPad apps. Find more of her wallpapers on her website, vickiwenderlich.com, as well as free art for app developers and tutorials for artists.”

到现在,你的屏幕应该像下面这样:

然后切换到AboutViewController.m文件,根据以下内容替换掉 shouldAutorotateToInterfaceOrienation方法,并且添加一个新方法:

- (void) viewWillAppear:(BOOL)animated{     [self.navigationController setNavigationBarHidden:NO animated:animated];
    [super viewWillAppear:animated];} - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{     return UIInterfaceOrientationIsLandscape(interfaceOrientation);}

很好,现在我们只需要设置我们的main menu来显示它了。打开MainMenuViewController.h文件,然后做以下修改:

// Add to top of file #import "AboutViewController.h" // Add inside @interface AboutViewController *_aboutViewController;// Add after @interface @property (retain) AboutViewController *aboutViewController;

然后选择MainMenuViewController.xib文件,从About button那里control-drag到assistant editor的MainMenuViewController.h文件中。设置Connection type为action,命名为aboutTapped,然后点击Connect。

然后切换到MainMenuViewController.m,并做以下改动:

// Add after @implementation @synthesize aboutViewController = _aboutViewController;// Add inside dealloc [_aboutViewController release];
_aboutViewController = nil;// Add inside aboutTapped if (_aboutViewController == nil) {     self.aboutViewController = [[[AboutViewController alloc] initWithNibName:nil bundle:nil] autorelease];} [self.navigationController pushViewController:_aboutViewController animated:YES];

几乎要完成了 – 还有很小的风格改动要弄。打开 MainWindow.xib文件,然后选择Navigation ControllerNavigation Bar。 选择Tint color,  然后设置R=168, G=215, B=224,提供一种漂亮的蓝绿色,以适应余下的app风格:

同时,选择Navigation Controller Main Menu View ControllerNavigation Item,然后设置Title为Home。就这样!编译运行,然后app完成了 – 伴随着一个About 页面和所有的东西!

现在还可以做什么?

蓝鸥iOS学院有完整的工程文件,包括了以上教程系列的所有代码。现在,你应该对如何在UIKit中结合使用Cocos2D挺熟悉了。有需要的童鞋,可以点击左侧的窗口索要“如何结合使用Cocos2d和UIKit.zip

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值