如何在 Java 的 运行 时间创建和绘制对象
How to create and paint objects at run-time in Java
所以我的目标是让对象从框架右侧连续不断地流出,并在它们到达框架左侧后 disappear/be 将其删除。我将有一个球 object/image,它将静止但看似向前移动并跳过我希望创建的矩形对象。我希望在用户 'survives' 一段时间后停止创建对象。
基本上我的问题是如何加载我的 JFrame,然后在 运行 时间继续绘制对象。这样做一段时间,不断地绘制和删除对象。
好的,这是一个非常基本的例子...
基本上,它有两个 List
,一个是 "pool" 个可用对象,另一个是 List
个当前在屏幕上的对象。
目的是减少需要创建的新对象的数量,而是保留一个可用对象池,主循环可以从中提取对象。如果池是空的并且需要新对象,它将被创建,但一旦它离开屏幕,它将被替换到池中,假设我们需要它。
此示例为您提供了动态更改活动对象数量 (25-10, 000) 的能力。系统将尝试将它在两个列表中管理的对象总数平衡到这个精确数量。如果您经常更改活动对象的数量,您可能会考虑允许池有一个 "fudge" 因子,但我会把它留给你。
还有一个增长率因子,这意味着当你增加活动对象的数量时,在主循环的每个循环中,可用对象的数量都会增加该数量,这有助于减少创建 9, 000 个新对象可能会导致延迟 ;)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
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 {
public TestPane() {
setLayout(new BorderLayout());
GamePane gamePane = new GamePane();
JSlider slider = new JSlider(25, 10000);
add(gamePane);
add(slider, BorderLayout.SOUTH);
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
gamePane.setActiveEntityCount(slider.getValue());
}
});
slider.setValue(25);
}
}
public class GamePane extends JPanel {
private List<MovableEntity> poolOfEntities;
private List<MovableEntity> activeEntities;
private int activeCount;
private int growthRate = 100;
public GamePane() {
poolOfEntities = new ArrayList<>(25);
activeEntities = new ArrayList<>(25);
setFont(getFont().deriveFont(Font.BOLD, 48f));
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Iterator<MovableEntity> it = activeEntities.iterator();
while (it.hasNext()) {
MovableEntity entity = it.next();
if (entity.update(getWidth(), getHeight())) {
it.remove();
// This drop objects if the total number of objects exceeds the activeCount
if (poolOfEntities.size() + activeEntities.size() < activeCount) {
poolOfEntities.add(entity);
}
}
}
for (int growth = 0; growth < growthRate && activeEntities.size() < activeCount; growth++) {
MovableEntity entity = null;
if (poolOfEntities.isEmpty()) {
entity = createNewEntity();
} else {
entity = poolOfEntities.remove(0);
}
activeEntities.add(entity);
}
repaint();
}
});
timer.start();
}
protected MovableEntity createNewEntity() {
int width = getWidth();
int height = getHeight();
if (width == 0) {
width = getPreferredSize().width;
} else if (height == 0) {
height = getPreferredSize().height;
}
return new ShapeEntity(width, height);
}
public void setActiveEntityCount(int count) {
if (count != activeCount) {
activeCount = count;
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Iterator<MovableEntity> it = activeEntities.iterator();
while (it.hasNext()) {
MovableEntity entity = it.next();
entity.paint(g2d);
}
String text = Integer.toString(activeEntities.size());
FontMetrics fm = g2d.getFontMetrics();
int x = getWidth() - fm.stringWidth(text);
int y = (getHeight() - fm.getHeight() )+ fm.getAscent();
g2d.setColor(Color.WHITE);
g2d.drawString(text, x, y);
g2d.dispose();
}
}
public interface Entity {
public void paint(Graphics2D g);
}
public interface MovableEntity extends Entity {
public boolean update(int width, int height);
}
public static class ShapeEntity implements MovableEntity {
protected static final Color COLORS[] = {Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GRAY, Color.GREEN, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.WHITE, Color.YELLOW};
protected static final Random RND = new Random();
private Rectangle bounds;
private final Color color = COLORS[RND.nextInt(COLORS.length)];
private int xDelta;
public ShapeEntity(int width, int height) {
reset(width, height);
}
protected void reset(int width, int height) {
bounds = new Rectangle();
bounds.width = 5 + RND.nextInt(25);
bounds.height = 5 + RND.nextInt(25);
bounds.x = -bounds.width; // offscreen
bounds.y = RND.nextInt(height - bounds.height);
xDelta = 1 + RND.nextInt(8);
}
@Override
public boolean update(int width, int height) {
boolean reset = false;
bounds.x += xDelta;
if (bounds.x > width) {
reset(width, height);
reset = true;
}
return reset;
}
@Override
public void paint(Graphics2D g) {
g.setColor(color);
g.fill(bounds);
}
}
}
如果您想知道,我已经测试过将增长率设置为 500 并将最大活动计数设置为 100、000 没有太大问题;)
所以我的目标是让对象从框架右侧连续不断地流出,并在它们到达框架左侧后 disappear/be 将其删除。我将有一个球 object/image,它将静止但看似向前移动并跳过我希望创建的矩形对象。我希望在用户 'survives' 一段时间后停止创建对象。
基本上我的问题是如何加载我的 JFrame,然后在 运行 时间继续绘制对象。这样做一段时间,不断地绘制和删除对象。
好的,这是一个非常基本的例子...
基本上,它有两个 List
,一个是 "pool" 个可用对象,另一个是 List
个当前在屏幕上的对象。
目的是减少需要创建的新对象的数量,而是保留一个可用对象池,主循环可以从中提取对象。如果池是空的并且需要新对象,它将被创建,但一旦它离开屏幕,它将被替换到池中,假设我们需要它。
此示例为您提供了动态更改活动对象数量 (25-10, 000) 的能力。系统将尝试将它在两个列表中管理的对象总数平衡到这个精确数量。如果您经常更改活动对象的数量,您可能会考虑允许池有一个 "fudge" 因子,但我会把它留给你。
还有一个增长率因子,这意味着当你增加活动对象的数量时,在主循环的每个循环中,可用对象的数量都会增加该数量,这有助于减少创建 9, 000 个新对象可能会导致延迟 ;)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
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 {
public TestPane() {
setLayout(new BorderLayout());
GamePane gamePane = new GamePane();
JSlider slider = new JSlider(25, 10000);
add(gamePane);
add(slider, BorderLayout.SOUTH);
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
gamePane.setActiveEntityCount(slider.getValue());
}
});
slider.setValue(25);
}
}
public class GamePane extends JPanel {
private List<MovableEntity> poolOfEntities;
private List<MovableEntity> activeEntities;
private int activeCount;
private int growthRate = 100;
public GamePane() {
poolOfEntities = new ArrayList<>(25);
activeEntities = new ArrayList<>(25);
setFont(getFont().deriveFont(Font.BOLD, 48f));
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Iterator<MovableEntity> it = activeEntities.iterator();
while (it.hasNext()) {
MovableEntity entity = it.next();
if (entity.update(getWidth(), getHeight())) {
it.remove();
// This drop objects if the total number of objects exceeds the activeCount
if (poolOfEntities.size() + activeEntities.size() < activeCount) {
poolOfEntities.add(entity);
}
}
}
for (int growth = 0; growth < growthRate && activeEntities.size() < activeCount; growth++) {
MovableEntity entity = null;
if (poolOfEntities.isEmpty()) {
entity = createNewEntity();
} else {
entity = poolOfEntities.remove(0);
}
activeEntities.add(entity);
}
repaint();
}
});
timer.start();
}
protected MovableEntity createNewEntity() {
int width = getWidth();
int height = getHeight();
if (width == 0) {
width = getPreferredSize().width;
} else if (height == 0) {
height = getPreferredSize().height;
}
return new ShapeEntity(width, height);
}
public void setActiveEntityCount(int count) {
if (count != activeCount) {
activeCount = count;
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Iterator<MovableEntity> it = activeEntities.iterator();
while (it.hasNext()) {
MovableEntity entity = it.next();
entity.paint(g2d);
}
String text = Integer.toString(activeEntities.size());
FontMetrics fm = g2d.getFontMetrics();
int x = getWidth() - fm.stringWidth(text);
int y = (getHeight() - fm.getHeight() )+ fm.getAscent();
g2d.setColor(Color.WHITE);
g2d.drawString(text, x, y);
g2d.dispose();
}
}
public interface Entity {
public void paint(Graphics2D g);
}
public interface MovableEntity extends Entity {
public boolean update(int width, int height);
}
public static class ShapeEntity implements MovableEntity {
protected static final Color COLORS[] = {Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GRAY, Color.GREEN, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.WHITE, Color.YELLOW};
protected static final Random RND = new Random();
private Rectangle bounds;
private final Color color = COLORS[RND.nextInt(COLORS.length)];
private int xDelta;
public ShapeEntity(int width, int height) {
reset(width, height);
}
protected void reset(int width, int height) {
bounds = new Rectangle();
bounds.width = 5 + RND.nextInt(25);
bounds.height = 5 + RND.nextInt(25);
bounds.x = -bounds.width; // offscreen
bounds.y = RND.nextInt(height - bounds.height);
xDelta = 1 + RND.nextInt(8);
}
@Override
public boolean update(int width, int height) {
boolean reset = false;
bounds.x += xDelta;
if (bounds.x > width) {
reset(width, height);
reset = true;
}
return reset;
}
@Override
public void paint(Graphics2D g) {
g.setColor(color);
g.fill(bounds);
}
}
}
如果您想知道,我已经测试过将增长率设置为 500 并将最大活动计数设置为 100、000 没有太大问题;)