如何在二维网格中生成多个圆圈? (JAVA)

How to spawn multiple circles in a 2d grid? (JAVA)

我目前正在尝试制作一个生成圆圈的游戏,玩家必须点击它才能获得分数。那里有一堆细节,但我想问这个问题。

由于原始代码来自 youtube 上“Bro Code”制作的贪吃蛇游戏,因此有些变量在那里未使用或不合适。我正在尝试使用他的代码作为基础。

如何在二维网格中无限制地生成多个圆圈?(我在始终触发的侦听器中测试了 spawnTarget() 方法,它只允许存在一个圆圈.)

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;


public class GamePanel extends JPanel implements ActionListener {

    static final int SCREEN_WIDTH = 1600;
    static final int SCREEN_HEIGHT = 1000;
    static final int UNIT_SIZE = 25;
    static final int GAME_UNITS = (SCREEN_WIDTH*SCREEN_HEIGHT)/UNIT_SIZE;
    static final int DELAY = 75;
    final int x[] = new int[GAME_UNITS];
    final int y[] = new int[GAME_UNITS];
    int bodyParts = 6;
    int applesEaten = 0;
    int appleX;
    int appleY;
    boolean running = false;
    Timer timer;
    Random random;

    //
    JPanel clockPanel;
    JLabel clock;
    long startTime;
    long endTime;
    //
    long elapsedSeconds;
    long elapsedTenthSeconds;
    //

    //

    GamePanel() {
        random = new Random();
        this.setPreferredSize(new Dimension(SCREEN_WIDTH,SCREEN_HEIGHT));
        this.setBackground(Color.black);
        this.setFocusable(true);
        this.addKeyListener(new MyKeyAdapter());
        startGame();
    }

    public void startGame() {
        running = true;
        timer = new Timer(DELAY,this);
        timer.start();
        clockMethod();
    }


    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        draw(g);
    }

    public void draw(Graphics g) {
        if (running) {
            // optional grid
            for(int i=0; i<SCREEN_WIDTH/UNIT_SIZE; i++) {
                g.drawLine(0, i*UNIT_SIZE, SCREEN_WIDTH, i*UNIT_SIZE);
                g.drawLine(i*UNIT_SIZE, 0, i*UNIT_SIZE, SCREEN_HEIGHT);
            }

            // apple
            g.setColor(Color.red);
            g.fillOval(appleX, appleY, UNIT_SIZE, UNIT_SIZE);

            // score
            g.setColor(Color.white);
            g.setFont(new Font("Courier New", Font.BOLD, 40));
            FontMetrics metrics = getFontMetrics(g.getFont());
            g.drawString(String.valueOf(applesEaten),(SCREEN_WIDTH - metrics.stringWidth(String.valueOf(applesEaten)))/2,2*g.getFont().getSize());
        }
        else {
            gameOver(g);
        }
    }

    public void newTargetCoords() {
        appleX = random.nextInt((int)(SCREEN_WIDTH/UNIT_SIZE))*UNIT_SIZE;
        appleY = random.nextInt((int)(SCREEN_HEIGHT/UNIT_SIZE))*UNIT_SIZE;
    }

    public void move() {

    }

    public void spawnTarget() {
        newTargetCoords();
    }

    public void checkApple() {
        if ((x[0] == appleX)&&(y[0] == appleY)) {
            bodyParts++;
            applesEaten++;
        }
    }

    public void checkCollisions() {

        if (!running) {
             timer.stop();
        }

    }

    public void gameOver(Graphics g) {
        // score
        g.setColor(Color.white);
        g.setFont(new Font("Courier New", Font.BOLD, 20));
        FontMetrics metrics1 = getFontMetrics(g.getFont());
        g.drawString("score: " + applesEaten,(SCREEN_WIDTH - metrics1.stringWidth("score: " + applesEaten))/2,g.getFont().getSize());

        // Game Over text
        g.setColor(Color.green);
        g.setFont(new Font("Courier New", Font.PLAIN, 40));
        FontMetrics metrics2 = getFontMetrics(g.getFont());
        g.drawString("game over",(SCREEN_WIDTH - metrics2.stringWidth("game over"))/2,SCREEN_HEIGHT/2);
    }

    public void restartGame() {
        setVisible(false);
        new GameFrame();
    }

    public void clockMethod() {
        clockPanel = new JPanel();

        clock = new JLabel("00:00");

        clockPanel.add(clock);

        startTime = System.currentTimeMillis();

        add(clockPanel);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (running) {
            move();
            checkApple();
            checkCollisions();
        }
        repaint();

        if(timer.isRunning())
        {
            endTime = System.currentTimeMillis();

            // elapsed quarter seconds for spawns
            elapsedTenthSeconds = (endTime-startTime)/100;

            // put elapsed seconds into variable
            elapsedSeconds = (endTime-startTime)/1000;

            // declare formatting
            int min = (int)elapsedSeconds/60;
            int sec = (int)elapsedSeconds%60;
            String minStr = (min<10 ? "0" : "")+min;
            String secStr = (sec<10 ? "0" : "")+sec;

            // display elapsed time (minutes:seconds)
            clock.setText(minStr+":"+secStr);

            // spawn circle
            spawnTarget();
        }
    }

    public class MyKeyAdapter extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_R) {
                restartGame();
            }
        }
    }
}

since the original code was from a snake game made by "Bro Code" on youtube

作为“一般”建议,我会避免使用 YouTube 来学习代码,除了很快就会过时之外,所以似乎花了很多时间来纠正来自 YouTube 的代码示例。

首先,我建议您花时间通读 Painting in AWT and Swing and Performing Custom Painting 以确保您对 Swing 中的绘制过程有基本的了解。

