如何为我的 Java 游戏添加流畅的动作?

问题描述

我的目标:在像乒乓球这样的游戏中,关键控制应该可以平稳地移动球棒。

期望:希望蝙蝠可以通过按键平滑地上下移动。 实际结果:蝙蝠像滞后或改变速度一样移动。

我尝试将速度变量与 lerp 一起使用,但它们滞后。 我尝试增加和减少 y 值,但效果更差。 我搜索过,但找不到解决方案。

我称之为矩形球棒和圆形乒乓球。 另外,这是我第一天在这里发帖

Screenshot from the game

这是通用代码:

import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Random;

import javax.swing.JPanel;
import javax.swing.Timer;

public class gamePanel extends JPanel implements ActionListener {

    /**
     * 
     */
    private static final long serialVersionUID = 6350159535293799269L;
    static final int SCREEN_WIDTH = 840;
    static final int SCREEN_HEIGHT = 610;
    static final int PONG_SPAWN_FRAME = 50;
    static final int padding = 30;

    
    
    static final int pongSize = 30;
    static final int batHeight = SCREEN_HEIGHT / 4;
    static final int batWidth = SCREEN_WIDTH / 32;
    static final int batSpeed = 4;
    static final int pongSpeed = 3;
    static final int scoreTextSize = 45;
    
    boolean running = false;
    
    int redX = padding;
    int redY = SCREEN_HEIGHT / 2;
    int redVelocity = 0;

    int blueX = SCREEN_WIDTH - padding;
    int blueY = SCREEN_HEIGHT / 2;
    int blueVelocity = 0;

    int redPoints = 0;
    int bluePoints = 0;

    int pongX = SCREEN_WIDTH / 4 - pongSize / 2;
    int pongY = 0;

    int pongVelocityX = pongSpeed;
    int pongVelocityY = pongSpeed;

    final int DELAY = 6;
    Timer timer;

    Random random;

    gamePanel() {
        random = new Random();
        pongY = random.nextInt((int) (SCREEN_HEIGHT - PONG_SPAWN_FRAME) + PONG_SPAWN_FRAME);
        if (pongVelocityY == 0) {
            if (pongY > SCREEN_HEIGHT / 2 + pongSize) {
                pongVelocityY = -pongSpeed;
            } else {
                pongVelocityY = pongSpeed;
            }
        }
        this.setPreferredSize(new Dimension(SCREEN_WIDTH,SCREEN_HEIGHT));
        this.setBackground(Color.DARK_GRAY);
        this.setFocusable(true);
        this.addKeyListener(new myKeyAdapter());
        startGame();
    }

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

    }

    void restart() {
        redX = padding;
        redY = SCREEN_HEIGHT / 2;
        redVelocity = 0;

        blueX = SCREEN_WIDTH - padding;
        blueY = SCREEN_HEIGHT / 2;
        blueVelocity = 0;

        pongX = SCREEN_WIDTH / 4 - pongSize / 2;
        pongY = 0;

        pongVelocityX = pongSpeed;
        pongVelocityY = pongSpeed;

        spawnPong();

    }

    void spawnPong() {
        pongY = random.nextInt((int) (SCREEN_HEIGHT - PONG_SPAWN_FRAME * 2)) + PONG_SPAWN_FRAME;
        if (pongVelocityY == 0) {
            if (pongY > SCREEN_HEIGHT / 2 + pongSize) {
                pongVelocityY = -pongSpeed;
            } else {
                pongVelocityY = pongSpeed;
            }
        }
    }

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

    void draw(Graphics g) {
        g.setColor(Color.LIGHT_GRAY);
        g.fillOval(pongX,pongY,pongSize,pongSize);
        g.setColor(Color.red);
        g.fillRect(redX - batWidth / 2,redY - batHeight / 2,batWidth,batHeight);
        g.setColor(Color.blue);
        g.fillRect(blueX - batWidth / 2,blueY - batHeight / 2,batHeight);
        g.setColor(Color.white);
        g.setFont(new Font("Roman New Times",Font.PLAIN,scoreTextSize));
        g.drawString(String.valueOf(redPoints),SCREEN_WIDTH / 4,scoreTextSize);
        g.drawString(String.valueOf(bluePoints),SCREEN_WIDTH / 4 * 3,scoreTextSize);
    }

    void pongMove() {
        pongX += pongVelocityX;
        pongY += pongVelocityY;
    }

    void batMove() {
        if (redY > batHeight / 2 && redVelocity < 0) {
            redY += redVelocity * batSpeed;
        }
        if (redY < SCREEN_HEIGHT - batHeight / 2 && redVelocity > 0) {
            redY += redVelocity * batSpeed;
        }

        blueY = pongY;
        /*
         * if (blueY>batHeight/2 && blueVelocity < 0) { blueY += blueVelocity *
         * pongSpeed; } if (blueY<SCREEN_HEIGHT-batHeight/2 && blueVelocity > 0) { blueY
         * += blueVelocity * pongSpeed; }
         */
    }

    void batLerp() {
        redVelocity *= 0.5f;
        blueVelocity *= 0.5f; 
    }

    void checkCollision() {
        if ((pongX > blueX - pongSize - batWidth / 2 && pongX < blueX - pongSize + batWidth / 2)
                && (pongY > blueY - batHeight / 2 && pongY < blueY + batHeight / 2)) {
            pongVelocityX *= -1;
        }
        if (pongX < redX + batWidth / 2 && (pongY > redY - batHeight / 2 && pongY < redY + batHeight / 2)) {
            pongVelocityX *= -1;
        }
        if (pongY < 0) {
            pongVelocityY *= -1;
        }
        if (pongY > SCREEN_HEIGHT - pongSize) {
            pongVelocityY *= -1;
        }
        if (pongX < -pongSize) {
            bluePoints += 1;
            restart();
        }
        if (pongX > SCREEN_WIDTH) {
            redPoints += 1;
            restart();
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (running) {
            checkCollision();
            batMove();
            batLerp();
            pongMove();
        }
        repaint();

    }

    public class myKeyAdapter extends KeyAdapter {
        public void keyPressed(KeyEvent e) {
            switch (e.getKeyCode()) {
            case KeyEvent.VK_W:
                redVelocity = -1;
                break;
            case KeyEvent.VK_S:
                redVelocity = 1;
                break;
            case KeyEvent.VK_UP:
                blueVelocity = -1;
                break;
            case KeyEvent.VK_DOWN:
                blueVelocity = 1;
                break;
            }
        }
    }
}

