Java學習之道:彈球遊戲分步解析(二)——讓小球飛
之前跟大家說要把事件監聽器和多執行緒分開來講,不過之後發現事件監聽器要涉及到很多小球運動的操作,所以,決定將兩個知識點合起來說,這樣會更便於大家理解:
實現小球在我們所繪製的窗體上運動就必須要用到多執行緒;讓小球類繼承Thread類,再重寫void run(){}方法即可;
先上程式碼:
(注:淺藍色字型不是本節要講的內容,可以忽略)
package jumpingBall;
import java.awt.Color;
import java.awt.Graphics;
import java.util.List;
import java.util.Random;
import javax.swing.JPanel;
//小球類是此遊戲的關鍵,我們首先要分析他應該具備什麼樣的屬性,實現什麼樣的方法
public class BallThread extends Thread {
private static final int North = 1;//用四個整形的資料代表四個方向
private static final int West = 2;
private static final int South = 3;
private static final int East = 4;
private int Xdirection = West;//記錄小球在X軸上的方向
private int Ydirection = North;//記錄小球在y軸上的方向
private int radios;//半徑
private Color color;//顏色
private int Xspeed;//x方向速度
private int Yspeed;//y方向速度
private int X;//當前位置x
private int Y;//當前位置y
private int LastX;//最後位置x
private int LastY;//最後位置y
private int xband;
private int yband;
private JPanel jp;//面板
private Graphics g;//畫布
private boolean pauseFlag = false;//暫停標誌(預設值否)
private boolean stopFlag = false;//停止標誌(false——表示存在)
private boolean beread = false;//是否被儲存在檔案裡過(預設值否)
public BallThread(JPanel jp){
this.jp = jp;
this.g = jp.getGraphics();
}
public void run(){
banLlistener band =new banLlistener(jp);//定義滑塊
jp.addMouseMotionListener(band);//給滑塊加入滑鼠監聽器
if(beread==false){//如果沒有被儲存在檔案裡過
this.setprivate();//方法作用:隨機生成它的部分屬性
}
//實現小球運動的思路為:才窗體上沿小球運動路線不斷繪製小球,並將之前的小球擦掉
while(true){//永真表示球會一直運動
try{
sleep(30);
}catch(InterruptedException e){
e.printStackTrace();
}
if(stopFlag==true){//如果停止標示為true,停止該執行緒
clearBall();
return;
}
if(pauseFlag==true){//如果暫停標示為真,跳過之後的步驟進入下次迴圈
continue;
}
this.clearBall();//清除原來位置的小球
this.move();//小球運動
this.drawBall();//在新的位置畫出小球
}
}
public void clearBall(){//清除小球
Color color = jp.getBackground();//
g.setColor(color);//
g.fillOval(X, Y, radios*2, radios*2);//用背景色,填充之前小球的印記
}
public void drawBall(){
g.setColor(color);//
g.fillOval(X, Y, radios*2, radios*2);
}
public void setprivate(){//設定小球屬性
Random random = new Random();//隨機
radios = 10+random.nextInt(10);//半徑
color=newColor(random.nextInt(255),random.nextInt(255),random.nextInt(255));//顏色
Xspeed = 2 + random.nextInt(10);//x方向速度
Yspeed = 2+random.nextInt(10);//y方向速度
}
public void exit(boolean flag){//設定停止標示
stopFlag = flag;
}
public void setpause(boolean flag){//設定暫停標示
pauseFlag = flag;
}
public void move(){//讓我們的小球在面板中飛起來,同時碰到邊界被彈回
switch(Xdirection){//對於x方向
case 2:X = X - Xspeed;break;//如果小球的運動方向向西,X值減小
case 4:X = X + Xspeed;break;//如果小球的運動方向向東,X值增大
}
switch(Ydirection){//對於y方向
case 1:Y = Y - Yspeed;break;//如果小球的運動方向向北,Y值減小
case 3:Y = Y + Yspeed;break;//如果小球的運動方向向難,Y值增大
}
//如果碰到邊界,方向改變
if(X<0+radios*2){
Xdirection = 4;
}
if(X>jp.getWidth()-radios*2){
Xdirection = 2;
}
if(Y<0+radios*2){
Ydirection = 3;
}
//如果掉到下面,該小球執行緒結束
if(Y>jp.getHeight()-radios*2){
//Ydirection = 1;
BallThread ball = new BallThread(jp);
this.exit(true);
for(int i = 0;i<Date.allBall.size();i++){
ball=Date.allBall.get(i);
if(X == ball.X&&Y == ball.Y){
Date.allBall.remove(i);
}
}
}
//如果碰到滑塊,彈回
if(Y<500+radios/2&&Y>490-radios*2&&X>=Date.xband&&X<=(Date.xband+100)){
Ydirection = 1;
}
}
//下面全是get和set方法(定義以下方法主要在檔案操作時用到):
public int getXdirection() {
return Xdirection;
}
public void setXdirection(int xdirection) {
Xdirection = xdirection;
}
public int getYdirection() {
return Ydirection;
}
public void setYdirection(int ydirection) {
Ydirection = ydirection;
}
public int getRadios() {
return radios;
}
public void setRadios(int radios) {
this.radios = radios;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getXspeed() {
return Xspeed;
}
public void setXspeed(int xspeed) {
Xspeed = xspeed;
}
public int getYspeed() {
return Yspeed;
}
public void setYspeed(int yspeed) {
Yspeed = yspeed;
}
public int getX() {
return X;
}
public void setX(int x) {
X = x;
}
public int getY() {
return Y;
}
public void setY(int y) {
Y = y;
}
public int getLastX() {
return LastX;
}
public void setLastX(int lastX) {
LastX = lastX;
}
public int getLastY() {
return LastY;
}
public void setLastY(int lastY) {
LastY = lastY;
}
public int getXband() {
return xband;
}
public void setXband(int xband) {
this.xband = xband;
}
public int getYband() {
return yband;
}
public void setYband(int yband) {
this.yband = yband;
}
public boolean isPauseFlag() {
return pauseFlag;
}
public boolean savePauseFlag() {
return false;
}
public void setPauseFlag(boolean pauseFlag) {
this.pauseFlag = pauseFlag;
}
public boolean isStopFlag() {
return stopFlag;
}
public void setStopFlag(boolean stopFlag) {
this.stopFlag = stopFlag;
}
public boolean isBeread() {
return beread;
}
public void setBeread(boolean beread) {
this.beread = beread;
}
public boolean saveBeread() {
return true;
}
public JPanel getJp() {
return jp;
}
public void setJp(JPanel jp) {
this.jp = jp;
}
}
定義了小球類之後,我們要考慮的另一個問題就是,我們什麼時候要讓小球在介面上飛,作為一個遊戲,至少需要有開始按鈕,當按鈕被觸發,遊戲開始,所以我們需要用到的另一個工具就是事件監聽器:
除了開始按鈕,我們還為這個遊戲做了其他功能:暫停,加一球,減一球;
接下來是程式碼:
檔案一:Date,將小球使用一個連結串列存起來:
package jumpingBall;
import java.util.ArrayList;
import java.util.List;
public class Date {//此類專門存放全域性變數
public static int xband;
public static int yband;
public static List<BallThread> allBall = new ArrayList<BallThread>();//將連結串列宣告為全域性變數
}
package jumpingBall;
import java.awt.event.ActionEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
import sun.net.www.content.audio.x_aiff;
public class ActionListener implements java.awt.event.ActionListener{
//private List<BallThread> allBall = new ArrayList<BallThread>();
private JPanel jpanel;
private int xband;
private int yband;
public ActionListener(JPanel jpanel){//構造方法
//this.allBall = allBall;
this.jpanel = jpanel;
}
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
String cmd = e.getActionCommand(); //
//選擇開始選擇
if(cmd.equals("Play")){
//如果場上沒球,新增一個
if(Date.allBall.size()==0){
BallThread ball = new BallThread(jpanel);
Date.allBall.add(ball);
ball.start();
System.out.println("增加一個小球,場上小球個數變為:"+Date.allBall.size());
}
//如果場上有球,讓所有靜止的球動起來
for(int i = 0;i<Date.allBall.size();i++){
BallThread ball= Date.allBall.get(i);
ball.setpause(false);
}
// banLlistener band = new banLlistener(jpanel);
//jpanel.addMouseMotionListener(band);
}
//選擇暫停選項
if(cmd.equals("Pause")){
//遍歷場上所有小球,修改ball的pause屬性
for(int i = 0;i<Date.allBall.size();i++){
BallThread ball= Date.allBall.get(i);
ball.setpause(true);
}
}
//選擇增球
if(cmd.equals("Add")){
BallThread ball = new BallThread(jpanel);
Date.allBall.add(ball);
ball.start();//執行執行緒
System.out.println("增加一個小球,場上小球個數變為:"+Date.allBall.size());
}
//選擇減一球選項
if(cmd.equals("Delet one ball")&&Date.allBall.size()!=0){
BallThread ball = Date.allBall.get(Date.allBall.size()-1);
ball.exit(true);//修改存在選項(設定其消失)
Date.allBall.remove(Date.allBall.size()-1);//減一球
System.out.println("減少一個小球,場上小球個數變為:"+Date.allBall.size());
}
}
}
此外還要在介面類中敲上以下程式碼(黑色加粗部分)
//javax.swing.JFileChooser使用此來選擇檔案
package jumpingBall;
import java.awt.Toolkit;
import java.awt.BorderLayout;
import java.awt.Menu;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSeparator;
public class JFjumpingBall extends JFrame{
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
JFjumpingBall ballgame = new JFjumpingBall();
ballgame.JFshow();
}
public void JFshow(){
//建立一個窗體:
double width = Toolkit.getDefaultToolkit().getScreenSize().width;//定義width獲取顯示器在該解析度下的寬
double height = Toolkit.getDefaultToolkit().getScreenSize().height;//定義height獲取顯示器在該解析度下的高
this.setSize((int)width,(int)height);//設定窗體大小
this.setTitle("小魚躲氣泡");//設定標題
this.setLocationRelativeTo(null);//設定視窗居中
this.setDefaultCloseOperation(3);//設定關閉視窗程式停止執行
this.setLayout(new BorderLayout());//設定窗體佈局(邊框式佈局)
JPanel northPanel = new JPanel();//北部面板
JPanel jpanel = new JPanel();//面板
JPanel southjpanel = new JPanel();//南部面板
northPanel.setLayout(new BorderLayout());//設定北部面板佈局方式
JPanel northLeftPanel = new JPanel();//北部面板左邊
JPanel northRightPanel = new JPanel();//……右邊
JPanel northCenterPanel = new JPanel();//……中間
northLeftPanel.setSize(100,100); //
northRightPanel.setSize(100, 100);//
JButton jbplay = new JButton("Play");//放置遊戲按鈕(開始遊戲)
JButton jbPause = new JButton("Pause");//放置遊戲按鈕(遊戲暫停)
JButton jbAdd = new JButton("Add");//放置遊戲按鈕(增加)
JButton deletone = new JButton("Delet one ball");//放置遊戲按鈕(減少一個小球)
northCenterPanel.add(jbplay);//將按鈕放置在北部面板的中間面板
northCenterPanel.add(jbPause);//同上
northCenterPanel.add(jbAdd);//同上
northCenterPanel.add(deletone);//同上
northPanel.add(northCenterPanel,BorderLayout.CENTER);//將北部面板的各部分面板加在北部面板裡
northPanel.add(northLeftPanel,BorderLayout.WEST);//
northPanel.add(northRightPanel,BorderLayout.EAST);//
this.add(northPanel,BorderLayout.NORTH);//佈置窗體
this.add(jpanel,BorderLayout.CENTER);//
this.add(southjpanel,BorderLayout.SOUTH);//
this.setJMenuBar(JMenuline(jpanel));//給窗體新增選單欄(選單欄方法:JMenuline;方法宣告在下部)
this.setVisible(true);//設定窗體可見
//List<BallThread> allBall = new ArrayList<BallThread>();//
ActionListener l = new ActionListener(jpanel);//動作監聽器
jbplay.addActionListener(l);//jbplay新增動作監聽器
jbPause.addActionListener(l);//jbPause新增動作監聽器
jbAdd.addActionListener(l);//jbAdd新增動作監聽器
deletone.addActionListener(l);//delete新增動作監聽器
}
public JMenuBar JMenuline(JPanel jpanel){
JMenuBar jme =new JMenuBar();//定義選單欄
JMenu menuF = new JMenu("檔案(F)");//選單欄按鈕
JMenuItem jmiFO = new JMenuItem("儲存(save)");//定義選單欄按鈕的選項(儲存)
JMenuItem jmiIO = new JMenuItem("讀取(login)");//定義選單欄按鈕的選項(讀取)
foListener listener = new foListener();//定義儲存監聽器
ioListener listener2 = new ioListener(jpanel);//定義讀取監聽器
jmiFO.addActionListener(listener);//將儲存監聽器加在jmiFO
jmiIO.addActionListener(listener2);//將讀取監聽器加在jmiIO
menuF.add(jmiFO);//將(儲存)選單欄按鈕加在menuF裡
JSeparator jSeparator = new JSeparator();//定義水平分割線
menuF.add(jSeparator);//將水平分割線加在menuF裡
menuF.add(jmiIO);//將(讀取)選單欄按鈕加在menuF裡
jme.add(menuF);//講此選單欄按鈕加在jme裡
return jme;
}
}