这是为 iPhone 游戏制作游戏循环的好方法吗?
Posted
技术标签:
【中文标题】这是为 iPhone 游戏制作游戏循环的好方法吗?【英文标题】:Is this a good way to do a game loop for an iPhone game? 【发布时间】:2010-03-11 19:14:59 【问题描述】:我是 iPhone 开发新手,但正在尝试构建 2D 游戏。我正在关注一本书,但它创建的游戏循环基本上是这样说的:
function gameLoop
update()
render()
sleep(1/30th second)
gameLoop
原因是这将以 30 fps 运行。然而,这似乎有点过于精神,因为如果我的帧需要 1/30 秒,那么它将以 15fps 运行(因为它会花费与更新一样多的时间来休眠)。
所以,我进行了一些挖掘并找到了 CADisplayLink 类,它将对我的 gameLoop 函数的调用同步到刷新率(或它的一小部分)。我找不到很多示例,所以我在这里发布代码审查:-) 它似乎按预期工作,它包括将经过(帧)时间传递给 Update 方法,因此我的逻辑可以是帧率-独立(但是我实际上无法在文档中找到如果我的框架花费的时间超过其允许的运行时间,CADisplayLink 会做什么 - 我希望它只是尽力赶上,并且不会崩溃!)。
//
// GameAppDelegate.m
//
// Created by Danny Tuppeny on 10/03/2010.
// Copyright Danny Tuppeny 2010. All rights reserved.
//
#import "GameAppDelegate.h"
#import "GameViewController.h"
#import "GameStates/gsSplash.h"
@implementation GameAppDelegate
@synthesize window;
@synthesize viewController;
- (void) applicationDidFinishLaunching:(UIApplication *)application
// Create an instance of the first GameState (Splash Screen)
[self doStateChange:[gsSplash class]];
// Set up the game loop
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(gameLoop)];
[displayLink setFrameInterval:2];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- (void) gameLoop
// Calculate how long has passed since the previous frame
CFTimeInterval currentFrameTime = [displayLink timestamp];
CFTimeInterval elapsed = 0;
// For the first frame, we want to pass 0 (since we haven't elapsed any time), so only
// calculate this in the case where we're not the first frame
if (lastFrameTime != 0)
elapsed = currentFrameTime - lastFrameTime;
// Keep track of this frames time (so we can calculate this next time)
lastFrameTime = currentFrameTime;
NSLog([NSString stringWithFormat:@"%f", elapsed]);
// Call update, passing the elapsed time in
[((GameState*)viewController.view) Update:elapsed];
- (void) doStateChange:(Class)state
// Remove the previous GameState
if (viewController.view != nil)
[viewController.view removeFromSuperview];
[viewController.view release];
// Create the new GameState
viewController.view = [[state alloc] initWithFrame:CGRectMake(0, 0, IPHONE_WIDTH, IPHONE_HEIGHT) andManager:self];
// Now set as visible
[window addSubview:viewController.view];
[window makeKeyAndVisible];
- (void) dealloc
[viewController release];
[window release];
[super dealloc];
@end
任何反馈都将不胜感激:-)
PS。如果你能告诉我为什么所有的书都使用“viewController.view”,而其他所有的书似乎都使用“[对象名称]”格式,那就加分了。为什么不是 [viewController 视图]?
【问题讨论】:
虽然这段代码有效,但似乎有点卡顿。帧率不低,只是看起来不太流畅。我认为这可能是因为我正在同步到帧速率,然后在我的游戏循环中更新。渲染是在drawRect中完成的。我认为我的代码应该直接在调用的方法中绘制(并且可能应该在绘制之后进行更新),但是我找不到任何好的 CADisplayLink 示例。 我将精灵的移动速度更改为每秒 30 像素,并且看起来非常流畅(偶尔会掉帧),所以我认为这只是我设置的速度 -例如。每帧移动略多于一个像素,这意味着最终它会在一帧中跳跃两个像素,使其看起来不均匀。我想这是不可避免的。 【参考方案1】:您在问题中将 Cocos2D 列为标签,但您实际上并未使用任何 Cocos2D 代码。你考虑过为你的游戏做一个 Cocos2D 实现吗?它会为您省去一些不必要的麻烦。
至于您的语法问题[myObject view]
用于调用myObject
上的方法,而myObject.view
用于设置/获取作为属性公开的实例变量。我不记得您是否也可以使用[myObject view]
检索实例变量,但如果这样可行,那么我猜两者之间的唯一区别是语法,您可以使用这两种方法来检索实例变量。
希望这些漫无边际的内容对你有用。
【讨论】:
哎呀!告诉你我不知道我在做什么!现在去掉了标签。对语法做了一些阅读,现在更好地理解它。似乎 [myclass property] 和 myclass.property 可以做不同的事情。第一个是属性(它可以执行代码,尽管在大多数情况下我怀疑它只是返回变量),而点直接访问变量(并且不会有自定义代码)。似乎在 C# 中公开私有字段和属性之间的区别,只是在这里它们可以看起来具有相同的名称! 其实,现在我很困惑,因为我遇到了错误:访问未知的 'rect' getter 方法这让我觉得使用 dot 与使用 get/set 方法完全一样!跨度> 如果使用点,则需要将要检索的实例变量定义为属性,然后将其合成。 “@property”和“@synthesize”调用的查找语法。 Getter/setter 方法会为您隐式创建,但仅在使用 @synthesize 之后。这就是为什么你会得到一个未知的“rect”getter 方法。 我现在已经这样做了,虽然我的理解是它只是创建了 get/set 方法,所以现在:object.myval = something 和做的一样:[object setMyval:something] ; ? 据我所知是 object.myval = something 和 [object setMyval:something] 应该是等价的。据我所知,在您合成它们之前,不会自动为您创建属性的获取/设置方法。【参考方案2】:从 Apple 的许多 GL 示例中,我认为您应该使用计时器。
animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0/60.0)
target:self
selector:@selector(updateAndRender)
userInfo:nil
repeats:TRUE];
【讨论】:
我相信添加 CADisplayLink 是一种更好的方式,但是因为它是最近才添加的,所以文档/示例似乎很少:(以上是关于这是为 iPhone 游戏制作游戏循环的好方法吗?的主要内容,如果未能解决你的问题,请参考以下文章
使用 OpengL Surface 为 Android 制作游戏循环的最佳方法