iOS5学习笔记_GLKit_Part1_王云鹏

来源:互联网 发布:淘宝首页卖家中心在哪 编辑:程序博客网 时间:2024/05/01 04:54

http://blog.csdn.net/volvet/article/details/50529632

Beginning OpenGL ES 2.0 with GLKit Part 1
   英文原文链接:http://www.raywenderlich.com/5223/beginning-opengl-es-2-0-with-glkit-part-1 对应图片参照这个链接地址吧。
    iOS 5提供了一系列新的API,简化了OpenGL的使用。
    新的API集合为GLKit。包括4个部分:
        -GLKView/GLKViewController:这些类抽取出大量的样板(boilerplate)代码,这些代码完成了OpenGL ES 项目的基本配置。
        -GLKEffects:这些类实现了OpenGL ES 1.0公共(common)的shading行为,简化(从1.0)到OpenGL ES 2.0的转化。它们也提供了让光和纹理(lighting and texturing)工作的简单方法。
        -GLMath:在iOS 5之前,每个游戏都需要它们自己的数学库,处理公共的向量和矩阵操作方法。现在有了GLMath,大多数的公共方法都可以从GLMath中获得。
        -GLKTextureLoader:这个类使得加载图像作为OpenGL使用的纹理更加简单。和写一个复杂的方法来处理大量不同的图像格式比,现在加载一个纹理只需要一个简单的方法调用。

     这篇导引的目标是在假定您没有任何相关经验的前提下,让您尽快学会通过GLKit使用OpenGL技术。我们将建立一个简单的app,在屏幕中绘制一个简单的方块,并让它旋转。
     在此过程中,你将学会使用这些新API的基础知识。无论你过去是否曾经使用过OpenGL,甚至你是一个完全的初学者,这都是一篇优秀的GLKit的介绍,
     注意这篇导引和其他的OpenGL ES 2.0导引有重复的部分。这篇导引假设你没有读过其他的导引,但是如果你已经读过,可跳过对应部分。

OpenGL ES 1.0 vs OpenGL ES 2.0

    在我们开始之前,我必须声明这篇导引将重点讲述OpenGL ES 2.0。
    如果你刚刚接触OpenGL ES 编程,下面是OpenGL ES 1.0 和 OpenGL ES 2.0的差别:
        -OpenGL ES 1.0使用一个固定的管线,这是一个好方法(fancy way),说明你在使用内建函数设置光、顶点、颜镜头,和其他。
        -OpenGL ES 2.0使用一个可编程的管线,这是一个好方法,说明你没有使用任何内建方法,你需要自己写全部的东西。
    “OMG!”你可能会想“如果只是增加了额外的工作量,我为啥要还要用OpenGL ES 2.0?!”,尽管确实增加了额外的工作量,使用OpenGL ES 2.0你可以做出一些非常酷的效果。
     效果:见原文吧。


     OpenGL ES 2.0只有iPhone 3GS+,iPod Touch 3G+,和所有的iPad支持。但是这些设备的用户现在已经是主流了,所以值得使用。
     OpenGL ES 2.0和OpenGL ES 1.0比有更高的学习曲线,但是现在使用GLKit学习曲线将更加简单,因为GLKeffects和GLKMath APIs使你更容易做许多OpenGL ES 1.0支持的事情。
     如果你是OpenGL 编程新手,最好是直接学习OpenGL ES 2.0,而不必尝试先学习1.0,再升级到2.0,特别是现在有了GLKit。这篇导引将帮助你开始学习基础知识。 

