javafx/javaFx打砖块游戏【背景音乐、闯关、界面美观】/java项目/java程序设计

javaFx打砖块:

环境:jdk17+maven+javaFx17

一.功能:

  1. 主界面
    1. 音乐关闭 开启
    2. 主题界面切换
    3. 游戏介绍
    4. 游戏排行版(前十名)

  1. 游戏UI化
  1. 加载游戏背景图片
  2. 加载小球图片
  3. 加载面板图面
  4. 加载砖块图片
  5. 加载砖块裂纹

  1. 游戏闯关机制和计分制度
  1. 右边是计分面板
  2. 游戏分为三关,每闯关一个加大难度,面板缩短

二.项目结构

三.相关类设计:

1.石砖类Brick:

package com.breakout_clone.model.Brick;

import com.breakout_clone.model.Ball.Ball;
import com.breakout_clone.model.RightMenu;
import javafx.geometry.Point2D;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;

import java.util.Random;

/**
 * Brick superClass
 * Contains inner classes Crack,
 *Responsible for dealing with brick parameters
 * @author xhj- modified
 */
abstract public class Brick  {

    public static final int MIN_CRACK = 1;
    public static final int MAX_CRACK = 3;
    public static final int DEF_CRACK_DEPTH = 1;
    public static final int DEF_STEPS = 35;
    public static final int UP_IMPACT = 100;
    public static final int DOWN_IMPACT = 200;
    public static final int LEFT_IMPACT = 300;
    public static final int RIGHT_IMPACT = 400;
    private Pane game_pane;
    private GraphicsContext gc;
    private Path brickCrack;
    protected Point2D pos;
    protected Rectangle size;

    protected RightMenu rightMenu;

    /**
     * set GamePane
     * @param game_pane
     */
    public void setGamePane(Pane game_pane) {
        this.game_pane =game_pane;
    }


    public void draw(GraphicsContext gc) {

    }

    /**
     * Crack class
     * Represents a crack in a brick
     */
    public class Crack{
        private static final int CRACK_SECTIONS = 3;
        private static final double JUMP_PROBABILITY = 0.7;
        public static final int LEFT = 10;
        public static final int RIGHT = 20;
        public static final int UP = 30;
        public static final int DOWN = 40;
        public static final int VERTICAL = 100;
        public static final int HORIZONTAL = 200;
        private Path crack;
        private int crackDepth;
        private int steps;

        /**
         * Crack constructor
         * load a path
         * @param crackDepth
         * @param steps
         */
        public Crack(int crackDepth, int steps){
            crack = new Path();
            this.crackDepth = crackDepth;
            this.steps = steps;
        }

        /**
         * getCrackPath
         * @return
         */
        public Path getCrackPath() {
            return crack;
        }

        /**
         * reset CrackPath
         */
        public void reset(){
            crack=new Path();
        }

        /**
         * makeCrack
         * @param point
         * @param direction
         */
        protected void makeCrack(Point2D point, int direction){
            Rectangle bounds = (Rectangle) Brick.this.brickFace;
            Point2D impact = new Point2D((int)point.getX(),(int)point.getY());
            Point2D start = new Point2D(0,0);
            Point2D end = new Point2D(0,0);


            switch(direction){
                case LEFT:
                    start=new Point2D(bounds.getX() + bounds.getWidth(), bounds.getY());
                    end=new Point2D(bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight());
                    Point2D tmp = makeRandomPoint(start,end,VERTICAL);
                    makeCrack(impact,tmp);

                    break;
                case RIGHT:
                    start=new Point2D(bounds.getX() , bounds.getY());
                    end=new Point2D(bounds.getX(), bounds.getY() + bounds.getHeight());
                    tmp = makeRandomPoint(start,end,VERTICAL);
                    makeCrack(impact,tmp);

                    break;
                case UP:
                    start=new Point2D(bounds.getX() , bounds.getY()+bounds.getHeight());
                    end=new Point2D(bounds.getX()+bounds.getWidth(), bounds.getY() + bounds.getHeight());
                    tmp = makeRandomPoint(start,end,HORIZONTAL);
                    makeCrack(impact,tmp);
                    break;
                case DOWN:
                    start=new Point2D(bounds.getX() , bounds.getY());
                    end=new Point2D(bounds.getX()+bounds.getWidth(), bounds.getY() );
                    tmp = makeRandomPoint(start,end,HORIZONTAL);
                    makeCrack(impact,tmp);
                    break;

            }
        }

        /**
         * create a Crack and paint to gameBoard
         * @param start  start point
         * @param end   end point
         */
        protected void makeCrack(Point2D start, Point2D end){
            MoveTo moveTo = new MoveTo();
            moveTo.setX(start.getX());
            moveTo.setY(start.getY());
            crack.getElements().add(moveTo);

            double w = (end.getX() - start.getX()) / (double)steps;
            double h = (end.getY() - start.getY()) / (double)steps;

            int bound = crackDepth;
            int jump  = bound * 5;
            double x,y;
            LineTo lineTo =null;
            for(int i = 1; i < steps;i++){
                x = (i * w) + start.getX();
                y = (i * h) + start.getY() + randomInBounds(bound);
                if(inMiddle(i,CRACK_SECTIONS,steps))
                    y += jumps(jump,JUMP_PROBABILITY);
                 lineTo = new LineTo();
                lineTo.setX(x);
                lineTo.setY(y);

            }

            crack.getElements().add(lineTo);
            crack.setFill(Color.GRAY);
            brickCrack =crack;
            game_pane.getChildren().add(crack);


        }


