首頁 > 軟體

Java實現經典遊戲打磚塊遊戲的範例程式碼

2022-02-24 10:00:47

前言

《JAVA打磚塊》遊戲是自制的遊戲。玩家操作一根螢幕上水平的“棒子”,讓一顆不斷彈來彈去的“球”在撞擊作為過關目標消去的“磚塊”的途中不會落到螢幕底下。

主要設計

  • 設計遊戲介面,用swing實現
  • 設計磚塊,磚塊類,
  • 設計小球,滿屏亂跑的小球類,負責打碎磚塊
  • 設計棒子,左右移動的木頭板類
  • 球碰到磚塊、棒子與底下以外的三邊會反彈,落到底下會失去一顆球,把磚塊全部消去就可以破關。
  • 小球碰到磚塊的回撥演演算法設計
  • 小球碰到棒子的回撥演演算法設計
  • 設計碰撞特效,一個負責顯示爆炸效果的類
  • 設計音效類,碰撞時發出音效。

功能截圖

遊戲開始:

碰撞效果:

程式碼實現

遊戲核心類

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最核心的核心就是物件導向思想,對於這一個概念,終於悟到了一些。

以上就是Java實現經典遊戲打磚塊遊戲的範例程式碼的詳細內容,更多關於Java打磚塊遊戲的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com