Java多线程程序中弹球的图形
Graphics for bouncing ball in multi-threaded program in Java
我正在尝试改编我创建的用于在 Java 中弹跳球的程序。如果解决方案很明显,我很新,所以很抱歉。我使用以下代码创建了一个弹跳球数组,然后创建了两个弹跳球线程。
我试图在没有数组的情况下实现这一点。所以,每个线程只有 1 个球。
我觉得答案就在眼前,但我就是无法解决问题。任何帮助将不胜感激。
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AClass implements Runnable {
private JFrame mainFrame;
private DrawPanel drawPanel;
// private java.util.List<Ball> balls;
private int windowWidth = 640;
private int windowHeight = 480;
private String windowLabel = "Multi-threaded ball application";
public void run() {
//balls = new ArrayList<>();
//Scanner sc = new Scanner(System.in);
//System.out.print("Enter the number of balls you would like to create:");
//int n = sc.nextInt();
//sc.close();
/* Generate balls */
//for (int i = 0; i < n; i++) {
Ball ball = new Ball(
/* Random positions from 0 to windowWidth or windowHeight */
(int) Math.floor(Math.random() * windowWidth),
(int) Math.floor(Math.random() * windowHeight),
/* Random size from 10 to 30 */
(int) Math.floor(Math.random() * 20) + 10,
/* Random RGB colors*/
new Color(
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256))
),
/* Random velocities from -5 to 5 */
(int) Math.floor((Math.random() * 10) - 5),
(int) Math.floor((Math.random() * 10) - 5)
);
// balls.add(ball);
// }
/* Initialize program */
mainFrame = new JFrame();
drawPanel = new DrawPanel();
mainFrame.getContentPane().add(drawPanel);
mainFrame.setTitle(windowLabel);
mainFrame.setSize(windowWidth, windowHeight);
mainFrame.setVisible(true);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while (true) {
//for (Ball b: balls) {
ball.update();
// }
/* Give Swing 10 milliseconds to see the update! */
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
mainFrame.repaint();
}
}
public class DrawPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
// for (Ball b: balls) {
ball.draw(graphics);
// }
}
}
class Ball {//ball class
private int posX, posY, size;
private Color color;
private int vx = 5;
private int vy = 5;
public Ball(int posX, int posY, int size, Color color, int vx, int vy) {
this.posX = posX;
this.posY = posY;
this.size = size;
this.color = color;
this.vx = vx;
this.vy = vy;
}
void update() {
if (posX > mainFrame.getWidth() || posX < 0) {
vx *= -1;
}
if (posY > mainFrame.getHeight() || posY < 0) {
vy *= -1;
}
if (posX > mainFrame.getWidth()) {
posX = mainFrame.getWidth();
}
if (posX < 0) {
posX = 0;
}
if (posY > mainFrame.getHeight()) {
posY = mainFrame.getHeight();
}
if (posY < 0) {
posY = 0;
}
this.posX += vx;
this.posY += vy;
}
void draw(Graphics g) {
g.setColor(color);
g.fillOval(posX, posY, size, size);
}
}
public static void main(String[] args) {
AClass ex = new AClass();
Thread t1= new Thread(ex);
Thread t2 = new Thread(ex);
t1.start();
t2.start();
//System.out.println("Hi");
}
}
首先,Swing 不是线程安全的。您不应该从事件调度线程的上下文之外更新 UI(或 UI 所依赖的任何状态)。
有关详细信息,请参阅 Concurrency in Swing。
我不认为你的意图是正确的方法(试图让每个球都是自己的Thread
),你很快就会遇到各种各样的碰撞检测问题,因为每个球的状态总是相互独立地变化,并且不会很好地缩放。数组和 Swing Timer
将是更合适的解决方案。
这可能是我能得到你想要的东西的最接近点,问题是,为了绘制它,你需要引用 Ball
,所以我扩展了 Ball
来自 JPanel
而不是。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new SurfacePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Surface {
public Dimension getSize();
public void repaint();
}
public class SurfacePane extends JPanel implements Surface {
public SurfacePane() {
setLayout(null);
for (int index = 0; index < 10; index++) {
Ball ball = new Ball(
/* Random positions from 0 to windowWidth or windowHeight */
(int) Math.floor(Math.random() * 400),
(int) Math.floor(Math.random() * 400),
/* Random size from 10 to 30 */
(int) Math.floor(Math.random() * 20) + 10,
/* Random RGB colors*/
new Color(
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256))
),
/* Random velocities from -5 to 5 */
(int) Math.floor((Math.random() * 10) - 5),
(int) Math.floor((Math.random() * 10) - 5),
this
);
add(ball);
ball.start();
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public class Ball extends JPanel {
private int posX, posY, size;
private Color color;
private int vx = 5;
private int vy = 5;
private Surface surface;
private Timer timer;
public Ball(int posX, int posY, int size, Color color, int vx, int vy, Surface surface) {
this.posX = posX;
this.posY = posY;
this.size = size;
this.color = color;
this.vx = vx;
this.vy = vy;
this.surface = surface;
setBackground(color);
setSize(size, size);
setOpaque(false);
}
public void start() {
if (timer != null) {
timer.stop();
}
timer = new Timer(10, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
update();
surface.repaint();
}
});
timer.start();
}
public void stop() {
if (timer == null) {
return;
}
timer.stop();
}
protected void update() {
int width = surface.getSize().width;
int height = surface.getSize().height;
if (posX > width || posX < 0) {
vx *= -1;
}
if (posY > height || posY < 0) {
vy *= -1;
}
if (posX > width) {
posX = width;
}
if (posX < 0) {
posX = 0;
}
if (posY > height) {
posY = height;
}
if (posY < 0) {
posY = 0;
}
this.posX += vx;
this.posY += vy;
setLocation(posX, posY);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
g.setColor(color);
g.fillOval(0, 0, size, size);
}
}
}
这种方法(甚至 Thread
ed 方法)的问题是,它无法很好地扩展。例如,在我的实验过程中,在开始出现响应性问题(调整 window 的大小滞后,很多)之前,我只得到了大约 5, 000 个球,而使用 ArrayList
的大约 20, 000 个球(和一个 Timer
) - 老实说,帧速率很糟糕,但 UI 仍然相对敏感 - 我可以调整 window 的大小而不会出现延迟
单个 Timer
,基于数组的示例...
import java.awt.Color;
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 java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new SurfacePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Surface {
public Dimension getSize();
public void repaint();
}
public class SurfacePane extends JPanel implements Surface {
private List<Ball> balls = new ArrayList<>(32);
public SurfacePane() {
for (int index = 0; index < 20_000; index++) {
Ball ball = new Ball(
/* Random positions from 0 to windowWidth or windowHeight */
(int) Math.floor(Math.random() * 400),
(int) Math.floor(Math.random() * 400),
/* Random size from 10 to 30 */
(int) Math.floor(Math.random() * 20) + 10,
/* Random RGB colors*/
new Color(
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256))
),
/* Random velocities from -5 to 5 */
(int) Math.floor((Math.random() * 10) - 5),
(int) Math.floor((Math.random() * 10) - 5),
this
);
balls.add(ball);
}
Timer timer = new Timer(10, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Ball ball : balls) {
ball.update();
}
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Ball ball : balls) {
ball.paint(g2d);
}
g2d.dispose();
}
}
public class Ball {
private int posX, posY, size;
private Color color;
private int vx = 5;
private int vy = 5;
private Surface surface;
private Timer timer;
public Ball(int posX, int posY, int size, Color color, int vx, int vy, Surface surface) {
this.posX = posX;
this.posY = posY;
this.size = size;
this.color = color;
this.vx = vx;
this.vy = vy;
this.surface = surface;
}
protected void update() {
int width = surface.getSize().width;
int height = surface.getSize().height;
if (posX > width || posX < 0) {
vx *= -1;
}
if (posY > height || posY < 0) {
vy *= -1;
}
if (posX > width) {
posX = width;
}
if (posX < 0) {
posX = 0;
}
if (posY > height) {
posY = height;
}
if (posY < 0) {
posY = 0;
}
this.posX += vx;
this.posY += vy;
}
public void paint(Graphics2D g2d) {
g2d.setColor(color);
g2d.fillOval(posX, posY, size, size);
}
}
}
我正在尝试改编我创建的用于在 Java 中弹跳球的程序。如果解决方案很明显,我很新,所以很抱歉。我使用以下代码创建了一个弹跳球数组,然后创建了两个弹跳球线程。
我试图在没有数组的情况下实现这一点。所以,每个线程只有 1 个球。 我觉得答案就在眼前,但我就是无法解决问题。任何帮助将不胜感激。
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AClass implements Runnable {
private JFrame mainFrame;
private DrawPanel drawPanel;
// private java.util.List<Ball> balls;
private int windowWidth = 640;
private int windowHeight = 480;
private String windowLabel = "Multi-threaded ball application";
public void run() {
//balls = new ArrayList<>();
//Scanner sc = new Scanner(System.in);
//System.out.print("Enter the number of balls you would like to create:");
//int n = sc.nextInt();
//sc.close();
/* Generate balls */
//for (int i = 0; i < n; i++) {
Ball ball = new Ball(
/* Random positions from 0 to windowWidth or windowHeight */
(int) Math.floor(Math.random() * windowWidth),
(int) Math.floor(Math.random() * windowHeight),
/* Random size from 10 to 30 */
(int) Math.floor(Math.random() * 20) + 10,
/* Random RGB colors*/
new Color(
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256))
),
/* Random velocities from -5 to 5 */
(int) Math.floor((Math.random() * 10) - 5),
(int) Math.floor((Math.random() * 10) - 5)
);
// balls.add(ball);
// }
/* Initialize program */
mainFrame = new JFrame();
drawPanel = new DrawPanel();
mainFrame.getContentPane().add(drawPanel);
mainFrame.setTitle(windowLabel);
mainFrame.setSize(windowWidth, windowHeight);
mainFrame.setVisible(true);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while (true) {
//for (Ball b: balls) {
ball.update();
// }
/* Give Swing 10 milliseconds to see the update! */
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
mainFrame.repaint();
}
}
public class DrawPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
// for (Ball b: balls) {
ball.draw(graphics);
// }
}
}
class Ball {//ball class
private int posX, posY, size;
private Color color;
private int vx = 5;
private int vy = 5;
public Ball(int posX, int posY, int size, Color color, int vx, int vy) {
this.posX = posX;
this.posY = posY;
this.size = size;
this.color = color;
this.vx = vx;
this.vy = vy;
}
void update() {
if (posX > mainFrame.getWidth() || posX < 0) {
vx *= -1;
}
if (posY > mainFrame.getHeight() || posY < 0) {
vy *= -1;
}
if (posX > mainFrame.getWidth()) {
posX = mainFrame.getWidth();
}
if (posX < 0) {
posX = 0;
}
if (posY > mainFrame.getHeight()) {
posY = mainFrame.getHeight();
}
if (posY < 0) {
posY = 0;
}
this.posX += vx;
this.posY += vy;
}
void draw(Graphics g) {
g.setColor(color);
g.fillOval(posX, posY, size, size);
}
}
public static void main(String[] args) {
AClass ex = new AClass();
Thread t1= new Thread(ex);
Thread t2 = new Thread(ex);
t1.start();
t2.start();
//System.out.println("Hi");
}
}
首先,Swing 不是线程安全的。您不应该从事件调度线程的上下文之外更新 UI(或 UI 所依赖的任何状态)。
有关详细信息,请参阅 Concurrency in Swing。
我不认为你的意图是正确的方法(试图让每个球都是自己的Thread
),你很快就会遇到各种各样的碰撞检测问题,因为每个球的状态总是相互独立地变化,并且不会很好地缩放。数组和 Swing Timer
将是更合适的解决方案。
这可能是我能得到你想要的东西的最接近点,问题是,为了绘制它,你需要引用 Ball
,所以我扩展了 Ball
来自 JPanel
而不是。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new SurfacePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Surface {
public Dimension getSize();
public void repaint();
}
public class SurfacePane extends JPanel implements Surface {
public SurfacePane() {
setLayout(null);
for (int index = 0; index < 10; index++) {
Ball ball = new Ball(
/* Random positions from 0 to windowWidth or windowHeight */
(int) Math.floor(Math.random() * 400),
(int) Math.floor(Math.random() * 400),
/* Random size from 10 to 30 */
(int) Math.floor(Math.random() * 20) + 10,
/* Random RGB colors*/
new Color(
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256))
),
/* Random velocities from -5 to 5 */
(int) Math.floor((Math.random() * 10) - 5),
(int) Math.floor((Math.random() * 10) - 5),
this
);
add(ball);
ball.start();
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public class Ball extends JPanel {
private int posX, posY, size;
private Color color;
private int vx = 5;
private int vy = 5;
private Surface surface;
private Timer timer;
public Ball(int posX, int posY, int size, Color color, int vx, int vy, Surface surface) {
this.posX = posX;
this.posY = posY;
this.size = size;
this.color = color;
this.vx = vx;
this.vy = vy;
this.surface = surface;
setBackground(color);
setSize(size, size);
setOpaque(false);
}
public void start() {
if (timer != null) {
timer.stop();
}
timer = new Timer(10, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
update();
surface.repaint();
}
});
timer.start();
}
public void stop() {
if (timer == null) {
return;
}
timer.stop();
}
protected void update() {
int width = surface.getSize().width;
int height = surface.getSize().height;
if (posX > width || posX < 0) {
vx *= -1;
}
if (posY > height || posY < 0) {
vy *= -1;
}
if (posX > width) {
posX = width;
}
if (posX < 0) {
posX = 0;
}
if (posY > height) {
posY = height;
}
if (posY < 0) {
posY = 0;
}
this.posX += vx;
this.posY += vy;
setLocation(posX, posY);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
g.setColor(color);
g.fillOval(0, 0, size, size);
}
}
}
这种方法(甚至 Thread
ed 方法)的问题是,它无法很好地扩展。例如,在我的实验过程中,在开始出现响应性问题(调整 window 的大小滞后,很多)之前,我只得到了大约 5, 000 个球,而使用 ArrayList
的大约 20, 000 个球(和一个 Timer
) - 老实说,帧速率很糟糕,但 UI 仍然相对敏感 - 我可以调整 window 的大小而不会出现延迟
单个 Timer
,基于数组的示例...
import java.awt.Color;
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 java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new SurfacePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Surface {
public Dimension getSize();
public void repaint();
}
public class SurfacePane extends JPanel implements Surface {
private List<Ball> balls = new ArrayList<>(32);
public SurfacePane() {
for (int index = 0; index < 20_000; index++) {
Ball ball = new Ball(
/* Random positions from 0 to windowWidth or windowHeight */
(int) Math.floor(Math.random() * 400),
(int) Math.floor(Math.random() * 400),
/* Random size from 10 to 30 */
(int) Math.floor(Math.random() * 20) + 10,
/* Random RGB colors*/
new Color(
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256))
),
/* Random velocities from -5 to 5 */
(int) Math.floor((Math.random() * 10) - 5),
(int) Math.floor((Math.random() * 10) - 5),
this
);
balls.add(ball);
}
Timer timer = new Timer(10, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Ball ball : balls) {
ball.update();
}
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Ball ball : balls) {
ball.paint(g2d);
}
g2d.dispose();
}
}
public class Ball {
private int posX, posY, size;
private Color color;
private int vx = 5;
private int vy = 5;
private Surface surface;
private Timer timer;
public Ball(int posX, int posY, int size, Color color, int vx, int vy, Surface surface) {
this.posX = posX;
this.posY = posY;
this.size = size;
this.color = color;
this.vx = vx;
this.vy = vy;
this.surface = surface;
}
protected void update() {
int width = surface.getSize().width;
int height = surface.getSize().height;
if (posX > width || posX < 0) {
vx *= -1;
}
if (posY > height || posY < 0) {
vy *= -1;
}
if (posX > width) {
posX = width;
}
if (posX < 0) {
posX = 0;
}
if (posY > height) {
posY = height;
}
if (posY < 0) {
posY = 0;
}
this.posX += vx;
this.posY += vy;
}
public void paint(Graphics2D g2d) {
g2d.setColor(color);
g2d.fillOval(posX, posY, size, size);
}
}
}