        /**
         * set randomInBounds location
         * @param bound  brick bound
         * @return
         */
        public int randomInBounds(int bound){
            int n = (bound * 2) + 1;
            return rnd.nextInt(n) - bound;
        }

        /**
         * check  crack location
         * @return if inMiddle is true,else false
         */
        public boolean inMiddle(int i,int steps,int divisions){
            int low = (steps / divisions);
            int up = low * (divisions - 1);

            return  (i > low) && (i < up);
        }

        /**
         * jumps
         * @param bound
         * @param probability
         * @return
         */
        public int jumps(int bound,double probability){

            if(rnd.nextDouble() > probability)
                return randomInBounds(bound);
            return  0;

        }

        /**
         * makeRandomPoint
         * @param from
         * @param to
         * @param direction
         * @return
         */
        public Point2D makeRandomPoint(Point2D from,Point2D to, int direction){

            Point2D out = new Point2D(0,0);
            int pos;

            switch(direction){
                case HORIZONTAL:
                    pos = rnd.nextInt((int)to.getX() -(int)from.getX()) + (int)from.getX();
                    out =new Point2D(pos,to.getY());
                    break;
                case VERTICAL:
                    pos = rnd.nextInt((int)to.getY() -(int) from.getY()) +(int)from.getY();
                    out=new Point2D(to.getX(),pos);
                    break;
            }
            return out;
        }

    }

    private static Random rnd;

    private String name;
    Shape brickFace;

    private Color border;
    private Color inner;

    private int fullStrength;
    private int strength;

    private boolean broken;

    /**
     * Brick constructor
     * @param name  Brick name
     * @param pos  Brick point
     * @param size  Brick size
     * @param border Brick borderColor
     * @param inner Brick innerColor
     * @param strength  Brick strength
     */
    public Brick(String name, Point2D pos, Rectangle size, Color border, Color inner, int strength){
        rnd = new Random();
        broken = false;
        this.name = name;
        brickFace = makeBrickFace(pos,size);
        this.pos =pos;
        this.size =size;
        this.border = border;
        this.inner = inner;
        this.fullStrength = this.strength = strength;


    }

    /**
     * makeBrickFace
     * @param pos  brick position
     * @param size brick size
     * @return
     */
    protected abstract Shape makeBrickFace(Point2D pos,Rectangle size);

    /**
     * set impact effect
     */
    public  boolean setImpact(Point2D point , int dir,GraphicsContext gc){
        if(broken)
            return false;
        impact();
        return  broken;
    }

    public abstract Shape getBrick();



    public Color getBorderColor(){
        return  border;
    }

    public Color getInnerColor(){
        return inner;
    }

    /**
     * findImpact
     * @param b ball
     * @return out
     */
    public final int findImpact(Ball b){
        if(broken)
            return 0;
        int out  = 0;
        if(brickFace.contains(b.right))
            out = LEFT_IMPACT;
        else if(brickFace.contains(b.left))
            out = RIGHT_IMPACT;
        else if(brickFace.contains(b.up))
            out = DOWN_IMPACT;
        else if(brickFace.contains(b.down))
            out = UP_IMPACT;
        return out;
    }

    /**
     * check isBrokens
     * @return true or false
     */
    public final boolean isBroken(){
        return broken;
    }

    /**
     * repair brick
     */
    public void repair() {

        broken = false;
        strength = fullStrength;
    }
    /**
     * set impact
     */
    public void impact(){
        strength--;
        broken = (strength == 0);
        if (isBroken()){
            rightMenu.setBrickCount( rightMenu.getBrickCount()-1);
            game_pane.getChildren().remove(brickCrack);
        }

    }


    public void setRightMenu(RightMenu rightMenu) {
        this.rightMenu = rightMenu;
    }

    public void setBroken(boolean broken) {
        this.broken = broken;
    }

    public Path getBrickCrack() {
        return brickCrack;
    }
}





 2.播放类SoundEffect:

package com.breakout_clone.model;


import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 Responsible for handling the sound of the game
 Including playback off continues
 @author xhj
 */
public enum SoundEffect {
	DESTROY_BRICK("src/main/resources/sounds/destroy_brick.wav"),
	FINISHED_LEVEL("src/main/resources/sounds/finished_level.wav"),
	GAME_OVER("src/main/resources/sounds/game_over.wav"),
	HIT_BRICK("src/main/resources/sounds/hit_brick.wav"),
	HIT_PADDLE("src/main/resources/sounds/hit_paddle.wav"),
	HIT_WALL("src/main/resources/sounds/hit_wall.wav"),
	LIFE_LOST("src/main/resources/sounds/life_lost.wav"),
	UNLOCK("src/main/resources/sounds/unlocked.wav"),
	BACK_MUSIC("src/main/resources/sounds/thunderwaveCave.wav");

	
	public static enum Volume {
		MUTE, LOW, MEDIUM, HIGH
	}
	