关于你的问题,是否需要跟踪可见的内容。根据您希望它如何工作,我可能有一个“实体”池,您可以随机选择这些实体并将它们移动到“可见实体”池中。当新的绘制周期运行时,您只需绘制“可见实体”即可。

您需要考虑一个实体应该显示多长时间以及每次您的“游戏循环”运行时,您需要检查是否有任何可见实体“死亡”,此时您将从“可见实体”池中移除并将它们放回“实体池”。

例如...

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 java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
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 TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class StopWatch {
        private Instant startedAt;
        private Duration duration;

        public void setDuration(Duration duration) {
            this.duration = duration;
        }

        public Duration getDuration() {
            return duration;
        }

        public void start() {
            startedAt = Instant.now();
        }

        public Instant getStartedAt() {
            return startedAt;
        }

        public Duration getTimeRemaining() {
            Instant startedAt = getStartedAt();
            Duration duration = getDuration();
            if (startedAt == null || duration == null) {
                return Duration.ZERO;
            }
            Duration runtime = Duration.between(startedAt, Instant.now());
            return duration.minus(runtime);
        }

        public boolean hasTimeRemaining() {
            Duration timeRemaining = getTimeRemaining();
            return timeRemaining.toMillis() > 0;
        }
    }

    public class Target {
        private int row;
        private int col;

        private StopWatch stopWatch = new StopWatch();

        public Target(int row, int col) {
            this.row = row;
            this.col = col;
        }

        public int getColumn() {
            return col;
        }

        public int getRow() {
            return row;
        }

        public void spawn(Duration lifeSpan) {
            stopWatch = new StopWatch();
            stopWatch.setDuration(lifeSpan);
            stopWatch.start();
        }

        public void die() {
            stopWatch = null;
        }

        public Instant getBirthDate() {
            if (stopWatch == null) {
                return null;
            }
            return stopWatch.getStartedAt();
        }

        public Duration getLifeSpan() {
            if (stopWatch == null) {
                return null;
            }
            return stopWatch.getDuration();
        }

        public Duration getTimeRemaining() {
            if (stopWatch == null) {
                return Duration.ZERO;
            }
            return stopWatch.getTimeRemaining();
        }

        public boolean isAlive() {
            if (stopWatch == null) {
                return false;
            }
            return stopWatch.hasTimeRemaining();
        }
    }

    public class TestPane extends JPanel {

        private List<Target> targets;
        private List<Target> visibleTargets;

        private int rows = 4;
        private int cols = 4;

        private Target clickedTarget;

        public TestPane() {
            targets = new ArrayList<>(getRows() * getColumns());
            visibleTargets = new ArrayList<>(getRows() * getColumns());

            for (int row = 0; row < rows; row++) {
                for (int col = 0; col < cols; col++) {
                    targets.add(new Target(row, col));
                }
            }

            Timer timer = new Timer(5, new ActionListener() {
                private Random rnd = new Random();
                private List<Target> deadTargets = new ArrayList<>(getRows() * getColumns());
                private StopWatch respawnStopWatch;

                protected void restartRespawnClock() {
                    // Spawn a new target every second
                    respawnStopWatch.setDuration(Duration.ofSeconds(rnd.nextInt(1) + 1));
                    respawnStopWatch.start();
                }

                @Override
                public void actionPerformed(ActionEvent e) {
                    if (respawnStopWatch == null) {
                        respawnStopWatch = new StopWatch();
                        restartRespawnClock();
                    }

                    if (!respawnStopWatch.hasTimeRemaining()) {
                        restartRespawnClock();
                        if (!targets.isEmpty()) {
                            Collections.shuffle(targets);
                            Target target = targets.remove(0);
                            Duration lifeSpan = Duration.ofSeconds(rnd.nextInt(5) + 3);
                            target.spawn(lifeSpan);
                            visibleTargets.add(target);
                        }
                    }
                    deadTargets.clear();
                    for (Target target : visibleTargets) {
                        if (!target.isAlive()) {
                            deadTargets.add(target);
                        }
                    }
                    visibleTargets.removeAll(deadTargets);
                    targets.addAll(deadTargets);
                    repaint();
                }
            });
            timer.start();

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    for (Target target : visibleTargets) {
                        Rectangle bounds = getBoundsFor(target);
                        if (bounds.contains(e.getPoint())) {
                            clickedTarget = target;
                            return;
                        }
                    }
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        public int getRows() {
            return rows;
        }

        public int getColumns() {
            return cols;
        }

        protected Rectangle getBoundsFor(Target target) {
            int width = getWidth() / getColumns();
            int height = getHeight() / getRows();

            int x = target.getColumn() * width;
            int y = target.getRow() * height;

            return new Rectangle(x, y, width, height);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            for (Target target : visibleTargets) {
                Rectangle bounds = getBoundsFor(target);
                if (target == clickedTarget) {
                    g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
                } else {
                    g2d.drawOval(bounds.x, bounds.y, bounds.width, bounds.height);
                }
            }
            g2d.dispose();
        }

    }
}

有几点需要注意...

  • 我没有缓存 Target 边界,因为它们是根据组件的当前大小动态计算的。实际上,您可以使用 ComponentListener 并在调用 componentResized 时使缓存无效,但这是您可以自行研究的额外增强功能。
  • 产生一个新目标之间的时间随机在 1-2 秒之间,这可以调整为使用毫秒而不是秒,但我相信大多数用户不会看到差异
  • 目标的随机生命周期为2-7秒,可随意修改。

该示例还演示了一种检测何时单击目标的简单方法,在上面的示例中,它会简单地导致填充目标。

我还会考虑为游戏和游戏结束屏幕使用单独的组件,可能会使用 CardLayout 在它们之间切换。这降低了 类.

的复杂性