解决方法

原答案

我修改了拨片移动,这样拨片只有在您按住按键时才会移动。为了测试,我不得不放慢游戏速度。

我无法遵循您的代码。您的 JPanel 类做的工作太多,太难理解了。我将从普通的 Java getter / setter 类创建一个 Ball 类和一个 Paddle 类来保存球和桨的字段。

这是完整的可运行代码。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class ClassicPongGUI implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new ClassicPongGUI());
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Pong");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(new GamePanel());

        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public class GamePanel extends JPanel implements ActionListener {

        private static final long serialVersionUID = 6350159535293799269L;

        static final int SCREEN_WIDTH = 840;
        static final int SCREEN_HEIGHT = 610;
        static final int PONG_SPAWN_FRAME = 50;
        static final int padding = 30;

        static final int pongSize = 30;
        static final int batHeight = SCREEN_HEIGHT / 4;
        static final int batWidth = SCREEN_WIDTH / 32;
        static final int batSpeed = 20;
        static final int pongSpeed = 1;
        static final int scoreTextSize = 45;

        boolean running = false;

        int redX = padding;
        int redY = SCREEN_HEIGHT / 2;
        int redVelocity = 0;

        int blueX = SCREEN_WIDTH - padding;
        int blueY = SCREEN_HEIGHT / 2;
        int blueVelocity = 0;

        int redPoints = 0;
        int bluePoints = 0;

        int pongX = SCREEN_WIDTH / 4 - pongSize / 2;
        int pongY = 0;

        int pongVelocityX = pongSpeed;
        int pongVelocityY = pongSpeed;

        final int DELAY = 6;
        Timer timer;

        Random random;

        public GamePanel() {
            random = new Random();
            pongY = random.nextInt((int) (SCREEN_HEIGHT - PONG_SPAWN_FRAME) + PONG_SPAWN_FRAME);
            if (pongVelocityY == 0) {
                if (pongY > SCREEN_HEIGHT / 2 + pongSize) {
                    pongVelocityY = -pongSpeed;
                } else {
                    pongVelocityY = pongSpeed;
                }
            }
            this.setPreferredSize(new Dimension(SCREEN_WIDTH,SCREEN_HEIGHT));
            this.setBackground(Color.DARK_GRAY);
            this.setFocusable(true);
            this.addKeyListener(new myKeyAdapter());
            startGame();
        }

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

            redX = padding;
            redY = SCREEN_HEIGHT / 2;
            redVelocity = 0;
        }

        void restart() {
            blueX = SCREEN_WIDTH - padding;
            blueY = SCREEN_HEIGHT / 2;
            blueVelocity = 0;

            pongX = SCREEN_WIDTH / 4 - pongSize / 2;
            pongY = 0;

            pongVelocityX = pongSpeed;
            pongVelocityY = pongSpeed;

            spawnPong();

        }

        void spawnPong() {
            pongY = random.nextInt((int) (SCREEN_HEIGHT - PONG_SPAWN_FRAME * 2)) + PONG_SPAWN_FRAME;
            if (pongVelocityY == 0) {
                if (pongY > SCREEN_HEIGHT / 2 + pongSize) {
                    pongVelocityY = -pongSpeed;
                } else {
                    pongVelocityY = pongSpeed;
                }
            }
        }

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

        void draw(Graphics g) {
            g.setColor(Color.LIGHT_GRAY);
            g.fillOval(pongX,pongY,pongSize,pongSize);
            g.setColor(Color.red);
            g.fillRect(redX - batWidth / 2,redY - batHeight / 2,batWidth,batHeight);
            g.setColor(Color.blue);
            g.fillRect(blueX - batWidth / 2,blueY - batHeight / 2,batHeight);
            g.setColor(Color.white);
            g.setFont(new Font("Times New Roman",Font.PLAIN,scoreTextSize));
            g.drawString(String.valueOf(redPoints),SCREEN_WIDTH / 4,scoreTextSize);
            g.drawString(String.valueOf(bluePoints),SCREEN_WIDTH / 4 * 3,scoreTextSize);
        }

        void pongMove() {
            pongX += pongVelocityX;
            pongY += pongVelocityY;
        }

        void batMove() {
            if (redY > batHeight / 2 && redVelocity < 0) {
                redY += redVelocity * batSpeed;
            }
            if (redY < SCREEN_HEIGHT - batHeight / 2 && redVelocity > 0) {
                redY += redVelocity * batSpeed;
            }

            blueY = pongY;
            /*
             * if (blueY>batHeight/2 && blueVelocity < 0) { blueY += blueVelocity *
             * pongSpeed; } if (blueY<SCREEN_HEIGHT-batHeight/2 && blueVelocity > 0) { blueY
             * += blueVelocity * pongSpeed; }
             */
        }

        void batLerp() {
            redVelocity = 0;
            blueVelocity = 0;
        }

        void checkCollision() {
            if ((pongX > blueX - pongSize - batWidth / 2 
                    && pongX < blueX - pongSize + batWidth / 2)
                    && (pongY > blueY - batHeight / 2 
                    && pongY < blueY + batHeight / 2)) {
                pongVelocityX *= -1;
            }
            if (pongX < redX + batWidth / 2 && (pongY > redY - batHeight / 2 
                    && pongY < redY + batHeight / 2)) {
                pongVelocityX *= -1;
            }
            if (pongY < 0) {
                pongVelocityY *= -1;
            }
            if (pongY > SCREEN_HEIGHT - pongSize) {
                pongVelocityY *= -1;
            }
            if (pongX < -pongSize) {
                bluePoints += 1;
                restart();
            }
            if (pongX > SCREEN_WIDTH) {
                redPoints += 1;
                restart();
            }
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (running) {
                checkCollision();
                batMove();
                batLerp();
                pongMove();
            }
            repaint();

        }

        public class myKeyAdapter extends KeyAdapter {
            @Override
            public void keyPressed(KeyEvent e) {
                switch (e.getKeyCode()) {
                case KeyEvent.VK_W:
                    redVelocity = -1;
                    break;
                case KeyEvent.VK_S:
                    redVelocity = 1;
                    break;
                case KeyEvent.VK_UP:
                    redVelocity = -1;
                    break;
                case KeyEvent.VK_DOWN:
                    redVelocity = 1;
                    break;
                }
            }
        }
        
    }

}

