如何在 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 没有太大问题;)