JAVA 实现《JAVA打砖块》游戏

前言

《JAVA打砖块》游戏是自制的游戏。玩家操作一根萤幕上水平的“棒子”,让一颗不断弹来弹去的“球”在撞击作为过关目标消去的“砖块”的途中不会落到萤幕底下。。

主要设计

  1. 设计游戏界面,用swing实现
  2. 设计砖块,砖块类,
  3. 设计小球,满屏乱跑的小球类,负责打碎砖块
  4. 设计棒子,左右移动的木头板类
  5. 球碰到砖块、棒子与底下以外的三边会反弹,落到底下会失去一颗球,把砖块全部消去就可以破关。
  6. 小球碰到砖块的回调算法设计
  7. 小球碰到棒子的回调算法设计
  8. 设计碰撞特效,一个负责显示爆炸效果的类
  9. 设计音效类,碰撞时发出音效。

功能截图

游戏开始:

JAVA 实现《JAVA打砖块》游戏_第1张图片

碰撞效果:

JAVA 实现《JAVA打砖块》游戏_第2张图片

代码实现

游戏核心类


public class BricksGame extends BaseGame {
    private ArrayList<GameObject> list = new ArrayList<>();
    private RenderTask render;
    private Random random = new Random();
    private boolean over;

    private Image memImage;
    private Graphics memG;// 双缓冲的画布

    public BricksGame(int width, int height, String title) throws HeadlessException {
        super(width, height, title);
        super.setBgColor(new Color(0x23,0x30,0x41));
        initGame(width, height);
    }

