总结:由于这几天快过年比较忙然后没怎么写,写代码途中一些经验总结现在给忘记了。这次的小项目感觉比上次写的思路清楚了点。没有之前第一次写那么逻辑混乱,结构也搞的比之前的要好,添加功能比较容易。学习了之前的经验,操作对象的方法由对象本身提供。不过这次小项目还有不足和不完善之处,有些可以做的功能没有实现,比如游戏存档,这里应该可以用下对象的序列化,还有游戏难度的设置也可以写个文件弄出来。要过年了,到处走亲戚没什么心思写了,这里只能留个尾巴了。

  前言:之前的及时通信项目完成后感觉线程方面和对java的运用还不是很熟悉,在另外一个学习视频中看到一个做坦克游戏的项目视频,便想自己试着做做看,先看视频把游戏规则什么的都搞清楚。然后开始一步一步实现。

主要功能步骤如下

* 1、画出坦克
* 2、我的坦克可以上下移动
* 3、可以发射子弹,子弹连发(或者最多5发)
* 4、当我的坦克击中敌人坦克时,敌人坦克消失(或者爆炸效果)
* 5、我被击中也显示爆炸效果。
* 6、游戏开始和游戏介绍选项

这次游戏界面没有进行设计,直接在一个frame上放个panel。

游戏设计仿mvc 这里不做详细介绍了直接上代码

model包

package com.gh.model;
/**
* 爆炸类
* 考虑到同时爆炸定义个类
* @author ganhang
*
*/
public class Bomb {
private int x;
private int y;//坐标
public boolean islive=true;
private int time=9;//炸弹生命
public Bomb() {
super();
}
public Bomb(int x, int y) {
super();
this.x = x;
this.y = y;
}
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 int getTime() {
return time;
}
//生命递减
public void livedown(){
if(time>0){
time--;
}else{
islive=false;
}
}
}

Bomb

 package com.gh.model;
/**
* 子弹类,
* 因为多个子弹同时运动所以需要个内部类做线程
* @author ganhang
*
*/
public class Bullet {
private int x;
private int y;
private int speed;
private int drect;
public boolean islive=true;
public Bullet(int x, int y, int speed, int drect) {
super();
this.x = x;
this.y = y;
this.speed = speed;
this.drect = drect;
new Thread(new BulletThread()).start();
}
public Bullet() {
super();
} 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;
} class BulletThread implements Runnable {
@Override
public void run() {
while (true) {
try {
Thread.sleep(50);
} catch (Exception e) {
e.printStackTrace();
}
switch (drect) {//判断方向坐标移动
case 0:
y-=speed;
break;
case 1:
x+=speed;
break;
case 2:
y+=speed;
break;
case 3:
x-=speed;
break;
}
if (x < 0 || y < 0 || x > 500 || y > 500||!islive) {
islive=false;
break;
}
}
}
}
}

Bullet

package com.gh.model;

/**
* 地图坐标标记
* 防止敌方坦克重叠
* @author ganhang
*
*/
public class Map {
public int[][] location=new int[500][500];
public Map() {
for (int i = 0; i < 500; i++) {
for (int j = 0; j <500; j++) {
location[i][j]=0;
}
}
}
}

Map

坦克类,刚才上传发现点问题暂时没改

 package com.gh.model;

 import java.util.Vector;
