今天在新的一章里面,我们将对GLKViewController类进行封装..前一章,我们人工的创建了GLKViewController和GLKView,因为这样可以帮助我们知道他们是怎样工作的.时机项目中我们可能不想什么事情都自己手工做,我们向充分的利用storyboard,我们实际上可以在任何地方增加一个controllor.
So let’s do a little refactoring to accomplish that.First, let’s create a subclass of GLKViewController that we can use to containour app’s logic. So create a new file with the iOS\Cocoa Touch\UIViewControllersubclass template, name the class HelloGLKitViewController, as a subclass ofGLKViewController (you can type this in even though it’s not in the dropdown).Make sure Targeted for iPad and With XIB for user interface are bothunselected, and create the file.
Open up HelloGLKitViewController.m, and start by adding aprivate category on the class to contain the instance variables we need, and anew property to store the context:
interface HelloWorldGLViewController () { float _curRed; BOOL _increasing; // 自己定义的私有成员 } @property (strong,nonatomic) EAGLContext *context; @end
@implementation HelloWorldGLViewController @synthesize context = _context; |
Then implement viewDidLoad and viewDidUnload as thefollowing:
- (void)viewDidLoad { [super viewDidLoad];
self.context = [[EAGLContextalloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; if (!self.context) { NSLog(@"Failed to create ES context"); }
GLKView *view = (GLKView *)self.view; view.context = self.context; }
- (void)viewDidUnload { [super viewDidUnload];
if ([EAGLContextcurrentContext] == self.context) { [EAGLContext setCurrentContext:nil]; } self.context =nil; } |
In viewDidLoad, we create an OpenGL ES 2.0 context (sameas we did last time in the App Delegate) and squirrel it away. Our root view isa GLKView (we know this because we set it up this way in the Storyboardeditor), so we cast it as one. We then set its context to the OpenGL context wejust created.
Note that we don’t have to set the view controller as theview’s delegate – GLKViewController does this automatically behind the scenes.
In viewDidUnload, we just do the opposite to clean up. Wehave to make sure there’s no references left to our context, so we check to seeif the current context is our context, and set it to nil if so. We also clearout or reference to it.
At the bottom of the file, add the implementations of theglkView:drawInRect and update callbacks, similar to before:
#pragma mark - GLKViewDelegate - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { glClearColor(_curRed, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); }
#pragma mark - GLKViewControllerDelegate - (void)update { if (_increasing) { _curRed += 1.0 * self.timeSinceLastUpdate; } else { _curRed -= 1.0 * self.timeSinceLastUpdate; }
if (_curRed >= 1.0) { _curRed = 1.0; _increasing = NO; }
if (_curRed <= 0.0) { _curRed = 0.0; _increasing = YES; } } |
Note that the update method is called plain “update”,because now that we’re inside the GLKViewCotroller we can just override thismethod instead of having to set a delegate. Also the timeSinceLastUpdate isaccess via “self”, not a passed in view controller.
下面我们来创建storyboard..
使用模板创建一个名字是MainStoryboard.storyboard.的storyboard.打开之,从右下角的objects panel中拖进来一个ViewController.在那一列中选择View Controller,设置他的类型为HelloGLKitViewController
同样的方法选中View,设置他的class为GLKView.
为了能够在程序起来的时候就把storyboard跑起来,打开HelloGLKit-Info.plist文件,按住ctrl键的同时鼠标单击,选择AddRow.选中Main storyboard file base name, and enterMainStoryboard.
工作基本完成,清理一下AppDelegate类. AppDelegate.m中去掉成员变量,去掉两个方法 glkView:drawInRect and glkViewControllerUpdate methods.
清理application:didFinishLaunchingWithOptions方法, so it looks like this:
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { return YES;} |
修改声明,不用实现那两个接口了.程序应该可以跑起来.
GLKViewController 可以暂停呦
现在我们自己创建了GLKViewController的子类,我们试着搞一搞GLKViewController的一些小特性---暂停.在HelloGLKitViewController.m中增加一句代码. - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { self.paused= !self.paused;} |
编译运行,发现当我们触摸屏幕的时候动画会停止.实现暂停按钮真的很简单.
除此之外, GLKViewController有一个pauseOnWillResignActive属性,默认设置是YES.也就是说:当用户点击了Home键或者收到终端命令(例如来电话的时候)我们的游戏会自动暂停.同样还有一个resumeOnDidBecomeActive属性,默认也是设置为YES,意味着:当用户重新回到游戏的时候会自动解除暂停.多方便呀.
We’ve covered almost every property of GLKViewControllerby now, except for the extra time info properties I discussed earlier:
· timeSinceLastDraw givesyou the elapsed time since the last call to the draw method. Note this might bedifferent than timeSinceLastUpdate, since your update method takes time! :]
· timeSinceFirstResume givesyou the elapsed time since the first time GLKViewController resumed sendingupdates. This often means the time since your app launched, if yourGLKViewController is the first thing that shows up.
· timeSinceLastResume givesyou the elapsed time since the last time GLKViewController resumed sendingupdates. This often means the last time your game was unpaused.
Let’s add some code to try these out. Add the followingcode to the top of touchesBegan:
NSLog(@"timeSinceLastUpdate: %f", self.timeSinceLastUpdate); NSLog(@"timeSinceLastDraw: %f", self.timeSinceLastDraw); NSLog(@"timeSinceFirstResume: %f", self.timeSinceFirstResume); NSLog(@"timeSinceLastResume: %f", self.timeSinceLastResume); |
Play around with it so you’re familiar with how theywork. As you can see, these can be pretty convenient!
// // HelloWorldGLViewController.m // HelloOpenGL // // Created by stephen.xing on 12/6/14. // Copyright (c) 2014 IDREAMSKEY. All rights reserved. // #import "HelloWorldGLViewController.h" @interface HelloWorldGLViewController () { float _curRed; BOOL _increasing; // 自己定义的私有成员 } @property (strong, nonatomic) EAGLContext *context; @end @implementation HelloWorldGLViewController @synthesize context = _context; - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self; } - (void)viewDidLoad { [super viewDidLoad]; self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; if (!self.context) { NSLog(@"Failed to create ES context"); } GLKView *view = (GLKView *)self.view; view.context = self.context; } - (void)viewDidUnload { [super viewDidUnload]; if ([EAGLContext currentContext] == self.context) { [EAGLContext setCurrentContext:nil]; } self.context = nil; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ #pragma mark - GLKViewDelegate - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { glClearColor(_curRed, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); } #pragma mark - GLKViewControllerDelegate - (void)update { if (_increasing) { _curRed += 1.0 * self.timeSinceLastUpdate; } else { _curRed -= 1.0 * self.timeSinceLastUpdate; } if (_curRed >= 1.0) { _curRed = 1.0; _increasing = NO; } if (_curRed <= 0.0) { _curRed = 0.0; _increasing = YES; } } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"timeSinceLastUpdate: %f", self.timeSinceLastUpdate); NSLog(@"timeSinceLastDraw: %f", self.timeSinceLastDraw); NSLog(@"timeSinceFirstResume: %f", self.timeSinceFirstResume); NSLog(@"timeSinceLastResume: %f", self.timeSinceLastResume); self.paused = !self.paused; } @end
// // HelloWorldGLViewController.h // HelloOpenGL // // Created by stephen.xing on 12/6/14. // Copyright (c) 2014 IDREAMSKEY. All rights reserved. // #import <GLKit/GLKit.h> @interface HelloWorldGLViewController : GLKViewController @end