    private void initGame(int width, int height) {
        setFps(60);
        over=false;

        // 获取窗口的内外大小
        Container contentPane = this.getContentPane();
        Dimension size = contentPane.getSize();
        int contentWidth = size.width;
        int contentHeight = size.height;
        this.setContentWidth(contentWidth);
        this.setContentHeight(contentHeight);
        //System.out.println(contentPane instanceof JPanel);// true
        //System.out.println(size.width);//582
        //System.out.println(size.height);//403
        // 窗口四个方向的边界值
        Insets insets = getInsets();
        //System.out.println(insets.top);
        //System.out.println(insets.bottom);
        //System.out.println(insets.left);
        //System.out.println(insets.right);

        Scene env = new Scene(width, height, new Margin(insets.left, insets.right, insets.top, insets.bottom));

        ImageIcon woodIcon = new ImageIcon("image/wood.png");
        int w = woodIcon.getIconWidth();
        int h = woodIcon.getIconHeight();
        Wood wood = new Wood(w, h, new Color(0x97, 0x5B, 0x12));
        wood.setScene(env);
        wood.setX(getWidth()/2 - w/2);
        wood.setY(getHeight()-50);
        wood.setImage(woodIcon.getImage());
        list.add(wood);

        Ball ball = new Ball(10, Color.WHITE);
        ImageIcon iconBall = new ImageIcon("image/ball2.png");
        ball.setImage(iconBall.getImage());
        ball.setScene(env);
        ball.setCoordinator(width / 2 - ball.getRadius(), wood.getY()-ball.getRadius()*2);
        ball.setWoodBar(wood);
        list.add(ball);

        Color[] colors = new Color[]{
                new Color(0xAA, 0xCF, 0x51),
                new Color(0xFC, 0xA9, 0x4B),
                new Color(0x73, 0xC7, 0xFF),
                Color.PINK,
                Color.GRAY
        };
        //ImageIcon brickIcon = new ImageIcon("image/brick_1.png");
        int brW = 60;
        int brH = 30;
        int start = 25;
        int brickX = start;
        int brickY = 100;
        int brickAreaWidth = getWidth() - start *2;
        int count = brickAreaWidth / brW - 1;
        int remainWidth = brickAreaWidth - count * brW;
        int intervalHort = brW + 3;
        start = brickX = (getWidth() - intervalHort * (count)) / 2;
        int intervalVert = brH + 3;

        HitBrick hitBrick = new HitBrick();
        for (int j = 0; j < 3; j++) {// brick line
            for(int i = 0; i< count; i++){// brick columns
                Brick brick = new Brick();
                brick.setColor(colors[i % colors.length]);
                //brick.setImage(brickIcon.getImage());
                brick.setWidth(brW);
                brick.setHeight(brH);
                brick.setX(brickX);
                brick.setY(brickY);
                brick.setBall(ball);
                brick.setHitListener(hitBrick);
                brickX += intervalHort;
                list.add(brick);
            }
            brickX = start;
            brickY += intervalVert;
        }

        // 双缓冲,在内存里面创建一个和窗口JFrame一样大小的Image
        memImage = createImage(getWidth(), getHeight());
        memG = memImage.getGraphics();
        GameOver gameOver = new GameOver(memG);
        ball.setGameOverListener(gameOver);

        // 键盘事件的监听
        Input input = new Input();
        input.init();
        addKeyListener(input);

        // 重新渲染画面任务
        render = new RenderTask(this);
        render.start();

        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                //super.mousePressed(e);
                System.out.println(String.format("x:%d, y:%d", e.getX(), e.getY()));
                //x:218,y:305,w:164,h:45
                if ((e.getX() >= 218 && e.getX() <=(218+164))
                        && (e.getY() >= 305 && e.getY() <= (305+45))
                ){
                    render.setExitd(true);
                    render = null;
                    memImage = null;
                    memG = null;
                    removeKeyListener(input);
                    removeMouseListener(this);
                    list.clear();

                    initGame(width, height);
                }
            }
        });
    }

    @Override
    public void paint(Graphics g) {
        clear(memG);// 将画布清空为背景色

        for (int i = 0; i < list.size(); i++) {
            list.get(i).onTick();
            list.get(i).draw(memG);
        }

        if (list.size() == 2){// 只剩下小球和挡板,则通关成功!
            Wood wood = (Wood) list.get(0);
            wood.setX(getWidth()/2 - wood.getWidth()/2);
            wood.setY(getHeight()-50);

            Ball ball = (Ball) list.get(1);
            ball.setCoordinator(getWidth() / 2 - ball.getRadius(), /*bottom*/wood.getY()-ball.getRadius()*2);
            ball.setMoving(false);

            String gameOver = "恭喜通关!";
            memG.setFont(new Font("Serif", Font.BOLD, 35));
            int stringWidth = memG.getFontMetrics().stringWidth(gameOver);
            memG.setColor(Color.RED);
            memG.drawString(gameOver, getWidth()/2 - stringWidth/2, getHeight()/2);

            stopRenderTask();
        }

        if (over) {
            String gameOver = "Game Over";
            memG.setFont(new Font("Serif", Font.BOLD, 35));
            int stringWidth = memG.getFontMetrics().stringWidth(gameOver);
            memG.setColor(Color.WHITE);
            memG.drawString(gameOver, getWidth()/2 - stringWidth/2, getHeight()/2);

            String playAgain = "重新开始";
            stringWidth = memG.getFontMetrics().stringWidth(playAgain);
            int increase = 16;
            int fontSize = memG.getFont().getSize();
            int rectH = (int) (fontSize * 1.3);
            int rx=getWidth()/2 - stringWidth/2 - increase /2;
            int ry=getHeight() - fontSize * 4-(rectH-fontSize)/2;
            int rw = stringWidth + increase;
            int rh = rectH;
            //System.out.println(String.format("x:%d,y:%d,w:%d,h:%d", rx, ry, rw, rh));
            memG.drawRect(rx, ry, rw, rh);
            memG.setColor(new Color(33, 165, 230));
            memG.drawString(playAgain, getWidth()/2 - stringWidth/2, getHeight() - fontSize * 3 - 5);
        }

        // 将内存Image的内容复制到窗口上
        g.drawImage(memImage, 0, 0, null);

        // 耗性能的轮询判断,一个对象是否要消失
        for (int i = 2; i < list.size(); i++) {// 0,1位置是挡板和小球,不能消失
            GameObject gameObject = list.get(i);
            if (gameObject.isGone()) {
                list.remove(i);
                --i;
            }
        }
    }

    private void stopRenderTask() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                render.setExitd(true);
            }
        }).start();
    }

    public void exit(){
        //System.exit(1);
    }

    public void clear(Graphics g){
        if (g!=null) {
            g.setColor(getBgColor());
            g.fillRect(0, 0, getWidth(), getHeight());
        }
    }

    // 小球碰到砖块的回调
    class HitBrick implements Brick.HitListener{
        private ExecutorService executorService = Executors.newFixedThreadPool(3);
        private File file = new File("audio/brick2.wav");
        private AudioClip audio;


        public HitBrick() {
            try {
                audio = Applet.newAudioClip(file.toURI().toURL());
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void hit(Brick brick) {
            executorService.execute(new PlayHitAudioTask(audio));

            ExplodeObject eo = new ExplodeObject();
            eo.x = brick.x;
            eo.y = brick.y;
            eo.width = brick.width;
            eo.height = brick.height;
            eo.color = brick.color;
            list.add(eo);
        }
    }

    // 游戏结束内容的绘制
    class GameOver implements Ball.GameOverListener{
        private Graphics memG;

        public GameOver(Graphics g) {
            this.memG = g;
        }

        @Override
        public void over() {
            over = true;
        }
    }
}

小球类


public class Ball extends RectGameObject{
    private int radius;
    private int speed = 4;// 小球移动速度
    private boolean moving;// 是否在移动
    private boolean gameOver;// 是否over
    private boolean postv;// 初始水平方向的移动左右方向
    private int horiMove;// 水平移动距离(正负号代表移动方向)
    private int vertMove;// 垂直移动距离(正负号代表移动方向)
    private Wood woodBar;//木头板
    private Image image;
    private Point center = new Point();
    private GameOverListener l;

    public Ball(int radius, Color color){
        this.radius = radius;
        this.color = color;
    }

    @Override
    public void draw(Graphics g) {
        g.setColor(color);
        //g.drawImage(image, x, y, null);
        g.fillOval(x, y, radius * 2, radius * 2);
    }

    @Override
    public void onTick() {
        if (!moving){
            if(Input.getKeyDown(KeyEvent.VK_UP)){
                postv = (Math.random() * 10) <= 4 ? true : false;
                moving = true;
                gameOver = false;
                horiMove = postv ? speed : -speed;
                vertMove = -speed;
            } /*else if(Input.getKeyDown(KeyEvent.VK_LEFT)){
                Wood wb = woodBar;
                if (!wb.isReachEdge()) {
                    transferBy(-wb.getSpeed(), 0);
                }
            } else if(Input.getKeyDown(KeyEvent.VK_RIGHT)){
                Wood wb = woodBar;
                if (!wb.isReachEdge()) {
                    transferBy(wb.getSpeed(), 0);
                }
            }*/
        }

        if (moving){
            // arrive at left and right edge
            Scene scene = getScene();
            Margin margin = scene.in;
            if (x <= margin.left || x >= scene.width - margin.right - radius * 2){
                horiMove = -horiMove;
            }

            // arrive at top edge
            if (y <= margin.top && vertMove < 0){
                vertMove = -vertMove;
            }

            // 小球落在了挡板上
            if(getCenter().x >= woodBar.getX()
                    && getCenter().x <= woodBar.getX() + woodBar.getWidth()
                    && Math.abs(getCenter().y - woodBar.y) <= radius
                    && vertMove > 0
            ){
                vertMove = -vertMove;
            }

            // arrive at bottom edge
            // 小球落在了窗口的底部,停住小球 GAME OVER
            if (y >= scene.height - margin.bottom - radius * 2){
                moving = false;
                gameOver = true;
                if (l != null)
                    l.over();
                return;
            }

            this.transferBy(horiMove, vertMove);
        }

    }

    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }

    public Wood getWoodBar() {
        return woodBar;
    }

    public void setWoodBar(Wood woodBar) {
        this.woodBar = woodBar;
    }

    public Image getImage() {
        return image;
    }

    public void setImage(Image image) {
        this.image = image;
    }

    public boolean isMoving() {
        return moving;
    }

    public void setMoving(boolean moving) {
        this.moving = moving;
    }

    public int getHoriMove() {
        return horiMove;
    }

    public void setHoriMove(int horiMove) {
        this.horiMove = horiMove;
    }

    public int getVertMove() {
        return vertMove;
    }

    public void setVertMove(int vertMove) {
        this.vertMove = vertMove;
    }

    @Override
    public int getWidth() {
        return 2 * radius;
    }

    @Override
    public int getHeight() {
        return getWidth();
    }

    public Point getCenter(){
        center.x = x + radius;
        center.y = y + radius;
        return center;
    }

    public boolean isGameOver() {
        return gameOver;
    }

    public void setGameOver(boolean gameOver) {
        this.gameOver = gameOver;
    }

    public GameOverListener getGameOverListener() {
        return l;
    }

    public void setGameOverListener(GameOverListener l) {
        this.l = l;
    }

    public interface GameOverListener{
        void over();
    }
}

