javaFx打砖块:
环境:jdk17+maven+javaFx17
一.功能:
- 主界面
- 音乐关闭 开启
- 主题界面切换
- 游戏介绍
- 游戏排行版(前十名)
- 游戏UI化
- 加载游戏背景图片
- 加载小球图片
- 加载面板图面
- 加载砖块图片
- 加载砖块裂纹
- 游戏闯关机制和计分制度
- 右边是计分面板
- 游戏分为三关,每闯关一个加大难度,面板缩短
二.项目结构
三.相关类设计:
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);
}
}