Getting Started

    好,让我们开始!
    在Xcode中创建一个新的项目,并选择iOS\Application\Empty Application Template。我们选择Empty Application template(不是OpenGL Game template), so you can put everything together from scratch and get a better idea how everything fits together.(直译没想好怎么译,把原文贴着,这是我的理解:这样你可以一步一步地学习每样东西,更好地理解它们是怎样协同工作的。)
    设置产品名称HelloGLKit,确保Device Family被设置为iPhone,确保"Use Automatic Reference Counting"被选中,其他选项不选,然后点Next按钮。选择一个文件夹保存你的项目并点Create按钮。
    如果你现在运行app,你将看到一个空的window:
    
    项目此时基本不包含任何代码,但是让我们快速的看一下,how it all fits together(它们是如何协同工作的)。如果你学习过Storyboard导引,这里将是一个好的回顾。
    打开main.m,你将看到app开始运行的时候,第一个函数被调用的函数:
int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
    最后一个参数告诉UIApplication创建这个类的实例并用它作为代理,在本例中是AppDelegate。
    切换到唯一的类,AppDelegate.m,看一下app启动时调用的方法:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [UIWindow alloc] initWithFrame:[UIScreen mainScreen] bounds];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}
     通过编程的方式创建app的主window,并使其可见。好,就这样!About as “from scratch” as you can get.(这句不会译,大概是:让我们从头开始做起)

     Introducing GLKView

    开始学习OpenGL ES 2.0,第一件事我们需要做的是为window增加一个subview,该subview通过OpenGL绘制。如果你曾经编写过OpenGL ES 2.0代码,你知道有很多样板文件代码,使其工作-比如创建渲染缓冲区和帧缓冲区,等。
    但是现在可以非常方便地使用新的GLKit类调用GLKView!任何时候你想在一个view中使用OpenGL进行渲染,你只须简单的增加一个GLKView(一个普通的UIView子类)并配置一些属性。
    之后你可以设置一个类作为GLKView的代理,当需要绘制时,将会调用代理类的方法。在该方法中你可以加入你的OpenGL命令!
    让我们看一下这是如何工作的。首先第一件事-你需要增加一些框架到你的项目,才可以使用GLKit。在项目导航器中选择你的HelloGLKit项目,选择HelloGLKit目标,选择Build Phases,展开Link Binary With Libraries section,并点+按钮。从列表中,选择如下的框架,并点Add:
QuartzCore.framework
OpenGLES.framework
GLKit.framework
原文此处有截图,可参考
 
    
    切换到AppDelegate.h,在顶部import GLKit的头文件:
#import <GLKit/GLKit.h>
    接下来切换到AppDelegate.m,并修改application:didFinishLaunchingWithOptions以增加一个GLKView作为main window的subview:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [UIWindow alloc] initWithFrame:[UIScreen mainScreen] bounds];

    EAGLContext * context = [EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; // 1
    GLKView *view = [GLKView alloc] initWithFrame:[UIScreen mainScreen] bounds]; // 2
    view.context = context; // 3
    view.delegate = self; // 4
    [self.window addSubview:view]; // 5

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}
    增加了标记注释的行是新增加的代码行-让我们逐行看一下。
1. Create a OpenGL context. 创建一个OpenGL 上下文。
    使用OpenGL做任何事前,你都需要先创建一个EAGLContext。
    iOS使用OpenGL进行绘制时需要一些信息,这些信息都由EAGLContext管理。这和你使用一个Core Graphics上下文差不多。
    当你创建一个上下文时,你需要定义要使用的API版本。这里你定义成使用OpenGL ES 2.0(比如程序运行在iPhone3G上的时候)。如果获取不到,app将退出。
2. Create a GLKView.创建一个GLKView。
    这创建了一个新的GLKView的实例,并使它和整个window一样大。
3. Set the GLKView’s context.设置GLKView上下文。
    当你创建一个GLKView的时候,你需要告诉它要使用的OpenGL 上下文,所以配置成我们刚刚创建的context(注释1)。
4. Set the GLKView’s delegate.设置GLKView的代理。
    这设置当前类(AppDelegate)作为GLKView的代理。这意味着无论何时该视图需要重绘时,都会调用代理的glkView:drawInRect方法。我们将在AppDelegate中马上实现这些,增加一些基本的OpenGL命令把屏幕绘制成红色。