砖块类

package game;

import java.awt.*;

public class Brick extends RectGameObject {
    private Ball ball;
    private Point leftTop = new Point();
    private Point leftBottom = new Point();
    private Point rightTop = new Point();
    private Point rightBottom = new Point();

    public Brick(){}

    @Override
    public void draw(Graphics g) {
        g.setColor(getColor());
        g.fillRect(x, y, getWidth(), getHeight());
    }

    @Override
    public void onTick() {
        if (ball.isMoving()) {

            //start 碰撞检测/
            boolean is = isSameQuadrant(ball.getCenter(), getLeftTop(), getRightBottom());
            if (is) {
                int r = ball.getRadius();
                Point lt = getLeftTop();
                Point lb = getLeftBottom();
                Point rt = getRightTop();
                Point rb = getRightBottom();
                Point c = ball.getCenter();
                int dx1 = Math.abs(c.x - lt.x), dy1 = Math.abs(c.y - lt.y);
                int dx2 = Math.abs(c.x - lb.x), dy2 = Math.abs(c.y - lb.y);
                int dx3 = Math.abs(c.x - rt.x), dy3 = Math.abs(c.y - rt.y);
                int dx4 = Math.abs(c.x - rb.x), dy4 = Math.abs(c.y - rb.y);

                if(((dx1*dx1) + (dy1*dy1) <= r*r)
                ||((dx2*dx2) + (dy2*dy2) <= r*r)
                ||((dx3*dx3) + (dy3*dy3) <= r*r)
                ||((dx4*dx4) + (dy4*dy4) <= r*r)){
                    System.out.println("发生了碰撞");
                    if (hitListener != null) {
                        hitListener.hit(this);
                    }
                    setGone(true);
                }

            } else {
                Point c = ball.getCenter();
                int squareW = ball.getRadius() * 2;
                int squareH = squareW;

                int brcx = x + getWidth() / 2;
                int brcy = y + getHeight() / 2;

                if((Math.abs(c.x - brcx) <= (squareW + getWidth())*0.5)
                        &&(Math.abs(c.y - brcy) <= (squareH + getHeight())*0.5)
                ){
                    System.out.println("......发生碰撞");
                    if (hitListener != null) {
                        hitListener.hit(this);
                    }
                    setGone(true);

                    /* 击中砖块,改变小球的方向 */
                    // 判断小球首先撞击的是砖块的左右还是上下侧,非常重要,否则出现不合理的移动方向。
                    double horizontal = (squareW + getWidth())*0.5 - Math.abs(c.x - brcx);
                    double vertical = (squareH + getHeight())*0.5 - Math.abs(c.y - brcy);
                    if (horizontal < vertical)
                        ball.setHoriMove(-ball.getHoriMove());
                    else
                        ball.setVertMove(-ball.getVertMove());
                }

            }
            //end碰撞检测//
        }
    }