	public static Volume volume = Volume.LOW;
	
	private Clip clip;

	/**
	 * constructor
	 * Incoming file path
	 * Set the Clip
	 * @param soundFileName
	 */
	SoundEffect(String soundFileName) {
		// sets the sound effect
		try {
			 File file = new File(soundFileName);
			URL url = file.toURL();
			AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);
			clip = AudioSystem.getClip();
			clip.open(audioInputStream);
		} catch (UnsupportedAudioFileException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (LineUnavailableException e) {
			e.printStackTrace();
		}
	}

	/**
	 * start clip
	 */
	public void play() {
		// plays the sound effect
		if (volume != Volume.MUTE) {

				//stop this clip thread and new clip start
				clip.flush();
				clip.start();

			clip.setFramePosition(0);
		}
	}
	/**
	 * stop clip
	 */
	public void stop() {
		// plays the sound effect
		if (volume != Volume.MUTE) {

			if(clip.isRunning()){
				clip.stop();
			}
			//stop this clip thread


			clip.setFramePosition(0);
		}
	}
	/**
	 * start clip
	 */
	public void start() {
		// plays the sound effect
		if (volume != Volume.MUTE) {
			clip.start();
			clip.setFramePosition(0);
		}
	}

	/**
	 * set music loop
	 */
	public void setToLoop() {
		// sets whether or not the sound effect loops
		clip.loop(clip.LOOP_CONTINUOUSLY);
	}

	/**
	 * set musicToLoud
	 */
	public void setToLoud() {
		// sets volume to be loud
		FloatControl gainControl =
				(FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);

		gainControl.setValue(+6.0f); // Reduce volume by 80 decibels.
		volume = Volume.HIGH;
	}
	/**
	 * set Music setToMUTE
	 */
	public void setToMUTE() {
		FloatControl gainControl =
				(FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);

		gainControl.setValue(-80.0f); // Reduce volume by 80 decibels.
		// sets volume to be loud
		volume = Volume.MUTE;

	}
}

 3.墙体类wall:

package com.breakout_clone.model;


import com.breakout_clone.controller.GameboardController;
import com.breakout_clone.model.Ball.Ball;
import com.breakout_clone.model.Ball.Ball1;
import com.breakout_clone.model.Brick.Brick;
import com.breakout_clone.model.Brick.Brick1;
import com.breakout_clone.model.Brick.Brick2;
import com.breakout_clone.model.Brick.Brick3;
import javafx.geometry.Point2D;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;

import java.util.Random;

/**
 * Wall class
 * Responsible for making bricks and balls and paddles
 * Responsible for detecting collisions between objects
 * Responsible for updating game status
 * * @author xhj- modified
 */
public class Wall {

    public static final int LEVELS_COUNT = 4;

    private static final int CLAY = 1;
    private static final int STEEL = 2;
    private static final int CEMENT = 3;

    private Random rnd;

    private Rectangle area;

    public Brick[] bricks;
    public Ball ball;
    public Paddle player;

    private Brick[][] levels;
    private int level;

    private Point2D startPoint;
    private int brickCount;
    private int ballCount;
    private boolean ballLost;
    private GraphicsContext gc;
    private Pane game_pane;
    private RightMenu rightMenu;
    private GameboardController gameboardController;

    public Wall(Rectangle drawArea, int brickCount, int lineCount, double brickDimensionRatio, Point2D ballPos, GraphicsContext gc, Pane game_pane, RightMenu rightMenu, GameboardController gameboardController){


        this.gc =gc;
        this.game_pane =game_pane;
        this.rightMenu =rightMenu;
        this.startPoint = new Point2D(ballPos.getX(),ballPos.getY());
        this.gameboardController =gameboardController;

        levels = makeLevels(drawArea,brickCount,lineCount,brickDimensionRatio);

        level = 0;
        ballCount = 3;
        ballLost = false;
        rnd = new Random();

        makeBall(ballPos);
        int speedX,speedY;
        do{
            speedX = rnd.nextInt(5) - 2;
        }while(speedX == 0);
        do{
            speedY = -rnd.nextInt(3);
        }while(speedY == 0);
        //set ball speed
        ball.setSpeed(speedX,speedY);
        double ballPosCloneX =ballPos.getX();
        double ballPosCloneY =ballPos.getY();
        Point2D clonePos =new Point2D(ballPosCloneX,ballPosCloneY);
        player = new Paddle( clonePos,150,10, drawArea);
        area = drawArea;




    }

