JAVA编程实战之编写小游戏-大球吃小球(eat ball game)
游戲界面:
點擊火箭開始游戲
點擊Exit退出游戲
左上角顯示當前成績和歷史最高分
退出自動保存最高成績
代碼獲取
掃碼關注微信公眾號【程序猿聲】
在后臺回復【EBG】不包括【】即可獲取。
玩法:
玩家通過鼠標控制小球所處位置
進度條緩慢減少,吃小球進度條增加
吃到黃球玩家變回初始大小
吃藥進度條大幅增加,吃到炸彈游戲結束
難度等級每十份增加一級
項目包含文件:
Circle.java
GUI.java
Game.java
MyUtils.java
PaintPanel.java
PlayerCircle.java
ProgressUI.java
代碼解析:
Circle.java
該文件定義了一個Circle類,用以生成每個球的對象。
屬性有圓的半徑、x坐標、y坐標、x方向的偏移量、y方向的偏移量、id。
方法有移動和獲取每個屬性的接口
package com.hust.game; import java.awt.*; import java.util.Random; public class Circle {protected int radius,x,y,dx,dy;private int id;protected GUI gui;String color;public Circle(int X,int Y,int R,int id,String color,GUI gui,int dx,int dy){x = X;y = Y;radius = R;Random random = new Random();this.dx = random.nextBoolean() ? dx : -dx;this.dy = random.nextBoolean() ? dy : -dy;this.gui = gui;this.id = id;this.color = color;draw();}public Circle(int X,int Y,int R,int id,String color,GUI gui){x = X;y = Y;radius = R;this.gui = gui;this.id = id;this.color = color;}public void draw(){gui.updateCircle(this);}public void move(){ // clearcircle()x += dx;y += dy;if(x + radius > gui.graphWidth || x - radius < 0){dx = -dx;}if(y + radius > gui.graphHeight || y - radius < GUI.PROGRESSWIDTH){dy = -dy;}draw();}public int getX(){return x;}public int getY(){return y;}public int getR(){return radius;}public int getD(){return radius << 1;}public int getID(){return id;} }PlayerCircle.java
該文件是用戶控制的小球類。繼承于Circle類,重構了移動的方法,由自動移動改為獲取鼠標坐標,使用戶可以通過鼠標控制小球。另外新增了Max屬性用以限制用戶小球的大小,避免發生小球過大以至于沒有威脅的情況,增強可玩性。
package com.hust.game; public class PlayerCircle extends Circle{private int Max;public PlayerCircle(int X,int Y,int R,int id,String color,GUI gui,int Maxsize){super(X,Y,R,id,color,gui);Max = Maxsize;draw();}public void resize(int plusSize){if(radius < Max){radius += plusSize;draw();}}public void move(){ // clearcirclex = gui.mouseX;y = gui.mouseY;draw();}public int getMax(){return Max;} }GUI.java
該文件是整個游戲的界面,主要功能有:游戲界面的設置、球的繪制、成績顯示等等一且UI控件的顯示和設計。
package com.hust.game; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.util.ArrayList;public class GUI {public final int graphWidth;public final int graphHeight;public final int STARTX = 180;public final int STARTY = 500;public final int buttonwidth = 100;public final int buttonheight = 60;public final int EXITX = 330;public final int EXITY = 500;public static final int PROGRESSWIDTH = 40;public static final int BOTTOM = 70;public int mouseX;public int mouseY;public Circle[] willPaint = new Circle[Game.CIRCLECOUNT+3];public PaintPanel conn = new PaintPanel(willPaint,"paintPanel");public JFrame jf;public JButton start;public JButton exit;public JLabel currentScoreLabel;public JLabel maxScoreLabel;public JLabel gameLevelLabel;public JLabel eatYourBalls;public ProgressUI jProBar;public GUI(){jf = new JFrame("Big ball eat Small ball");Toolkit kit = Toolkit.getDefaultToolkit();graphWidth = kit.getScreenSize().width;graphHeight = kit.getScreenSize().height-BOTTOM;jf.setBounds(graphWidth/2-300, graphHeight/2-400, 600, 800);jf.setLayout(null);jf.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);conn.setLayout(null);start = new JButton();start.setBounds(STARTX,STARTY,64,64);exit = new JButton();exit.setBounds(EXITX,EXITY,64,64);start.setIcon(new ImageIcon("./res/start.png"));start.setMargin(new Insets(0,0,0,0));//將邊框外的上下左右空間設置為0 start.setIconTextGap(0);//將標簽中顯示的文本和圖標之間的間隔量設置為0 start.setBorderPainted(false);//不打印邊框 start.setBorder(null);//除去邊框 start.setFocusPainted(false);//除去焦點的框 start.setContentAreaFilled(false);//除去默認的背景填充 exit.setIcon(new ImageIcon("./res/exit.png"));exit.setMargin(new Insets(0,0,0,0));//將邊框外的上下左右空間設置為0 exit.setIconTextGap(0);//將標簽中顯示的文本和圖標之間的間隔量設置為0 exit.setBorderPainted(false);//不打印邊框 exit.setBorder(null);//除去邊框 exit.setFocusPainted(false);//除去焦點的框 exit.setContentAreaFilled(false);//除去默認的背景填充 Font font1=MyUtils.getSelfDefinedFont("./res/hkww.ttc", 18);Font font2=MyUtils.getSelfDefinedFont("./res/font.ttf", 60);currentScoreLabel = new JLabel();currentScoreLabel.setVisible(false);currentScoreLabel.setFont(font1);currentScoreLabel.setBounds(10, 40, 200, 20);maxScoreLabel = new JLabel();maxScoreLabel.setVisible(false);maxScoreLabel.setFont(font1);maxScoreLabel.setBounds(10, 60, 200, 20);gameLevelLabel = new JLabel();gameLevelLabel.setVisible(false);gameLevelLabel.setFont(font1);gameLevelLabel.setBounds(10, 80, 200, 20);eatYourBalls = new JLabel("吃掉比你小的球!");eatYourBalls.setVisible(true);eatYourBalls.setFont(font2);eatYourBalls.setBounds(60, 120, 500, 100);eatYourBalls.setForeground(Color.decode("#8A2BE2"));conn.add(start);conn.add(exit);conn.add(currentScoreLabel);conn.add(maxScoreLabel);conn.add(gameLevelLabel);conn.add(eatYourBalls);jProBar = new ProgressUI();jProBar.getjProgressBar().setSize(graphWidth, PROGRESSWIDTH);jProBar.getjProgressBar().setLocation(0, 0);jProBar.getjProgressBar().setVisible(false);conn.add(jProBar.getjProgressBar());jf.addMouseMotionListener(new MouseMotionListener() {@Overridepublic void mouseDragged(MouseEvent e) {}@Overridepublic void mouseMoved(MouseEvent e) {mouseX = e.getX();mouseY = e.getY() - 20;}});jf.setContentPane(conn);jf.setVisible(true);jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}public void updateCircle(Circle c){if(c != null){if(c.getID() == Game.CIRCLECOUNT) {willPaint[c.getID()] = c;jf.getContentPane().repaint();}else {willPaint[c.getID()] = c;}}}public void printAllEnemiesCircles() {jf.getContentPane().repaint();}public void clear(){for(int i = 0; i < willPaint.length; i++){willPaint[i] = null;}jf.getContentPane().repaint();} }PaintPanel.java
本文件實現具體的重繪操作。
package com.hust.game;import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit;import javax.swing.JPanel;@SuppressWarnings("serial") public class PaintPanel extends JPanel{String name;private Circle[] willPaint;public PaintPanel(Circle[] willPaint,String name){this.willPaint = willPaint;this.name = name;}public String toString(){return name;}@Overridepublic void paint(Graphics g){super.paint(g);for(Circle nowPainting : willPaint){int maxBoomsNr = 0;int maxLifesNr = 0;if(nowPainting != null){ // System.out.println(nowPainting.color + " " + nowPainting.getID());if(nowPainting.color.equals("#EED5D2") && (maxBoomsNr <= 3)) {Image image=Toolkit.getDefaultToolkit().getImage("./res/boom.png");nowPainting.radius = 16;g.drawImage(image, nowPainting.getX(), nowPainting.getY(), this); maxBoomsNr++;continue;}else if(nowPainting.color.equals("#6A5ACD") && (maxLifesNr <= 3)) {Image image=Toolkit.getDefaultToolkit().getImage("./res/life.png");nowPainting.radius = 16;g.drawImage(image, nowPainting.getX(), nowPainting.getY(), this); maxLifesNr++;continue;}g.setColor(Color.decode(nowPainting.color));g.fillOval(nowPainting.getX() - nowPainting.getR(),nowPainting.getY() - nowPainting.getR(),nowPainting.getD(),nowPainting.getD());}}}}MyUtils.java
本文件實現一些工具函數的整合,比如獲取隨機顏色、讀取成績、將成績寫入文件等。
package com.hust.game;import java.awt.Color; import java.awt.FontFormatException; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.util.Random; import java.util.Scanner;public class MyUtils {static String getRandomColor(Random r) {int count=(int) (r.nextInt(18)+1);String returnColor;switch (count) {case 1:returnColor = "#00FF00";break;case 2:returnColor = "#00FFFF";break;case 3:returnColor = "#00BFFF";break;case 4:returnColor = "#8A7CDA";break;case 5: //yellow , reset player smallreturnColor = "#FF00FF";break;case 6:returnColor = "#FF0000";break;case 7:returnColor = "#FA663C";break;case 8:returnColor = "#B5B5B5";break;case 9:returnColor = "#BF487A";break; case 10:returnColor = "#90EE90";break;case 11:returnColor = "#C978E7";break;case 12: //boomreturnColor = "#EED5D2";break;case 13:returnColor = "#52EA41";break; case 14:returnColor = "#FF7F00";break; case 15:returnColor = "#8B658B";break;case 16:returnColor = "#7FFFD4";break;case 17: //lifereturnColor = "#6A5ACD";break;case 18:returnColor = "#9BCD9B";break; default:returnColor = "#8B0A50";break;}return returnColor;}//filepath字體文件的路徑public static java.awt.Font getSelfDefinedFont(String filepath, int size){java.awt.Font font = null;File file = new File(filepath);try{font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, file);font = font.deriveFont(java.awt.Font.PLAIN, size);}catch (FontFormatException e){return null;}catch (FileNotFoundException e){return null;}catch (IOException e){return null;}return font;}public static int readRecordFromFile(String filename) {int record = 0;try { Scanner in = new Scanner(new FileReader(filename));if(in.hasNext()) {record = in.nextInt();}else {System.out.println("no record!");}in.close();} catch (FileNotFoundException e) {// File not foundSystem.out.println("File not found!");}return record;}public static void writeRecordInFile(String filename, int record) {File file = new File(filename);Writer writer = null;StringBuilder outputString = new StringBuilder();try {outputString.append(record);writer = new FileWriter(file, false); // true表示追加writer.write(outputString.toString());writer.close();} catch (IOException e) {e.printStackTrace();}} }Game.java
本文件實現具體的操作邏輯,分別對于用戶操作的小球和游戲控制的小球建立兩個新的線程,以及建立一個檢測碰撞和生死的線程。還有更新成績、更新難度等級、游戲結束回收資源等操作。并且還要更新游戲的進度條。
package com.hust.game;import javax.swing.*;import java.awt.Color; import java.awt.MouseInfo; import java.awt.Point; import java.awt.PointerInfo; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.*; import java.util.ArrayList; import java.util.Comparator; import java.util.Random; import java.util.Scanner;public class Game {public boolean boom(Circle a, Circle b) {return a.getR() + b.getR() >=Math.sqrt(Math.pow(a.getX() - b.getX(), 2) + Math.pow(a.getY() - b.getY(), 2));}public void backMain(GUI gui){gui.jf.getContentPane().setBackground(initColor);gui.jf.setBounds(gui.graphWidth/2-300, gui.graphHeight/2-400, 600, 800);gui.exit.setVisible(true);gui.start.setVisible(true);gui.eatYourBalls.setText("再試試看吧!");gui.eatYourBalls.setVisible(true);gui.eatYourBalls.setBounds(120, 120, 500, 100);gui.jProBar.getjProgressBar().setVisible(false);gui.maxScoreLabel.setText("歷史最高:"+historyScore);gui.clear();new Circle(240, 350, 80, Game.CIRCLECOUNT+1, "#00CED1",gui, 0, 0);new Circle(350, 380, 40, Game.CIRCLECOUNT+2, "#ADFF2F",gui, 0, 0);gui.jf.getContentPane().repaint();}public static final int ORIGNALR = 15;public static final int CIRCLECOUNT = 50;public static final int MAX = 100;public static final int MIN = 10;public static int historyScore = 0;public static volatile int score = 0;public static volatile boolean gameplaying;public static volatile boolean lookingscore;public static Color initColor;public static int enemyMovingSpeed = 100;public static Random random;class PLAY {public int cnt;public int score;public PLAY(int cnt, int score) {this.cnt = cnt;this.score = score;}}public synchronized void startGame(final GUI gui) throws InterruptedException {final PlayerCircle[] player = {new PlayerCircle(gui.mouseX, gui.mouseY, ORIGNALR, CIRCLECOUNT, "#000000", gui, MAX)};final Circle[] enemies = new Circle[CIRCLECOUNT];score = 0;gameplaying = true;random = new Random();for (int i = 0; i < CIRCLECOUNT; i++) {if (i < CIRCLECOUNT / 4 * 3) {int enermyR = random.nextInt(player[0].getR()) + MIN;if (i == CIRCLECOUNT / 2) {do {enemies[i] = new Circle(random.nextInt(gui.graphWidth - enermyR * 2) + enermyR, random.nextInt(gui.graphHeight- enermyR * 2-GUI.BOTTOM) + enermyR+GUI.PROGRESSWIDTH, enermyR, i, "#FFFF00",gui, random.nextInt(10) + 1, random.nextInt(10) + 1);} while (boom(enemies[i], player[0]));}else {do {enemies[i] = new Circle(random.nextInt(gui.graphWidth - enermyR * 2) + enermyR, random.nextInt(gui.graphHeight- enermyR * 2-GUI.BOTTOM) + enermyR+GUI.PROGRESSWIDTH, enermyR, i, MyUtils.getRandomColor(random),gui, random.nextInt(10) + 1, random.nextInt(10) + 1);} while (boom(enemies[i], player[0]));}} else {int enermyR = random.nextInt(MAX - player[0].getR()) + player[0].getR();do {enemies[i] = new Circle(random.nextInt(gui.graphWidth - enermyR * 2) + enermyR, random.nextInt(gui.graphHeight- enermyR * 2-GUI.BOTTOM) + enermyR+GUI.PROGRESSWIDTH, enermyR, i, MyUtils.getRandomColor(random),gui, random.nextInt(3) + 1, random.nextInt(3) + 1);} while (boom(enemies[i], player[0]));}}gui.jf.getContentPane().repaint();System.out.println("start and score has already unvisible");class playerMovingCircle implements Runnable {@Overridepublic synchronized void run() {System.out.println("player moving");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}while (gameplaying && player[0] != null) {player[0].move();}System.out.println("player done");}}class enemyMoving implements Runnable {public synchronized void run() {System.out.println("enemies moving");try {Thread.sleep(1000);} catch (InterruptedException e1) {e1.printStackTrace();}while (gameplaying && player[0] != null) {for (int i = 0; i < enemies.length; i++) {if (enemies[i] == null) {int enermyR;if (i < CIRCLECOUNT / 4 * 3) {enermyR = random.nextInt(player[0].getR() - MIN) + MIN;} else {enermyR = random.nextInt(MAX - player[0].getR()) + player[0].getR();}do{enemies[i] = new Circle(random.nextInt(gui.graphWidth - enermyR * 2) + enermyR, random.nextInt(gui.graphHeight- enermyR * 2-GUI.BOTTOM) + enermyR+GUI.PROGRESSWIDTH, enermyR, i, MyUtils.getRandomColor(random),gui, random.nextInt(3) + 1, random.nextInt(3) + 1);} while (boom(enemies[i], player[0]));if (i == CIRCLECOUNT / 2) {do {enemies[i] = new Circle(random.nextInt(gui.graphWidth - enermyR * 2) + enermyR, random.nextInt(gui.graphHeight- enermyR * 2-GUI.BOTTOM) + enermyR+GUI.PROGRESSWIDTH, enermyR, i, "#FFFF00",gui, random.nextInt(10) + 1, random.nextInt(10) + 1);} while (boom(enemies[i], player[0]));}}enemies[i].move();}gui.printAllEnemiesCircles(); //鏇存柊鎵�鏈� try {Thread.sleep(enemyMovingSpeed);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("enemies done");}}class countScore implements Runnable {public synchronized void run() {System.out.println("counting score");while (gameplaying) {for (int i = 0; i < enemies.length; i++) {if (enemies[i] != null && player[0] != null) {if (boom(enemies[i], player[0])) {//炸彈檢測if(enemies[i].color.equals("#EED5D2")) {gameplaying = false;break;}//藥 檢測if(enemies[i].color.equals("#6A5ACD")) {gui.jProBar.addValue(5);enemies[i] = null;continue;}//普通球檢測if (player[0].getR() > enemies[i].getR()) {if (i != CIRCLECOUNT / 2) {player[0].resize(1);score++;gui.currentScoreLabel.setText("當前成績:" + score);if(score != 0 && score % 10 == 0) {if(enemyMovingSpeed > 40) {enemyMovingSpeed -= 20;}else if(enemyMovingSpeed > 20) {enemyMovingSpeed -= 5;}else if(enemyMovingSpeed > 1) {enemyMovingSpeed -= 1;}gui.gameLevelLabel.setText("難度等級:" + (100- enemyMovingSpeed));}gui.jProBar.addValue(3);} else {player[0].resize(-1 * (player[0].getR() - ORIGNALR));}enemies[i] = null;} else {gameplaying = false;break;}}}}}//娓告垙緇撴潫錛屾敹灝�for (int i = 0; i < enemies.length; i++) {enemies[i] = null;}if(score > historyScore) {historyScore = score;}player[0] = null;gui.jf.getContentPane().setBackground(Color.RED);//gui.jf.getContentPane().repaint();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}backMain(gui);System.out.println(score);}}class progressUI implements Runnable {public synchronized void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}while(gui.jProBar.getValue() > 0 && gameplaying) {gui.jProBar.addValue(-1);try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}}gameplaying = false;}}playerMovingCircle pmc = new playerMovingCircle();Thread playerMC = new Thread(pmc);enemyMoving em = new enemyMoving();Thread eM = new Thread(em);countScore cs = new countScore();Thread cS = new Thread(cs);progressUI pUI = new progressUI();Thread tProgress = new Thread(pUI);System.out.println("榪涚▼瀹氫箟瀹屾瘯");playerMC.start();eM.start();cS.start();tProgress.start();System.out.println("涓葷嚎紼媟unning");// gui.jf.setContentPane(oriView);}public static void main(String[] args) {final Game mian = new Game();final GUI gui = new GUI();System.out.println("gui鍒涘緩瀹屾瘯");gameplaying = false;initColor = gui.jf.getContentPane().getBackground();historyScore = MyUtils.readRecordFromFile("./res/record.txt");new Circle(240, 350, 80, Game.CIRCLECOUNT+1, "#00CED1",gui, 0, 0);new Circle(350, 380, 40, Game.CIRCLECOUNT+2, "#ADFF2F",gui, 0, 0);gui.jf.getContentPane().repaint();gui.start.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {gui.start.setVisible(false);gui.exit.setVisible(false);gui.eatYourBalls.setVisible(false);gui.currentScoreLabel.setVisible(true);gui.maxScoreLabel.setVisible(true);gui.gameLevelLabel.setVisible(true);score = 0;enemyMovingSpeed = 100;gui.currentScoreLabel.setText("當前成績:" + score);gui.maxScoreLabel.setText( "歷史最高:" + historyScore);gui.gameLevelLabel.setText( "難度等級:" + (100-enemyMovingSpeed));gui.clear();gui.jProBar.getjProgressBar().setValue(100);gui.jProBar.getjProgressBar().setVisible(true);gui.jf.setExtendedState(JFrame.MAXIMIZED_BOTH); try {mian.startGame(gui);} catch (InterruptedException e1) {e1.printStackTrace();}}});gui.jf.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {MyUtils.writeRecordInFile("./res/record.txt", historyScore);}});gui.exit.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {MyUtils.writeRecordInFile("./res/record.txt", historyScore);System.exit(0);}});}}ProgressUI.java
該文件控制進度條的增減。
package com.hust.game; import java.awt.Color; import java.awt.Graphics;import javax.swing.JComponent; import javax.swing.JProgressBar; import javax.swing.plaf.basic.BasicProgressBarUI;public class ProgressUI {private JProgressBar jProgressBar;private int progressvalue;ProgressUI() {this.jProgressBar = new JProgressBar();}ProgressUI(JProgressBar jProgressBar) {this.jProgressBar = jProgressBar;}public void addValue(int num) {progressvalue=this.jProgressBar.getValue() + num;this.jProgressBar.setValue(progressvalue);if(progressvalue<20){this.jProgressBar.setForeground(Color.RED);}else if(progressvalue<40){this.jProgressBar.setForeground(Color.YELLOW);} else if(progressvalue<60){this.jProgressBar.setForeground(Color.BLUE);} else if(progressvalue<80){this.jProgressBar.setForeground(Color.GREEN);} else{this.jProgressBar.setForeground(Color.CYAN);}this.jProgressBar.setValue(progressvalue);}public int getValue() {return jProgressBar.getValue();}public JProgressBar getjProgressBar() {return jProgressBar;}public void setjProgressBar(JProgressBar jProgressBar) {this.jProgressBar = jProgressBar;} }小結:
-
采用git版本控制,Github云平臺協同開發。
-
基于傳統大球吃小球游戲,融入更多新鮮有趣元素。
-
游戲邏輯、代碼框架等由小組各成員自主思考完成
擴展:
- 鍵盤控制模式
- 敵人的球相互碰撞反彈
- 網絡雙人對戰模式、多人挑戰模式
代碼獲取
掃碼關注微信公眾號【程序猿聲】
在后臺回復【EBG】不包括【】即可獲取。
總結
以上是生活随笔為你收集整理的JAVA编程实战之编写小游戏-大球吃小球(eat ball game)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mac彻底删除Paragon NTFS
- 下一篇: iOS录屏/自定义相机