    public Ball getBall() {
        return ball;
    }

    public void setBall(Ball ball) {
        this.ball = ball;
    }

    public Point getLeftTop(){
        leftTop.x = x;
        leftTop.y = y;
        return leftTop;
    }

    public Point getRightTop(){
        rightTop.x = x+getWidth();
        rightTop.y = y;
        return rightTop;
    }

    public Point getLeftBottom(){
        leftBottom.x = x;
        leftBottom.y = y + getHeight();
        return leftBottom;
    }

    public Point getRightBottom(){
        rightBottom.x = x + getWidth();
        rightBottom.y = y + getHeight();
        return rightBottom;
    }

    private HitListener hitListener;
    public void setHitListener(HitListener hitListener){
        this.hitListener = hitListener;
    }

    public interface HitListener {
        void hit(Brick brick);
    }
}

总结

通过此次的《JAVA打砖块》实现,让我对JAVA的相关知识有了进一步的了解,对java这门语言也有了比以前更深刻的认识。

java的一些基本语法,比如数据类型、运算符、程序流程控制和数组等,理解更加透彻。java最核心的核心就是面向对象思想,对于这一个概念,终于悟到了一些。

源码获取

源码下载地址:传送门------->

点赞,关注博主后,私聊博主免费获取
需要技术指导,写项目程序,等更多服务请联系博主

今天是持续写作的第 21 / 100 天。
可以关注我,点赞我、评论我、收藏我啦。

你可能感兴趣的:(JAVA,游戏库,java,游戏,开发语言)