    /**
     * makeSingleTypeLevel
     * @param drawArea area
     * @param brickCount count brick
     * @param lineCount lint count
     * @param brickSizeRatio bricksizeRatio
     * @param type type
     * @return
     */
    public Brick[] makeSingleTypeLevel(Rectangle drawArea, int brickCount, int lineCount, double brickSizeRatio, int type){

        // if brickCount is not divisible by line count,brickCount is adjusted to the biggest multiple of lineCount smaller then brickCount
        brickCount -= brickCount % lineCount;
        int brickOnLine = brickCount / lineCount;
        double brickLen = drawArea.getWidth() / brickOnLine;
        double brickHgt = brickLen / brickSizeRatio;
        brickCount += lineCount / 2;
        Brick[] tmp  = new Brick[brickCount];
        Rectangle brickSize = new Rectangle((int) brickLen,(int) brickHgt);
        Point2D p = null;
        int i;
        for(i = 0; i < tmp.length; i++){
            int line = i / brickOnLine;
            if(line == lineCount)
                break;
            double x = (i % brickOnLine) * brickLen;
            x =(line % 2 == 0) ? x : (x - (brickLen / 2));
            double y = (line) * brickHgt;
            p =new Point2D(x,y);
            tmp[i] = makeBrick(p,brickSize,type);
        }
        for(double y = brickHgt;i < tmp.length;i++, y += 2*brickHgt){
            double x = (brickOnLine * brickLen) - (brickLen / 2);
            p=new Point2D(x,y);
            tmp[i] = new Brick2(p,brickSize,rightMenu);
        }
        return tmp;

    }

    /**
     * makeChessboardLevel
     * @param drawArea area
     * @param brickCnt count brick
     * @param lineCnt lint count
     * @param brickSizeRatio bricksizeRatio
     */
    public Brick[] makeChessboardLevel(Rectangle drawArea, int brickCnt, int lineCnt, double brickSizeRatio, int typeA, int typeB){
        // if brickCount is not divisible by line count, brickCount is adjusted to the biggest multiple of lineCount smaller then brickCount
        brickCnt -= brickCnt % lineCnt;

        int brickOnLine = brickCnt / lineCnt;

        int centerLeft = brickOnLine / 2 - 1;
        int centerRight = brickOnLine / 2 + 1;

        double brickLen = drawArea.getWidth() / brickOnLine;
        double brickHgt = brickLen / brickSizeRatio;

        brickCnt += lineCnt / 2;

        Brick[] tmp  = new Brick[brickCnt];

        Rectangle brickSize = new Rectangle((int) brickLen,(int) brickHgt);
        Point2D p = null;

        int i;
        for(i = 0; i < tmp.length; i++){
            int line = i / brickOnLine;
            if(line == lineCnt)
                break;
            int posX = i % brickOnLine;
            double x = posX * brickLen;
            x =(line % 2 == 0) ? x : (x - (brickLen / 2));
            double y = (line) * brickHgt;
            p=new Point2D(x,y);

            boolean b = ((line % 2 == 0 && i % 2 == 0) || (line % 2 != 0 && posX > centerLeft && posX <= centerRight));
            tmp[i] = b ?  makeBrick(p,brickSize,typeA) : makeBrick(p,brickSize,typeB);
        }

        for(double y = brickHgt;i < tmp.length;i++, y += 2*brickHgt){
            double x = (brickOnLine * brickLen) - (brickLen / 2);
            p=new Point2D(x,y);
            tmp[i] = makeBrick(p,brickSize,typeA);
        }
        return tmp;
    }

    /**
     * make ball
     *
     * @param ballPos
     */
    public void makeBall(Point2D ballPos){
        ball = new Ball1(ballPos);
    }

    /**
     * makeLevels
     * @param drawArea
     * @param brickCount
     * @param lineCount
     * @param brickDimensionRatio
     * @return
     */
    public Brick[][] makeLevels(Rectangle drawArea,int brickCount,int lineCount,double brickDimensionRatio){

        Brick[][] tmp = new Brick[LEVELS_COUNT][];
        tmp[0] = makeSingleTypeLevel(drawArea,brickCount,lineCount,brickDimensionRatio,CLAY);
        tmp[1] = makeChessboardLevel(drawArea,brickCount,lineCount,brickDimensionRatio,CLAY,CEMENT);
        tmp[2] = makeChessboardLevel(drawArea,brickCount,lineCount,brickDimensionRatio,CLAY,STEEL);
        tmp[3] = makeChessboardLevel(drawArea,brickCount,lineCount,brickDimensionRatio,STEEL,CEMENT);
        return tmp;
    }

    /**
     * paddle move
     * ball move
     */
    public void move(){
        player.move();
        ball.move();
    }

    /**
     * findImpacts
     * havae player impact ball,
     * ball impactWall,
     * ball impact boder
     */
    public void findImpacts(){
        if(player.impact(ball)){
            ball.reverseY();
            SoundEffect.HIT_PADDLE.play();
        }
        else if(impactWall()){
            // for efficiency reverse is done into method impactWall because for every brick program checks for horizontal and vertical impacts
            brickCount--;
            SoundEffect.HIT_BRICK.play();
        }
        else if(impactBorder()) {
            ball.reverseX();
            SoundEffect.HIT_WALL.play();
        }
        else if(ball.getPosition().getY() < area.getY()){
            System.out.println("22222222");
            ball.reverseY();
        }
        else if(ball.getPosition().getY() > area.getY() + area.getHeight()){
            rightMenu.setBallCount(rightMenu.getBallCount()-1);
            ballCount--;
            ballLost = true;
            SoundEffect.LIFE_LOST.play();
        }
    }