5. Add the GLKView as a subview.把GLKView作为subview添加。
    这行代码把GLKView作为main window的subview添加。
    
    因为我们把AppDelegate设置为GLKView的代理,所以我们需要让它实现GLKiewDelegate协议。让我们切换到AppDelegate.h并修改@interface 行如下:
@interface AppDelegate : UIResponder <UIApplicationDelegate, GLKViewDelegate>
    还差一步!切换回AppDelegate.m,在@end之前增加如下的代码:
#pragma mark - GLKViewDelegate

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {

    glClearColor(1.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

}

    第一行调用glClearColor定义用来清理屏幕的RGB颜色和alpha(透明度)值。这里我们设置红色。
    第二行调用glClear实际执行清理操作。记住缓冲区有不同的类型,比如渲染/颜色缓冲区,以及其他我们现在还没有使用过的缓冲区,像深度或者模板缓冲区。
    就这样!编译并运行,只使用了7行代码我们就使用OpenGL对屏幕进行了渲染。
    
    如果你完全是OpenGL的新手,可能会没什么感觉,但是之前用过的,将非常高兴地发现其简单性。

GLKView Properties and Methods

    我们这里只设置了GLKView一些属性(上下文和代理),但是我要提一下GLKView的其他属性和方法,它们后续将对你非常有用。
    这是一个可选的参考部分,对相关属性和方法进行说明。如果你想继续编码,可以跳到下一段。
    context and delegate
    我们已经在上一段中进行了讲述,这里不再重复。
    drawableColorFormat
    你的OpenGL上下文有一个缓冲区,它用以存储将在屏幕中显示的颜色。你可以使用其属性来设置缓冲区中每个像素的颜色格式。
    缺省值是GLKViewDrawableColorFormatRGBA8888,即缓冲区的每个像素使用8个bit(所以每个像素4个字节)。这非常好,因为它给了你提供了最广泛的颜色范围,让你的app看起来更好。
    但是如果你的app允许更小范围的颜色,你可以设置为GLKViewDrawableColorFormatRGB565,从而使你的app消耗更少的资源(内存和处理时间)。
    drawableDepthFormat
    你的OpenGL上下文还可以(可选地)有另一个缓冲区,称为深度缓冲区。这帮助我们确保更接近观察者的对象显示在远一些的对象的前面(意思就是离观察者近一些的对象会挡住在它后面的对象)。
    其缺省的工作方式是:OpenGL把接近观察者的对象的所有像素存储到深度缓冲区,当开始绘制一个像素时,它(OpenGL)首先检查深度缓冲区,看是否已经绘制了更接近观察者的什么东西,如果是则忽略它(要绘制的像素,就是说,在绘制一个像素之前,看看前面有没有挡着它的东西,如果有那就不用绘制了)。否则,把它增加到深度缓冲区和颜色缓冲区。
    你可以设置这个属性,以选择深度缓冲区的格式。缺省值是GLKViewDrawableDepthFormatNone,意味着完全没有深度缓冲区。
    但是如果你要使用这个属性(一般用于3D游戏),你应该选择GLKViewDrawableDepthFormat16或GLKViewDrawableDepthFormat24。这里的差别是使用GLKViewDrawableDepthFormat16将消耗更少的资源,但是当对象非常接近彼此时,你可能存在渲染问题()。
   drawableStencilFormat
   你的OpenGL上下文的另一个可选的缓冲区是stencil(模板)缓冲区。它帮助你把绘制区域限定到屏幕的一个特定部分。它还用于像影子一类的事物=比如你可以使用stencil缓冲区确保影子投射到地板。
   缺省值是GLKViewDrawableStencilFormatNone,意思是没有stencil缓冲区,但是你可以通过设置其值为GLKViewDrawableStencilFormat8(唯一的其他选项)使能它。
    drawableMultisample
    这是你可以设置的最后一个可选缓冲区,对应的GLKView属性是multisampling。如果你曾经尝试过使用OpenGL画线并关注过"锯齿壮线",multisampling就可以帮助你处理。
    Basically what it does is instead of calling the fragment shader one time per pixel, it divides up the pixel into smaller units and calls the fragment shader multiple times at smaller levels of detail. It then merges the colors returned, which often results in a much smoother look around edges of geometry.
    以前对于每个像素,都会调用一次fragment shader(片段着色器),drawableMultisample基本上替代了这个工作,它将一个像素分成更小的单元,并在更细微的层面上多次调用fragment shader。之后它将返回的颜色合并,生成更光滑的几何边缘效果。
    要小心此操作,因为它需要占用你的app的更多的处理时间和内存。缺省值是GLKViewDrawableMultisampleNone,但是你可以通过设置其值GLKViewDrawableMultisample4X为来使能它。
    drawableHeight/drawableWidth
    这些“只读”属性代表各种各样的缓冲区的整形的高度和宽度值(高度还理解,宽度啥意思,不明白。。。)。根据视图的边界和contentSize的变化-缓冲区自动调整这些值大小。
    snapshot
    这是一种从视图当前上下文中获得UIImage的便捷方法。
    bindDrawable
    OpenGL还有另一个缓冲区-帧缓冲区,这基本上是前面提到的其他缓冲区的集合(颜色,深度,stencil等缓冲区)。
    在你的glkView:drawInRect方法被调用之前,GLKit将在幕后绑定到为你配置好的帧缓冲区。但是如果你的游戏需要转到一个不同的帧缓冲区去执行其他的渲染操作(比如,你要渲染另一个纹理),你可以使用方法bindDrawable通知GLKit重新绑定回为你配置好的帧缓冲区。
    deleteDrawable
    GLKView和OpenGL为这些缓冲区使用大量的内存,如果你的GLKView不可见,你会发现在其可见之前临时释放这些内存是很有用的。如果你要这么做,就使用这个方法!
    下次视图被绘制时,GLKView将在幕后自动重新分配内存。很方便不是吗?
    enableSetNeedsDisplay and display
    我不想破坏惊喜-我们将在下一段解释这些。

Updating the GLKView

    让我们试着定期更新我们的GLKView,就像我们在游戏中一样。我们把屏幕从红色跳转到黑色怎么样,就像红色警报一样!
    到AppDelegate.m的顶部,修改@implementation行,增加2个私有变量:
  @implementation AppDelegate {
    float _curRed;
    BOOL _increasing;
}
    在application:didFinishLaunchingWithOptions中初始化这些变量:
_increasing = YES;
_curRed = 0.0;
   之后回到glkView:drawInRect方法,按照下面这样进行更新:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {

    if (_increasing) {
        _curRed += 0.01;
    } else {
        _curRed -= 0.01;
    }
    if (_curRed >= 1.0) {
        _curRed = 1.0;
        _increasing = NO;
    }
    if (_curRed <= 0.0) {
        _curRed = 0.0;
        _increasing = YES;
    }

    glClearColor(_curRed, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

}
    每次glkView:drawInRect被调用时,它都根据是_increasing增加还是减少(YES/NO)来更新一点(0.01)_curRed的值。注意这段代码不是完美的,因为它没考虑调用glkView:drawInRect的时间间隔。这意味着根据调用glkView:drawInRect的速度(即多长时间调用一次),动画会时快时慢。我们后面再讨论解决这一问题的方法。
    编译并运行,等待一分钟,什么都没发生!
    缺省情况下,GLKView只在需要的时候才更行自己,比如当视图第一次显示,大小改变,或者类似的场景。然而对于游戏编程,我们经常需要重新绘制每一
    我们可以将enableSetNeedsDisplay设置为false,来关闭GLKView的这一缺省行为。之后我们可以调用GLKView的display方法来控制什么时候重新绘制。
    理想情况下我们更希望同步屏幕的刷新率和OpenGL的渲染频率。
    幸运的是,苹果提供了一种简单方法来完成这项工作,使用CADisplayLink!它非常容易使用,让我们来看看。首先增加这个import在AppDelegate.m的顶部:
    #import <QuartzCore/QuartzCore.h>
    之后,增加这些行代码到application:didFinishLaunchingWithOptions:
view.enableSetNeedsDisplay = NO;
CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; // 加入循环
    然后增加一个新的渲染函数:
- (void)render:(CADisplayLink*)displayLink {
    GLKView * view = [self.window.subviews objectAtIndex:0];
    [view display]; // 间接调用drawInRect

    编译并运行,你将看到一个很酷的闪动的红色警报效果!

Introducing GLKViewController

    你理解我们前面刚刚写的代码吗?你可以忘记它了,因为有更简单的方法-使用GLKViewController。
    我先给你展示怎么使用普通的GLKView的原因是让你在使用(文艺的?哈~)GLKViewController前,先理解其工作原理。GLKViewController使你不必再写那些代码了,另外,它还增加了一些新的特性,不过这些特性必须编码才能使用。
    所以让我们来尝试一下GLKViewController吧,像这样修改你的application:didFinishLaunchingWithOptions方法:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [UIWindow alloc] initWithFrame:[UIScreen mainScreen] bounds];

    EAGLContext * context = [EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    GLKView *view = [GLKView alloc] initWithFrame:[UIScreen mainScreen] bounds];
    view.context = context;
    view.delegate = self;
    //[self.window addSubview:view];

    _increasing = YES;
    _curRed = 0.0;

    //view.enableSetNeedsDisplay = NO;
    //CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)];
    //[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 

    GLKViewController * viewController = [GLKViewController alloc] initWithNibName:nil bundle:nil]; // 1
    viewController.view = view; // 2
    viewController.delegate = self; // 3
    viewController.preferredFramesPerSecond = 60; // 4
    self.window.rootViewController = viewController; // 5

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

    您可以直接删除注释掉的行,我刚把他们注释掉,这样更容易看出哪些代码是不再需要的。这里还有4行新代码(用注视标记了):
1. Create a GLKViewController.
    这编程创建了一个新的 GLKViewController实例。在这种情况下,没有关联XIB文件。
2. Set the GLKViewController’s view.
     GLKViewController的root view应该是一个GLKView,所以我们把它设置成前面刚刚创建的那个。
3. Set the GLKViewController’s delegate.
    我们设置当前类(AppDelegate)作为GLKViewController的代理,这样每一帧GLKViewController都会通知我们(游戏停止时也会通知我们),让我们有机会执行游戏逻辑(这是GLKViewController的一个不错的内建属性,我们稍后会介绍)。
    原文:We set the current class (AppDelegate) as the delegate of the GLKViewController. This means that the GLKViewController will notify us each frame so we can run game logic, or when the game pauses (a nice built-in feature of GLKViewController we’ll demonstrate later).

4. Set the preferred FPS.
    GLKViewController在1秒内会多次调用你的draw方法,设置调用的次数来让GLKViewController知道你期待被调用的频率。当然,如果你的游戏花了很多时间对帧进行渲染,实际的调用次数将你设置的值。
    缺省值是30FPS。苹果的指导意见是把这个值设置成你的app能够稳定支持的帧率,以保持一致,看起来不卡。这个app非常简单,可以按照60FPS运行,所以我们设置成60FPS。
    和FYI一样(FYI是神马啊?),如果你想知道OS实际尝试调用你的update/draw方法的次数,可以检查只读属性framesPerSecond。
5. Set the rootViewController. 
    我们让这个试图控制器作为第一个要显示的东西,所以我们将其作为window的rootViewController加进来。注意我们不再需要手工为window增加subview了,因为它是GLKViewController的root view。
    注意我们不再需要运行循环渲染的代码来告诉GLView刷新每一帧了-GLKViewController在后台为我们做了这一切。所以接下来要把render方法注释掉。
    别忘了我们把GLKViewController’s的代理设置为当前的类(AppDelegate),所以要实现GLKViewControllerDelegate协议。切换到AppDelegate.h文件并替换如下:
@interface AppDelegate : UIResponder <UIApplicationDelegate, GLKViewDelegate, GLKViewControllerDelegate>
    最后一步是更新方法glkView:drawInRect,并增加GLKViewController的回调实现glkViewControllerUpdate:
#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)glkViewControllerUpdate:(GLKViewController *)controller {
    if (_increasing) {
        _curRed += 1.0 * controller.timeSinceLastUpdate;
    } else {
        _curRed -= 1.0 * controller.timeSinceLastUpdate;
    }
    if (_curRed >= 1.0) {
        _curRed = 1.0;
        _increasing = NO;
    }
    if (_curRed <= 0.0) {
        _curRed = 0.0;
        _increasing = YES;
    }
}

    注意,我们把代码从draw方法移到update方法中。
    还要注意,我们把颜色的计数值由硬编码的值改为计算出的值-基于上次更新时的时间进行计算。这样可以很好地保证动画被匀速地处理,不必关心帧率的问题。(个人理解:原来是基于次数的,现在是基于时间的,基于次数的调用时间间隔是不确定的,因此颜色变化并不流畅,而基于时间的则是根据逝去时间来计算当前应显示的颜色,因此是流畅的)。
    这是GLKViewController为你做的另一件方便的事情!我们不必写特殊的代码去保存上次更新时的次数-GLKViewController已经为你保存了。还有一些基于时间的属性,我们后面再讨论。
    (先调用drawInRect方法 然后再调用 glkViewController的 Update 方法。调用频率由系统参考viewController.preferredFramesPerSecond,结合当前运行情况决定。)
