创建游戏重播时的计时问题(赛车幽灵)
timing issues while creating replay of game (ghost for racing)
我在 cocos2d 中创建了一个使用 box2d 进行物理处理的 iOS 游戏。我想为这个游戏提供重播功能(你可以在其中与之前的尝试进行比赛)我知道 box2d 是确定性的(好吧,它适用于相同的硬件......所以 运行 在你的相同phone 会起作用)所以这应该是可行的。目前,在测试期间,我已经用
记录了用户输入的时间
NSLog(@"%f", [[NSDate date] timeIntervalSince1970] - gameTime);
其中 gameTime
是游戏开始的时间
然后我这样做是为了重新玩游戏
[self performSelector:@selector(simulateFingerDown:) withObject:nil afterDelay:0.753302];
[self performSelector:@selector(simulateFingerUp:) withObject:nil afterDelay:1.382405];
[self performSelector:@selector(simulateFingerDown:) withObject:nil afterDelay:2.066786];
[self performSelector:@selector(simulateFingerUp:) withObject:nil afterDelay:2.800533];
[self performSelector:@selector(simulateFingerDown:) withObject:nil afterDelay:3.950479];
[self performSelector:@selector(simulateFingerUp:) withObject:nil afterDelay:4.933555];
[self performSelector:@selector(simulateFingerDown:) withObject:nil afterDelay:6.607358];
[self performSelector:@selector(simulateFingerUp:) withObject:nil afterDelay:8.067316];
[self performSelector:@selector(simulateFingerDown:) withObject:nil afterDelay:8.700970];
[self performSelector:@selector(simulateFingerUp:) withObject:nil afterDelay:8.934012];
其中 simulateFingerUp/Down
调用与 ccTouchBegan/Ended
调用相同的东西。我游戏中的唯一功能是触摸屏幕或不触摸屏幕,所以我只是在特定延迟中硬编码何时触摸或不触摸(测试 "ghost" 功能)这个过程按预期工作......并且游戏是 "simulated/replayed" 但是应用程序的每个 运行 都会得到不同的结果。大概调度选择器依赖于系统资源或类似的东西所以它会导致 运行s 之间的不同时间(这可能会导致我的游戏出现很大差异)
我应该用什么来模拟回放?我可以记录用户输入的时间,它只可能在计算减法 [[NSDate date] timeIntervalSince1970] - gameTime
时关闭,但似乎用 performSelector 回放它并不可靠。有人有建议吗?
我也认为这可能相关? box2d 的 step
函数总是:_world->Step(0, 8, 3);
我不完全确定为什么是这些值...我从在线教程中获取了一些 box2d 代码,但没有解释这些特定值
有趣,但用错了设备!我会说(只是猜测)使用时基将非常困难。尽管回放录音的设备是同一台设备,但 运行 循环在录音和回放期间都存在一定的随机性。为了说服自己,只需登录任何 'update' 方法的 dt 参数,您就会看到(假设您以 60 fps 的速度播放)一些频繁的 'skipped frames' ...
.016
.016
.033 <-啊...这里刚刚发生了什么?可能设备中的某些其他任务消耗了足够的资源来阻止此 运行 循环足够长的时间以致完全错过调度帧!
现在,录制和回放的随机性会有所不同。例如,您可能会在 phone 上收到一条通知,或者 AdSheet 可能正在忙着做一些 重要 的事情(比如准备在后台为某个应用投放下一个广告) ticks ...等。因此,您将永远无法准确地重复时基。
我觉得。只是一个猜测:)
一种解决方案是运行 以固定的时间步长进行模拟。假设您选择每次更新 16 毫秒。但是您仍然希望事物按实际经过的时间缩放。
现在假设一帧需要 20 毫秒。这意味着您将进行一次 16 毫秒更新,并为下一帧剩余 4 毫秒。然后说下一帧需要15ms。这意味着您需要进行一次 16 毫秒更新,然后为下一帧剩余 3 毫秒,依此类推。然后假设您有一个需要 40 毫秒的长帧 - 您需要为该帧执行两次 16 毫秒的更新。或者可能一帧需要 10 毫秒,而没有足够的余数来达到 16 毫秒 - 在这种情况下,您 不会 执行物理更新。
现在你有一个(希望如此)确定性*、可重新播放的更新循环。
到目前为止,此实现的问题是运动不会显得非常流畅;你会遇到一些小问题,因为固定的 16 毫秒更新与你实际经过的时间不完全一致。
在您的渲染函数中,使用 "remainder" 时间(例如 4 毫秒)并按该数量及时向前插值。因此,使用汽车的当前速度和 angular 速度,将其渲染到您估计未来 4 毫秒的位置。这应该足够准确 "look" 正确。希望你能从物理引擎中得到汽车速度和angular速度。
*我满怀希望地说,因为浮点数是一件棘手的事情,即使您认为它应该是确定性的,也可能不是。我不确定 iOS FP 数学是否是确定性的,或者它是否取决于编译器设置或什么。
我在 cocos2d 中创建了一个使用 box2d 进行物理处理的 iOS 游戏。我想为这个游戏提供重播功能(你可以在其中与之前的尝试进行比赛)我知道 box2d 是确定性的(好吧,它适用于相同的硬件......所以 运行 在你的相同phone 会起作用)所以这应该是可行的。目前,在测试期间,我已经用
记录了用户输入的时间NSLog(@"%f", [[NSDate date] timeIntervalSince1970] - gameTime);
其中 gameTime
是游戏开始的时间
然后我这样做是为了重新玩游戏
[self performSelector:@selector(simulateFingerDown:) withObject:nil afterDelay:0.753302];
[self performSelector:@selector(simulateFingerUp:) withObject:nil afterDelay:1.382405];
[self performSelector:@selector(simulateFingerDown:) withObject:nil afterDelay:2.066786];
[self performSelector:@selector(simulateFingerUp:) withObject:nil afterDelay:2.800533];
[self performSelector:@selector(simulateFingerDown:) withObject:nil afterDelay:3.950479];
[self performSelector:@selector(simulateFingerUp:) withObject:nil afterDelay:4.933555];
[self performSelector:@selector(simulateFingerDown:) withObject:nil afterDelay:6.607358];
[self performSelector:@selector(simulateFingerUp:) withObject:nil afterDelay:8.067316];
[self performSelector:@selector(simulateFingerDown:) withObject:nil afterDelay:8.700970];
[self performSelector:@selector(simulateFingerUp:) withObject:nil afterDelay:8.934012];
其中 simulateFingerUp/Down
调用与 ccTouchBegan/Ended
调用相同的东西。我游戏中的唯一功能是触摸屏幕或不触摸屏幕,所以我只是在特定延迟中硬编码何时触摸或不触摸(测试 "ghost" 功能)这个过程按预期工作......并且游戏是 "simulated/replayed" 但是应用程序的每个 运行 都会得到不同的结果。大概调度选择器依赖于系统资源或类似的东西所以它会导致 运行s 之间的不同时间(这可能会导致我的游戏出现很大差异)
我应该用什么来模拟回放?我可以记录用户输入的时间,它只可能在计算减法 [[NSDate date] timeIntervalSince1970] - gameTime
时关闭,但似乎用 performSelector 回放它并不可靠。有人有建议吗?
我也认为这可能相关? box2d 的 step
函数总是:_world->Step(0, 8, 3);
我不完全确定为什么是这些值...我从在线教程中获取了一些 box2d 代码,但没有解释这些特定值
有趣,但用错了设备!我会说(只是猜测)使用时基将非常困难。尽管回放录音的设备是同一台设备,但 运行 循环在录音和回放期间都存在一定的随机性。为了说服自己,只需登录任何 'update' 方法的 dt 参数,您就会看到(假设您以 60 fps 的速度播放)一些频繁的 'skipped frames' ...
.016 .016 .033 <-啊...这里刚刚发生了什么?可能设备中的某些其他任务消耗了足够的资源来阻止此 运行 循环足够长的时间以致完全错过调度帧!
现在,录制和回放的随机性会有所不同。例如,您可能会在 phone 上收到一条通知,或者 AdSheet 可能正在忙着做一些 重要 的事情(比如准备在后台为某个应用投放下一个广告) ticks ...等。因此,您将永远无法准确地重复时基。
我觉得。只是一个猜测:)
一种解决方案是运行 以固定的时间步长进行模拟。假设您选择每次更新 16 毫秒。但是您仍然希望事物按实际经过的时间缩放。
现在假设一帧需要 20 毫秒。这意味着您将进行一次 16 毫秒更新,并为下一帧剩余 4 毫秒。然后说下一帧需要15ms。这意味着您需要进行一次 16 毫秒更新,然后为下一帧剩余 3 毫秒,依此类推。然后假设您有一个需要 40 毫秒的长帧 - 您需要为该帧执行两次 16 毫秒的更新。或者可能一帧需要 10 毫秒,而没有足够的余数来达到 16 毫秒 - 在这种情况下,您 不会 执行物理更新。
现在你有一个(希望如此)确定性*、可重新播放的更新循环。
到目前为止,此实现的问题是运动不会显得非常流畅;你会遇到一些小问题,因为固定的 16 毫秒更新与你实际经过的时间不完全一致。
在您的渲染函数中,使用 "remainder" 时间(例如 4 毫秒)并按该数量及时向前插值。因此,使用汽车的当前速度和 angular 速度,将其渲染到您估计未来 4 毫秒的位置。这应该足够准确 "look" 正确。希望你能从物理引擎中得到汽车速度和angular速度。
*我满怀希望地说,因为浮点数是一件棘手的事情,即使您认为它应该是确定性的,也可能不是。我不确定 iOS FP 数学是否是确定性的,或者它是否取决于编译器设置或什么。