我正试图让我的游戏中的一个桶自己向下浮动,我已经尝试了一些东西并查找了一些教程,但我似乎无法使其工作。也许我试图在paintcomponent()中做这个以错误的方式接近这个?在我的JPanel中,我还有一个car对象,它是由KeyListeners控制的JPanel的扩展。
// this is inside a JPanel class that I made that's painting out the game
OilBarrel barrel = new OilBarrel();
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
road.paintComponent(g);
this.add(car);
this.add(barrel);
barrel.setBounds(barrelX, barrel.getBarrelY(), 150, 180);
int carX = car.getCarX();
car.setBounds(carX, carY, 800, 400);
barrel.setBounds(550, barrel.getBarrelY(), 800, 400);
while (true) {
barrel.move();
repaint();
try {
Thread.sleep(1);
} catch (Exception e) {
}
}
}
// this is the oilbarrel class
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
public class OilBarrel extends JLabel{
int barrelX;
int barrelY = 0;
public OilBarrel() {
setVisible(true);
this.setFocusable(true);
Image img = new ImageIcon(this.getClass().getResource("/oil.png")).getImage();
this.setIcon(new ImageIcon(img));
}
public void move(){
if(barrelY<1000)
barrelY +=1;
}
public int getBarrelY() {
return barrelY;
}
}
答案 0 :(得分:3)
Swing是单线程的,这意味着for-loop
方法中的paintComponent
阻止了事件调度线程,阻止它更新UI。
有关详细信息,请参阅Concurrency in Swing
解决问题的最简单的解决方案是使用Swing Timer
,因为它允许在EDT之外安排一个小延迟,但是它会触发它在EDT上的更新,使其成为可能可以安全地从内部更新UI。
组件动画并不难,但确实需要不同的工作流程,例如:
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private int xDelta = 1;
public TestPane() {
setLayout(null);
label = new JLabel("*");
label.setSize(label.getPreferredSize());
label.setLocation(0, (200 - label.getHeight()) / 2);
add(label);
Timer timer = new Timer(10, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int x = label.getX();
x += xDelta;
if (x + label.getWidth() > getWidth()) {
x = getWidth() - label.getWidth();
xDelta *= -1;
} else if (x < 0) {
x = 0;
xDelta *= -1;
}
label.setLocation(x, label.getY());
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
作为个人喜好,我会避免使用组件来完成这项工作,并倾向于使用完整的自定义绘画路线。
这有很多原因,组件是复杂的对象,需要很多细节。向他们应用额外的效果或转换也很困难
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Rectangle box;
private int xDelta = 1;
public TestPane() {
setLayout(null);
box = new Rectangle(0, 75, 50, 50);
Timer timer = new Timer(10, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int x = box.x;
x += xDelta;
if (x + box.width > getWidth()) {
x = getWidth() - box.width;
xDelta *= -1;
} else if (x < 0) {
x = 0;
xDelta *= -1;
}
box.setLocation(x, box.y);
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.draw(box);
g2d.dispose();
}
}
}
答案 1 :(得分:0)
当线程需要更新GUI时,请使用SwingUtilities.invokeLater。 https://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html#invokeLater(java.lang.Runnable)
Thread t = new Thread(){
public void run(){
while( true ){
// queue on Event Dispatch Thread
SwingUtilities.invokeLater( new Runnable(){
public void run(){
barrel.move();
repaint();
}
});
try{
Thread.sleep( 100 );
}catch( InterruptedException ie ){}
}
}
};
t.start();
另外,查找Swing Timer。我认为这可能会更好。