- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在制作一个小型小行星游戏,但在控制动画速度时遇到了一些问题。
例如,假设我的游戏中有 20 个小行星,当我摧毁一个小行星时,小行星的数量就会减少(显然)。因为游戏中的物体越来越少,所以fps越来越高,小行星的动画速度也越来越快。
我通过根据游戏中小行星的数量调整动画速度来修复它,但当我摧毁小行星时,我还面临另一个爆炸问题。我想我可以对小行星做同样的事情,但我只是认为这不是一个非常明智的“解决”它的方法,而且对我来说似乎是不好的做法。
我想过限制 fps,但我不太确定该怎么做。我想获得一些建议以及处理此类情况的最佳方法是什么。
我将在此处发布我的主要游戏类,包括游戏循环,以及爆炸类的示例,以便您了解代码的总体思路。
游戏类和循环:
import com.asteroids.view.*;
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = -8921419424614180143L;
public static final int WIDTH = 1152, HEIGHT = WIDTH / 8 * 5;
private Thread thread;
private boolean isRunning;
private LoadImages loadImages = new LoadImages();
private Player player = new Player();
private AllObjects objects;
private KeyInput keyInput;
private long delay = 80;
private long currentTime = System.currentTimeMillis();
private long expectedTime = currentTime + delay;
public static BufferedImage test;
public Game() {
new Window(WIDTH, HEIGHT, "Asteroids!", this);
objects = new AllObjects();
objects.addObject(player);
for (int i = 0; i < 20; i++) {
objects.addObject(new Rock((int) (Math.random() * (Game.WIDTH - 64) + 1),
(int) (Math.random() * (Game.HEIGHT - 64) + 1)));
}
keyInput = new KeyInput(player);
this.addKeyListener(keyInput);
}
public void run() {
this.requestFocus();
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
// main game loop.
while (isRunning) {
adjustAsteroidsSpeed();
destroyAsteroids();
collisionLoop();
// used to set delay between every bullet(milliseconds)
currentTime = System.currentTimeMillis();
if (KeyInput.shoot && currentTime >= expectedTime) {
// calculates the accurate position of the x,y on the "circumference" of the
// player
float matchedX = player.getX() + 1 + (float) ((player.getRadius() + 32) * Math.cos(player.getRadian()));
float matchedY = player.getY() - 7 + (float) ((player.getRadius() + 32) * Math.sin(player.getRadian()));
objects.addObject(new Bullet(matchedX, matchedY, player));
expectedTime = currentTime + delay;
}
destroyBullets();
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1) {
tick();
delta--;
}
if (isRunning)
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
render();
stop();
System.exit(1);
}
private void stop() {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(1);
}
private void render() {
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.drawImage(LoadImages.getbackground(), 0, 0, getWidth(), getHeight(), this);
objects.render(g);
player.render(g);
g.dispose();
bs.show();
}
private void tick() {
player.tick();
objects.tick();
}
// starting thread and game loop.
public void start() {
thread = new Thread(this);
thread.start();
isRunning = true;
}
// minimum and maximum possible position for object.
public static float Bounds(float value, float min, float max) {
if (value >= max) {
return value = max;
}
if (value <= min) {
return value = min;
} else {
return value;
}
}
// detects collision between two objects
public boolean collision(GameObject a, GameObject b) {
return (b.getX() - a.getX() + 10) * (b.getX() - a.getX() + 10)
+ (b.getY() - a.getY() + 10) * (b.getY() - a.getY() + 10) < (a.getRadius() + b.getRadius())
* (a.getRadius() + b.getRadius());
}
// destroys bullets once they go out of the screen
public void destroyBullets() {
for (int i = 0; i < objects.getSize(); i++) {
if (objects.get(i).getId() == ID.BULLET) {
GameObject bullet = objects.get(i);
if (bullet.getX() > Game.WIDTH || bullet.getX() < 0 || bullet.getY() > Game.HEIGHT
|| bullet.getY() < 0) {
objects.removeObject(bullet);
}
}
}
}
// whenever a collision between an asteroid and a bullet occurs, the asteroid and the bullets are destroyed
public void destroyAsteroids() {
GameObject bullet = null;
GameObject bigRock = null;
for (int i = 0; i < objects.getSize(); i++) {
if (objects.get(i).getId() == ID.BULLET) {
bullet = (Bullet) objects.get(i);
for (int q = 0; q < objects.getSize(); q++) {
if (objects.get(q).getId() == ID.BIGROCK) {
bigRock = objects.get(q);
if (bullet != null && bigRock != null) {
if (collision(bigRock, bullet)) {
objects.addObject(new Explosion(bigRock.getX(), bigRock.getY(), objects));
objects.removeObject(bigRock);
objects.removeObject(bullet);
}
}
}
}
}
}
}
// calculates the amount of asteroids in the game and adjust the asteroids speed
public void adjustAsteroidsSpeed() {
int rocksCount = 0;
Rock rock;
for (GameObject object : objects.link()) {
if (object.getId() == ID.BIGROCK) {
rocksCount++;
}
}
for (GameObject object : objects.link()) {
if (object.getId() == ID.BIGROCK) {
rock = (Rock) object;
rock.setAnimSpeed(rocksCount * 0.002f);
}
}
}
爆炸等级:
package com.asteroids.model;
import java.awt.Graphics;
import java.awt.Image;
import com.asteroids.controller.*;
import com.asteroids.view.LoadImages;
public class Explosion extends GameObject {
private AllObjects objects;
private Image explosion;
private float frame = 0;
private float animSpeed = 0.09f;
private int frameCount = 48;
public Explosion(float x, float y, AllObjects objects) {
super(x, y, ID.EXPLOSION, 1);
this.objects = objects;
}
public void render(Graphics g) {
explosion(g);
}
public void explosion(Graphics g) {
frame += animSpeed;
if (frame > frameCount) {
frame -= frameCount;
}
explosion = LoadImages.getExplosion().getSubimage((int) frame * 256, 0, 256, 256);
g.drawImage(explosion, (int) x, (int) y, 110, 110, null);
if (frame >= 47.8f) {
objects.removeObject(this);
}
}
public void tick() {
}
public void setAnimSpeed(float animSpeed) {
this.animSpeed = animSpeed;
}
}
最佳答案
您的主循环正在生成不均匀的更新。如果我什么都不做,我会得到 7799913
和 8284754
fps 之间的任何位置,但是,如果我加入 8
毫秒延迟(以模拟某些工作) ),它下降到大约 115
-120
fps。
您的目的是尝试使帧速率尽可能均匀,这将确保动画速度保持不变
就我个人而言,我不喜欢“随心所欲”的游戏循环风格,这意味着循环可以消耗CPU周期而不实际做任何事情,这些周期可以用来做更重要的工作,比如更新 UI。
在大多数情况下,我只是使用 Swing Timer
设置为 5
毫秒间隔,然后利用日期/时间 API 来计算现在之间的差异和最后一次更新并选择要做什么,但是,这假设您正在使用基于 Swing 的绘画路径。如果您正在执行直接绘制路径(即 BufferStrategy
),您可以使用类似的想法和“循环”来代替...
public void run() throws InterruptedException {
int frames = 0;
Duration threashold = Duration.ofMillis(1000 / 59);
Duration cycle = Duration.ofSeconds(1);
Instant cycleStart = Instant.now();
// main game loop.
while (isRunning) {
Instant start = Instant.now();
// Some update function...
Thread.sleep(rnd.nextInt(32));
Duration processTime = Duration.between(start, Instant.now());
Duration remainingTime = threashold.minusMillis(processTime.toMillis());
long delay = remainingTime.toMillis();
if (delay > 0) {
Thread.sleep(delay);
} else {
System.out.println("Dropped frame");
}
frames++;
// Render the output
Duration cycleTime = Duration.between(cycleStart, Instant.now());
if (cycleTime.compareTo(cycle) >= 0) {
cycleStart = Instant.now();
System.out.println(frames);
frames = 0;
}
}
}
在此示例中,您的更新和绘制调度代码只需 16 毫秒即可完成工作,否则会丢帧。如果工作时间少于 16 毫秒,循环将“等待”剩余时间,以便为 CPU 提供一些喘息空间,以便为其他线程提供时间(并且不会在 CPU 上花费不必要的更新时间)
在上面的示例中,我生成了最多 32 毫秒的“随机”延迟进行测试。将其设置回 16,您应该获得(大约)60fps。
现在,我知道人们对这些事情非常热情,所以如果使用 Thread.sleep
和 Duration
让你起鸡皮疙瘩,你“可以”使用“免费” Wheeling”循环,类似于 Java Main Game Loop 中介绍的循环
下面是一个示例实现,我将每秒更新数和帧数设置为 60,但您可以更改这些值以满足您的需求...
public void run() throws InterruptedException {
double ups = 60;
double fps = 60;
long initialTime = System.nanoTime();
final double timeU = 1000000000 / ups;
final double timeF = 1000000000 / fps;
double deltaU = 0, deltaF = 0;
int frames = 0, ticks = 0;
long timer = System.currentTimeMillis();
while (isRunning) {
long currentTime = System.nanoTime();
deltaU += (currentTime - initialTime) / timeU;
deltaF += (currentTime - initialTime) / timeF;
initialTime = currentTime;
if (deltaU >= 1) {
Thread.sleep(rnd.nextInt(32));
//getInput();
//update();
ticks++;
deltaU--;
}
if (deltaF >= 1) {
Thread.sleep(rnd.nextInt(32));
//render();
frames++;
deltaF--;
}
if (System.currentTimeMillis() - timer > 1000) {
System.out.println(String.format("UPS: %s, FPS: %s", ticks, frames));
frames = 0;
ticks = 0;
timer += 1000;
}
}
}
同样,这里的Thread.sleep
只是为了注入(inject)随机量的“工作”。因为它允许超过 16 毫秒的延迟,所以您还会发现它“丢帧”。您的工作是将每次工作时间降低到 16 毫秒以下
关于java - 控制 2d 游戏的动画速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50788618/
我正在编写一个具有以下签名的 Java 方法。 void Logger(Method method, Object[] args); 如果一个方法(例如 ABC() )调用此方法 Logger,它应该
我是 Java 新手。 我的问题是我的 Java 程序找不到我试图用作的图像文件一个 JButton。 (目前这段代码什么也没做,因为我只是得到了想要的外观第一的)。这是我的主课 代码: packag
好的,今天我在接受采访,我已经编写 Java 代码多年了。采访中说“Java 垃圾收集是一个棘手的问题,我有几个 friend 一直在努力弄清楚。你在这方面做得怎么样?”。她是想骗我吗?还是我的一生都
我的 friend 给了我一个谜语让我解开。它是这样的: There are 100 people. Each one of them, in his turn, does the following
如果我将使用 Java 5 代码的应用程序编译成字节码,生成的 .class 文件是否能够在 Java 1.4 下运行? 如果后者可以工作并且我正在尝试在我的 Java 1.4 应用程序中使用 Jav
有关于why Java doesn't support unsigned types的问题以及一些关于处理无符号类型的问题。我做了一些搜索,似乎 Scala 也不支持无符号数据类型。限制是Java和S
我只是想知道在一个 java 版本中生成的字节码是否可以在其他 java 版本上运行 最佳答案 通常,字节码无需修改即可在 较新 版本的 Java 上运行。它不会在旧版本上运行,除非您使用特殊参数 (
我有一个关于在命令提示符下执行 java 程序的基本问题。 在某些机器上我们需要指定 -cp 。 (类路径)同时执行java程序 (test为java文件名与.class文件存在于同一目录下) jav
我已经阅读 StackOverflow 有一段时间了,现在我才鼓起勇气提出问题。我今年 20 岁,目前在我的家乡(罗马尼亚克卢日-纳波卡)就读 IT 大学。足以介绍:D。 基本上,我有一家提供簿记应用
我有 public JSONObject parseXML(String xml) { JSONObject jsonObject = XML.toJSONObject(xml); r
我已经在 Java 中实现了带有动态类型的简单解释语言。不幸的是我遇到了以下问题。测试时如下代码: def main() { def ks = Map[[1, 2]].keySet()
一直提示输入 1 到 10 的数字 - 结果应将 st、rd、th 和 nd 添加到数字中。编写一个程序,提示用户输入 1 到 10 之间的任意整数,然后以序数形式显示该整数并附加后缀。 public
我有这个 DownloadFile.java 并按预期下载该文件: import java.io.*; import java.net.URL; public class DownloadFile {
我想在 GUI 上添加延迟。我放置了 2 个 for 循环,然后重新绘制了一个标签,但这 2 个 for 循环一个接一个地执行,并且标签被重新绘制到最后一个。 我能做什么? for(int i=0;
我正在对对象 Student 的列表项进行一些测试,但是我更喜欢在 java 类对象中创建硬编码列表,然后从那里提取数据,而不是连接到数据库并在结果集中选择记录。然而,自从我这样做以来已经很长时间了,
我知道对象创建分为三个部分: 声明 实例化 初始化 classA{} classB extends classA{} classA obj = new classB(1,1); 实例化 它必须使用
我有兴趣使用 GPRS 构建车辆跟踪系统。但是,我有一些问题要问以前做过此操作的人: GPRS 是最好的技术吗?人们意识到任何问题吗? 我计划使用 Java/Java EE - 有更好的技术吗? 如果
我可以通过递归方法反转数组,例如:数组={1,2,3,4,5} 数组结果={5,4,3,2,1}但我的结果是相同的数组,我不知道为什么,请帮助我。 public class Recursion { p
有这样的标准方式吗? 包括 Java源代码-测试代码- Ant 或 Maven联合单元持续集成(可能是巡航控制)ClearCase 版本控制工具部署到应用服务器 最后我希望有一个自动构建和集成环境。
我什至不知道这是否可能,我非常怀疑它是否可能,但如果可以,您能告诉我怎么做吗?我只是想知道如何从打印机打印一些文本。 有什么想法吗? 最佳答案 这里有更简单的事情。 import javax.swin
我是一名优秀的程序员,十分优秀!