计时器在 Java 后加速
Timer speeding up in Java
我知道还有其他问题,但 none 似乎可以帮助我解决问题。在我的游戏中,有一个玩家发射一枚导弹,飞向负 y 直到它从屏幕上消失,然后回到原来的位置等待再次发射。然而,每次发射导弹时,它都会变得更快,就像 Timer() 中的延迟变短一样。代码:
主要class:
public class ShooterGame extends JFrame{
static int playerX=500;
static int playerY=520;
InputHandler input = new InputHandler(this);
public static Player player = new Player(playerX,playerY,50,50);
public static void main(String[] args){
ShooterGame game = new ShooterGame();
game.run();
System.exit(0);
}
static int windowWidth = 1300;
static int windowHeight = 600;
static int fps = 30;
public static BufferedImage backBuffer = new BufferedImage(windowWidth, windowHeight, BufferedImage.TYPE_INT_RGB);
public static Graphics bbg;
public void run(){
boolean running = true;
initialize();
while(running){
long time = System.currentTimeMillis();
update();
draw();
time = (1000 / fps) - (System.currentTimeMillis() - time);
if (time > 0) {
try{
Thread.sleep(time);
}
catch(Exception e){};
};
}
}
public void initialize(){
setTitle("--- Shooter Game ---");
setSize(windowWidth, windowHeight);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public void update(){
player.update(input);
}
public void draw(){
Graphics g = getGraphics();
Graphics bbg = backBuffer.getGraphics();
bbg.setColor(Color.BLACK);
bbg.fillRect(0, 0, windowWidth, windowHeight);
player.Draw(bbg);
enemie1.Draw(bbg,10,100);
if(game.player.Player.missileRunning) game.player.Player.missile.Draw(bbg);
g.drawImage(backBuffer, 0, 0, this);
}
public static Graphics getMainGraphics(){
return bbg;
}
}
玩家class:
public class Player{
private BufferedImage sprite;
public BufferedImage missileSprite;
public int x, y, width, height;
private int fixedX;
private final double speed = 5.0d;
public static Missile missile;
public static boolean missileRunning = false;
public static boolean missileReady = true;
public Player(int x, int y, int width, int height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
missile = new Missile(this.x);
try{
URL url = this.getClass().getResource("ship.png");
sprite = ImageIO.read(url);
} catch(IOException e){}
try{
URL url2 = this.getClass().getResource("missile.png");
missileSprite = ImageIO.read(url2);
} catch(IOException e){}
}
public void keyPlayer(double delta, InputHandler i){
if(i.isKeyDown(KeyEvent.VK_D)){
if(this.x>=1240) return;
else this.x+=speed*delta;
}
if(i.isKeyDown(KeyEvent.VK_A)){
if(this.x<=0) return;
else this.x-=speed*delta;
}
if(i.isKeyDown(KeyEvent.VK_SPACE)){
if(missileReady){
try {
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File("C:/Users/Gabriel/Desktop/Programacao/Other/java/ShootGame/game/player/Fire.wav").getAbsoluteFile());
Clip clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.start();
} catch(Exception ex) {
System.out.println("Error with playing sound.");
ex.printStackTrace();
}
missile.x=this.x + 20;
missileRunning=true;
}
}
}
public void update(InputHandler inputP){
keyPlayer(2.0, inputP);
updateMissile(game.ShooterGame.backBuffer.getGraphics());
}
public void updateMissile(Graphics g){
if(missileRunning){
missileReady=false;
missile.update(g);
}
}
public Rectangle missileBounds(){
return new Rectangle(missile.x, game.player.Missile.y, 6, 18);
}
public void Draw(Graphics a){
a.drawImage(sprite,x,y,width,height,null);
}
public Rectangle getBounds(){
return new Rectangle(x,y,width,height);
}
}
导弹class:
public class Missile{
public BufferedImage sprite;
public static int x;
public static int y=504;
private int interval = 2000;
private Timer timer2;
private boolean isAlive = game.player.Player.missileRunning;
static final AtomicInteger count = new AtomicInteger();
public static boolean timerReady;
public Missile(int x){
this.x=x;
this.y=504;
this.sprite=sprite;
try{
URL url = this.getClass().getResource("missile.png");
sprite = ImageIO.read(url);
} catch(IOException e){System.out.println("Error loading image");}
}
public void Draw(Graphics g){
g.drawImage(sprite,x,this.y,6,18,null);
}
public void update(Graphics g){ //The problem
if(game.player.Player.missileRunning==true){
timerReady=true;
if(checkTimer()){
timer2 = new Timer();
timer2.schedule(new Move(), 0, interval);
}
this.y = y;
if(y <= 0){
game.player.Player.missileRunning=false;
timerReady=false;
y=504;
if(!checkTimer()){
timer2.cancel();
timer2.purge();
}
timer2=null;
reload();
}
}
}
public boolean checkTimer(){
if(timerReady){
return true;
} else {
return false;
}
}
class Move extends TimerTask{
public void run(){
int keeper = 3;
if(keeper>0) y-=interval/1000;
}
}
public synchronized void reload(){
Timer missileBetween = new Timer();
missileBetween.cancel();
missileBetween = new Timer();
TimerTask readyMissile = new TimerTask(){
public void run(){
game.player.Player.missileReady=true;
}
};
missileBetween.schedule(readyMissile, 20);
}
public static int getNumber(){
return count.get();
}
public static AtomicInteger getAtomic(){
return count;
}
}
(我不会发布我的所有程序,只发布与问题相关的部分)
(如有遗漏请指出)
谢谢
没有 运行 代码...
timerReady=true;
if(checkTimer()){
timer2 = new Timer();
timer2.schedule(new Move(), 0, interval);
}
基本上意味着 checkTimer
将始终 return true
,这意味着每次调用 update
时,您都会创建另一个 Timer
,这大概是在创建几十个定时器,全部独立更新游戏状态...这会导致对象加速...
这种逻辑和功能应该由主循环控制。在每个循环中,你应该检查对象的状态并做出移动或删除它的决定,这里不应该有其他 "timers" 或 "loops" 涉及...
哦,学会不用static
,它会给你带来更多的问题,然后它会在这种情况下解决。
还有...
Timer missileBetween = new Timer();
missileBetween.cancel();
missileBetween = new Timer();
...毫无意义,您创建一个本地 Timer
,取消它,即使它实际上不是,运行,创建一个新的本地实例并安排一些任务...
你丢失了对本地版本的引用,无法再修改...
你在ShooterGame.run()
方法中睡觉的时间是错误的。而不是 time = (1000 / fps) - (System.currentTimeMillis() - time);
为什么不简单地 time = 1000/fps
.
第一次进入运行中的while
循环时,time
大约是1000/fps
,因为两次方法调用之间的时间很少。通过 运行 的未来循环会给你一个负数(System.currentTimeMillis() 与 1000/fps 相比是一个很大的数字)这意味着你不会调用 Thread.sleep(time)
。在那里设置一个断点并告诉我们这是否正确。
我知道还有其他问题,但 none 似乎可以帮助我解决问题。在我的游戏中,有一个玩家发射一枚导弹,飞向负 y 直到它从屏幕上消失,然后回到原来的位置等待再次发射。然而,每次发射导弹时,它都会变得更快,就像 Timer() 中的延迟变短一样。代码:
主要class:
public class ShooterGame extends JFrame{
static int playerX=500;
static int playerY=520;
InputHandler input = new InputHandler(this);
public static Player player = new Player(playerX,playerY,50,50);
public static void main(String[] args){
ShooterGame game = new ShooterGame();
game.run();
System.exit(0);
}
static int windowWidth = 1300;
static int windowHeight = 600;
static int fps = 30;
public static BufferedImage backBuffer = new BufferedImage(windowWidth, windowHeight, BufferedImage.TYPE_INT_RGB);
public static Graphics bbg;
public void run(){
boolean running = true;
initialize();
while(running){
long time = System.currentTimeMillis();
update();
draw();
time = (1000 / fps) - (System.currentTimeMillis() - time);
if (time > 0) {
try{
Thread.sleep(time);
}
catch(Exception e){};
};
}
}
public void initialize(){
setTitle("--- Shooter Game ---");
setSize(windowWidth, windowHeight);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public void update(){
player.update(input);
}
public void draw(){
Graphics g = getGraphics();
Graphics bbg = backBuffer.getGraphics();
bbg.setColor(Color.BLACK);
bbg.fillRect(0, 0, windowWidth, windowHeight);
player.Draw(bbg);
enemie1.Draw(bbg,10,100);
if(game.player.Player.missileRunning) game.player.Player.missile.Draw(bbg);
g.drawImage(backBuffer, 0, 0, this);
}
public static Graphics getMainGraphics(){
return bbg;
}
}
玩家class:
public class Player{
private BufferedImage sprite;
public BufferedImage missileSprite;
public int x, y, width, height;
private int fixedX;
private final double speed = 5.0d;
public static Missile missile;
public static boolean missileRunning = false;
public static boolean missileReady = true;
public Player(int x, int y, int width, int height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
missile = new Missile(this.x);
try{
URL url = this.getClass().getResource("ship.png");
sprite = ImageIO.read(url);
} catch(IOException e){}
try{
URL url2 = this.getClass().getResource("missile.png");
missileSprite = ImageIO.read(url2);
} catch(IOException e){}
}
public void keyPlayer(double delta, InputHandler i){
if(i.isKeyDown(KeyEvent.VK_D)){
if(this.x>=1240) return;
else this.x+=speed*delta;
}
if(i.isKeyDown(KeyEvent.VK_A)){
if(this.x<=0) return;
else this.x-=speed*delta;
}
if(i.isKeyDown(KeyEvent.VK_SPACE)){
if(missileReady){
try {
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File("C:/Users/Gabriel/Desktop/Programacao/Other/java/ShootGame/game/player/Fire.wav").getAbsoluteFile());
Clip clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.start();
} catch(Exception ex) {
System.out.println("Error with playing sound.");
ex.printStackTrace();
}
missile.x=this.x + 20;
missileRunning=true;
}
}
}
public void update(InputHandler inputP){
keyPlayer(2.0, inputP);
updateMissile(game.ShooterGame.backBuffer.getGraphics());
}
public void updateMissile(Graphics g){
if(missileRunning){
missileReady=false;
missile.update(g);
}
}
public Rectangle missileBounds(){
return new Rectangle(missile.x, game.player.Missile.y, 6, 18);
}
public void Draw(Graphics a){
a.drawImage(sprite,x,y,width,height,null);
}
public Rectangle getBounds(){
return new Rectangle(x,y,width,height);
}
}
导弹class:
public class Missile{
public BufferedImage sprite;
public static int x;
public static int y=504;
private int interval = 2000;
private Timer timer2;
private boolean isAlive = game.player.Player.missileRunning;
static final AtomicInteger count = new AtomicInteger();
public static boolean timerReady;
public Missile(int x){
this.x=x;
this.y=504;
this.sprite=sprite;
try{
URL url = this.getClass().getResource("missile.png");
sprite = ImageIO.read(url);
} catch(IOException e){System.out.println("Error loading image");}
}
public void Draw(Graphics g){
g.drawImage(sprite,x,this.y,6,18,null);
}
public void update(Graphics g){ //The problem
if(game.player.Player.missileRunning==true){
timerReady=true;
if(checkTimer()){
timer2 = new Timer();
timer2.schedule(new Move(), 0, interval);
}
this.y = y;
if(y <= 0){
game.player.Player.missileRunning=false;
timerReady=false;
y=504;
if(!checkTimer()){
timer2.cancel();
timer2.purge();
}
timer2=null;
reload();
}
}
}
public boolean checkTimer(){
if(timerReady){
return true;
} else {
return false;
}
}
class Move extends TimerTask{
public void run(){
int keeper = 3;
if(keeper>0) y-=interval/1000;
}
}
public synchronized void reload(){
Timer missileBetween = new Timer();
missileBetween.cancel();
missileBetween = new Timer();
TimerTask readyMissile = new TimerTask(){
public void run(){
game.player.Player.missileReady=true;
}
};
missileBetween.schedule(readyMissile, 20);
}
public static int getNumber(){
return count.get();
}
public static AtomicInteger getAtomic(){
return count;
}
}
(我不会发布我的所有程序,只发布与问题相关的部分) (如有遗漏请指出)
谢谢
没有 运行 代码...
timerReady=true;
if(checkTimer()){
timer2 = new Timer();
timer2.schedule(new Move(), 0, interval);
}
基本上意味着 checkTimer
将始终 return true
,这意味着每次调用 update
时,您都会创建另一个 Timer
,这大概是在创建几十个定时器,全部独立更新游戏状态...这会导致对象加速...
这种逻辑和功能应该由主循环控制。在每个循环中,你应该检查对象的状态并做出移动或删除它的决定,这里不应该有其他 "timers" 或 "loops" 涉及...
哦,学会不用static
,它会给你带来更多的问题,然后它会在这种情况下解决。
还有...
Timer missileBetween = new Timer();
missileBetween.cancel();
missileBetween = new Timer();
...毫无意义,您创建一个本地 Timer
,取消它,即使它实际上不是,运行,创建一个新的本地实例并安排一些任务...
你丢失了对本地版本的引用,无法再修改...
你在ShooterGame.run()
方法中睡觉的时间是错误的。而不是 time = (1000 / fps) - (System.currentTimeMillis() - time);
为什么不简单地 time = 1000/fps
.
第一次进入运行中的while
循环时,time
大约是1000/fps
,因为两次方法调用之间的时间很少。通过 运行 的未来循环会给你一个负数(System.currentTimeMillis() 与 1000/fps 相比是一个很大的数字)这意味着你不会调用 Thread.sleep(time)
。在那里设置一个断点并告诉我们这是否正确。