Iphone iOS上的核心图形性能 总结

Iphone iOS上的核心图形性能 总结,iphone,ios,performance,core-graphics,quartz-2d,Iphone,Ios,Performance,Core Graphics,Quartz 2d,我正在为iOS开发一款相当简单的2D塔防游戏 到目前为止,我一直只使用核心图形来处理渲染。应用程序中根本没有图像文件(尚未)。我在做相对简单的绘图时遇到了一些重要的性能问题,我正在寻找解决方法,除了转到OpenGL 游戏设置 在高层,我有一个Board类,它是UIView的一个子类,用来表示游戏板。游戏中的所有其他对象(塔、爬物、武器、爆炸等)也是UIView的子类,在创建时作为子视图添加到棋盘上 我将游戏状态与对象内的视图属性完全分开,每个对象的状态在主游戏循环中更新(由NSTimer以60-

我正在为iOS开发一款相当简单的2D塔防游戏

到目前为止,我一直只使用核心图形来处理渲染。应用程序中根本没有图像文件(尚未)。我在做相对简单的绘图时遇到了一些重要的性能问题,我正在寻找解决方法,除了转到OpenGL

游戏设置 在高层,我有一个Board类,它是
UIView
的一个子类,用来表示游戏板。游戏中的所有其他对象(塔、爬物、武器、爆炸等)也是
UIView
的子类,在创建时作为子视图添加到棋盘上

我将游戏状态与对象内的视图属性完全分开,每个对象的状态在主游戏循环中更新(由
NSTimer
以60-240 Hz的频率触发,具体取决于游戏速度设置)。游戏完全可以玩,而无需绘制、更新或设置视图动画

我使用
CADisplayLink
定时器以本机刷新率(60 Hz)处理视图更新,该定时器在需要根据游戏状态的变化更新视图属性的棋盘对象上调用
setNeedsDisplay
。电路板上的所有对象覆盖
drawRect:
,在其框架内绘制一些非常简单的二维图形。因此,例如,当武器设置动画时,它将根据武器的新状态重新绘制自身

性能问题 在iPhone5上进行测试时,游戏板上总共有大约24个游戏对象,帧速率明显低于60 FPS(目标帧速率),通常在10-20 FPS的范围内。随着屏幕上更多的动作,它从这里开始走下坡路。在iPhone4上,情况更糟

使用仪器,我确定只有大约5%的CPU时间用于实际更新游戏状态——其中绝大多数用于渲染。具体来说,
CGContextDrawPath
函数(据我所知,矢量路径的光栅化就是在这里完成的)占用了大量的CPU时间。有关更多详细信息,请参见底部的Instruments屏幕截图

从StackOverflow和其他网站的一些研究来看,核心图形似乎不能满足我的需要。显然,笔划向量路径非常昂贵(尤其是在绘制不透明且alpha值小于1.0的对象时)。我几乎可以肯定OpenGL会解决我的问题,但它的级别非常低,我对使用它并不感到兴奋——我在这里做的事情似乎不需要它

问题 我是否应该考虑进行任何优化,以获得流畅的60 FPS的核心图形?

一些想法。。。

有人建议我把所有的对象都画成一个单独的代码> Calayer-<代码>,而不是每个对象都有自己的代码<代码> Calaye,但是我不相信这将有助于基于什么工具显示。 就个人而言,我有一个理论,使用

cGraffineTransforms
制作动画(即在
drawRect:
中绘制对象的形状一次,然后在后续帧中进行变换以移动/旋转/调整其层的大小)可以解决我的问题,因为这些都是直接基于OpenGL的。但我不认为这比直接使用OpenGL更容易做到

示例代码 为了让您了解我正在进行的绘图级别,下面是我的一个武器对象的
drawRect:
实现示例(从塔楼发射的“光束”)

注意:此梁可以“重定目标”,并且它可以穿过整个电路板,因此为简单起见,其框架的尺寸与电路板的尺寸相同。然而,电路板上的大多数其他对象都将其框架设置为可能的最小外切矩形

仪器运行 让游戏运行一段时间后,我们来看一下仪器:

TDGreenBeam类具有上面示例代码部分所示的确切的
drawRect:
实现。


很有可能,你已经透支了。也就是说,提取冗余信息

因此,您需要将视图层次分解为多个层(正如您还提到的)。仅更新/绘制所需内容。层可以缓存合成的中间层,然后GPU可以快速合成所有这些层。但是您需要小心,只绘制需要绘制的内容,并且只使实际更改的图层区域无效

调试它:打开“Quartz调试”并启用“Flash相同屏幕更新”,然后在模拟器中运行应用程序。你要尽量减少那些彩色闪光


一旦完成了超图,考虑一下在二次线程上可以呈现的内容(例如<代码> Calay.DracabySimule),或者如何接近合成中间表示(例如缓存)或光栅化不变层/ ReCts。在执行这些更改时,请小心测量成本(例如内存和CPU)。

核心图形工作由CPU执行。然后将结果推送到GPU。调用
setNeedsDisplay
时,表示需要重新进行绘图工作

假设您的许多对象保持一致的形状,只需在父视图上调用
setNeedsLayout
,然后将该视图的
layoutSubviews
中的最新对象位置推送到
中心
属性。仅仅调整位置并不会导致一件东西需要重新绘制;合成器将简单地要求GPU在不同的位置复制它已经拥有的图形

对于游戏来说,一个更通用的解决方案可能是忽略
中心
边界
框架
,而不是初始设置。辛普
- (void)drawRect:(CGRect)rect
{
    CGContextRef c = UIGraphicsGetCurrentContext();

    // Draw beam
    CGContextSetStrokeColorWithColor(c, [UIColor greenColor].CGColor);
    CGContextSetLineWidth(c, self.width);
    CGContextMoveToPoint(c, self.origin.x, self.origin.y);
    CGPoint vector = [TDBoard vectorFromPoint:self.origin toPoint:self.destination];
    double magnitude = sqrt(pow(self.board.frame.size.width, 2) + pow(self.board.frame.size.height, 2));
    CGContextAddLineToPoint(c, self.origin.x+magnitude*vector.x, self.origin.y+magnitude*vector.y);
    CGContextStrokePath(c);

}