    /**
     * impactWall
     * @return true or false
     */
    public boolean impactWall(){
        for(Brick b : bricks){
            b.setGamePane(this.game_pane);
            switch(b.findImpact(ball)) {
                //Vertical Impact
                case Brick.UP_IMPACT:
                    ball.reverseY();
                    return b.setImpact(ball.down, Brick.Crack.UP,gc);
                case Brick.DOWN_IMPACT:
                    ball.reverseY();
                    return b.setImpact(ball.up,Brick.Crack.DOWN,gc);

                //Horizontal Impact
                case Brick.LEFT_IMPACT:
                    ball.reverseX();
                    return b.setImpact(ball.right,Brick.Crack.RIGHT,gc);
                case Brick.RIGHT_IMPACT:
                    ball.reverseX();
                    return b.setImpact(ball.left,Brick.Crack.LEFT,gc);
            }
        }
        return false;
    }

    /**
     * impactBorder
     * @return true or false
     */
    public boolean impactBorder(){
        Point2D p = ball.getPosition();
        return ((p.getX() < area.getX()) ||(p.getX() > (area.getX() + area.getWidth())));
    }

    public int getBrickCount(){
        return brickCount;
    }

    public int getBallCount(){
        return ballCount;
    }

    public boolean isBallLost(){
        return ballLost;
    }

    public void ballReset(){
        player.moveTo(startPoint);
        ball.moveTo(startPoint);
        int speedX,speedY;
        do{
            speedX = rnd.nextInt(5) - 2;
        }while(speedX == 0);
        do{
            speedY = -rnd.nextInt(3);
        }while(speedY == 0);

        if(ball.getSpeedY()>0){
            speedY=-ball.getSpeedY();
        }
        ball.setSpeed(speedX,speedY);
        ballLost = false;
    }

    public void wallReset(){

        for(Brick b : bricks){
            b.repair();
            game_pane.getChildren().remove(b.getBrickCrack());
        }
        gameboardController.setWall();
        brickCount = bricks.length;
        rightMenu.setBrickCount(brickCount);
        rightMenu.setBallCount(ballCount);
        ballCount = 3;
    }

    public boolean ballEnd(){
        return ballCount == 0;
    }

    public boolean isDone(){
        return brickCount == 0;
    }

    public void nextLevel(){

        if(level+1<=levels.length){
            bricks = levels[level++];
            this.brickCount = bricks.length;
            gameboardController.setWall();
            if(level>1){
                gameboardController.setNextLevelConfig();
            }

            rightMenu.setLevel(rightMenu.getLevel()+1);

        }

    }

    public boolean hasLevel(){
        return level < levels.length;
    }

    public void setBallXSpeed(int s){
        ball.setXSpeed(s);
    }

    public void setBallYSpeed(int s){
        ball.setYSpeed(s);
    }

    public void resetBallCount(){
        ballCount = 3;
    }

    public Brick makeBrick(Point2D point, Rectangle size, int type){
        Brick out = null;
        switch(type){
            case CLAY:
                out = new Brick2(point,size,rightMenu);
                break;
            case STEEL:
                out = new Brick3(point,size,rightMenu);
                break;
            case CEMENT:
                out = new Brick1(point, size,rightMenu);
                break;
            default:
                throw  new IllegalArgumentException(String.format("Unknown Type:%d\n",type));
        }
        return  out;
    }



    public int getLevel() {
        return level;
    }

    public void setBrickCount(int brickCount) {
        this.brickCount = brickCount;
    }

    public Paddle getPlayer() {
        return player;
    }

    public Ball getBall() {
        return ball;
    }
}

4.小球类:ball

package com.breakout_clone.model.Ball;


import javafx.geometry.Point2D;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Shape;

/**
 *  ball super class
 *  Responsible for updating ball position
 * Setting ball Parameters
 * @author xhj- modified
 */
abstract public class Ball {

    private Shape ballFace;
    private Point2D center;
    public Point2D up;
    public Point2D down;
    public Point2D left;
    public Point2D right;
    private Color border;
    private Color inner;
    private int speedX;
    private int speedY;

    public Ball(Point2D center,int radiusA,int radiusB,Color inner,Color border){
        this.center = center;

        up=new Point2D(center.getX(),center.getY()-(radiusB / 2));
        down=new Point2D(center.getX(),center.getY()+(radiusB / 2));
        left=new Point2D(center.getX()-(radiusA /2),center.getY());
        right=new Point2D(center.getX()+(radiusA /2),center.getY());


        ballFace = makeBall(center,radiusA,radiusB);
        this.border = border;
        this.inner  = inner;
        speedX = 0;
        speedY = 0;
    }

    protected abstract Shape makeBall(Point2D center,int radiusA,int radiusB);

    public void move(){

        Ellipse tmp = (Ellipse) ballFace;
        center= new Point2D((center.getX() + speedX),(center.getY() + speedY));
        double w = tmp.getRadiusX();
        double h = tmp.getRadiusY();
        tmp.setCenterX((center.getX() -(w / 2)));
        tmp.setCenterY((center.getY() - (h / 2)));
        setPoints(w,h);
        ballFace = tmp;
    }

