草庐IT

java - "deWiTTERS Game Loop"是否假设不间断电源?

coder 2024-04-02 原文

我最近尝试进入游戏编程。我对 Java 很有经验,但对游戏编程没有经验。我读了 http://www.koonsolo.com/news/dewitters-gameloop/并使用以下代码实现了那里提出的游戏循环:

private static int UPDATES_PER_SECOND = 25;
private static int UPDATE_INTERVAL = 1000 / UPDATES_PER_SECOND * 1000000;
private static int MAX_FRAMESKIP = 5;

public void run() {
    while (true) {
        int skippedFrames = 0;
        while (System.nanoTime() > this.nextUpdate && skippedFrames < MAX_FRAMESKIP) {
            this.updateGame();
            this.nextUpdate += UPDATE_INTERVAL;
            skippedFrames++;
        }

        long currentNanoTime = System.nanoTime();
        double interpolation = (currentNanoTime + UPDATE_INTERVAL - this.nextUpdate) / UPDATE_INTERVAL;
        this.repaintGame(interpolation);
    }
}

这个循环看起来很有希望而且很容易,但是现在我实际上正在尝试用它做一些事情,我不再那么确定了。
如果我没记错的话updateGame()负责计算位置,移动敌人,计算碰撞,......?由于插值没有传递给 updateGame() ,这是否意味着我们假设 updateGame()准确而稳定地调用 UPDATES_PER_SECOND每秒多少次?这是否意味着我们所有的计算都基于该假设?如果 - 无论出于何种原因 - 对 updateGame() 的调用延迟,这不会给我们带来很多麻烦吗?
例如,如果我的角色 Sprite 应该向右走,我们根据它在每个 updateGame() 上的速度移动它。 - 如果该方法被延迟,那将意味着我们的计算完全关闭并且角色会滞后?

在网站上,说明了以下插值示例:

If in the 10Th gametick the position is 500, and the speed is 100, then in the 11Th gametick the position will be 600. So where will you place your car when you render it? You could just take the position of the last gametick (in this case 500). But a better way is to predict where the car would be at exact 10.3, and this happens like this:
view_position = position + (speed * interpolation)
The car will then be rendered at position 530.



我知道这只是一个例子,但这不会使汽车速度取决于 UPDATES_PER_SECOND ?所以更多的 UPS 意味着一辆更快的汽车?这不可能吧……?

任何帮助,教程,任何赞赏。

更新/解决方案

在所有的帮助之后(谢谢!),这是我目前正在使用的 - 到目前为止效果很好(用于移动角色 Sprite ),但让我们等待真正复杂的游戏内容来决定这一点。尽管如此,我还是想分享这个。
我的游戏循环现在看起来像这样:
private static int UPDATES_PER_SECOND = 25;
private static int UPDATE_INTERVAL = 1000 / UPDATES_PER_SECOND * 1000000;
private static int MAX_FRAMESKIP = 5;

private long nextUpdate = System.nanoTime();

public void run() {
    while (true) {
        int skippedFrames = 0;
        while (System.nanoTime() > this.nextUpdate && skippedFrames < MAX_FRAMESKIP) {
            long delta = UPDATE_INTERVAL;
            this.currentState = this.createGameState(delta);
            this.newPredictedNextState = this.createGameState(delta + UPDATE_INTERVAL, true);

            this.nextUpdate += UPDATE_INTERVAL;
            skippedFrames++;
        }

        double interpolation = (System.nanoTime() + UPDATE_INTERVAL - this.nextUpdate) / (double) UPDATE_INTERVAL;
        this.repaintGame(interpolation);
    }
}

