概括
我正在为 iOS 开发一款相当简单的 2D 塔防游戏。
到目前为止,我一直在专门使用 Core Graphics 来处理渲染。应用程序中根本没有图像文件(还)。我在做相对简单的绘图时遇到了一些重大的性能问题,我正在寻找关于如何解决这个问题的想法,而不是转向 OpenGL。
游戏设置
在高层次上,我有一个 Board 类,它是 的子类UIView
,用于表示游戏板。游戏中的所有其他对象(塔、小兵、武器、爆炸等)也是 的子类UIView
,并在创建时作为子视图添加到 Board。
我将游戏状态与对象内的视图属性完全分开,并且每个对象的状态都会在主游戏循环中更新(由NSTimer
60-240 Hz 触发,取决于游戏速度设置)。该游戏完全可以玩,无需绘制、更新或动画视图。
我使用CADisplayLink
计时器以本机刷新率 (60 Hz) 处理视图更新,该计时器调用setNeedsDisplay
需要根据游戏状态变化更新其视图属性的棋盘对象。板上的所有对象都覆盖drawRect:
以在其框架内绘制一些非常简单的 2D 形状。因此,例如,当一个武器被激活时,它会根据武器的新状态重新绘制自己。
性能问题
在 iPhone 5 上进行测试,板上总共有大约 2 打游戏对象,帧速率明显低于 60 FPS(目标帧速率),通常在 10-20 FPS 范围内。随着屏幕上的更多动作,它从这里开始走下坡路。而在 iPhone 4 上,情况更糟。
使用 Instruments,我确定只有大约 5% 的 CPU 时间用于实际更新游戏状态——其中绝大多数用于渲染。具体来说,该CGContextDrawPath
函数(据我了解,它是矢量路径的光栅化完成的地方)占用了大量的 CPU 时间。有关详细信息,请参阅底部的仪器屏幕截图。
从对 StackOverflow 和其他网站的一些研究来看,Core Graphics 似乎无法满足我的需求。显然,描边矢量路径非常昂贵(尤其是在绘制不透明且 alpha 值 < 1.0 的东西时)。我几乎可以肯定 OpenGL 会解决我的问题,但它的级别很低,我对不得不使用它并不感到兴奋——对于我在这里所做的事情来说,它似乎不是必需的。
问题
是否有任何优化我应该考虑尝试从 Core Graphics 中获得流畅的 60 FPS?
一些想法...
有人建议我考虑将我的所有对象都绘制到一个单独CALayer
的对象上,而不是让每个对象单独存在CALayer
,但我不相信这会根据 Instruments 显示的内容有所帮助。
就个人而言,我有一个理论,即使用CGAffineTransforms
来做我的动画(即drawRect:
一次绘制对象的形状,然后在后续帧中进行变换以移动/旋转/调整其图层的大小)将解决我的问题,因为这些直接基于OpenGL。但我认为这样做不会比直接使用 OpenGL 更容易。
示例代码
为了让您了解我正在做的绘图水平,这是我的drawRect:
一个武器对象(从塔发射的“光束”)的实现示例。
注意:此光束可以“重新定位”并且它穿过整个电路板,因此为简单起见,它的框架与电路板的尺寸相同。然而,板上的大多数其他对象都将其框架设置为可能的最小外接矩形。
- (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);
}
仪器运行
下面是让游戏运行一段时间后的 Instruments:
该类具有上面示例代码部分中显示TDGreenBeam
的确切实现。drawRect: