接下來的幾篇博客,想記錄一下通過學習坦克大戰項目來循序漸進的學習Java基礎。主要是為了鞏固基礎知識,當然學習編程重要的還是多敲,問題通常是在敲代碼的過程中發現的,積累也是在敲代碼中尋求的經驗。這個坦克大戰項目是利用Java圖形界面來做的,比較簡陋。但是,在不斷的往裡面加功能的時候,可以學到很多知識 ...
接下來的幾篇博客,想記錄一下通過學習坦克大戰項目來循序漸進的學習Java基礎。主要是為了鞏固基礎知識,當然學習編程重要的還是多敲,問題通常是在敲代碼的過程中發現的,積累也是在敲代碼中尋求的經驗。這個坦克大戰項目是利用Java圖形界面來做的,比較簡陋。但是,在不斷的往裡面加功能的時候,可以學到很多知識,最重要的還是體會Java的面向對象編程思想。下麵介紹幾個用的上的Demo,最後是坦克大戰的1.0版本。
Demo1:回顧事件處理機制
/* * 功能:事件處理機制(ActionListener的應用) */ package com.fanghua1; import java.awt.*; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.*; public class Demo1_1 extends JFrame implements ActionListener { // 定義一個panel MyP mp = null; JButton jb1 = null; JButton jb2 = null; public static void main(String[] args) { // TODO Auto-generated method stub Demo1_1 be = new Demo1_1(); } public Demo1_1() { mp = new MyP(); jb1 = new JButton("黑色"); jb2 = new JButton("紅色"); this.add(jb1, BorderLayout.NORTH); mp.setBackground(Color.black); this.add(mp);// 中間是預設的,不用加參數 this.add(jb2, BorderLayout.SOUTH); // 註冊監聽(this 代表Demo9_4)(每創建一個對象就有一個this) //jb1、jb2都是事件源對象 jb1.addActionListener(this); // 指定Action命令 jb1.setActionCommand("aa"); jb2.addActionListener(this); jb2.setActionCommand("bb"); jb1.addActionListener(new Cat()); jb2.addActionListener(new Cat()); this.setSize(200, 150); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } @Override // 對事件處理的方法,actionPerformed函數 //ActionEvent e 是事件對象 public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub // System.out.println("OK");//用來測試監聽有沒有實現,看控制台 // 判斷那個按鈕被點擊 if (e.getActionCommand().equals("aa")) { System.out.println("你點擊的是黑色"); mp.setBackground(Color.black); } else if (e.getActionCommand().equals("bb")) { System.out.println("你點擊的是紅色"); mp.setBackground(Color.red); } else { System.out.println("不知道"); } } class MyP extends JPanel { public void Paint(Graphics g) { super.paint(g); g.fillRect(0, 0, 30, 30); } } } //一個事件源並不是只有一個事件監聽者,他可以有多個事件監聽者 class Cat implements ActionListener{ @Override public void actionPerformed(ActionEvent e) { if(e.getActionCommand().equals("aa")){ System.out.println("我監聽到了黑色"); }else if(e.getActionCommand().equals("bb")){ System.out.println("我監聽到了紅色"); }else{ System.out.println("Nothing"); } } }
Demo2:加深對事件處理機制的理解
/* * 加深對事件處理機制的理解 * 通過上下左右鍵,來控制小球的位置 */ package com.fanghua1; import java.awt.*; import javax.swing.*; import java.awt.event.*; public class Demo1_2 extends JFrame { // 定義 PaintO jp = null; // 構造函數中初始化 public Demo1_2() { jp = new PaintO(); this.add(jp); this.addKeyListener(jp); this.setSize(400, 300); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } public static void main(String[] args) { // TODO Auto-generated method stub new Demo1_2(); } } // 定義自己的面板 class PaintO extends JPanel implements java.awt.event.KeyListener { int x = 10; int y = 10; public void paint(Graphics g) { super.paint(g); g.fillOval(x, y, 40, 40); } @Override // 鍵的一個值被輸出 public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override // 鍵被按下 public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub // System.out.print("按下"+(char)e.getKeyCode()); if (e.getKeyCode() == KeyEvent.VK_DOWN) { // x=x;這樣就不用寫了 y++; // y+=5;提速 // 調用repaint()函數,來重繪界面 this.repaint(); } else if (e.getKeyCode() == KeyEvent.VK_UP) { y--; this.repaint(); } else if (e.getKeyCode() == KeyEvent.VK_LEFT) { x--; this.repaint(); } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) { x++; this.repaint(); } } @Override // 鍵被釋放 public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } }
//功能:事件處理機制 演示1.2 package com.fanghua1; import java.awt.*; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; public class Demo1_3 extends JFrame { Mypanl1_3 mp1 = null; public static void main(String[] args) { // TODO Auto-generated method stub new Demo1_3(); } public Demo1_3() { // 這裡經常忘記 mp1 = new Mypanl1_3(); this.add(mp1); // 註冊監聽 this.addKeyListener(mp1); this.addMouseListener(mp1); this.addWindowListener(mp1); this.addMouseMotionListener(mp1); // this.addActionListener(mp1); // 按鈕用這個,this.addActionListener(mp1); this.setSize(300, 400); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } } // 這個經常用,java.awt.event.MouseMotionListener 滑鼠移動和拖拽 class Mypanl1_3 extends JPanel implements java.awt.event.MouseMotionListener, KeyListener, MouseListener, WindowListener { public void paint(Graphics g) { super.paint(g); } @Override public void windowOpened(WindowEvent e) { // TODO Auto-generated method stub System.out.print("開啟視窗被調用" + e.getClass()); } @Override public void windowClosing(WindowEvent e) { // TODO Auto-generated method stub } @Override public void windowClosed(WindowEvent e) { // TODO Auto-generated method stub System.out.println("視窗關閉了"); } @Override public void windowIconified(WindowEvent e) { // TODO Auto-generated method stub } @Override public void windowDeiconified(WindowEvent e) { // TODO Auto-generated method stub } @Override // 視窗激活 public void windowActivated(WindowEvent e) { // TODO Auto-generated method stub System.out.println("視窗激活"); } @Override // 視窗不激活 public void windowDeactivated(WindowEvent e) { // TODO Auto-generated method stub System.out.println("視窗不激活"); } @Override // 1.滑鼠點擊 public void mouseClicked(MouseEvent e) { // TODO Auto-generated method stub System.out .println("當前滑鼠點擊的橫坐標是" + e.getX() + "當前滑鼠點擊de縱坐標是" + e.getY()); } @Override // 2.滑鼠按壓,沒鬆開 public void mousePressed(MouseEvent e) { // TODO Auto-generated method stub System.out.println("滑鼠按壓,沒鬆開"); } @Override // 3.滑鼠鬆開 public void mouseReleased(MouseEvent e) { // TODO Auto-generated method stub System.out.println("滑鼠鬆開"); } @Override // 4.滑鼠移動到MyPanel public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub System.out.println("滑鼠移動到面板"); } @Override // 5.滑鼠離開 public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub System.out.println("滑鼠離開"); } @Override // 1.鍵輸入 (與 keyPressed的不同是,外圍一圈的鍵都不會有反應) public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override // 2.鍵按下(我測試過了: // 字母和少數鍵沒反應,其他鍵盤最外一圈F1-F12,Delete等控制台之類都有反應) public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub // 註意:這裡切換到美式鍵盤下演示。我在搜狗輸入法下演示了,結果總出不來 System.out.println(e.getKeyChar() + "鍵按下"); } @Override // 鍵鬆開 public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } @Override // 重要:滑鼠拖拽 public void mouseDragged(MouseEvent e) { // TODO Auto-generated method stub System.out.println("滑鼠拖拽了"); } @Override // 重要:滑鼠移動 public void mouseMoved(MouseEvent e) { // TODO Auto-generated method stub // System.out.println("滑鼠移動了"); System.out.println("當前移動移動橫坐標是" + e.getX() + "當前移動縱坐標是" + e.getY()); } }
Demo3:進程與線程
/* 功能:進程與線程練習 * Java中一個類要當作線程來使用,方法有兩種: * 1.繼承Thread類,並重寫run函數 * 2.實現Runnable介面,並重寫run函數 * 在這裡就可以看出繼承(類)和實現(類)的區別了 */ package com.fanghua1; public class Demo1_4 extends Thread { public static void main(String[] args) { // TODO Auto-generated method stub Demo1_4 de = new Demo1_4(); // 啟動run函數 de.start(); } int times = 0; @Override public void run() { // TODO Auto-generated method stub // 在控制台,每隔一秒輸出一句“HeLLo Word” while (true) { // 每隔一秒=休眠一秒 try { Thread.sleep(1000);// 1000表示1000毫秒 // sleep 會讓該線程進入到Blocked(阻塞)狀態,並釋放資源 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } times++; System.out.println("HeLLo Word" + times); if (times == 10) { // 退出程式 break; } } } }
/* 功能:加深理解多線程 * 編寫程式,該程式可以接收一個整數n * 創建一個線程計算從1+......+n 並輸出結果 * 創建另一個線程每隔一秒在控制台輸出“我是另一個線程,我輸出第n個HeLLo” * 兩個工作要同時進行 * 註意:如果說沒有任何要求的情況下來開發線程,最好用介面來實現,給別人繼承的空間 *之後會具體說繼承和實現的區別 */ package com.fanghua1; public class Demo1_6 { public static void main(String[] args) { // TODO Auto-generated method stub new Thread(new Bird(10)).start(); new Thread(new Pig(10)).start(); } } // 計算需求 class Bird implements Runnable { int n = 0; int res = 0; int times = 0; public Bird(int n) { this.n = n; } @Override public void run() { // TODO Auto-generated method stub // 每隔1秒算一次 while (true) { try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } res += (++times); System.out.print("當前結果是" + res); if (times == n) { System.out.print("最後結果是" + res); break; } } } } // 輸出需求 class Pig implements Runnable { int n = 0; int times = 0; public Pig(int n) { this.n = n; } public void run() { // TODO Auto-generated method stub while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("我是另一個線程,我輸出第" + times + "個HeLLo"); times++; if (times == n) { break; } } } }
坦克大戰(1.0版本)
/*
* 功能:畫出坦克 * 學習:圖形界面 */ package com.fanghua1; import java.awt.*; import javax.swing.*; public class MyTankGame1_1 extends JFrame { Mypanel mp=null; public static void main(String[] args) { // TODO Auto-generated method stub new MyTankGame1_1(); } // 構造函數 public MyTankGame1_1() { mp=new Mypanel(); this.add(mp); this.setSize(600,500); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } } // 我的面板,不要在JFram上面畫坦克,會很亂 class Mypanel extends JPanel{ //定義一個我的坦克 Hero hero=null; //在構造函數里給坦克意義初始位置 public Mypanel(){ hero=new Hero(10,10); } //重寫paint函數 public void paint(Graphics g){ //一定要調用 super.paint(g); //設置背景顏色 g.fillRect(0, 0, 600, 500); //調用繪畫坦克 this.drawTank(hero.getX(), hero.getY(), g, 0, 1); } //畫出坦克的函數 public void drawTank(int x,int y,Graphics g,int direct,int type){ //坦克類型 switch(type){ case 0: g.setColor(Color.green); break; case 1: g.setColor(Color.yellow); break; } //判斷方向 switch(direct){ //向上 case 0: //畫出坦克函數(已封裝) //畫出左邊的矩形 g.fill3DRect(x,y, 5, 30,false); g.fill3DRect(x+15, y, 5,30,false); //畫出中間矩形 g.fill3DRect(x+5, y+5, 10, 20,false); //畫出圓形 g.fillOval(x+5, y+10, 10, 10); //畫出線 g.drawLine(x+10,y+15, x+10, y); break; } } } // 坦克類(自己和敵人的坦克的父類) class Tank { // x表示坦克的橫坐標,y代表坦克的縱坐標 int x = 0; public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } int y = 0; // 構造函數 public Tank(int x, int y) { this.x = x; this.y = y; } } // 我的坦克 class Hero extends Tank { // 因為坦克裡面沒有定義無參構造函數 // 會報錯:Implicit super constructor Tank() is undefined for default // constructor. Must define an explicit constructor // 解決1.在Tank類裡面定義無參構造函數。 // 2用父類的構造函數,來初始化子類的成員變數。如下: public Hero(int x, int y) { super(x, y); } } /* * public void paint(Graphics g){ *一定要調用 *super.paint(g); *g.fillRect(0, 0, 600, 500);q *畫出坦克函數(未封裝) *畫出左邊的矩形 *g.fill3DRect(hero.getX(),hero.getY(), 5, 30,false); *g.fill3DRect(hero.getX()+15, hero.getY(), 5,30,false); *畫出中間矩形 *g.fill3DRect(hero.getX()+5, hero.getY()+5, 10, 20,false); *畫出圓形 *g.fillOval(hero.getX()+5, hero.getY()+10, 10, 10); *畫出線 *g.drawLine(hero.getX()+10,hero.getY()+15, hero.getX()+10, hero.getY()); *} */
坦克大戰(1.0.1版本)
/* * 功能:坦克可移動 * 學習:事件處理機制 */ package com.fanghua1; import java.awt.*; import java.awt.event.KeyEvent; import javax.swing.*; public class MyTankGame1_2 extends JFrame { Mypanel1_2 mp = null; public static void main(String[] args) { // TODO Auto-generated method stub new MyTankGame1_2(); } // 構造函數 public MyTankGame1_2() { mp = new Mypanel1_2(); this.add(mp); // 註冊監聽 this.addKeyListener(mp); this.setSize(600, 500); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } } // 我的面板,不要在JFram上面畫坦克,會很亂 class Mypanel1_2 extends JPanel implements java.awt.event.KeyListener { // 定義一個我的坦克 Hero1_2 hero = null; // 在構造函數里給坦克意義初始位置 public Mypanel1_2() { hero = new Hero1_2(10, 10); } // 重寫paint函數 public void paint(Graphics g) { // 一定要調用 super.paint(g); // 設置背景顏色 g.fillRect(0, 0, 600, 500); // 調用繪畫坦克 this.drawTank(hero.getX(), hero.getY(), g, 0, 1); } // 畫出坦克的函數 public void drawTank(int x, int y, Graphics g, int direct, int type) { // 坦克類型 switch (type) { case 0: g.setColor(Color.green); break; case 1: g.setColor(Color.yellow); break; } // 判斷方向 switch (direct) { // 向上 case 0: // 畫出坦克函數(已封裝) // 畫出左邊的矩形 g.fill3DRect(x, y, 5, 30, false); g.fill3DRect(x + 15, y, 5, 30, false); // 畫出中間矩形 g.fill3DRect(x + 5, y + 5, 10, 20, false); // 畫出圓形 g.fillOval(x + 5, y + 10, 10, 10); // 畫出線 g.drawLine(x + 10, y + 15, x + 10, y); break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub // e.getKeyCode() ==KeyEvent.VK_W 這句話我試了好久,都不能實現 // 原因:我在測試的時候,輸入法是搜狗輸入法,應該切換到美式鍵盤或者英文狀態下!!!!! if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_W) { // 這裡有三處不好的地方: // 1.方向(比如this.hero.y--;)寫死了,以後想改變移動速度,這裡實現起來麻煩。更正(將“速度”在坦克類里封裝成函數) // 2.重繪(this.repaint();)不用每個都寫一遍,在for迴圈之外,一個就可以了 // 3.方向設置最好按照順時針或者逆時針,依次。我這個上下左右,容易亂 this.hero.moveUp();// 之前版本:this.hero.y--; // this.repaint(); // 設置我的坦克的方向 this.hero.setDirect(0); } else if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_S) { this.hero.moveDown();// 之前版本: this.hero.y++; // this.repaint(); this.hero.setDirect(2); } else if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_A) { this.hero.moveLeft();// 之前版本:this.hero.x--; // this.repaint(); this.hero.setDirect(3); } else if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_D) { // this.repaint(); this.hero.setDirect(1); this.hero.moveRight();// 之前版本:this.hero.x++; } this.repaint(); } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } } // 坦克類 (自己和敵人坦克的父類) class Tank1_2 { // 坦克的坐標:x橫坐標,y縱坐標 int x = 0; int y = 0; // 坦克方向:0表示上,1表示右,2表示下,3表示左 int direct = 0; // 坦克的速度(預設為1,如果將來想給自己和敵人的坦克設置速度,set一下就可以了) int speed = 1; public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public int getDirect() { return direct; } public void setDirect(int direct) { this.direct = direct; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } // 構造函數 public Tank1_2(int x, int y) { this.x = x; this.y = y; } } // 我的坦克 class Hero1_2 extends Tank1_2 { // 因為坦克裡面沒有定義無參構造函數 // 會報錯:Implicit super constructor Tank() is undefined for default // constructor. Must define an explicit constructor // 解決: // 1.在Tank類裡面定義無參構造函數。 // 2.用父類的構造函數,來初始化子類的成員變數。如下: public Hero1_2(int x, int y) { super(x, y); } // 坦克向上移動 public void moveUp() { y -= speed; } // 坦克向右移動 public void moveRight() { x += speed; } // 坦克向下移動 public void moveDown() { y += speed; } // 坦克向左移動 public void moveLeft() { x -= speed; } }
接下來幾天我會連續更新幾篇博客,介紹該坦克大戰的漸進完善過程,和中間需要初學者掌握的基礎知識,有很多代碼實例,也會漸進的增加坦克大戰裡面的元素和功能。
PS:這個好久之前的了,現在看著有些想看小時候的日記,自我感覺很好玩。