    public void setSpeed(int x,int y){
        speedX = x;
        speedY = y;
    }

    public void setXSpeed(int s){
        speedX = s;
    }

    public void setYSpeed(int s){
        speedY = s;
    }

    public void reverseX(){
        speedX *= -1;
    }

    public void reverseY(){
        speedY *= -1;
    }

    public Color getBorderColor(){
        return border;
    }

    public Color getInnerColor(){
        return inner;
    }

    public Point2D getPosition(){
        return center;
    }

    public Shape getBallFace(){
        return ballFace;
    }

    public void moveTo(Point2D p){
        center= p;
        Ellipse tmp = (Ellipse) ballFace;
        double w = tmp.getRadiusX();
        double h = tmp.getRadiusY();
        tmp.setCenterX((center.getX() -(w / 2)));
        tmp.setCenterY((center.getY() - (h / 2)));
        tmp.setRadiusX(w);
        tmp.setLayoutY(h);
        ballFace = tmp;
    }

    public void setPoints(double width,double height){
        up =new Point2D(center.getX(),center.getY()-(height / 2));
        down =new Point2D(center.getX(),center.getY()+(height / 2));
        left =new Point2D(center.getX()-(width / 2),center.getY());
        right =new Point2D(center.getX()+(width / 2),center.getY());
    }

    public int getSpeedX(){
        return speedX;
    }

    public int getSpeedY(){
        return speedY;
    }


    public void darw(GraphicsContext gc) {

    }
}

5.右边面板 RightMenu

package com.breakout_clone.model;

import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;

/**
 * RightMenu class
 *
 * Responsible for data update operation on the right panel of the game
 *
 * @author xhj
 */
public class RightMenu {

    Image backImage =new Image(getClass().getResource("/img/rightMenu/back.png").toString());
    private final int WIDTH =200;
    private final int HEIGHT =450;
    private final int LocationX =600;
    private  final int LocationY =0;
    private Wall wall;

    private int ballCount;
    private int brickCount;
    private int level ;
    private int score;

    public  void init() {
        ballCount =wall.getBallCount();
        brickCount =wall.getBrickCount();
        score =0;
        level =1;
    }

    /**
     * draw RightMenuPane
     * @param gc GraphicsContext
     */
    public void draw(GraphicsContext gc){

        Font font =new Font("Britannic Bold",20);
        gc.setFont(font);
        gc.drawImage(backImage,LocationX,LocationY,WIDTH,HEIGHT);
        gc.setFill(Color.WHITE);
        gc.fillText("INFORMATION ",640,40);
        gc.fillText("Level :"+level,660,100);
        gc.fillText("Life  :"+ballCount,660,150);
        gc.fillText("Brick :"+brickCount,660,200);
        gc.fillText("Score :"+score,660,250);
    }


    public int getBallCount() {
        return ballCount;
    }

    public void setBallCount(int ballCount) {
        this.ballCount = ballCount;
    }

    public int getBrickCount() {
        return brickCount;
    }

    public void setBrickCount(int brickCount) {
        this.brickCount = brickCount;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public void setWall(Wall wall) {
        this.wall =wall;
    }
}

6.主页类

package com.breakout_clone.mainClass;


import com.breakout_clone.controller.*;
import com.breakout_clone.model.SoundEffect;
import com.breakout_clone.model.Wall;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

import java.io.IOException;
/**
 javafx peoject laucher Class,
 Responsible for loading FXML,
  switch Stage.
 @author xhj
 */

public class Laucher extends Application {

    private static Stage stage;


    @Override
    public void start(Stage stage) throws Exception {
        loadGame(stage);
    }

    /**
     * load Game
     */
    private void loadGame(Stage stage) {
        this.stage = stage;
        newMenuPane();
        this.stage.setTitle("Breakout Clone");
        this .stage.getIcons().add(new Image(getClass().getResource("/img/ball/ball.png").toString()));
        this. stage.setResizable(false);
        this. stage.show();
    }


    /**
     * load  GameBoard.fxml
     * load  GameBoard ui
     */
    public  static Object startGame(String text) {
        GameboardController gameboardController = null;
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(Laucher.class.getResource("/ui/GameBoard.fxml"));
            AnchorPane anchorPane = fxmlLoader.load();
             gameboardController = fxmlLoader.getController();
            gameboardController.setStage(stage);
            gameboardController.setPlayerName(text);
            gameboardController. launchGame();
            Scene scene = new Scene(anchorPane);
            stage.setTitle("Breakout Clone,space=start/pause,←/→ =moveleft/right,esc=menu");
            stage.setScene(scene);
            stage.centerOnScreen();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return  gameboardController;

    }