GLKViewController and Storyboards

    到目前为止,我们手工创建了GLKViewController和GLKView,因为这是向您介绍它们工作原理的简单方法。但是在实际的app中你可能不想这样做,更好的办法是利用Storyboards的力量,这样你就可以在你app体系结构的任何地方包含这个视图控制器。
    所以让我们做一点重构来完成它。首先,让我们创建一个GLKViewController的子类,来包含我们app的逻辑。所以使用 iOS\Cocoa Touch\UIViewController子类模板创建一个新的文件,命名为HelloGLKitViewController,作为GLKViewController的子类(you can type this in even though it’s not in the dropdown-虽然下拉菜单里没有,但是你可以手工输入GLKViewController。学了个单词dropdown,即下拉菜单,呵呵)。确保Targeted和iPad and With XIB都没有选中,然后创建文件。
    打开HelloGLKitViewController.m,增加类的私有成员,包含我们需要的实例变量,增加一个存储上下文的属性。
@interface HelloGLKitViewController () {
    float _curRed;
    BOOL _increasing;

}
@property (strong, nonatomic) EAGLContext *context;

@end

@implementation HelloGLKitViewController 
@synthesize context = _context;
    之后实现viewDidLoad 和 viewDidUnload方法:
- (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;
}
    在viewDidLoad中,我们创建了OpenGL ES 2.0上下文(和上次在App Delegate做的一样)并保存。我们的root view是一个GLKView(我们知道是因为我们在Storyboard中做了配置-就是我们需要在Storyboard中把view的类名称设置成GLKView),所以我们先做一下类型转化(cast)。之后我们将其上下文设置为OpenGL上下文。
    注意我们不必把视图控制器设置为视图的代理-GLKViewController在幕后自动完成这项工作。
    在viewDidUnload中,我们只需做反向清理工作。我们必须确保这里没有遗漏的对上下文的引用,所以我们先检查当前上下文是不是我们的上下文,如果是的话,将其设置为nil。我们还将我们的引用也设置为nil。
    在文件的底部,增加glkView:drawInRect和update回调的视线,和前面相似:
#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;
    }
}

    注意update方法只是被命名为update,因为现在我们在GLKViewCotroller子类内部,我们只覆盖这个方法,不必再实现前面提到的GLKViewCotroller代理方法。timeSinceLastUpdate也可以直接通过self访问,不需要使用视图控制器。
    做好这些后,让我们创建Storyboard。使用iOS\User Interface\Storyboard模板创建一个新的文件,设备族选择iPhone,并保存为MainStoryboard.storyboard。
    打开MainStoryboard.storyboard,从对象面板拽一个试图控制器到网格区。选择试图控制器,在Identity Inspector中把类设置为HelloGLKitViewController:
 此处有图,参考原文。 
    还有,在视图控制器中选择视图,在Identity Inspector中把类设置为GLKView。
    为了在启动的时候运行此Storyboard,打开HelloGLKit-Info.plist,按住ctrl,点空白区域,选择增加行。从下拉菜单中选择Main storyboard file base name,并输入MainStoryboard。
    这基本上完成了我们要做的所有事情,但是我们仍有一些就代码在AppDelegate中需要清理掉。先从AppDelegate.m中删除_curRed和_increasing实例变量。还要删除glkView:drawInRect和glkViewControllerUpdate方法。
    然后删除application:didFinishLaunchingWithOptions的所有东西如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    return YES;
}
    修改AppDelegate @interface,去掉GLKit的两个代理,因为我们不再使用他们了。