修改后的答案

我认为这会是一个有趣的项目,所以我重新编写了代码。这是 GUI 的显示方式。

Pong GUI

W 和向上箭头键向上移动蓝色拨片。 S 和向下箭头键向下移动蓝色拨片。必须按住这些键才能继续运动。释放键停止桨运动,

空格键重新开始游戏。

当我创建 Swing GUI 时,我使用 model / view / controller (MVC) 模式。这种模式使我能够将关注点分开,一次专注于应用程序的一小部分。

对于 Swing GUI,MVC 模式定义为:

  1. 视图从模型中读取信息。
  2. 视图不会更新模型。
  3. 控制器更新模型并重新绘制/重新验证视图。

Swing GUI 可以有许多控制器类。我为此 Pong 游戏创建了三个控制器类。

我创建了 8 个不同的类。三个类构成应用程序模型,两个类定义视图,三个控制器类管理应用程序。

型号

我创建了一个 Ball 类来保存创建球和在屏幕上移动球所需的字段。我使用 Point 来保持中心点,使用 int 来保持球的半径。

Ball 类不是典型的 getter/setter 类。 moveBall 方法使用极坐标来计算球的新位置。将极坐标转换为笛卡尔坐标以在绘图 JPanel 上绘制球。

Paddle 类创建一个桨。我将桨的形状存储在 Rectangle 中。这是一个更典型的 Java getter/setter 类,尽管有代码来限制桨可以在 Y 轴上移动多远。