如您所见,有一些变化:
  • delta = UPDATE_INTERVAL ?是的。到目前为止,这是实验性的,但我认为它会起作用。问题是,一旦您实际从两个时间戳计算增量,就会引入浮点计算错误。这些很小,但考虑到你的更新被调用了数百万次,它们加起来了。由于第二个 while 循环确保我们 catch 错过的更新(例如,如果渲染需要很长时间),我们可以非常确定我们每秒获得 25 次更新。最坏的情况:我们错过了超过 MAX_FRAMESKIP更新 - 在这种情况下,更新会丢失并且游戏会滞后。不过,就像我说的,实验性的。我可能会再次将其更改为实际增量。
  • 预测下一场比赛状态?是的。 GameState 是保存所有相关游戏信息的对象,渲染器通过这个对象将游戏渲染到屏幕上。在我的例子中,我决定给渲染器两个状态:我们通常会通过他的状态,当前游戏状态,以及预测的 future 状态,UPDATE_INTERVAL在将来。通过这种方式,渲染器可以使用插值值轻松地在两者之间进行插值。计算 future 的游戏状态实际上很容易 - 因为你的更新方法( createGameState() )无论如何都需要一个增量值,只需将增量增加 UPDATE_INTERVAL - 这样就可以预测 future 的状态。当然, future 状态假设用户输入等保持不变。如果没有,下一次游戏状态更新将处理更改。
  • 其余部分几乎保持不变,取自 deWiTTERS 游戏循环。 MAX_FRAMESKIP在硬件真的很慢的情况下,它几乎是一种故障保护,以确保我们不时渲染一些东西。但如果这开始,我猜无论如何我们都会有极端的滞后。插值与以前相同 - 但现在渲染器可以简单地在两个游戏状态之间进行插值,除了插值数字外,它不必具有任何逻辑。那很好!

  • 也许为了澄清,举个例子。这是我计算字符位置的方法(稍微简化了一点):
    public GameState createGameState(long delta, boolean ignoreNewInput) {
        //Handle User Input and stuff if ignoreNewInput=false
    
        GameState newState = this.currentState.copy();
        Sprite charSprite = newState.getCharacterSprite();
        charSprite.moveByX(charSprite.getMaxSpeed() * delta * charSprite.getMoveDirection().getX()); 
        //getMoveDirection().getX() is 1.0 when the right arrow key is pressed, otherwise 0.0
    }
    

    ...然后,在窗口的paint方法中...
    public void paint(Graphics g) {
        super.paint(g);
    
        Graphics2D g2d = (Graphics2D) g;
    
        Sprite currentCharSprite = currentGameState.getCharacterSprite();
        Sprite nextCharSprite = predictedNextState.getCharacterSprite(); 
        Position currentPos = currentCharSprite.getPosition();
        Position nextPos = nextCharSprite.getPosition();
        //Interpolate position
        double x = currentPos.getX() + (nextPos.getX() - currentPos.getX()) * this.currentInterpolation;
        double y = currentPos.getY() + (nextPos.getY() - currentPos.getY()) * this.currentInterpolation;
        Position interpolatedCharPos = new Position(x, y);
        g2d.drawImage(currentCharSprite.getImage(), (int) interpolatedCharPos.getX(), (int) interpolatedCharPos.getY(), null);
    }
    

    最佳答案

    不要将您的游戏逻辑建立在更新间隔恒定的假设之上。包括一个精确测量两次更新之间耗时的游戏时钟。然后,您可以根据延迟进行所有计算,而不必担心实际更新率。

    在这种情况下,汽车速度将以单位/秒为单位给出,增量将是自上次更新以来的总秒数:

    car.position += car.velocity * delta;
    

    将更新游戏逻辑与绘制框架分开是一种常见的做法。就像您说的那样,这可以通过不时跳过渲染帧来保持稳定的更新速率。

    但这并不是保持更新间隔不变。每个时间单位的更新次数最少非常重要。想象一下,一辆汽车以非常快的速度驶向障碍物。如果更新频率太低,两次更新之间的行进距离可能大于障碍物的总大小。车辆将直接通过它。

    关于java - "deWiTTERS Game Loop"是否假设不间断电源?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15100348/

    有关java - "deWiTTERS Game Loop"是否假设不间断电源?的更多相关文章

    1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

      我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

    2. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

      我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

    3. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

      给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

    4. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

      为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

    5. ruby - 检查数组是否在增加 - 2

      这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

    6. java - 等价于 Java 中的 Ruby Hash - 2

      我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

    7. ruby-on-rails - 迷你测试错误 : "NameError: uninitialized constant" - 2

      我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test

    8. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

      我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

    9. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

      我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

    10. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

      我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

    随机推荐