@interface AppDelegate : UIResponder <UIApplicationDelegate>
    好了!编译,运行,你将看到红色警报的效果依然。
     到这里,你已经非常接近了使用Storyboard创建OpenGL Game 模板(except it has a lot of other code in there you can just delete if you don’t need it)。以后当你创建一个新的OpenGL项目的时候,你可以自由选择那种方式来节省一点时间,但是现在你知道它是如何从基础开始工作的了。
    
GLKViewController and Pausing

    现在我们已经完美地配置了一个定制GLKViewController子类,让我们来搞一下GLKViewController的新特性-停止!
    为了看它如何工作的,增加下面的代码到HelloGLKitViewController.m的底部:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    self.paused = !self.paused;
}
    编译并运行app,现在任何时候你的点击都会停止动画!在幕后,GLKViewController停止调用你的update方法和你的draw方法。这是在游戏中实现暂停按钮的一个非常简单的方法。
    除此之外,GLKViewController有一个pauseOnWillResignActive属性,缺省被设置为YES。这表示当用户按home键的时候或者被中断的情况,比如接到电话,你的游戏将自动暂停!类似的,还有一个resumeOnDidBecomeActive属性缺省被设置为YES,当用户返回到你的app时,会自动结束暂停状态。非常简单!
    至此我们差不多介绍了GLKViewController的全部属性,除了我们前面讨论过的额外的时间信息属性:
    timeSinceLastDraw:给你提供上次调用draw方法后的逝去时间。注意这和timeSinceLastUpdate不同,因为你的update方法会花费时间!
    timeSinceFirstResume:给你提供从GLKViewController第一次恢复(resumed)发送更新(update)消息后的逝去时间。如果GLKViewController是第一个显示的东西,这通常是你的app启动时间。
    timeSinceLastResume:给你提供从GLKViewController最后一次恢复发送更新消息后的逝去时间。这通常是你的游戏上次结束暂停状态的时间。

    让我们增加一些代码来试试它们。增加如下的代码到touchesBegan方法的顶部:
NSLog(@"timeSinceLastUpdate: %f", self.timeSinceLastUpdate);
NSLog(@"timeSinceLastDraw: %f", self.timeSinceLastDraw);
NSLog(@"timeSinceFirstResume: %f", self.timeSinceFirstResume);
NSLog(@"timeSinceLastResume: %f", self.timeSinceLastResume);

    多多尝试,这样你就会熟悉它们是如何工作的。就像你看到的,这些非常方便。

0 0
原创粉丝点击