- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我开始玩一款安卓游戏。它有一个 GameLoop(线程)类,用于跟踪 fps 值和一个 GameView(surfaceview),用于实例化 GameLoop 对象并设置它运行等等。现在我有一个单独的 FpsText 对象,它知道如何将自己绘制到屏幕上,但要更新我需要执行类似 fpsText.setText(gameloop.getFps()); 的操作
如果我在 GameView 类中执行此操作,这不是问题,但我想尝试拥有一个可以自行更新的 FpsText
对象。我没有任何其他线程方面的经验,我认为这可能会适得其反,但尽管如此,我还是尝试将 fpsText
的 gameloop
引用作为参数传递。毫不奇怪,每次运行该应用程序时,我都会得到不同的不需要的结果,所以我想知道您认为处理这种情况的最佳方法是什么?
作为在 GameView
中更新 fpsText
的替代方案,我总是可以将 gameloop 类中的 fps 值公开,但我知道这是不好的做法!听起来都不是好的解决方案,也许有更好的方法?
这里是我的游戏循环,供引用,其中实现了 RedOrav 建议的第二个更改:
package biz.hireholly.engine;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
import biz.hireholly.gameplay.FpsText;
import java.util.ArrayList;
/**
* The GameLoop is a thread that will ensure updating and drawing is done at set intervals.
* The thread will sleep when it has updated/rendered quicker than needed to reach the desired fps.
* The loop is designed to skip drawing if the update/draw cycle is taking to long, up to a MAX_FRAME_SKIPS.
* The Canvas object is created and managed to some extent in the game loop,
* this is so that we can prevent multiple objects trying to draw to it simultaneously.
* Note that the gameloop has a reference to the gameview and vice versa.
*/
public class GameLoop extends Thread {
private static final String TAG = GameLoop.class.getSimpleName();
//desired frames per second
private final static int MAX_FPS = 30;
//maximum number of drawn frames to be skipped if drawing took too long last cycle
private final static int MAX_FRAME_SKIPS = 5;
//ideal time taken to update & draw
private final static int CYCLE_PERIOD = 1000 / MAX_FPS;
private SurfaceHolder surfaceHolder;
//the gameview actually handles inputs and draws to the surface
private GameView gameview;
private boolean running;
private long beginTime = 0; // time when cycle began
private long timeDifference = 0; // time it took for the cycle to execute
private int sleepTime = 0; // milliseconds to sleep (<0 if drawing behind schedule)
private int framesSkipped = 0; // number of render frames skipped
private double lastFps = 0; //The last FPS tracked, the number displayed onscreen
private ArrayList<Double> fpsStore = new ArrayList<Double>(); //For the previous fps values
private long lastTimeFpsCalculated = System.currentTimeMillis(); //used in trackFps
FpsText fpsText;
public GameLoop(SurfaceHolder surfaceHolder, GameView gameview, FpsText fpsText) {
super();
this.surfaceHolder = surfaceHolder;
this.gameview = gameview;
this.fpsText = fpsText;
}
public void setRunning(boolean running) {
this.running = running;
}
@Override
public void run(){
Canvas c;
while (running) {
c = null;
//try locking canvas, so only we can edit pixels on surface
try{
c = this.surfaceHolder.lockCanvas();
//sync so nothing else can modify while were using it
synchronized (surfaceHolder){
beginTime = System.currentTimeMillis();
framesSkipped = 0; //reset frame skips
this.gameview.update();
this.gameview.draw(c);
//calculate how long cycle took
timeDifference = System.currentTimeMillis() - beginTime;
//good time to trackFps?
trackFps();
//calculate potential sleep time
sleepTime = (int)(CYCLE_PERIOD - timeDifference);
//sleep for remaining cycle
if (sleepTime >0){
try{
Thread.sleep(sleepTime); //saves battery! :)
} catch (InterruptedException e){}
}
//if sleepTime negative then we're running behind
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS){
//update without rendering to catch up
this.gameview.update();
//skip as many frame renders as needed to get back into
//positive sleepTime and continue as normal
sleepTime += CYCLE_PERIOD;
framesSkipped++;
}
}
} finally{
//finally executes regardless of exception,
//so surface is not left in an inconsistent state
if (c != null){
surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
/* Calculates the average fps every second */
private void trackFps(){
synchronized(fpsStore){
long currentTime = System.currentTimeMillis();
if(timeDifference != 0){
fpsStore.add((double)(1000 / timeDifference));
}
//If a second has past since last time average was calculated,
// it's time to calculate a new average fps to display
if ((currentTime - 1000) > lastTimeFpsCalculated){
for (Double fps : fpsStore){
lastFps += fps;
}
lastFps /= fpsStore.size();
synchronized(fpsText){ //update the Drawable FpsText object
fpsText.setText(String.valueOf((int)lastFps));
}
lastTimeFpsCalculated = System.currentTimeMillis();
//Log.d("trackFPS()", " fpsStore.size() = "+fpsStore.size()+"\t"+fpsStore.toString());
fpsStore.clear();
}
}
}
}
最佳答案
您不想要的结果是什么?就获取 fps 值而言,公开 fps 变量和设置 getfps() 是完全相同的事情,并且修改 GameView 中的 fps 值没有任何意义。
我怀疑您在 GameLoop 中所做的事情是在几秒之间修改您的 fps 变量,即获取您的 GameView 可能也在读取的中间值。我建议每秒修改 fps 并保留时间变量以进行计算。假设你一开始的帧率为 0。 GameLoop 进行计算,一秒钟过去后,您就已经收集了它处理的帧数和时间(1 秒)。在此之前,无论 GameLoop 在做什么,GameView 始终读取 0。在那一秒之后,假设您达到了 60 fps,fps 变量将被修改,并且在下一秒之前保持不变。因此,在接下来的一秒中,即使 GameView 对变量进行更多读取,它也始终会得到 60。
// Inside GameLoop
private int fps; // Variable you're going to read
private int frameCount; // Keeps track or frames processed
private long lastUpdated;
while(running) {
if(System.currentTimeMillis() - lastUpdated > 1000) { // 1 second elapsed
fps = frameCount;
frameCount = 0;
}
frameCount++;
}
public int getFps() {
return fps;
}
另一种方法是将 fpsText 的引用传递给 GameLoop,它只会在每一秒过去后对其进行修改。这样,您的渲染线程只需担心渲染,而您的 GameLoop 则只需担心每秒的帧数。但请记住,您可能会遇到竞争条件(您在渲染方法中间的 GameLoop 中 setText() ),而您不必担心其他情况。当您渲染和修改它时,将 fpsText 对象括在同步括号中应该可以解决您的问题。
让我们知道这是否有帮助!
关于java - 有没有办法安全地传递线程作为参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11807810/
Github:https://github.com/jjvang/PassIntentDemo 我一直在关注有关按 Intent 传递对象的教程:https://www.javacodegeeks.c
我有一个 View ,其中包含自动生成的 text 类型的 input 框。当我单击“通过电子邮件发送结果”按钮时,代码会将您带到 CalculatedResults Controller 中的 Em
我有一个基本的docker镜像,我将以此为基础构建自己的镜像。我没有基础镜像的Dockerfile。 基本上,基本镜像使用两个--env arg,一个接受其许可证,一个选择在容器中激活哪个框架。我可以
假设我想计算 2^n 的总和,n 范围从 0 到 100。我可以编写以下内容: seq { 0 .. 100 } |> Seq.sumBy ((**) 2I) 但是,这与 (*) 或其他运算符/函数不
我有这个网址: http://www.example.com/get_url.php?ID=100&Link=http://www.test.com/page.php?l=1&m=7 当我打印 $_G
我想将 window.URL.createObjectURL(file) 创建的地址传递给 dancer.js 但我得到 GET blob:http%3A//localhost/b847c5cd-aa
我想知道如何将 typedef 传递给函数。例如: typedef int box[3][3]; box empty, *board[3][3]; 我如何将 board 传递给函数?我
我正在将一些代码从我的 Controller 移动到核心数据应用程序中的模型。 我编写了一个方法,该方法为我定期发出的特定获取请求返回 NSManagedObjectID。 + (NSManagedO
为什么我不能将类型化数组传递到采用 any[] 的函数/构造函数中? typedArray = new MyType[ ... ]; items = new ko.observableArray(ty
我是一名新的 Web 开发人员,正在学习 html5 和 javascript。 我有一个带有“选项卡”的网页,可以使网页的某些部分消失并重新出现。 链接如下: HOME 和 JavaScript 函
我试图将对函数的引用作为参数传递 很难解释 我会写一些伪代码示例 (calling function) function(hello()); function(pass) { if this =
我在尝试调用我正在创建的 C# 项目中的函数时遇到以下错误: System.Runtime.InteropServices.COMException: Operation is not allowed
使用 ksh。尝试重用当前脚本而不修改它,基本上可以归结为如下内容: `expr 5 $1 $2` 如何将乘法命令 (*) 作为参数 $1 传递? 我首先尝试使用“*”,甚至是\*,但没有用。我尝试
我一直在研究“Play for Java”这本书,这本书非常棒。我对 Java 还是很陌生,但我一直在关注这些示例,我有点卡在第 3 章上了。可以在此处找到代码:Play for Java on Gi
我知道 Javascript 中的对象是通过引用复制/传递的。但是函数呢? 当我跳到一些令人困惑的地方时,我正在尝试这段代码。这是代码片段: x = function() { console.log(
我希望能够像这样传递参数: fn(a>=b) or fn(a!=b) 我在 DjangoORM 和 SQLAlchemy 中看到了这种行为,但我不知道如何实现它。 最佳答案 ORM 使用 specia
在我的 Angular 项目中,我最近将 rxjs 升级到版本 6。现在,来自 npm 的模块(在 node_modules 文件夹内)由于一些破坏性更改而失败(旧的进口不再有效)。我为我的代码调整了
这个问题在这里已经有了答案: The issue of * in Command line argument (6 个答案) 关闭 3 年前。 我正在编写一个关于反向波兰表示法的 C 程序,它通过命
$(document).ready(function() { function GetDeals() { alert($(this).attr("id")); } $('.filter
下面是一个例子: 复制代码 代码如下: use strict; #这里是两个数组 my @i =('1','2','3'); my @j =('a','b','c'); &n
我是一名优秀的程序员,十分优秀!