Java培訓新手實戰必備!單機版坦克大戰分步實現項目源碼
很多java初學者,在學習一段時間java后,都想寫一個項目練練手來檢測一下學習成果,而坦克大戰就是一個非常好的試手項目。坦克大戰本身就是一個非常經典的單機項目,其次使用java來實現,技術點幾乎能夠涉及到java所有的基礎知識。
那么千鋒老謝就從最開始手把手的教你實現坦克大戰,簡單明了,步驟詳細,絕對讓你寫出自己的坦克大戰,同時加深對java知識的理解。那我們就開始吧。
1. 窗體繪制
JFrame是指一個計算機語言-java的GUI程序的基本思路是以JFrame為基礎,它是屏幕上window的對象,能夠最大化、最小化、關閉。
1.1 創建窗口
創建類繼承JFrame
創建啟動方法launch()
setTitle() 設置標題
setSize(width, height); 設置寬高
setLocationRelativeTo(null); 設置位置,null為居中
setResizable(); 是否可拉伸
setVisible(); 是否可見
setDefaultCloseOperation(3); 1,2,3 (點差號,關閉程序)
package com.qf;
import javax.swing.JFrame;
public class GamePanel extends JFrame {
private static final long serialVersionUID = 7471238471259660459L;
//窗口寬高
int width = 800;
int height = 610;
public void launch() {
//設置窗口標題
setTitle("千鋒坦克大戰");
//設置寬高
setSize(width, height);
//居中
setLocationRelativeTo(null);
//差掉窗口,結束
setDefaultCloseOperation(3);
//窗口不可拉伸
setResizable(false);
//可見
setVisible(true);
}
public static void main(String[] args) {
GamePanel gp = new GamePanel();
gp.launch();
}
}
1.2 為窗口上色
重寫paint()方法,參數g為圖形
設置圖形顏色
設置圖形大小
package com.qf;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class GamePanel extends JFrame {
private static final long serialVersionUID = 7471238471259660459L;
//窗口寬高
int width = 800;
int height = 610;
//獲取畫筆(為窗口上色)
@Override
public void paint(Graphics g) {
//設置顏色
g.setColor(Color.GRAY);
//填充窗口
g.fillRect(0, 0, width, height);
}
public void launch() {
setTitle("千鋒坦克大戰");
//設置寬高
setSize(width, height);
//居中
setLocationRelativeTo(null);
//差掉窗口,結束
setDefaultCloseOperation(3);
//窗口不可拉伸
setResizable(false);
//可見
setVisible(true);
}
public static void main(String[] args) {
GamePanel gp = new GamePanel();
gp.launch();
}
}
1.3 添加選項文字
設置字體顏色,不要和背景色一樣
設置字體,字體大小
添加文字,及文字位置
package com.qf;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JFrame;
public class GamePanel extends JFrame {
private static final long serialVersionUID = 7471238471259660459L;
//窗口寬高
int width = 800;
int height = 610;
//獲取畫筆
@Override
public void paint(Graphics g) {
g.setColor(Color.GRAY);
g.fillRect(0, 0, width, height);
//添加游戲選項
g.setColor(Color.BLUE);
g.setFont(new Font("仿宋", Font.BOLD, 50));
g.drawString("選擇游戲模式", 220, 100);
g.drawString("單人模式", 220, 200);
g.drawString("雙人模式", 220, 300);
}
public void launch() {
setTitle("千鋒坦克大戰");
//設置寬高
setSize(width, height);
//居中
setLocationRelativeTo(null);
//差掉窗口,結束
setDefaultCloseOperation(3);
//窗口不可拉伸
setResizable(false);
//可見
setVisible(true);
}
public static void main(String[] args) {
GamePanel gp = new GamePanel();
gp.launch();
}
}
1.4 為窗口添加鍵盤事件
添加內部類,實現KeyAdapter類,重寫keyPressed方法,監聽按鈕
class KeyMonitor extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
System.out.println(e.getKeyChar());
}
}
窗口添加鍵盤監視器
this.addKeyListener(new GamePanel.KeyMonitor());
package com.qf;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class GamePanel extends JFrame {
private static final long serialVersionUID = 7471238471259660459L;
//窗口寬高
int width = 800;
int height = 610;
//獲取畫筆
@Override
public void paint(Graphics g) {
g.setColor(Color.GRAY);
g.fillRect(0, 0, width, height);
//添加游戲選項
g.setColor(Color.BLUE);
g.setFont(new Font("仿宋", Font.BOLD, 50));
g.drawString("選擇游戲模式", 220, 100);
g.drawString("單人模式", 220, 200);
g.drawString("雙人模式", 220, 300);
}
//添加內部類,實現KeyAdapter類,重寫keyPressed方法
class KeyMonitor extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
System.out.println(e.getKeyChar());
}
}
public void launch() {
setTitle("千鋒坦克大戰");
//設置寬高
setSize(width, height);
//居中
setLocationRelativeTo(null);
//差掉窗口,結束
setDefaultCloseOperation(3);
//窗口不可拉伸
setResizable(false);
//可見
setVisible(true);
this.addKeyListener(new GamePanel.KeyMonitor());
}
public static void main(String[] args) {
GamePanel gp = new GamePanel();
gp.launch();
}
}
1.5 鍵盤控制選擇游戲模式
添加指針圖片
在項目文件夾下創建文件夾images,關于項目的圖片,插件都放在這個文件夾內
創建圖片對象
Image select = Toolkit.getDefaultToolkit().getImage("images/mytank_right.gif");
設置初始縱坐標為150
int y = 150;
在paint()方法,添加此圖片
g.drawImage(select, 160, y, null);
在launch()方法中重繪圖形(每隔30毫秒)
//重繪
while(true) {
repaint();
try {
Thread.sleep(25);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
重寫鍵盤事件,1選擇單人模式,2選擇雙人模式
//添加內部類,實現KeyAdapter類,重寫keyPressed方法
class KeyMonitor extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_1 :
y = 150;
break;
case KeyEvent.VK_2 :
y = 250;
break;
case KeyEvent.VK_ENTER :
break;
default:
break;
}
}
}
定義選擇相應模式后,點擊回車鍵,顯示響應窗口
定義模式state 0:未選擇,1:單人 2:雙人
//模式state 0:未選擇,1:單人 2:雙人
int state = 0;
int a = 0;
重寫點擊事件
//添加內部類,實現KeyAdapter類,重寫keyPressed方法
class KeyMonitor extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_1 :
a = 1; // 單人
y = 150;
break;
case KeyEvent.VK_2 :
a = 2; //雙人
y = 250;
break;
case KeyEvent.VK_ENTER :
state = a;
break;
default:
break;
}
}
}
寫入圖形判斷
if (state == 0 ) {
g.drawString("選擇游戲模式", 220, 100);
g.drawString("單人模式", 220, 200);
g.drawString("雙人模式", 220, 300);
//繪制指針
g.drawImage(select, 160, y, null);
} else if (state ==1 || state ==2) {
g.drawString("游戲開始", 220, 100);
if (state ==1) {
g.drawString("單人模式", 220, 200);
} else if(state ==2) {
g.drawString("雙人模式", 220, 200);
}
}
整體代碼
package com.qf;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class GamePanel extends JFrame {
private static final long serialVersionUID = 7471238471259660459L;
//指針圖片
Image select = Toolkit.getDefaultToolkit().getImage("images/mytank_right.gif");
int y =150; //縱坐標
//模式state 0:未選擇,1:單人 2:雙人
int state = 0;
int a = 0;
//窗口寬高
int width = 800;
int height = 610;
//獲取畫筆
@Override
public void paint(Graphics g) {
g.setColor(Color.GRAY);
g.fillRect(0, 0, width, height);
//添加游戲選項
g.setColor(Color.BLUE);
g.setFont(new Font("仿宋", Font.BOLD, 50));
if (state == 0 ) {
g.drawString("選擇游戲模式", 220, 100);
g.drawString("單人模式", 220, 200);
g.drawString("雙人模式", 220, 300);
//繪制指針
g.drawImage(select, 160, y, null);
} else if (state ==1 || state ==2) {
g.drawString("游戲開始", 220, 100);
if (state ==1) {
g.drawString("單人模式", 220, 200);
} else if(state ==2) {
g.drawString("雙人模式", 220, 200);
}
}
}
//添加內部類,實現KeyAdapter類,重寫keyPressed方法
class KeyMonitor extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_1 :
a = 1; // 單人
y = 150;
break;
case KeyEvent.VK_2 :
a = 2; //雙人
y = 250;
break;
case KeyEvent.VK_ENTER :
state = a;
break;
default:
break;
}
}
}
public void launch() {
setTitle("千鋒坦克大戰");
//設置寬高
setSize(width, height);
//居中
setLocationRelativeTo(null);
//差掉窗口,結束
setDefaultCloseOperation(3);
//窗口不可拉伸
setResizable(false);
//可見
setVisible(true);
this.addKeyListener(new GamePanel.KeyMonitor());
//重繪
while(true) {
repaint();
try {
Thread.sleep(25);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
GamePanel gp = new GamePanel();
gp.launch();
}
}
1.6 解決閃頻
創建一個圖片
//解決閃動問題
Image offScreemImage = null;
重寫paint()方法
創建一個和彈出窗口寬高相同的圖片,
獲取該圖片的圖形對象,把所有內容添加到該圖片中
//創建和容器一樣大小的Image圖片
if(offScreemImage == null) {
offScreemImage = this.createImage(width,height);
}
//獲該圖片的圖形
Graphics gImage = offScreemImage.getGraphics();
gImage.setColor(Color.GRAY);
gImage.fillRect(0, 0, width, height);
//添加游戲選項
gImage.setColor(Color.BLUE);
gImage.setFont(new Font("仿宋", Font.BOLD, 50));
if (state == 0 ) {
gImage.drawString("選擇游戲模式", 220, 100);
gImage.drawString("單人模式", 220, 200);
gImage.drawString("雙人模式", 220, 300);
//繪制指針
gImage.drawImage(select, 160, y, null);
} else if (state ==1 || state ==2) {
gImage.drawString("游戲開始", 220, 100);
if (state ==1) {
gImage.drawString("單人模式", 220, 200);
} else if(state ==2) {
gImage.drawString("雙人模式", 220, 200);
}
}
把該圖片寫入到窗口中
g.drawImage(offScreemImage, 0, 0,null);
package com.qf;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class GamePanel extends JFrame {
private static final long serialVersionUID = 7471238471259660459L;
//解決閃動問題
Image offScreemImage = null;
//指針圖片
Image select = Toolkit.getDefaultToolkit().getImage("images/mytank_right.gif");
int y =150; //縱坐標
//模式state 0:未選擇,1:單人 2:雙人
int state = 0;
int a = 0;
//窗口寬高
int width = 800;
int height = 610;
//獲取畫筆
@Override
public void paint(Graphics g) {
//創建和容器一樣大小的Image圖片
if(offScreemImage == null) {
offScreemImage = this.createImage(width,height);
}
//獲該圖片的圖形
Graphics gImage = offScreemImage.getGraphics();
gImage.setColor(Color.GRAY);
gImage.fillRect(0, 0, width, height);
//添加游戲選項
gImage.setColor(Color.BLUE);
gImage.setFont(new Font("仿宋", Font.BOLD, 50));
if (state == 0 ) {
gImage.drawString("選擇游戲模式", 220, 100);
gImage.drawString("單人模式", 220, 200);
gImage.drawString("雙人模式", 220, 300);
//繪制指針
gImage.drawImage(select, 160, y, null);
} else if (state ==1 || state ==2) {
gImage.drawString("游戲開始", 220, 100);
if (state ==1) {
gImage.drawString("單人模式", 220, 200);
} else if(state ==2) {
gImage.drawString("雙人模式", 220, 200);
}
}
g.drawImage(offScreemImage, 0, 0,null);
}
//添加內部類,實現KeyAdapter類,重寫keyPressed方法
class KeyMonitor extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_1 :
a = 1; // 單人
y = 150;
break;
case KeyEvent.VK_2 :
a = 2; //雙人
y = 250;
break;
case KeyEvent.VK_ENTER :
state = a;
break;
default:
break;
}
}
}
public void launch() {
setTitle("千鋒坦克大戰");
//設置寬高
setSize(width, height);
//居中
setLocationRelativeTo(null);
//差掉窗口,結束
setDefaultCloseOperation(3);
//窗口不可拉伸
setResizable(false);
//可見
setVisible(true);
this.addKeyListener(new GamePanel.KeyMonitor());
//重繪
while(true) {
repaint();
try {
Thread.sleep(25);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
GamePanel gp = new GamePanel();
gp.launch();
}
}
2. 鍵盤控制坦克移動
2.1 添加游戲類父類
屬性
圖片
位置(坐標)
面板
方法
構造方法
在圖形中構建自己
獲取自身矩形(用于比較中彈)
package com.qf;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
//游戲父類
public abstract class GameObject {
//圖片
public Image img;
//位置(坐標) 縱坐標 豎坐標
public int x;
public int y;
//在哪個面板
public GamePanel gamePanel;
//有參構造器 img參數為字符串,圖片路徑
public GameObject(String img, int x, int y, GamePanel gamePanel) {
super();
this.img = Toolkit.getDefaultToolkit().getImage(img);
this.x = x;
this.y = y;
this.gamePanel = gamePanel;
}
//畫自己
public abstract void paintSelf(Graphics g);
//獲取自身矩形
public abstract Rectangle getRec();
}
2.2 添加坦克類
尺寸(寬高)
速度
方向
不同方向圖片
package com.qf;
import java.awt.Point;
import java.awt.Toolkit;
//坦克父類
public abstract class Tank extends GameObject {
//尺寸
public int width = 40;
public int height = 50;
//速度
public int speed = 3;
//方向
public Direction direction = Direction.UP;
//四個方向圖片
public String upImg;
public String leftImg;
public String rightImg;
public String downImg;
public Tank(String img, int x, int y, GamePanel gamePanel,
String upImg, String leftImg, String rightImg, String downImg) {
super(img, x, y, gamePanel);
this.upImg = upImg;
this.leftImg = leftImg;
this.rightImg = rightImg;
this.downImg = downImg;
}
}
2.3 添加玩家類
package com.qf;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
//玩家
public class PlayerOne extends Tank {
public PlayerOne(String img, int x, int y, GamePanel gamePanel, String upImg, String leftImg, String rightImg,
String downImg) {
super(img, x, y, gamePanel, upImg, leftImg, rightImg, downImg);
}
@Override
public void paintSelf(Graphics g) {
g.drawImage(img, x, y, null);
}
@Override
public Rectangle getRec() {
return new Rectangle(x, y, width, height);
}
}
2.4 在窗口中添加玩家
創建玩家對象
//添加玩家一到面板
PlayerOne playerOne = new PlayerOne("images/mytank_up.gif", 125, 510,
this,
"images/mytank_up.gif",
"images/mytank_left.gif",
"images/mytank_right.gif",
"images/mytank_down.gif");
把玩家對象添加到圖形中
playerOne.paintSelf(g);
package com.qf;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
/*
* 已解決閃動
*/
public class GamePanel extends JFrame {
private static final long serialVersionUID = -9194828502749643640L;
//解決閃動問題
Image offScreemImage = null;
//窗口長寬
int width = 800;
int height = 610;
//指針圖片
Image select = Toolkit.getDefaultToolkit().getImage("images/mytank.gif");
int y =150; //縱坐標
//定義游戲模式 0:游戲未開始 1:單人模式 2:雙人模式
int state = 0;
int a = 0; //根據輸入的鍵值,修改a值,再賦值給state
//添加玩家一到面板
PlayerOne playerOne = new PlayerOne("images/mytank_up.gif", 125, 510,
this,
"images/mytank_up.gif",
"images/mytank_left.gif",
"images/mytank_right.gif",
"images/mytank_down.gif");
//窗口的啟動方法
public void launch() {
setTitle("千鋒坦克大戰");
//設置寬高
setSize(width, height);
//居中
setLocationRelativeTo(null);
//差掉窗口,結束
setDefaultCloseOperation(3);
//窗口不可拉伸
setResizable(false);
//可見
setVisible(true);
//添加鍵盤監視器
this.addKeyListener(new GamePanel.KeyMonitor());
//重繪
while(true) {
repaint();
try {
Thread.sleep(25); //間隔時間
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//畫 圖形
@Override
public void paint(Graphics g) {
//創建和容器一樣大小的Image圖片
if(offScreemImage == null) {
offScreemImage = this.createImage(width,height);
}
//獲的該圖片的畫筆
Graphics gImage = offScreemImage.getGraphics();
//把窗口背景色設置為灰色
gImage.setColor(Color.GRAY);
//把這個背景色填充整個窗口
gImage.fillRect(0, 0, width, height);
//設置顏色
gImage.setColor(Color.BLUE);
//設置字體
gImage.setFont(new Font("仿宋", Font.BOLD, 50));
if(state == 0 ) {
//添加文字
gImage.drawString("選擇游戲模式", 220, 100);
gImage.drawString("單人游戲", 220, 200);
gImage.drawString("雙人游戲", 220, 300);
//繪制指針
gImage.drawImage(select, 160, y, null);
} else if (state == 1 || state == 2) {
gImage.drawString("游戲開始", 220, 100);
if(state == 1) {
gImage.drawString("單人游戲", 220, 200);
} else {
gImage.drawString("雙人游戲", 220, 200);
}
//添加玩家
playerOne.paintSelf(gImage);
}
g.drawImage(offScreemImage, 0, 0,null);
}
//鍵盤監視器,需要添加到窗口中
class KeyMonitor extends KeyAdapter {
//按下鍵盤的回調方法
@Override
public void keyPressed(KeyEvent e) {
//返回按鈕的值
int key = e.getKeyChar();
switch(key) {
case KeyEvent.VK_1 :
a = 1;
y = 150;
break;
case KeyEvent.VK_2 :
a = 2;
y = 250;
break;
case KeyEvent.VK_ENTER :
state = a;
break;
default:
}
}
}
public static void main(String[] args) {
GamePanel gp = new GamePanel();
gp.launch();
}
}
2.5使用鍵盤控制坦克移動
在坦克類(Tank)中添加上下左右四個方法
不同方向設置不同圖片
不同的方向
//移動方法
public void leftward() {
x -=speed;
direction = Direction.LEFT;
setImg(leftImg);
}
public void rightward() {
x +=speed;
direction = Direction.RIGHT;
setImg(rightImg);
}
public void upward() {
y -=speed;
direction = Direction.UP;
setImg(upImg);
}
public void downward() {
y +=speed;
direction = Direction.DOWN;
setImg(downImg);
}
public void setImg(String img) {
this.image = Toolkit.getDefaultToolkit().getImage(img);
}
在PlayerOne類中按鈕點擊,松開方法,及移動方法
W上,A左,S下,D右
boolean left, right, up, down;
//鍵盤按下去方法
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_A : left=true; break;
case KeyEvent.VK_S : down=true; break;
case KeyEvent.VK_D : right=true; break;
case KeyEvent.VK_W : up=true; break;
}
}
//鍵盤松開方法
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_A : left=false; break;
case KeyEvent.VK_S : down=false; break;
case KeyEvent.VK_D : right=false; break;
case KeyEvent.VK_W : up=false; break;
}
}
//移動的方法,每次在繪制的時候移動
public void move() {
if(left) {
leftward();
} else if (right) {
rightward();
} else if (up) {
upward();
} else if(down) {
downward();
}
}
@Override
public void paintSelf(Graphics g) {
g.drawImage(image, x, y, null);
//每次繪圖的時候,根據最新的屬性,繪圖
move();
}
在主窗口類中,添加對A,S,D,W按鍵的監控
//鍵盤監控
class KeyMonitor extends KeyAdapter {
//點擊鍵盤的回調方法
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyChar();
switch(key) {
case KeyEvent.VK_1:
y = 150;
a = 1;
break;
case KeyEvent.VK_2:
y = 250;
a = 2;
break;
case KeyEvent.VK_ENTER : //點擊回車,選擇相應的模式
state = a;
default:
playerOne.keyPressed(e); //這里
}
}
@Override
public void keyReleased(KeyEvent e) { //監聽按鈕的松開事件
playerOne.keyReleased(e); //這里
}
}
3. 坦克發射子彈
3.1 創建子彈類
屬性
尺寸
速度
方向
//尺寸
int width = 10;
int height =10;
//速度
int speed = 7;
//方向,方向和坦克方向一致
Direction direction;
方法
左移,右移,下移,上移
根據坦克方向不同,移動方向不同
//移動方法
public void leftward() {
x -=speed;
}
public void rightward() {
x +=speed;
}
public void upward() {
y -=speed;
}
public void downward() {
y +=speed;
}
//根據方向移動,繪制自己時,調用
public void go() {
switch(direction) {
case LEFT: leftward(); break;
case DOWN: downward(); break;
case RIGHT: rightward();break;
case UP: upward(); break;
}
}
全部代碼:
package com.qf;
import java.awt.Graphics;
import java.awt.Rectangle;
public class Bullet extends GameObject {
//尺寸
int width = 10;
int height =10;
//速度
int speed = 7;
//方向,方向和坦克方向一致
Direction direction;
//移動方法
public void leftward() {
x -=speed;
}
public void rightward() {
x +=speed;
}
public void upward() {
y -=speed;
}
public void downward() {
y +=speed;
}
//繪制自己時,調用
public void go() {
switch(direction) {
case LEFT: leftward(); break;
case DOWN: downward(); break;
case RIGHT: rightward();break;
case UP: upward(); break;
}
}
public Bullet(String imageUrl, int x, int y, GamePanal gamePanal,Direction direction) {
super(imageUrl, x, y, gamePanal);
this.direction = direction;
}
@Override
public void paintSelf(Graphics g) {
g.drawImage(image, x, y, null);
//繪制子彈時,可以移動
go();
}
@Override
public Rectangle getRec() {
return new Rectangle(x, y, width, height);
}
}
3.2 實現坦克發射子彈
在坦克類中
獲取坦克的頭部坐標
public Point getHeadPoint() {
switch(direction) {
case LEFT: return new Point(x, y+height/2);
case DOWN: return new Point(x+width/2, y+height);
case RIGHT: return new Point(x+width, y+height/2);
case UP: return new Point(x+width/2, y);
default: return null;
}
}
添加發射方法
根據坦克頭坐標,及坦克方向創建子彈
把創建的子彈添加到面板中創建子彈集合中
//發射子彈
public void attack() {
//獲取頭坐標
Point point = getHeadPoint();
//創建子彈
Bullet bullet = new Bullet("images/bullet.gif", point.x, point.y,
this.gamePanal, this.direction);
//可以一次按很多次發射,把產生的子彈存儲在面板的子彈集合中
this.gamePanal.bulletList.add(bullet);
}
//子彈集合 在面板類中定義
List<Bullet> bulletList = new ArrayList<Bullet>();
//繪制玩家
playerOne.paintSelf(gImage);
//繪制子彈 在面板類中操作
for(Bullet bullet : bulletList) {
bullet.paintSelf(gImage);
}
在PlayerOne類中添加鍵盤事件
當點擊空格按鈕,就會調用射擊方法
//鍵盤按下去方法
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_A : left=true; break;
case KeyEvent.VK_S : down=true; break;
case KeyEvent.VK_D : right=true; break;
case KeyEvent.VK_W : up=true; break;
case KeyEvent.VK_SPACE: attack(); //添加發送子彈方法
}
}
3.3 添加子彈冷卻時間
在坦克類中添加冷卻狀態,冷卻時間
//冷卻狀態
boolean attackCoolDown = true;
//冷卻時間
int attackCoolTime = 1000;
創建冷卻線程
//冷卻線程
class AttackCool extends Thread {
public void run() {
attackCoolDown = false;
try {
Thread.sleep(attackCoolTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
attackCoolDown = true;
}
}
修改發射方法,每發射一次,啟動冷卻線程
//發射子彈
public void attack() {
if (attackCoolDown) {
//獲取頭坐標
Point point = getHeadPoint();
//創建子彈
Bullet bullet = new Bullet("images/bullet.gif", point.x, point.y,
this.gamePanal, this.direction);
//可以一次按很多次發射,把產生的子彈存儲在面板的子彈集合中
this.gamePanal.bulletList.add(bullet);
//啟動冷卻線程
new AttackCool().start();
}
}
4. 敵方坦克
4.1 隨機添加敵方坦克
添加敵方坦克類
//敵方坦克
public class Bot extends Tank{
public Bot(String imageUrl, int x, int y, GamePanal gamePanal, String upImg, String leftImg, String rightImg,
String downImg) {
super(imageUrl, x, y, gamePanal, upImg, leftImg, rightImg, downImg);
}
@Override
public void paintSelf(Graphics g) {
g.drawImage(image, x, y, null);
}
@Override
public Rectangle getRec() {
return new Rectangle(x, y, width, height);
}
}
批量把敵方坦克圖片添加到面板上
把敵方坦克的圖片添加到項目中(4個方向各一個)
在面板中創建敵方坦克集合
面板每重新加載100次,添加一個敵方坦克,敵方坦克最多有10各
//重繪次數
int count =0;
//敵方坦克個數
int enemyCount = 0;
在重新繪制循環中,添加敵方坦克
//重新載入窗口
while(true) {
if(count%100 ==1 && enemyCount<5 ) {
//添加敵方坦克
Random r = new Random();
int rnum = r.nextInt(800);
botList.add(new Bot("images/enemy_up.png", rnum, 110,
this, "images/enemy_up.png", "images/enemy_left.png",
"images/enemy_right.png", "images/enemy_down.gif"));
enemyCount++;
}
this.repaint();
try {
Thread.sleep(25);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
在開始游戲加載面板中顯示敵方坦克并對count自增(paint方法)
if (state ==0) {
//220離左邊的距離, 100離上面的距離
gImage.drawString("選擇游戲模式", 220, 100);
gImage.drawString("單人模式", 220, 200);
gImage.drawString("雙人模式", 220, 300);
//畫入圖像
gImage.drawImage(select, 140, y, null);
} else {
gImage.drawString("開始游戲", 220, 100);
if(state == 1) {
gImage.drawString("單人模式", 220, 200);
} else if (state == 2) {
gImage.drawString("雙人模式", 220, 200);
}
//繪制玩家
playerOne.paintSelf(gImage);
//繪制子彈
for(Bullet bullet : bulletList) {
bullet.paintSelf(gImage);
}
//繪制敵方坦克
for(Bot bot : botList) {
bot.paintSelf(gImage);
}
count++;
}
4.2 敵方坦克隨機移動
隨機生成方向(Bot)
//隨機生成個數,表示方向
public Direction getRandomDirection() {
Random random = new Random();
int rnum = random.nextInt(4); //0-3
switch(rnum) {
case 0 : return Direction.LEFT;
case 1 : return Direction.RIGHT;
case 2 : return Direction.UP;
case 3 : return Direction.DOWN;
default: return null;
}
}
添加運動方法,每20次,轉化一次方向(Bot)
//每間隔20,轉換方向一次
int moveTime = 20;
//行走方法
public void go() {
if (moveTime >=20) {
direction = getRandomDirection();
moveTime=0;
} else {
moveTime++;
}
switch(direction) {
case LEFT: leftward(); break;
case DOWN: downward(); break;
case RIGHT: rightward();break;
case UP: upward(); break;
}
}
//勿忘把重繪方法添加到繪制自己方法中
@Override
public void paintSelf(Graphics g) {
g.drawImage(image, x, y, null);
go();
}
4.3 敵方坦克射擊
創建敵方子彈類,直接繼承Bullet即可
package com.qf;
import java.awt.Graphics;
import java.awt.Rectangle;
//敵方子彈類
public class EnemyBullet extends Bullet {
public EnemyBullet(String imageUrl, int x, int y, GamePanal gamePanal, Direction direction) {
super(imageUrl, x, y, gamePanal, direction);
}
@Override
public void paintSelf(Graphics g) {
g.drawImage(image, x, y, null);
//在圖形繪制自己時移動
go();
}
@Override
public Rectangle getRec() {
return new Rectangle(x, y, width, height);
}
}
在機器人坦克類中添加攻擊發射方法
機器人自行發射,把該方法添加到go()方法中
//攻擊類,添加到go方法中
public void attack() {
Point p = getHeadPoint();
Random random = new Random();
int num = random.nextInt(100); // [0,99]
if(num <4) { //4%設計概率,可自行調節發射頻率 0,1,2,3
this.gamePanal.bulletList.add(new EnemyBullet("images/bullet.gif",
p.x, p.y, this.gamePanal, this.direction));
}
}
//機器人坦克自行行走
public void go() {
attack(); // 添加到這里,邊走邊發射子彈
if (moveTime>=20) {
direction = getRandomDirection();
moveTime =0;
} else {
moveTime++;
}
switch(direction) {
case LEFT : leftward(); break;
case RIGHT : rightward(); break;
case DOWN : downward(); break;
case UP : upward(); break;
}
}
5. 碰撞檢測
5.1 我方子彈(Bullet)和敵方坦克(Bot)碰撞
在面板類中定義要刪除/消失的子彈(GamePanal)
在每次重繪時,把刪除的子彈從繪制子彈集合中刪除
//碰撞后要刪除的子彈
List<Bullet> removeList = new ArrayList<Bullet>();
//繪制子彈
for(Bullet bullet : bulletList) {
bullet.paintSelf(gImage);
}
//去除要刪除的子彈
bulletList.removeAll(removeList);
在子彈類中添加碰撞方法(Bullet)
在繪制每個子彈時,把當前子彈對象和所有計算機坦克比較是否碰撞
該方法要添加到繪制方法中
//碰撞 添加到paintSelf方法中,每次繪制時,檢測
public void hitBot() {
List<Bot> bots = this.gamePanal.botList;
for(Bot bot : bots) {
if (this.getRec().intersects(bot.getRec())) {
this.gamePanal.botList.remove(bot); // 坦克消失
this.gamePanal.removeList.add(this); //子彈消失
break;
}
}
}
@Override
public void paintSelf(Graphics g) {
g.drawImage(image, x, y, null);
//在圖形繪制自己時移動
go();
//每次繪制時,檢測碰撞
hitBot();
}
5.2 敵方子彈和我方坦克碰撞
添加我方坦克集合(為后續擴展使用)
把玩家都添加到此集合中,在選擇游戲模式時添加
玩家的繪制,修改成遍歷該集合
不要忘記把原繪制玩家代碼刪除
//玩家坦克集合
List<Tank> playerList = new ArrayList<Tank>();
//鍵盤監控
class KeyMonitor extends KeyAdapter {
//點擊鍵盤的回調方法
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyChar();
switch(key) {
case KeyEvent.VK_1:
y = 150;
a = 1;
break;
case KeyEvent.VK_2:
y = 250;
a = 2;
break;
case KeyEvent.VK_ENTER : //點擊回車,選擇相應的模式
state = a;
//添加玩家,雙人待做
playerList.add(playerOne); //----這里添加
default:
playerOne.keyPressed(e); //坦克的移動
}
}
//繪制玩家
// playerOne.paintSelf(gImage);
for(Tank player : playerList) {
player.paintSelf(gImage);
}
在敵方子彈類中添加碰撞方法
該方法添加到繪制方法中
//碰撞 添加到paintSelf方法中,每次繪制時,檢測
public void hitPlayer() {
List<Tank> tanks = this.gamePanel.playerList;
for(Tank t : tanks) {
if (this.getRec().intersects(t.getRec())) {
this.gamePanel.playerList.remove(t); // 玩家坦克消失
this.gamePanel.removeList.add(this); //子彈消失
break;
}
}
}
@Override
public void paintSelf(Graphics g) {
g.drawImage(image, x, y, null);
//在圖形繪制自己時移動
go();
//檢測碰撞
hitPlayer();
}
5.3 與圍墻的碰撞檢測
5.3.1面板中添加圍墻
添加圍墻類
package com.qf;
import java.awt.Graphics;
import java.awt.Rectangle;
public class Wall extends GameObject {
//圍墻尺寸
int length = 60;
public Wall(String imageUrl, int x, int y, GamePanel gamePanal) {
super(imageUrl, x, y, gamePanel);
}
@Override
public void paintSelf(Graphics g) {
g.drawImage(image, x, y, null);
}
@Override
public Rectangle getRec() {
return new Rectangle(x, y, length, length);
}
}
在面板中添加圍墻集合,在lunch方法中添加圍墻,然后把集合繪制到面板中
//圍墻列表
List<Wall> wallList = new ArrayList<Wall>();
//在lunch方法中
//添加圍墻
for(int i = 0; i<14; i++) {
wallList.add(new Wall("images/wall.png", i*60, 170, this));
}
wallList.add(new Wall("images/wall.png", 305, 560, this));
wallList.add(new Wall("images/wall.png", 305, 500, this));
wallList.add(new Wall("images/wall.png", 365, 500, this));
wallList.add(new Wall("images/wall.png", 425, 500, this));
wallList.add(new Wall("images/wall.png", 425, 560, this));
//在paint方法中繪制圍墻
for(Wall wall : wallList) {
wall.paintSelf(gImage);
}
5.3.2 添加圍墻和子彈碰撞檢測
子彈類添加和圍墻碰撞方法(Bullet)
把該方法添加到go()方法中,讓敵方子彈也可以和圍墻碰撞
//子彈和圍墻碰撞,添加到go方法中,讓敵方子彈也可以擊破圍墻
public void hitWall() {
List<Wall> walls = this.gamePanal.wallList;
for(Wall wall : walls) {
if (this.getRec().intersects(wall.getRec())) {
this.gamePanal.wallList.remove(wall); // 圍墻消失
this.gamePanal.removeList.add(this); //子彈消失
break;
}
}
}
public void go() {
switch(direction) {
case LEFT : leftward(); break;
case RIGHT : rightward(); break;
case DOWN : downward(); break;
case UP : upward(); break;
}
hitWall(); //---------------添加到這里
}
5.3.3 坦克和圍墻碰撞
當坦克和圍墻碰撞時,坦克無法移動
在坦克類中添加圍墻碰撞方法
然后在移動類中判斷是否會和圍墻碰撞,如果碰撞則不移動
//與圍墻碰撞檢測
public boolean hitWall(int x, int y) {
List<Wall> wallList = this.gamePanal.wallList;
//坦克下一步將要移動后的矩形
Rectangle next = new Rectangle(x, y, width, height);
for(Wall wall : wallList) {
if(next.intersects(wall.getRec())) {
return true;
}
}
return false;
}
修改所有移動的方法
//移動方法 左移
public void leftward() {
if(!hitWall(x-speed, y)) {
x -=speed;
}
direction = Direction.LEFT;
setImg(leftImg);
}
//移動方法 右移
public void rightward() {
if(!hitWall(x+speed, y)) {
x +=speed;
}
direction = Direction.RIGHT;
setImg(rightImg);
}
//移動方法 上移
public void upward() {
if(!hitWall(x, y-speed)) {
y -=speed;
}
direction = Direction.UP;
setImg(upImg);
}
//移動方法 下移
public void downward() {
if(!hitWall(x, y+speed)) {
y +=speed;
}
direction = Direction.DOWN;
setImg(downImg);
}
5.4 判斷坦克邊緣的碰撞
坦克類中添加邊緣碰撞判斷(Tank)
該判斷添加到移動方法中
//判斷和邊緣的碰撞
public boolean moveToBorder(int x,int y) {
if(x<0) {
return true;
} else if (x+width > this.gamePanal.width) {
return true;
} else if (y <0) {
return true;
} else if (y+height > this.gamePanal.height) {
return true;
}
return false;
}
//移動方法 左移
public void leftward() {
if(!hitWall(x-speed, y) && !moveToBorder(x-speed, y)) {
x -=speed;
}
direction = Direction.LEFT;
setImg(leftImg);
}
//移動方法 右移
public void rightward() {
if(!hitWall(x+speed, y) && !moveToBorder(x+speed, y)) {
x +=speed;
}
direction = Direction.RIGHT;
setImg(rightImg);
}
//移動方法 上移
public void upward() {
if(!hitWall(x, y-speed)&&!moveToBorder(x, y-speed)) {
y -=speed;
}
direction = Direction.UP;
setImg(upImg);
}
//移動方法 下移
public void downward() {
if(!hitWall(x, y+speed)&&!moveToBorder(x, y+speed)) {
y +=speed;
}
direction = Direction.DOWN;
setImg(downImg);
}
5.5判斷子彈是否出界
判斷子彈坐標是否出界,如果出界則刪除
該方法添加到go方法中
//如果子彈出界,該方法添加在go方法中,敵人子彈也可以刪除 刪除子彈,節省資源
public void moveToBorder() {
if(x<0 || x+width > this.gamePanel.width) {
this.gamePanel.removeList.add(this);
}
if(y<0 || y+height > this.gamePanel.height) {
this.gamePanel.removeList.add(this);
}
}
//子彈的移動是根據方向來的
public void go() {
switch(direction) {
case LEFT : leftward(); break;
case RIGHT : rightward(); break;
case DOWN : downward(); break;
case UP : upward(); break;
}
hitWall(); //子彈撞墻
moveToBorder(); //子彈出界刪除
}
6. 添加基地
6.1 創建并添加基地
創建基地類
package com.qf;
import java.awt.Graphics;
import java.awt.Rectangle;
//基地類
public class Base extends GameObject {
int length = 60;
public Base(String imageUrl, int x, int y, gamePanel gamePanal) {
super(imageUrl, x, y, gamePanal);
}
@Override
public void paintSelf(Graphics g) {
g.drawImage(image, x, y, null);
}
@Override
public Rectangle getRec() {
return new Rectangle(x, y, length, length);
}
}
在面板中添加基地
//基地集合
List<Base> baseList = new ArrayList<Base>();
//在lunch方法添加創建一個基地
Base base = new Base("images/base.png", 365, 560, this);
baseList.add(base);
//繪制基地
for(Base base : baseList) {
base.paintSelf(gImage);
}
6.2 子彈與基地的碰撞測試
在子彈類中添加碰撞方法
該方法添加到go方法中
//與基地碰撞方法
public void hitBase() {
List<Base> bases = this.gamePanel.baseList;
for(Base base : bases) {
if (this.getRec().intersects(base.getRec())) {
this.gamePanel.baseList.remove(base); // 基地消失
this.gamePanel.removeList.add(this); //子彈消失
break;
}
}
}
public void go() {
switch(direction) {
case LEFT : leftward(); break;
case RIGHT : rightward(); break;
case DOWN : downward(); break;
case UP : upward(); break;
}
hitWall(); //子彈撞墻
moveToBorder(); //子彈出界刪除
hitBase(); //---銷毀基地
}
7. 游戲規則
我方勝利
把所有的敵人干倒
機器贏了
玩家坦克被干掉
基地被倒了
//判斷規則,在每次重繪時
//玩家贏了
if(botList.size() == 0 && enemyCount == 3) {
state = 3;
}
//機器贏了
if((playerList.size()==0&&(state ==1 || state ==2))
|| baseList.size() ==0) {
state = 4;
}
//獲取畫筆
@Override
public void paint(Graphics g) {
if(offScreemImage==null) {
offScreemImage = this.createImage(width, height);
}
Graphics gImage = offScreemImage.getGraphics();
//設置圖形的顏色
gImage.setColor(Color.GRAY);
//把設置的顏色,填充整個矩形
gImage.fillRect(0, 0, width, height);
//重新設置字體顏色
gImage.setColor(Color.BLUE);
gImage.setFont(new Font("楷體", Font.BOLD, 50));
if (state ==0) {
//220離左邊的距離, 100離上面的距離
gImage.drawString("選擇游戲模式", 220, 100);
gImage.drawString("單人模式", 220, 200);
gImage.drawString("雙人模式", 220, 300);
//畫入圖像
gImage.drawImage(select, 140, y, null);
} else if (state == 1 || state ==2){
gImage.drawString("開始游戲", 220, 100);
if(state == 1) {
gImage.drawString("單人模式", 220, 200);
} else if (state == 2) {
gImage.drawString("雙人模式", 220, 200);
}
//繪制玩家
// playerOne.paintSelf(gImage);
//繪制玩家列表
for(Tank tank : playerList) {
tank.paintSelf(gImage);
}
//繪制子彈
for(Bullet bullet : bulletList) {
bullet.paintSelf(gImage);
}
//刪除發生碰撞的子彈
bulletList.removeAll(removeList);
//繪制機器人坦克
for (Bot bot : botList) {
bot.paintSelf(gImage);
}
//繪制城墻
for (Wall wall : wallList) {
wall.paintSelf(gImage);
}
//繪制基地
for (Base base : baseList) {
base.paintSelf(gImage);
}
count++;
} else if(state ==3) {
gImage.drawString("我贏了", 220, 100);
} else if(state == 4) {
gImage.drawString("機器贏了", 220, 100);
}
//把重新繪制的圖片放到窗口中(從左上角開始放)
g.drawImage(offScreemImage, 0, 0, null);
}

猜你喜歡LIKE
最新文章NEW
相關推薦HOT
更多>>熱門推薦
零基礎必看的前端HTML+CSS教程
沸Java培訓新手實戰必備!單機版坦克大戰分步實現項目源碼
熱3種Javascript圖片預加載的方法詳解
熱長沙前端培訓:一招教你用vue3+canvas實現坦克大戰
新互聯網涼了?參加長沙Java培訓能找到工作嗎?
長沙Java培訓實戰項目,出游咨詢訂票系統開發流程
不參加長沙Java培訓能學會Java嗎?2022Java技能學習路線圖
千鋒長沙Java培訓分享之怎么學習Java集合?
千鋒長沙前端培訓分享之JavaScript面向對象編程思想詳解
千鋒長沙前端培訓分享之web前端的回流和重繪
千鋒長沙前端培訓分享之3種Javascript圖片預加載的方法詳解
千鋒長沙前端培訓分享之利用Jest測試React組件
千鋒長沙前端培訓分享之JavaScript中Slice的用例
千鋒長沙java培訓分享之Socket編程