/**
* 坦克类
* 每个坦克就是一个线程,
* 这里自己坦克并没有启动线程
* @author ganhang
*
*/
public class Tank implements Runnable {
private int x = 0;
private int y = 0;// 坐标
private int drect = 0;// 方向 0向上,1向右,2向下,3向左;
private int type = 0;// 坦克类型 0表示自己
private int speed = 3;// 速度
public Vector<Bullet> mybs = new Vector<Bullet>();// 子弹集
private Bullet myBullet;// 子弹
public boolean islive = true;
private Map map;
public boolean start = true;
public Map getMap() {
return map;
} public void setMap(Map map) {
this.map = map;
} public Tank(int x, int y, int drect, int type) {
super();
this.x = x;
this.y = y;
this.drect = drect;
this.type = type;
} public Tank() {
super();
} public Bullet getMyBullet() {
return myBullet;
} public int getSpeed() {
return speed;
} public void setSpeed(int speed) {
this.speed = speed;
} public int getX() {
return x;
} public int getDrect() {
return drect;
} public void setDrect(int drect) {
this.drect = drect;
} public int getType() {
return type;
} public void setType(int type) {
this.type = type;
} public void setX(int x) {
this.x = x;
} public int getY() {
return y;
} public void setY(int y) {
this.y = y;
} public void moveUp() {
if (y - speed < 0)
y = 0;
else {
y -= speed;
map.location[x][y]=1;//标记此坦克坐标在地图上防止其他坦克过来占用导致重叠
// 这里只标记了坦克坐标那一个点,会有bug,部分坦克还是有重叠现象,
// 这里可以遍历整个坦克坐标(x到x+20,y到y+30)设置标记。
// for(int i=x;i<x+20;i++){
// for (int j = y; j < y+30; j++) {
// map.location[x][y]=1;
// }
// }
}
} public void moveDown() {
if (y + speed > 470)
y = 470;
else {
y += speed;
map.location[x][y]=1;
}
} public void moveRight() {
if (x + speed > 470)
x = 470;
else {
x += speed;
map.location[x][y]=1;
}
} public void moveLeft() {
if (x - speed < 0)
x = 0;
else {
x -= speed;
map.location[x][y]=1;
}
} public void shot() {
switch (drect) {
case 0:
myBullet = new Bullet(x + 10, y, 5, 0);
mybs.add(myBullet);
break;
case 1:
myBullet = new Bullet(x + 30, y + 10, 5, 1);
mybs.add(myBullet);
break;
case 2:
myBullet = new Bullet(x + 10, y + 30, 5, 2);
mybs.add(myBullet);
break;
case 3:
myBullet = new Bullet(x, y + 10, 5, 3);
mybs.add(myBullet);
break;
}
} @Override
public void run() {
while (islive) {
if (start) {
int step;
int s;
try {
switch (drect) {
case 0:
step = (int) (Math.random() * 30);
for (int i = 0; i < step; i++) {
moveUp();
if (y <= 0)
break;// 撞墙跳出循环
if (y >= 30)// 仿数组越界
if (map.location[x][y - 30] == 1 || map.location[x][y - 20] == 1) {
map.location[x][y - 30] = 0;//这里没分开判断
map.location[x][y - 20] = 0;
break;
}
Thread.sleep(80);
}
break;
case 1:
step = (int) (Math.random() * 30);
for (int i = 0; i < step; i++) {
moveRight();
if (x >= 500)
break;
if (x < 470)
if (map.location[x + 20][y] == 1 || map.location[x + 30][y] == 1) {
map.location[x + 20][y] = 0;
map.location[x + 30][y] = 0;
break;
}
Thread.sleep(80);
}
break;
case 2:
step = (int) (Math.random() * 30);
for (int i = 0; i < step; i++) {
moveDown();
if (y >= 500)
break;
if (y < 470)
if (map.location[x][y + 30] == 1 || map.location[x][y + 20] == 1) {
map.location[x][y + 30] = 0;
map.location[x][y + 20] = 0;
break;
}
Thread.sleep(80);
}
break;
case 3:
step = (int) (Math.random() * 30);
for (int i = 0; i < step; i++) {
moveLeft();
if (x <= 0)
break;
if (x >= 30)
if (map.location[x - 20][y] == 1 || map.location[x - 30][y] == 1) {
map.location[x - 20][y] = 0;
map.location[x - 30][y] = 0;
break;
}
Thread.sleep(80);
}
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
drect = (int) (Math.random() * 4);// 随机方向
s = (int) (Math.random() * 10);
if (s > 8) {
shot();
}
}
}
}
}

Tank

view包

 package com.gh.view;

 import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.util.Vector; import javax.swing.JOptionPane;
import javax.swing.JPanel; import com.gh.model.Bomb;
import com.gh.model.Bullet;
import com.gh.model.Map;
import com.gh.model.Tank; /**
* 游戏显示面板
*
* @author ganhang
*
*/ public class Mypanel extends JPanel implements Runnable {
public Tank mytank = null;// 我的坦克
Tank ek = null;
Image img;
Vector<Tank> eks = new Vector<Tank>();//地方坦克集
Vector<Bomb> bs = new Vector<Bomb>();//爆炸集合
Map map = new Map(); public Mypanel() {
mytank = new Tank(200, 200, 0, 0);
mytank.setMap(map);
// 创建敌人坦克
for (int i = 0; i < 17; i++) {
ek = new Tank(i * 30, 10, 2, 1);
eks.add(ek);
ek.setMap(map);
new Thread(ek).start();
}
img = Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/1.png"));
} @Override
public void paint(Graphics g) {
super.paint(g);
// 画背景
g.fillRect(0, 0, 500, 500);
// 画自己的坦克
if (mytank.islive)
drawTank(mytank.getX(), mytank.getY(), g, mytank.getDrect(), mytank.getType());
// 画自己的子弹
for (int i = 0; i < mytank.mybs.size(); i++) {// 循环时删除集合时,不要用foreach,用for
Bullet b = new Bullet();
b = mytank.mybs.get(i);
if (b.islive) {
g.setColor(Color.white);
g.fill3DRect(b.getX(), b.getY(), 2, 2, false);
} else
mytank.mybs.remove(b);
}
// 画敌人坦克
for (int i = 0; i < eks.size(); i++) {
Tank ek = new Tank();
ek = eks.get(i);
if (ek.islive)
drawEnemyTank(ek.getX(), ek.getY(), ek.getDrect(), g);
// 画敌人子弹
for (int j = 0; j < ek.mybs.size(); j++) {
Bullet eb = new Bullet();
eb = ek.mybs.get(j);
if (eb.islive) {
g.setColor(Color.green);
g.fill3DRect(eb.getX(), eb.getY(), 2, 2, false);
} else
ek.mybs.remove(eb);
}
}
// 画爆炸,这里有个bug第一次爆炸没有爆炸效果图出来,检查原因是只一闪而过
// 添加休眠好了点,不过影响后面爆炸效果,不明白为什么第一次画得快些
for (int i = 0; i < bs.size(); i++) {
// System.out.println(bs.size());
Bomb bb = bs.get(i);
if (bb.islive) {
if (bb.getTime() > 6) {
try {
Thread.sleep(50);
} catch (Exception e) {
e.printStackTrace();
}
g.drawImage(img, bb.getX(), bb.getY(), 30, 30, this);
} else if (bb.getTime() > 3) {
g.drawImage(img, bb.getX(), bb.getY(), 15, 15, this);
} else if (bb.getTime() > 0) {
g.drawImage(img, bb.getX(), bb.getY(), 1, 1, this);
}
}
bb.livedown();
if (bb.getTime() == 0)
bs.remove(bb);
}
} public boolean isHitEnemy(Bullet b, Tank ek) {
if (ek.getDrect() == 0 || ek.getDrect() == 2) {
// 坦克竖着时宽20,高30
if (b.getX() >= ek.getX() && b.getX() <= ek.getX() + 20 && b.getY() >= ek.getY()
&& b.getY() <= ek.getY() + 30) {
b.islive = false;
ek.islive = false;
Bomb bb = new Bomb(ek.getX(), ek.getY());
bs.add(bb);
return true;
}
return false;
} else {// 横着宽30,高20;
if (b.getX() >= ek.getX() && b.getX() <= ek.getX() + 30 && b.getY() >= ek.getY()
&& b.getY() <= ek.getY() + 20) {
ek.islive = false;
b.islive = false;
Bomb bb = new Bomb(ek.getX(), ek.getY());
bs.add(bb);
return true;
}
return false;
}
} public void drawEnemyTank(int x, int y, int drect, Graphics g) {
drawTank(x, y, g, drect, 1);
} public void drawTank(int x, int y, Graphics g, int drect, int type) {
switch (type) {
case 0:
g.setColor(Color.cyan);
break;
case 1:
g.setColor(Color.GREEN);
default:
break;
}
switch (drect) {
case 0:
// 坦克向上时宽20,高30
g.fill3DRect(x, y, 5, 30, false);
g.fill3DRect(x + 15, y, 5, 30, false);
g.fill3DRect(x + 5, y + 9, 10, 15, false);
g.drawLine(x + 10, y + 14, x + 10, y);
break;
case 1:
// 坦克向右时宽30,高20
g.fill3DRect(x, y, 30, 5, false);
g.fill3DRect(x, y + 15, 30, 5, false);
g.fill3DRect(x + 7, y + 5, 15, 10, false);
g.drawLine(x + 13, y + 10, x + 30, y + 10);
break;
case 2:
g.fill3DRect(x, y, 5, 30, false);
g.fill3DRect(x + 15, y, 5, 30, false);
g.fill3DRect(x + 5, y + 7, 10, 15, false);
g.drawLine(x + 10, y + 12, x + 10, y + 30);
break;
case 3:
g.fill3DRect(x, y, 30, 5, false);
g.fill3DRect(x, y + 15, 30, 5, false);
g.fill3DRect(x + 8, y + 5, 15, 10, false);
g.drawLine(x, y + 10, x + 13, y + 10);
break;
}
} @Override
public void run() {
while (true) {
try {
Thread.sleep(50);// 画板刷新频率
} catch (InterruptedException e) {
e.printStackTrace();
}
// 判断自己坦克的子弹是否击中敌人坦克
for (int i = 0; i < mytank.mybs.size(); i++) {
Bullet mb = new Bullet();
mb = mytank.mybs.get(i);
if (mb.islive) {
for (int j = 0; j < eks.size(); j++) {
Tank ek = new Tank();
ek = eks.get(j);
if (ek.islive) {
isHitEnemy(mb, ek);
}
}
}
}
// 判断敌方坦克 的子弹是否击中我方坦克
for (int i = 0; i < eks.size(); i++) {
Tank et = new Tank();
et = eks.get(i);
for (int j = 0; j < et.mybs.size(); j++) {// 这里写错ek查到死。。。
Bullet etb = new Bullet();
etb = et.mybs.get(j);
if (etb.islive) {
isHitEnemy(etb, mytank);
}
}
}
this.repaint();// 刷新
if (!mytank.islive) {
JOptionPane.showMessageDialog(this, "你被GG");
mytank.islive = true;
}
}
}
}

Mypanel

开始游戏类

 package com.gh.view;

 import java.awt.EventQueue;
/**
* 1、画出坦克
* 2、我的坦克可以上下移动
* 3、可以发射子弹,子弹连发(或者最多5发)
* 4、当我的坦克击中敌人坦克时,敌人坦克消失(或者爆炸效果)
* 5、我被击中也显示爆炸效果。
* 6、游戏开始选项
* @author ganhang
*
*/
public class TankGame {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TankGame window = new TankGame();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
} /**
* Create the application.
*/
public TankGame() {
initialize();
} /**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setTitle("\u5766\u514B\u5927\u6218");
frame.setBounds(450, 70, 600, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JMenuBar menuBar = new JMenuBar();
frame.setJMenuBar(menuBar); JMenu mnNewMenu = new JMenu("\u9009\u9879");
mnNewMenu.setFont(new Font("微软雅黑", Font.PLAIN, 13));
menuBar.add(mnNewMenu); JMenuItem mntmNewMenuItem = new JMenuItem("开始游戏");
mntmNewMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Mypanel mp = new Mypanel();
Thread t=new Thread(mp);
t.start();
frame.getContentPane().add(mp, BorderLayout.CENTER);
frame.addKeyListener(new KeyListen(mp));
frame.setVisible(true);
}
}); mntmNewMenuItem.setFont(new Font("微软雅黑", Font.PLAIN, 12));
mnNewMenu.add(mntmNewMenuItem); JMenu mnNewMenu_1 = new JMenu("\u6E38\u620F\u8BF4\u660E");
mnNewMenu_1.setFont(new Font("微软雅黑", Font.PLAIN, 13));
menuBar.add(mnNewMenu_1); JMenuItem mntmNewMenuItem_1 = new JMenuItem("\u5173\u4E8E\u6E38\u620F");
mntmNewMenuItem_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frame, "上:W, 下:A ,左:S, 右:D ,射击:空格\n Made by Ganhang");;
}
});
mntmNewMenuItem_1.setFont(new Font("微软雅黑", Font.PLAIN, 12));
mnNewMenu_1.add(mntmNewMenuItem_1); } }

TankGame

control包 这里放的事件监听主要业务逻辑在画板类和模型类里写了

 package com.gh.control;

 import java.awt.event.KeyEvent;
import java.awt.event.KeyListener; import com.gh.view.Mypanel; /**
* 事件监听
* 这里有个控制最大发射子弹数
* @author ganhang
*/
public class KeyListen implements KeyListener{
private Mypanel mp=null; public KeyListen(Mypanel mp) {
super();
this.mp = mp;
}
@Override
public void keyTyped(KeyEvent e) { } @Override
public void keyPressed(KeyEvent e) {
//方向键监听
if(e.getKeyCode()==KeyEvent.VK_W){
mp.mytank.setDrect(0);
mp.mytank.moveUp();
}else if(e.getKeyCode()==KeyEvent.VK_S){
mp.mytank.setDrect(2);
mp.mytank.moveDown();
}else if(e.getKeyCode()==KeyEvent.VK_D){
mp.mytank.setDrect(1);
mp.mytank.moveRight();
}else if(e.getKeyCode()==KeyEvent.VK_A){
mp.mytank.setDrect(3);
mp.mytank.moveLeft();
}
//发射子弹监听
if(e.getKeyCode()==KeyEvent.VK_SPACE){
if(mp.mytank.mybs.size()<5)
mp.mytank.shot();
}
mp.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub }
}

KeyListen

github:https://github.com/ganhang/My_TankGame

想到什么再更吧。。

java学习之坦克大战游戏的更多相关文章

  1. 分享一下Java写的坦克大战游戏,相信很多人小时候玩过

    写在前面 程序是五六年前读书的时候写的,里面会有一些不规范的,我已经将代码传到github上了,有时间会优化修改. 程序运行机制 定义了一个JPanel,然后每隔一小段时间重绘一遍. 重绘的内容如下: ...

  2. Java小项目--坦克大战(version1.0)

    Java小项目--坦克大战<TankWar1.0> 这个小项目主要是练习j2se的基础内容和面向对象的思想.项目实现了基本的简单功能,我方一辆坦克,用上下左右键控制移动方向,按F键为发射炮 ...

  3. 阶段学习总结-坦克大战(2D)案例

    这是前几天学习的案例,这两天学习了NGUI和UGUI,被UI搞得很烦躁,但是今天还是将前几天学习的坦克大战总结以下.这个游戏是小时候的经典红白机游戏的复刻,见截图: 一.游戏物体 游戏中包含地图元素( ...

  4. 3D坦克大战游戏源码

    3D坦克大战游戏源码,该游戏是基于xcode 4.3,ios sdk 5.1开发.在xcode4.3.3上完美无报错.兼容ios4.3-ios6.0 ,一款ios平台上难得的3D坦克大战游戏源码,有2 ...

  5. 3D坦克大战游戏iOS源码

    3D坦克大战游戏源码,该游戏是基于xcode 4.3,ios sdk 5.1开发.在xcode4.3.3上完美无报错.兼容ios4.3-ios6.0 ,一款ios平台上难得的3D坦克大战游戏源码,有2 ...

  6. cocos2d-x游戏开发系列教程-坦克大战游戏启动界面的编写

    用前面介绍的方法,创建一个cocos2d-x项目,可以看到新项目内容如下图:

  7. 小强的HTML5移动开发之路(8)——坦克大战游戏2

    来自:http://blog.csdn.net/cai_xingyun/article/details/48629015 在上一篇文章中我们已经画出了自己的坦克,并且可以控制自己的坦克移动,我们继续接 ...

  8. 小强的HTML5移动开发之路(7)——坦克大战游戏1

    来自:http://blog.csdn.net/dawanganban/article/details/17693145 上一篇中我们介绍了关于Canvas的基础知识,用Canvas绘制各种图形和图片 ...

  9. java课程设计-坦克大战

    团队课程设计博客链接 个人负责模块 枚举类.工具类.子弹类.图片素材的查找,地图制作 Git管理 包名类名的命名 详细说明 枚举类 如 单人和双人模式 工具类 将每个图片的路径使用字符串保存,便于调用 ...

随机推荐

  1. 穿透的 div ( pointer-events )

    pointer-events 是一個滿有趣的 CSS3 屬性,雖然主要是針對 SVG ,但其中幾個屬性應用在 div 上也是頗有意思.顧名思義,這是一個針對滑鼠事件的屬性,預設值為 auto,若值為 ...

  2. CSS截取字符串

    /*溢出的字...处理*/ .updatecsssubstring { text-overflow: ellipsis; -o-text-overflow: ellipsis; white-space ...

  3. R与数据分析旧笔记(十三) 聚类初步

    聚类 聚类 关键度量指标:距离 常用距离 绝对值距离 绝对值距离也称为"棋盘距离"或"城市街区距离". 欧氏(Euclide)距离 闵可夫斯基(Minkowsk ...

  4. C语言迭代求解

    date : 2013/8/12           desinger :pengxiaoen 今天看  国外电子信息科学经典教材系列   <电子电路分析与设计> 电子工业出版社的 的19 ...

  5. Linux 中执行命令

    第一步: 在txt文件(文件名为cmd_file)中写入代码:echo this is content! 第二步: 授权chmod 555 cmd_file 第三步: 运行 ./cmd_file -- ...

  6. MySQL 创建用户与修改密码

    创建用户的 3 方法: 1 .create user userName identifed by 'yourPassword'; 2. grant select on *.* to userName@ ...

  7. SMT实用工艺

    第一章 SMT概述 SMT(表面组装技术)是新一代电子组装技术.经过20世纪80年代和90年代的迅速发展,已进入成熟期.SMT已经成为一个涉及面广,内容丰富,跨多学科的综合性高新技术.最新几年,SMT ...

  8. 教你看懂C++类库函数定义之二---STDMETHOD介绍

    一切从一个C++ 类库头文件开始,现在在做一个C++的项目,期间用到一个开源的界面库DUILib(类似MFC),这个东西还不错能很容易的写出漂亮的界面,比如QQ的界面,可以去下载下来研究研究,地址:h ...

  9. SPOJ 7258 Lexicographical Substring Search(后缀自动机)

    [题目链接] http://www.spoj.com/problems/SUBLEX/ [题目大意] 给出一个字符串,求其字典序排名第k的子串 [题解] 求出sam上每个节点被经过的次数,然后采用权值 ...

  10. swig include使用方法

    {% block content2 %} {% include "footer.html" %} {% endblock %} include语句必须放到 block模块中,不然不 ...