游戏循环和时间跟踪

发布于 2024-08-27 22:32:15 字数 2898 浏览 3 评论 0原文

也许我只是个白痴,但我一整天都在尝试实现游戏循环,但它只是没有点击。我几乎阅读了在谷歌上找到的每一篇文章,但问题是它们都使用不同的计时机制,这使得它们很难应用于我的特定情况(有些使用毫秒,其他使用刻度等)。

基本上,我有一个 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 类,它保存 UpdateDraw 方法需要知道的各种时间。

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.TotalVirtualTimeTime.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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

吐个泡泡 2024-09-03 22:32:15

XNA 中主要使用的方法是以某个固定速率调用 Update(),并在一定限制内尽可能多地调用 Draw()。在 Update() 方法中,您只需更新变量以反映新时刻的世界,而当调用 Draw() 时,您只需绘制它到屏幕上。

要让 Update() 以固定速率运行,您可能需要使用计时器或更准确的计时:

[DllImport("kernel32.dll")]
public static extern long GetTickCount();

然后您可以使用另一个(较慢的)计时器来调用 Invalidate( ) 在您用来绘制游戏的对象(画布)上。在画布的 Paint 事件中,您调用 Draw()。这样,每当系统认为有必要时(当它使您的画布无效时),或者一旦您调用 Invalidate(),它就有机会这样做,您的世界就会被绘制。

The approach mainly used in XNA is to call Update() at some fixed rate, and call Draw() as much as it can, up to some limit. In the Update() method, you'd only update variables to reflect the world at the new moment in time, and when Draw() 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:

[DllImport("kernel32.dll")]
public static extern long GetTickCount();

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 call Draw(). 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 called Invalidate().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文