    /**
     * load  Setting.fxml
     * load  DebugPane ui
     */
     public  static void newSettingPane(Wall wall, GameboardController gameboardController) {
        Stage stage =new Stage();
        SettingController debugPaneController = null;
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(Laucher.class.getResource("/ui/Setting.fxml"));
            AnchorPane anchorPane = fxmlLoader.load();
            debugPaneController = fxmlLoader.getController();
            debugPaneController.setWall(wall);
            Scene scene = new Scene(anchorPane);
            stage.setResizable(false);
            stage.centerOnScreen();
            stage.setScene(scene);
            stage.show();
            //Listen for the stage closing event
            stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
                @Override
                public void handle(WindowEvent event) {
                    gameboardController.setRunning(true);
                    gameboardController.getAnimationTimer().start();
                    SoundEffect.UNLOCK.play();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * load  Menu.fxml
     * load  DebugPane ui
     */
    public  static void newMenuPane() {
        MenuController menuController = null;
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(Laucher.class.getResource("/ui/Menu.fxml"));
            AnchorPane anchorPane = fxmlLoader.load();
            menuController = fxmlLoader.getController();
            Scene scene = new Scene(anchorPane);
            stage.setResizable(false);
            stage.setScene(scene);
            stage.centerOnScreen();
            stage.show();
            SoundEffect.BACK_MUSIC.setToLoop();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    public  static void newNamePane() {
        Stage stage =new Stage();
        NameController nameController = null;
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(Laucher.class.getResource("/ui/NamePane.fxml"));
            AnchorPane anchorPane = fxmlLoader.load();
            nameController = fxmlLoader.getController();
            nameController.setStage(stage);
            Scene scene = new Scene(anchorPane);
            stage.setResizable(false);
            stage.setScene(scene);
            stage.setTitle("input name");
            stage.getIcons().add(new Image(Laucher.class.getResource("/img/ball/ball.png").toString()));
            stage.centerOnScreen();
            stage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public  static void switchHightScorePane() {
        HighScoreController highScoreController = null;
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(Laucher.class.getResource("/ui/HighScorePane.fxml"));
            AnchorPane anchorPane = fxmlLoader.load();
            highScoreController = fxmlLoader.getController();
            Scene scene = new Scene(anchorPane);
            stage.setResizable(false);
            stage.setScene(scene);
            stage.centerOnScreen();
            stage.show();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    public  static void switchAboutPane() {
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(Laucher.class.getResource("/ui/AboutPane.fxml"));
            AnchorPane anchorPane = fxmlLoader.load();
            Scene scene = new Scene(anchorPane);
            stage.setResizable(false);
            stage.setScene(scene);
            stage.centerOnScreen();
            stage.show();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    public static void main(String[] args) {
        launch(args);

    }

}

7.小球类1(子类):

package com.breakout_clone.model.Ball;


import javafx.geometry.Point2D;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Shape;
/**
 *  ball subclass class
 *  Responsible for updating ball position
 * Setting ball Parameters
 * @author xhj- modified
 */
public class Ball1 extends Ball {

    Image ballImage =new Image(getClass().getResource("/img/ball/ball.png").toString());
    private static final String NAME = "Rubber Ball";
    private static final int DEF_RADIUS = 10;
    private static final Color DEF_INNER_COLOR = Color.rgb(255, 219, 88);
    private static final Color DEF_BORDER_COLOR = DEF_INNER_COLOR.darker().darker();


    public Ball1(Point2D center){
        super(center,DEF_RADIUS,DEF_RADIUS,DEF_INNER_COLOR,DEF_BORDER_COLOR);
    }


    @Override
    protected Shape makeBall(Point2D center, int radiusA, int radiusB) {
        double x = center.getX() - (radiusA / 2);
        double y = center.getY() - (radiusB / 2);
        return new Ellipse(x,y,radiusA,radiusB);
    }

    @Override
    public void darw(GraphicsContext gc) {
        Ellipse s = (Ellipse)getBallFace();
        gc.drawImage(ballImage,s.getCenterX(),s.getCenterY(),s.getRadiusX(),s.getRadiusY());
    }
}

8.砖块类1(子类)

package com.breakout_clone.model.Brick;


import com.breakout_clone.model.RightMenu;
import javafx.geometry.Point2D;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;

/**
 * /**
 *  * Brick subClass--------Cement Brick
 *  * Contains inner classes Crack,
 *  *Responsible for dealing with brick parameters
 *  * @author xhj- modified
 * */

public class Brick1 extends Brick {
    Image cementImage =new Image(getClass().getResource("/img/bricks/cement1.png").toString());

    private static final String NAME = "Cement Brick";
    private static final Color DEF_INNER =  Color.rgb(147, 147, 147);
    private static final Color DEF_BORDER =  Color.rgb(217, 199, 175);
    private static final int CEMENT_STRENGTH = 2;

    private Crack crack;
    private Shape brickFace;


    public Brick1(Point2D point, Rectangle size, RightMenu rightMenu){
        super(NAME,point,size,DEF_BORDER,DEF_INNER,CEMENT_STRENGTH);
        crack = new Crack(DEF_CRACK_DEPTH,DEF_STEPS);

        crack.getCrackPath().setStrokeWidth(2);
        crack.getCrackPath().setSmooth(false);
        crack.getCrackPath().setStroke(Color.WHITE);
        brickFace = super.brickFace;
        this.rightMenu =rightMenu;
    }

    @Override
    protected Shape makeBrickFace(Point2D pos, Rectangle size) {
        return new Rectangle(pos.getX(),pos.getY(),size.getWidth(),size.getHeight());
    }

    @Override
    public boolean setImpact(Point2D point, int dir,GraphicsContext gc) {

            super.impact();
            if(isBroken()){
                rightMenu.setScore( super.rightMenu.getScore()+2);

            }


        if(!super.isBroken()){
            crack.makeCrack(point,dir);
            return false;
        }
        return true;
    }

    @Override
    public void draw(GraphicsContext gc) {
        gc.drawImage(cementImage,pos.getX(),pos.getY(),size.getWidth(),size.getHeight());
    }

    @Override
    public Shape getBrick() {
        return brickFace;
    }



    public void repair(){
        super.repair();
        crack.reset();
        brickFace = super.brickFace;
    }
}

9.砖块类2(子类)

package com.breakout_clone.model.Brick;

import com.breakout_clone.model.RightMenu;
import javafx.geometry.Point2D;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;

/**
 * /**
 *  * Brick subClass--------Clay Brick
 *  * Contains inner classes Crack,
 *  *Responsible for dealing with brick parameters
 *  * @author xhj- modified
 * */
public class Brick2 extends Brick {
    Image clayImage =new Image(getClass().getResource("/img/bricks/clay.png").toString());
    private static final String NAME = "Clay Brick";
    private static final Color DEF_INNER =  Color.rgb(178, 34, 34).darker();
    private static final Color DEF_BORDER = Color.GRAY;
    private static final int CLAY_STRENGTH = 1;



    public Brick2(Point2D point, Rectangle size, RightMenu rightMenu){
        super(NAME,point,size,DEF_BORDER,DEF_INNER,CLAY_STRENGTH);
        this.rightMenu =rightMenu;
    }


    @Override
    public void draw(GraphicsContext gc) {
        gc.drawImage(clayImage,pos.getX(),pos.getY(),size.getWidth(),size.getHeight());
    }
    @Override
    protected Shape makeBrickFace(Point2D pos, Rectangle size) {
        return new Rectangle(pos.getX(),pos.getY(),size.getWidth(),size.getHeight());
    }

    @Override
    public Shape getBrick() {
        return super.brickFace;
    }

    @Override
    public void impact() {
        super.impact();
        rightMenu.setScore( rightMenu.getScore()+1);


    }
}

10.测试类(TestGameboardController )

package com.breakout_clone.test;

import javafx.geometry.Point2D;
import javafx.scene.shape.Rectangle;
import org.junit.Test;

import java.util.Random;

import static org.junit.Assert.assertEquals;

/**
 * TestGameboardController Fuction
 * @author  xhj
 */
public class TestGameboardController {

    /**
     * test Range of random numbers
     */
    @Test
    public void testRandom(){
        Random random =new Random();

//        int s = random.nextInt(max) % (max - min + 1) + min;
        int s = random.nextInt(5) % (5 - 0 + 1) + 0;
        System.out.println(s);
        assertEquals(true,s<=5);

    }

    /**
     * Detects whether two rectangles collide
     */
    @Test
    public void impact(){

        Rectangle r1 =new Rectangle(0,0,100,200);
        Rectangle r2 =new Rectangle(0,0,100,300);
        assertEquals(true,r1.intersects(r2.getLayoutBounds()));

    }
    /**
     * Detects whether rectangles contain the point
     */
    @Test
    public void containPoint(){

        Rectangle r1 =new Rectangle(0,0,100,200);
        Point2D p =new Point2D(0,1);
        assertEquals(true,r1.contains(p));

    }

    /**
     * Test get list length
     */
    @Test
    public void testSize(){
        int[] birck =new int[2];
        assertEquals(true,birck.length==2);
    }





}

 11.测试类(TestIoController )

package com.breakout_clone.test;

import com.breakout_clone.model.Player;
import org.apache.commons.io.FileUtils;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static org.junit.Assert.assertEquals;

/**
 * TestIoController Fuction
 * @author  xhj
 */
public class TestIoController {

    /**
     * test sort score
     */
    @Test
    public void sort(){
        ArrayList<Integer> socres =new ArrayList<>();
        socres.add(8);
        socres.add(5);
        socres.add(1);
        Collections.sort(socres);
        System.out.println(socres.get(0));

        assertEquals(true,socres.get(0)==1);

    }
    /**
     * test  read score From txt
     */
    @Test
    public void readFromtxt() throws IOException {

        List<String> Scores=  FileUtils.readLines(new File("src/main/resources/hightScore/hightScore.txt"),"UTF-8");


        assertEquals(true,Scores!=null);

    }

    /**
     * test  read score From txt
     * and create Player ArrayList
     */
    @Test
    public void readFromtxt2() throws IOException {
        ArrayList<Player> Players =new ArrayList<>();
        List<String> Scores=  FileUtils.readLines(new File("src/main/resources/hightScore/hightScore.txt"),"UTF-8");
        for (String s : Scores) {
            String []line =s.split(" ");
            String name = line[0];
            int score = Integer.parseInt(line[1]);
            Player Player =new Player(name ,score);
            Players.add(Player);
        }
        assertEquals(true,Players!=null);

    }
}

四.项目运行视频:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

厉掣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值