千锋教育-做有情怀、有良心、有品质的职业教育机构

400-811-9990
手机站
千锋教育

千锋学习站 | 随时随地免费学

千锋教育

扫一扫进入千锋手机站

领取全套视频
千锋教育

关注千锋学习站小程序
随时随地免费学习课程

上海
  • 北京
  • 郑州
  • 武汉
  • 成都
  • 西安
  • 沈阳
  • 广州
  • 南京
  • 深圳
  • 大连
  • 青岛
  • 杭州
  • 重庆
当前位置:长沙千锋IT培训  >  技术要点  >  Java培训新手实战必备!单机版坦克大战分步实现项目源码

Java培训新手实战必备!单机版坦克大战分步实现项目源码

来源:千锋教育
发布人:千锋长沙
时间: 2022-03-29 17:16:42

       很多java初学者,在学习一段时间java后,都想写一个项目练练手来检测一下学习成果,而坦克大战就是一个非常好的试手项目。坦克大战本身就是一个非常经典的单机项目,其次使用java来实现,技术点几乎能够涉及到java所有的基础知识。

src=http___pic4.zhimg.com_v2-48388a17e9d61ac740611d2c7c9791fb_1200x500.jpg&refer=http___pic4.zhimg.webp

       那么千锋老谢就从最开始手把手的教你实现坦克大战,简单明了,步骤详细,绝对让你写出自己的坦克大战,同时加深对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

更多>>

快速通道 更多>>

最新开班信息 更多>>

网友热搜 更多>>