GameModel 类是应用程序模型。该类包含一个 Ball 实例和两个 Paddle 实例,以及两个分数 ints 和游戏运行的帧速率。此类的实例通过视图和控制器类传递。

查看

我创建了一个 JFrame 和一个绘图 JPanel。绘图 JPanel 绘制球的当前位置、两个球拍和得分。时期。没有别的。

控制器类负责移动球和桨并重新绘制 JPanel

我使用 KeyBindings 将键盘按键设置为各种操作。使用 KeyBindings,我不必担心绘图 JPanel 是否有焦点。

控制器

TimerListener 类为 Pong 游戏制作动画。

MovePaddleAction 类根据按下的键向上或向下移动拨片。必须按住该键才能继续运动。

RestartAction 类重新开始游戏。

代码

这是完整的可运行代码。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;
import java.util.Random;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class ClassicPongGUI implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new ClassicPongGUI());
    }
    
    private final Dimension panelSize;
    
    private GameModel gameModel;
    
    private GamePanel gamePanel;
    
    private Timer timer;
    
    public ClassicPongGUI() {
        this.panelSize = new Dimension(840,610);
        this.gameModel = new GameModel(panelSize);
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Pong");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        this.gamePanel = new GamePanel(this,gameModel);
        frame.add(gamePanel,BorderLayout.CENTER);

        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
        
        setKeyBindings();
        startTimer();
    }
    
    private void setKeyBindings() {
        String moveUp = "moveUp";
        String moveDown = "moveDown";
        String restart = "restart";
        
        InputMap inputMap = gamePanel.getInputMap(
                JPanel.WHEN_IN_FOCUSED_WINDOW);
        ActionMap actionMap = gamePanel.getActionMap();
        
        inputMap.put(KeyStroke.getKeyStroke(
                KeyEvent.VK_W,0),moveUp);
        inputMap.put(KeyStroke.getKeyStroke(
                KeyEvent.VK_UP,moveUp);
        inputMap.put(KeyStroke.getKeyStroke(
                KeyEvent.VK_S,moveDown);
        inputMap.put(KeyStroke.getKeyStroke(
                KeyEvent.VK_DOWN,moveDown);
        inputMap.put(KeyStroke.getKeyStroke(
                KeyEvent.VK_SPACE,restart);
        
        int increment = 15;
        actionMap.put(moveUp,new MovePaddleAction(
                this,gameModel,-increment));
        actionMap.put(moveDown,increment));
        actionMap.put(restart,new RestartAction(
                this,gameModel));
    }
    
    public void startTimer() {
        int delay = 1000 / gameModel.getFrameRate();
        timer = new Timer(delay,new TimerListener(this,gameModel));
        timer.setInitialDelay(5000);
        timer.start();
    }
    
    public void stopTimer() {
        if (timer != null) {
            timer.stop();
        }
    }
    
    public Dimension getPanelSize() {
        return panelSize;
    }

    public void repaint() {
        gamePanel.repaint();
    }

    public class GamePanel extends JPanel {

        private static final long serialVersionUID = 1L;
        
        private final GameModel model;
        
        public GamePanel(ClassicPongGUI frame,GameModel model) {
            this.model = model;
            
            this.setBackground(Color.DARK_GRAY);
            this.setPreferredSize(frame.getPanelSize());
        }
        
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            
            drawBall(g);
            drawPaddles(g);
            drawScores(g);
        }

        private void drawBall(Graphics g) {
            Ball ball = model.getBall();
            g.setColor(ball.getColor());
            int radius = ball.getRadius();
            int diameter = radius + radius;
            Point point = ball.getCenterPoint();
            g.fillOval(point.x - radius,point.y - radius,diameter,diameter);
        }

        private void drawPaddles(Graphics g) {
            Paddle[] paddles = model.getPaddles();
            for (Paddle paddle : paddles) {
                g.setColor(paddle.getColor());
                Rectangle r = paddle.getPaddle();
                g.fillRect(r.x,r.y,r.width,r.height);
            }
        }

        private void drawScores(Graphics g) {
            int[] scores = model.getScore();
            int scoreTextSize = 45;
            g.setColor(Color.WHITE);
            g.setFont(new Font("Times New Roman",scoreTextSize));
            g.drawString(String.valueOf(scores[0]),panelSize.width / 4,scoreTextSize);
            g.drawString(String.valueOf(scores[1]),panelSize.width * 3 / 4,scoreTextSize);
        }
        
    }
    
    public class TimerListener implements ActionListener {
        
        private final ClassicPongGUI frame;
        
        private final GameModel model;

        public TimerListener(ClassicPongGUI frame,GameModel model) {
            this.frame = frame;
            this.model = model;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            int frameRate = model.getFrameRate();
            model.getBall().moveBall(frameRate);
            
            Point point = model.getBall().getCenterPoint();
            Paddle[] paddles = model.getPaddles();
            for (int i = 0; i < paddles.length; i++) {
                Rectangle p = paddles[i].getPaddle();
                int y = point.y - p.height / 2;
                if (i == 0) {
                    paddles[i].setPaddle(y);
                }
                model.contactsPaddle();
            }
            
            if (point.x < 0) {
                model.incrementScore(1,1);
                model.resetBall();
            }
            
            if (point.x > frame.getPanelSize().width) {
                model.incrementScore(0,1);
                model.resetBall();
            }
            
            frame.repaint();
        }
        
    }
    
    public class MovePaddleAction extends AbstractAction {

        private static final long serialVersionUID = 1L;
        
        private final int increment;

        private final ClassicPongGUI frame;
        
        private final GameModel model;

        public MovePaddleAction(ClassicPongGUI frame,GameModel model,int increment) {
            this.frame = frame;
            this.model = model;
            this.increment = increment;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            model.getPaddles()[1].movePaddle(increment);
            frame.repaint();
        }
        
    }
    
    public class RestartAction extends AbstractAction {

        private static final long serialVersionUID = 1L;

        private final ClassicPongGUI frame;
        
        private final GameModel model;

        public RestartAction(ClassicPongGUI frame,GameModel model) {
            this.frame = frame;
            this.model = model;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            frame.stopTimer();
            model.restartGame();
            frame.startTimer();
            frame.repaint();
        }
        
    }
        
    public class GameModel {

        private int[] score;
        
        /** frames per second **/
        private final int frameRate;

        private Ball ball;

        private final Dimension panelSize;

        private Paddle[] paddles;

        public GameModel(Dimension panelSize) {
            this.panelSize = panelSize;
            this.score = new int[2];
            this.paddles = new Paddle[2];
            this.frameRate = 50;

            int ballRadius = 15;
            Point centerPoint = new Point(panelSize.width / 2,panelSize.height / 2);
            this.ball = new Ball(centerPoint,ballRadius,panelSize,Color.LIGHT_GRAY);

            int batWidth = panelSize.width / 32;
            int batHeight = panelSize.height / 4;
            int padding = 30;
            int x = padding;
            int y = calculateCenterY(batHeight);
            Rectangle r = new Rectangle(x,y,batHeight);
            this.paddles[0] = new Paddle(r,panelSize.height,Color.RED);

            x = panelSize.width - padding - batWidth;
            r = new Rectangle(x,batHeight);
            this.paddles[1] = new Paddle(r,Color.BLUE);

            resetScore();
        }
        
        public void restartGame() {
            int batHeight = panelSize.height / 4;
            resetBall();
            paddles[0].setPaddle(calculateCenterY(batHeight));
            paddles[1].setPaddle(calculateCenterY(batHeight));
            resetScore();
        }
        
        public void resetBall() {
            ball.setCenterPoint(new Point(panelSize.width / 2,panelSize.height / 2));
        }

        private int calculateCenterY(int batHeight) {
            return (panelSize.height - batHeight) / 2;
        }
        
        public void contactsPaddle() {
            int radius = ball.getRadius();
            int diameter = radius + radius;
            Point point = ball.getCenterPoint();
            Rectangle b = new Rectangle(point.x - radius,diameter);
            for (Paddle paddle : paddles) {
                if (paddle.getPaddle().intersects(b)) {
                    ball.reverseXDirection(frameRate);
                }
            }
            
        }

        public void resetScore() {
            score[0] = 0;
            score[1] = 0;
        }

        public void incrementScore(int index,int increment) {
            score[index] += increment;
        }

        public int[] getScore() {
            return score;
        }

        public Ball getBall() {
            return ball;
        }

        public Paddle[] getPaddles() {
            return paddles;
        }

        public int getFrameRate() {
            return frameRate;
        }

    }

    public class Paddle {

        private final int upperBound;

        private final Color color;

        private Rectangle paddle;

        public Paddle(Rectangle paddle,int upperBound,Color color) {
            this.paddle = paddle;
            this.upperBound = upperBound;
            this.color = color;
        }

        public void movePaddle(int increment) {
            paddle.y += increment;
            limitMovement();
        }
        
        public void setPaddle(int y) {
            paddle.y = y;
            limitMovement();
        }

        private void limitMovement() {
            paddle.y = Math.max(paddle.y,0);
            int height = paddle.y + paddle.height;
            height = Math.min(height,upperBound);
            paddle.y = height - paddle.height;
        }

        public Rectangle getPaddle() {
            return paddle;
        }

        public Color getColor() {
            return color;
        }

    }

    public class Ball {
        
        /** Pixels per second **/
        private final double velocity;

        private final int radius;
        private int angle;

        private final Color color;

        private final Dimension panelSize;

        private Point centerPoint;
        
        private Point2D.Double doubleCenterPoint;
        
        private Random random;

        public Ball(Point centerPoint,int radius,Dimension panelSize,Color color) {
            this.centerPoint = centerPoint;
            setDoubleCenterPoint(centerPoint);
            this.radius = radius;
            this.panelSize = panelSize;
            this.color = color;
            this.velocity = 300.0;
            this.random = new Random();
            setRandomAngle();
        }
        
        public void reverseXDirection(int frameRate) {
            angle = bounceXAngle(angle);
            angle += (angle < 0) ? 360 : 0;
            moveBall(frameRate);
        }
        
        public void moveBall(int frameRate) {
            double theta = Math.toRadians(angle);
            double distance = velocity / frameRate;
            doubleCenterPoint.x = Math.cos(theta) * distance + 
                    doubleCenterPoint.x;
            doubleCenterPoint.y = Math.sin(theta) * distance + 
                    doubleCenterPoint.y;
            
            if (doubleCenterPoint.y < radius) {
                double adjustment = radius - doubleCenterPoint.y;
                doubleCenterPoint.y += adjustment;
                angle = bounceYAngle(angle);
            }
            
            if ((doubleCenterPoint.y + radius) > panelSize.height) {
                double adjustment = panelSize.height - 
                        doubleCenterPoint.y - radius;
                doubleCenterPoint.y += adjustment;
                angle = bounceYAngle(angle);
            }
            
            int x = (int) Math.round(doubleCenterPoint.x);
            int y = (int) Math.round(doubleCenterPoint.y);
            this.centerPoint = new Point(x,y);
        }
        
        private int bounceXAngle(int angle) {
            return 180 - angle;
        }

        
        private int bounceYAngle(int angle) {
            return 360 - angle;
        }
        
        public Point getCenterPoint() {
            return centerPoint;
        }

        public void setCenterPoint(Point centerPoint) {
            this.centerPoint = centerPoint;
            setRandomAngle();
            setDoubleCenterPoint(centerPoint);
        }

        private void setDoubleCenterPoint(Point centerPoint) {
            this.doubleCenterPoint = 
                    new Point2D.Double(centerPoint.x,centerPoint.y);
        }
        
        private void setRandomAngle() {
            int[] angles = {30,150,210,330 };
            this.angle = angles[random.nextInt(angles.length)];
        }

        public int getRadius() {
            return radius;
        }

        public Color getColor() {
            return color;
        }

    }

}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...