iOS OpenGL ES在后台应用程序栏可见时屏幕旋转

7
我的应用使用GLKitOpenGL ES渲染3D场景。
一切都很好,除了一个问题。当我在iPad上启动我的应用程序并显示背景应用程序栏(双击“主页”按钮),然后改变设备的方向时,场景会错误地更新(最后呈现的图像只是被拉伸以填充新矩形)。
我找到了原因。当背景应用程序栏出现时,“GLKViewController'spaused会自动设置为YES(应用程序委托接收到-applicationWillResignActive:),直到关闭此栏目为止不会进行任何渲染。
我在苹果指南中找到了一些信息(OpenGL ES Programming Guide for iOS / Implementing a Multitasking-aware OpenGL ES Application),在接收到-applicationWillResignActive:后,应用程序应停止GL渲染或将被终止。所以看起来一切都还好,除了旋转后的错误渲染:)
我检查了一些OpenGL游戏。它们在显示此栏目时也变成“暂停”状态,但以某种方式正确地更新了暂停的场景并进行了设备旋转。他们是如何做到的呢?
2个回答

2

简而言之,您可能只需要将GLKViewcontentMode设置为UIViewContentModeRedraw

首先,我认为您的应用程序实际上并没有进入后台,我认为它只是变得不活动。区分applicationWillResignActiveapplicationDidEnterBackground委托方法之间的区别。假设应用程序仅处于非活动状态,则使用以下内容;如果实际上将应用程序放在后台,请参见下文。

苹果文档表示,在调用applicationWillResignActive时,“应该降低OpenGL ES帧速率”,而不是禁止使用OpenGL ES调用,该情况仅在应用程序进入后台后发生。

这意味着GLKitGLKView/GLKViewController在这方面可能有些过度热衷。要解决此问题,您需要确保:

  1. GLKViewcontentMode设置为UIViewContentModeRedraw
  2. GLKViewdrawRect方法确实在应用程序非活动但帧发生更改时绘制帧,但在应用程序位于后台时不要绘制帧(即使用OpenGL ES调用)。

然而,我假设当应用程序在后台运行时,drawRect方法甚至没有被调用,因此您可能真的不必担心在glkView:drawInRect委托方法中使用OpenGL ES调用。但是,在您的情况下,这个函数没有被调用的原因是视图没有失效。不被使无效的原因有两个:

  1. GLKViewController中的主帧循环会定期使视图失效,该循环由paused属性暂停。
  2. GLKViewcontentMode可能是默认的“UIViewContetModeScaleToFill”。

由于GLKViewdrawRect方法可能根本没有查看paused属性,所以仅更改contentMode可能已经足够。


如果应用程序实际上确实进入后台,则建议采用以下解决方案。由于在后台运行时不允许使用OpenGL ES调用,因此解决方案非常简单:

在进入后台之前,执行所有需要执行的OpenGL ES调用以支持您想要的功能。

即,在applicationWillResignActive中执行以下操作:

  1. 暂停你的游戏循环(通过设置GLKViewControllerpaused属性)
  2. 暂停你的渲染循环(通过设置GLKViewControllerpaused属性)
  3. 获取当前方向状态下的当前帧缓存
  4. 使用与旋转方向状态相对应的帧缓存和视口再次呈现当前游戏状态,并获取该帧缓存

此外,您需要将GLKViewcontentMode设置为UIViewContentModeRedraw,以便在方向更改后调用drawRect方法。

最后,在GLKViewdrawRect方法中,您需要检查paused是否为YESNO。如果是NO,则按照正常方式进行渲染;如果是YES,则使用在applicationWillResignActive中保存的帧缓存之一,并使用常规的UIKit调用将其绘制到视图中。

我不确定这种hackish解决方案与GLKit的集成情况如何,您可能需要进行一些子类化。


我刚在模拟器中测试了我的一个项目,当应用栏打开时,动画确实会暂停,但是当我旋转虚拟iPad时,帧会以新的纵横比重新绘制。这似乎无论内容模式如何都会发生,在iPad iOS 6.1上进行测试。 - wich
主要问题是场景只重新绘制到新的(结果)宽高比。我的场景中的对象取决于视图框架 - 我会重新计算它们的大小和位置,以填充尽可能多的屏幕空间。因此,在动画期间,我希望平滑地重新计算,而不是“起始大小和位置”->“结束大小和位置”-从一个过渡到另一个。如果重复使用相同的帧缓冲区,我们会看到拉伸而不是重新定位。 - kpower
除非你开始使用GLKit进行编程,否则你将无法获得从一个动画到另一个动画的效果,因为帧循环会被暂停,当你的应用程序处于非活动状态时,你不希望(也不应该)让帧循环继续运行,例如在iPhone上接收到来电时也会发生这种情况。唯一能做的就是根据新的视图边界简单地重新绘制场景,在GLKit中可以正确地工作。个人认为设备旋转将切换演示文稿而不是动画它们并不是一个问题。 - wich
感谢您的回答、评论和时间。虽然不是我想要的100%的答案,但却是我期望的答案。:) 现在(至少)我知道没有什么“我漏掉了”的东西。 - kpower

0

实现代理方法(如果您尚未完成)

-(void)applicationWillEnterForeground ,然后从那里取消暂停 GLKViewController

根据我从您的问题中了解的内容,您可以在此方法中保持游戏暂停但调整glView的大小,但是如果没有看到任何代码,也很难真正了解发生了什么。


当进入前台时,GLKViewController 会自动取消暂停。当后台应用程序栏可见时,应用程序处于“后台”状态,此时需要更新 GLKView - kpower
@kpower 我现在想我理解了你的问题。问题是当应用程序正在运行但您双击了主页按钮时。如果您有一个控制渲染循环的计时器,那么是的,在 didenterbackground 中使其无效而不是在 willresignactive 中。它应该像之前一样保持动画。 - Andres Bucci

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接