游戏循环和时间跟踪
也许我只是个白痴,但我一整天都在尝试实现游戏循环,但它只是没有点击。我几乎阅读了在谷歌上找到的每一篇文章,但问题是它们都使用不同的计时机制,这使得它们很难应用于我的特定情况(有些使用毫秒,其他使用刻度等)。
基本上,我有一个 Clock 对象,每次游戏循环执行时都会更新。
internal class Clock {
public static long Timestamp {
get { return Stopwatch.GetTimestamp(); }
}
public static long Frequency {
get { return Stopwatch.Frequency; }
}
private long _startTime;
private long _lastTime;
private TimeSpan _totalTime;
private TimeSpan _elapsedTime;
/// <summary>
/// The amount of time that has passed since the first step.
/// </summary>
public TimeSpan TotalTime {
get { return _totalTime; }
}
/// <summary>
/// The amount of time that has passed since the last step.
/// </summary>
public TimeSpan ElapsedTime {
get { return _elapsedTime; }
}
public Clock() {
Reset();
}
public void Reset() {
_startTime = Timestamp;
_lastTime = 0;
_totalTime = TimeSpan.Zero;
_elapsedTime = TimeSpan.Zero;
}
public void Tick() {
long currentTime = Timestamp;
if (_lastTime == 0)
_lastTime = currentTime;
_totalTime = TimestampToTimeSpan(currentTime - _startTime);
_elapsedTime = TimestampToTimeSpan(currentTime - _lastTime);
_lastTime = currentTime;
}
public static TimeSpan TimestampToTimeSpan(long timestamp) {
return TimeSpan.FromTicks(
(timestamp * TimeSpan.TicksPerSecond) / Frequency);
}
}
我的大部分内容都基于 XNA GameClock
,但它已大大简化。然后,我有一个 Time
类,它保存 Update
和 Draw
方法需要知道的各种时间。
public class Time {
public TimeSpan ElapsedVirtualTime { get; internal set; }
public TimeSpan ElapsedRealTime { get; internal set; }
public TimeSpan TotalVirtualTime { get; internal set; }
public TimeSpan TotalRealTime { get; internal set; }
internal Time() { }
internal Time(TimeSpan elapsedVirtualTime, TimeSpan elapsedRealTime,
TimeSpan totalVirutalTime, TimeSpan totalRealTime) {
ElapsedVirtualTime = elapsedVirtualTime;
ElapsedRealTime = elapsedRealTime;
TotalVirtualTime = totalVirutalTime;
TotalRealTime = totalRealTime;
}
}
我的主类保留一个 Time
实例,它应该在游戏循环期间不断更新。到目前为止,我有这样的:
private static void Loop() {
do {
Clock.Tick();
Time.TotalRealTime = Clock.TotalTime;
Time.ElapsedRealTime = Clock.ElapsedTime;
InternalUpdate(Time);
InternalDraw(Time);
} while (!_exitRequested);
}
时间类的实时属性结果很好。现在我想要一个正确的更新/绘制循环工作,以便状态每帧更新可变次数,但以固定的时间步长。同时,Time.TotalVirtualTime
和 Time.ElapsedVirtualTime
应相应更新。此外,我打算在未来支持多人游戏,以防对游戏循环的设计产生任何影响。
关于如何实现这一点的任何提示或示例(除了文章链接之外)?
Maybe I'm just an idiot, but I've been trying to implement a game loop all day and it's just not clicking. I've read literally every article I could find on Google, but the problem is that they all use different timing mechanisms, which makes them difficult to apply to my particular situation (some use milliseconds, other use ticks, etc).
Basically, I have a Clock
object that updates each time the game loop executes.
internal class Clock {
public static long Timestamp {
get { return Stopwatch.GetTimestamp(); }
}
public static long Frequency {
get { return Stopwatch.Frequency; }
}
private long _startTime;
private long _lastTime;
private TimeSpan _totalTime;
private TimeSpan _elapsedTime;
/// <summary>
/// The amount of time that has passed since the first step.
/// </summary>
public TimeSpan TotalTime {
get { return _totalTime; }
}
/// <summary>
/// The amount of time that has passed since the last step.
/// </summary>
public TimeSpan ElapsedTime {
get { return _elapsedTime; }
}
public Clock() {
Reset();
}
public void Reset() {
_startTime = Timestamp;
_lastTime = 0;
_totalTime = TimeSpan.Zero;
_elapsedTime = TimeSpan.Zero;
}
public void Tick() {
long currentTime = Timestamp;
if (_lastTime == 0)
_lastTime = currentTime;
_totalTime = TimestampToTimeSpan(currentTime - _startTime);
_elapsedTime = TimestampToTimeSpan(currentTime - _lastTime);
_lastTime = currentTime;
}
public static TimeSpan TimestampToTimeSpan(long timestamp) {
return TimeSpan.FromTicks(
(timestamp * TimeSpan.TicksPerSecond) / Frequency);
}
}
I based most of that on the XNA GameClock
, but it's greatly simplified. Then, I have a Time
class which holds various times that the Update
and Draw
methods need to know.
public class Time {
public TimeSpan ElapsedVirtualTime { get; internal set; }
public TimeSpan ElapsedRealTime { get; internal set; }
public TimeSpan TotalVirtualTime { get; internal set; }
public TimeSpan TotalRealTime { get; internal set; }
internal Time() { }
internal Time(TimeSpan elapsedVirtualTime, TimeSpan elapsedRealTime,
TimeSpan totalVirutalTime, TimeSpan totalRealTime) {
ElapsedVirtualTime = elapsedVirtualTime;
ElapsedRealTime = elapsedRealTime;
TotalVirtualTime = totalVirutalTime;
TotalRealTime = totalRealTime;
}
}
My main class keeps a single instance of Time
, which it should constantly update during the game loop. So far, I have this:
private static void Loop() {
do {
Clock.Tick();
Time.TotalRealTime = Clock.TotalTime;
Time.ElapsedRealTime = Clock.ElapsedTime;
InternalUpdate(Time);
InternalDraw(Time);
} while (!_exitRequested);
}
The real time properties of the time class turn out great. Now I'd like to get a proper update/draw loop working so that the state is updated a variable number of times per frame, but at a fixed timestep. At the same time, the Time.TotalVirtualTime
and Time.ElapsedVirtualTime
should be updated accordingly. In addition, I intend for this to support multiplayer in the future, in case that makes any difference to the design of the game loop.
Any tips or examples on how I could go about implementing this (aside from links to articles)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
XNA 中主要使用的方法是以某个固定速率调用
Update()
,并在一定限制内尽可能多地调用Draw()
。在Update()
方法中,您只需更新变量以反映新时刻的世界,而当调用Draw()
时,您只需绘制它到屏幕上。要让
Update()
以固定速率运行,您可能需要使用计时器或更准确的计时:然后您可以使用另一个(较慢的)计时器来调用
Invalidate( )
在您用来绘制游戏的对象(画布)上。在画布的 Paint 事件中,您调用Draw()
。这样,每当系统认为有必要时(当它使您的画布无效时),或者一旦您调用Invalidate()
,它就有机会这样做,您的世界就会被绘制。The approach mainly used in XNA is to call
Update()
at some fixed rate, and callDraw()
as much as it can, up to some limit. In theUpdate()
method, you'd only update variables to reflect the world at the new moment in time, and whenDraw()
is called, you only draw it to the screen.To get
Update()
to run at a fixed rate, you might want to use you timer or an even more accurate timing using:Then you could use another (slower) timer which calls
Invalidate()
on the object you use to paint your game (the canvas). In the canvas' Paint-event, you callDraw()
. This way, your world gets drawn each time the system deems it necessary (when it invalidates your canvas) or as soon as it has the opportunity to do so once you calledInvalidate()
.