原创性声明
此博文的出处 为 http://blog.csdn.net/zhujunxxxxx/article/details/40460931如果进行转载请注明出处。本文作者原创,邮箱zhujunxxxxx@163.com,如有问题请联系作者
前言
很久没碰java了,今天突然找出了大二时期写的一个坦克大战游戏的源码,然后运行了一下,竟然依然如此的亲切,突然想到了以前大二的时候的点点时光。好了不废话了,先给出一张图。
这个就是整个游戏的界面了,界面全是用的java画的,不是很好看。如果想要好看的得做成图片版本的
原理
游戏的基本原理就是,一开始创建N个敌人的实例,多线程在界面中移动(移动使用的是重写paint函数来实现的),然后随机移动和发射子弹。
还有就是使用了简单的碰撞检测方法来实现子弹击中别人和碰到障碍物
双缓冲技术,消除频闪
坦克类
坦克类是这个游戏的主要对象,可以分为三种坦克 一种是自己的坦克,一种是普通坦克,而另一种是BOSS坦克(血多 伤害大),不过都是一个类来实现的
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class Tank {
public static final int XSPEED = 5;
public static final int YSPEED = 5;
public static final int WIDTH = 30;
public static final int HEIGHT = 30;
TankClient tc;
private int x, y;//坦克的坐标,即位置
private int oldX, oldY;//用来保存上一步的坐标,撞墙的时候调整方向用的
private boolean bL=false, bU=false, bR=false, bD = false;//方向的标志变量
enum Direction {L, LU, U, RU, R, RD, D, LD, STOP};//方向的枚举
private Direction dir = Direction.STOP;//我方坦克,初始方向
private Direction ptDir = Direction.D;//敌方坦克初始方向,向下
private int step = r.nextInt(12) + 3;
private boolean isSuper=false;
public boolean isSuper()
{
return isSuper;
}
public void setSuper(boolean isSuper)
{
this.isSuper = isSuper;
}
private static Random r = new Random();//随机数产生器
private boolean good;//区分好坏的变量
private boolean boss;
public boolean isBoss()
{
return boss;
}
public boolean isGood()
{
return good;
}
public void setGood(boolean good)
{
this.good = good;
}
private BloodBar bb=new BloodBar();//血条
//坦克生命值的变量
private int life=100;
public int getLife()
{
return life;
}
public void setLife(int life)
{
this.life = life;
}
//坦克生死的变量
private boolean live=true;
public boolean isLive()
{
return live;
}
public void setLive(boolean live)
{
this.live = live;
}
//坦克的构造方法
public Tank(int x, int y,boolean good) {
this.x = x;
this.y = y;
this.oldX = x;
this.oldY = y;
this.good=good;
}
public Tank(int x, int y,boolean good,Direction dir, TankClient tc) {
this(x, y,good);
this.dir=dir;
this.tc = tc;
}
public Tank(int x, int y,boolean good,boolean boss, Direction dir, TankClient tc) {
this(x, y,good);
this.dir=dir;
this.boss=boss;
this.tc = tc;
}
//画坦克的方法
public void draw(Graphics g) {
if(!live) return;//Tank死了就不在重画了
Color c = g.getColor();
if(good)//通过好坏设置颜色
{
g.setColor(Color.CYAN);
}
else if(boss)
{
g.setColor(Color.RED);
}
else {
g.setColor(Color.BLUE);
}
g.fillOval(x, y, WIDTH, HEIGHT);
g.setColor(c);
if(good) bb.draw(g);//我方坦克有血条
if(boss) bb.draw(g);//boss有血条
switch(ptDir) {//通过方位判断,画炮筒位置
case L:
g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x, y + Tank.HEIGHT/2);
break;
case LU:
g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x, y);
break;
case U:
g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x + Tank.WIDTH/2, y);
break;
case RU:
g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x + Tank.WIDTH, y);
break;
case R:
g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x + Tank.WIDTH, y + Tank.HEIGHT/2);
break;
case RD:
g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x + Tank.WIDTH, y + Tank.HEIGHT);
break;
case D:
g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x + Tank.WIDTH/2, y + Tank.HEIGHT);
break;
case LD:
g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x, y + Tank.HEIGHT);
break;
}
move();//重画时,进行移动
}
//坦克的移动方法,在重画的时候,通过方向定位来进行移动
void move() {
this.oldX = x;
this.oldY = y;
switch(dir) {
case L:
x -= XSPEED;
break;
case LU:
x -= XSPEED;
y -= YSPEED;
break;
case U:
y -= YSPEED;
break;
case RU:
x += XSPEED;
y -= YSPEED;
break;
case R:
x += XSPEED;
break;
case RD:
x += XSPEED;
y += YSPEED;
break;
case D:
y += YSPEED;
break;
case LD:
x -= XSPEED;
y += YSPEED;
break;
case STOP:
break;
}
if(this.dir != Direction.STOP) {//炮筒位置的定位,和行动方向一致
this.ptDir = this.dir;
}
//判断坦克是否出界,遇到边界则停止
if(x < 0) x = 0;
if(y < 30) y = 30;
if(x + Tank.WIDTH > TankClient.GAME_WIDTH) x = TankClient.GAME_WIDTH - Tank.WIDTH;
if(y + Tank.HEIGHT > TankClient.GAME_HEIGHT) y = TankClient.GAME_HEIGHT - Tank.HEIGHT;
//坏蛋坦克的随机移动,以及发子弹的时间
if(!good&&!boss) {
Direction[] dirs = Direction.values();
if(step == 0) {
step = r.nextInt(12) + 3;
int rn = r.nextInt(dirs.length);
dir = dirs[rn];
}
step --;
if(r.nextInt(40) > 38) this.fire();
}
//boss坦克的攻击方法
if(!good&&boss)
{
Direction[] dirs = Direction.values();
if(step == 0) {
step = r.nextInt(12) + 3;
int rn = r.nextInt(dirs.length);
dir = dirs[rn];
}
step --;
if(r.nextInt(40) > 20)this.fire();
}
}
//按下键时进行设置键位标志,用于方向定位判断
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_CAPS_LOCK:
this.setSuper(!this.isSuper());
case KeyEvent.VK_F2 :
if(!this.live) {
this.live = true;
this.life = 100;
}
case KeyEvent.VK_CONTROL:
fire();
break;
case KeyEvent.VK_LEFT :
bL = true;
break;
case KeyEvent.VK_UP :
bU = true;
break;
case KeyEvent.VK_RIGHT :
bR = true;
break;
case KeyEvent.VK_DOWN :
bD = true;
break;
}
locateDirection();//每次按键后就判断方向
}
//Tank的方向定位,通过按键后设置的标志进行判断
void locateDirection() {
if(bL && !bU && !bR && !bD) dir = Direction.L;
else if(bL && bU && !bR && !bD) dir = Direction.LU;
else if(!bL && bU && !bR && !bD) dir = Direction.U;
else if(!bL && bU && bR && !bD) dir = Direction.RU;
else if(!bL && !bU && bR && !bD) dir = Direction.R;
else if(!bL && !bU && bR && bD) dir = Direction.RD;
else if(!bL && !bU && !bR && bD) dir = Direction.D;
else if(bL && !bU && !bR && bD) dir = Direction.LD;
else if(!bL && !bU && !bR && !bD) dir = Direction.STOP;
}
//方向键抬起来时的,恢复键位标志,也进行方向判断
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_LEFT :
bL = false;
break;
case KeyEvent.VK_UP :
bU = false;
break;
case KeyEvent.VK_RIGHT :
bR = false;
break;
case KeyEvent.VK_DOWN :
bD = false;
break;
case KeyEvent.VK_A :
superFire();
break;
}
locateDirection();
}
//发出一发子弹的方法,向missiles里面添加子弹
public Missile fire() {
if(!live) return null;
int x = this.x + Tank.WIDTH/2 - Missile.WIDTH/2;
int y = this.y + Tank.HEIGHT/2 - Missile.HEIGHT/2;
Missile m;
if(good&&this.isSuper())//判断是不是超级炮弹
{
m = new Missile(x, y,good, ptDir,this.tc,true);
}
else {
m = new Missile(x, y,good, ptDir,this.tc);
}
tc.missiles.add(m);
return m;
}
//SuperFire向每个方向发子弹的方法
public Missile fire(Direction Dir) {
if(!live) return null;
int x = this.x + Tank.WIDTH/2 - Missile.WIDTH/2;
int y = this.y + Tank.HEIGHT/2 - Missile.HEIGHT/2;
Missile m = new Missile(x, y,good, Dir,this.tc);
tc.missiles.add(m);
return m;
}
private void superFire()
{
Direction[] dirs = Direction.values();
for(int i=0;i<8;i++)
{
//tc.missiles.add(fire(dirs[i]));
fire(dirs[i]);
}
}
//判断相撞的方法所需的
public Rectangle getRect() {
return new Rectangle(x, y, WIDTH, HEIGHT);
}
//与坦克和墙相撞时停下来的方法
private void stay() {
x = oldX;
y = oldY;
}
//判断坦克是否与墙相撞的方法
public boolean collidesWithWall(Wall w) {
if(this.live && this.getRect().intersects(w.getRect())) {
this.stay();
return true;
}
return false;
}
//判断坦克是否与坦克相撞的方法
public boolean collidesWithTanks(java.util.List<Tank> tanks) {
for(int i=0; i<tanks.size(); i++) {
Tank t = tanks.get(i);
if(this != t) {
if(this.live && t.isLive() && this.getRect().intersects(t.getRect())) {
this.stay();
t.stay();
return true;
}
}
}
return false;
}
//血条的内部类
private class BloodBar {
public void draw(Graphics g) {
Color c = g.getColor();
g.setColor(Color.orange);
g.drawRect(x, y-10, WIDTH, 10);
int w = WIDTH * life/100 ;
g.fillRect(x, y-10, w, 10);
g.setColor(c);
}
}
//吃血块的方法
public boolean eat(Blood b) {
if(this.live && b.isLive() && this.getRect().intersects(b.getRect())) {
this.life = 100;
b.setLive(false);
return true;
}
return false;
}
}
子弹类
子弹类就是分为敌方子弹和我方子弹,具有移动速度等属性,具有碰撞检测等功能。
import java.awt.*;
import java.util.List;
public class Missile {
public static final int XSPEED = 10;//子弹速度的变量
public static final int YSPEED = 10;
public static int WIDTH = 10;//子弹大小
public static int HEIGHT = 10;
int x, y;
private TankClient tc;
Tank.Direction dir;//方向变量
private boolean isSuper;
private boolean live = true;//子弹的生死变量
public boolean isLive() {
return live;
}
private boolean good;//坦克的好坏之分变量
public boolean isGood()
{
return good;
}
//Missile的两个构造方法
public Missile(int x, int y, Tank.Direction dir) {
this.x = x;
this.y = y;
this.dir = dir;
}
public Missile(int x, int y,boolean good, Tank.Direction dir, TankClient tc) {
this(x, y, dir);
this.tc = tc;
this.good=good;
}
public Missile(int x, int y,boolean good, Tank.Direction dir, TankClient tc,boolean isSuper) {
this(x, y, dir);
this.tc = tc;
this.isSuper=isSuper;
this.good=good;
}
//画子弹的方法
public void draw(Graphics g) {
if(!live) {
tc.missiles.remove(this);
return;
}
Color c = g.getColor();
if(good&&isSuper)//是超级炮弹就变大
{
g.setColor(Color.YELLOW);
g.fillOval(x-5, y-5, 20, 20);
}
else if(good&&!isSuper) {
g.setColor(Color.YELLOW);
g.fillOval(x, y, WIDTH, HEIGHT);
}
else {
g.setColor(Color.RED);
g.fillOval(x, y, WIDTH, HEIGHT);
}
g.setColor(c);
move();//画子弹的时候就要移动
}
//子弹的移动方法,与坦克方位有关
private void move() {
switch(dir) {//通过方位判断速度
case L:
x -= XSPEED;
break;
case LU:
x -= XSPEED;
y -= YSPEED;
break;
case U:
y -= YSPEED;
break;
case RU:
x += XSPEED;
y -= YSPEED;
break;
case R:
x += XSPEED;
break;
case RD:
x += XSPEED;
y += YSPEED;
break;
case D:
y += YSPEED;
break;
case LD:
x -= XSPEED;
y += YSPEED;
break;
case STOP:
break;
}
//子弹越界了就要死
if(x < 0 || y < 0 || x > TankClient.GAME_WIDTH || y > TankClient.GAME_HEIGHT) {
live = false;
tc.missiles.remove(this);
}
}
public Rectangle getRect() {
return new Rectangle(x, y, WIDTH, HEIGHT);
}
//子弹与子弹相撞的方法,且子弹分好坏
public boolean hitMissile(Missile m) {
if(this.live && this.getRect().intersects(m.getRect()) && m.isLive() && this.good !=m.isGood() )
{
this.live=false;
m.live=false;
return true;
}
return false;
}
public boolean hitMissiles(List<Missile> missiles) {
for(int i=0; i<missiles.size(); i++) {
if(hitMissile(missiles.get(i))) {//调用了hitMissile方法
missiles.remove(missiles.get(i));
return true;
}
}
return false;
}
public boolean hitTank(Tank t) {
if(this.live && this.getRect().intersects(t.getRect()) && t.isLive() && this.good !=t.isGood() )
{
if(t.isGood())//自己被击中减血
{
t.setLife(t.getLife()-20);
if(t.getLife()<=0)
t.setLive(false);
}
else if(t.isBoss())//BOSS被击中
{
if(this.isSuper)//被超级炮弹击中
{
t.setLife(t.getLife()-33);
if(t.getLife()<=0)
t.setLive(false);
}
else {
t.setLife(t.getLife()-5);
if(t.getLife()<=0)
t.setLive(false);
}
}
else//坏人直接死
t.setLive(false);
//t.setLive(false);
this.live = false;
Explode e = new Explode(x, y, tc);
tc.explodes.add(e);
return true;
}
return false;
}
//子弹与坦克相撞,且子弹分好坏(打敌人坦克的方法)
public boolean hitTanks(List<Tank> tanks) {
for(int i=0; i<tanks.size(); i++) {
if(hitTank(tanks.get(i))) {//调用了hitTank方法
tanks.remove(tanks.get(i));
return true;
}
}
return false;
}
//子弹与墙相撞的方法
public boolean hitWall(Wall w) {
if(this.live && this.getRect().intersects(w.getRect())) {
this.live = false;
return true;
}
return false;
}
}
主程序类
主类主要是实现显示界面的功能,把在内存中的对象画出来,实现动画的效果。
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.ArrayList;
import org.omg.CORBA.PUBLIC_MEMBER;
public class TankClient extends Frame{
public static int GAME_WIDTH = 800;
public static int GAME_HEIGHT = 600;
Tank myTank = new Tank(200, 300,true,Tank.Direction.STOP, this);
Tank BossTank = new Tank(60, 50,false,true,Tank.Direction.D,this);
List<Missile> missiles=new ArrayList<Missile>();//装子弹的容器
List<Explode> explodes = new ArrayList<Explode>();//装爆炸的容器
List<Tank> tanks = new ArrayList<Tank>();//装敌方坦克的容器
Blood b = new Blood();//实例化一个血块
//实例化两个墙
Wall w1=new Wall(300, 200, 20,150, this);
Wall w2=new Wall(200, 100, 20,150, this);
//双缓冲技术,消除频闪
Image offScreenImage = null;
public void update(Graphics g) {
if(offScreenImage == null) {
offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
}
Graphics gOffScreen = offScreenImage.getGraphics();
Color c = gOffScreen.getColor();
gOffScreen.setColor(Color.gray);
gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
gOffScreen.setColor(c);
paint(gOffScreen);
g.drawImage(offScreenImage, 0, 0, null);
}
public void paint(Graphics g)//会自动调用这个方法
{
g.drawString("tanks count:" + tanks.size(), 10, 35);
g.drawString("explodes count:" + explodes.size(), 10, 50);
g.drawString("missiles count:" + missiles.size(), 10, 65);
g.drawString("Tank life:" + myTank.getLife(), 10, 80);
if(tanks.size() <= 0) {
for(int i=0; i<5; i++) {
tanks.add(new Tank(50 + 40*(i+1), 50, false, Tank.Direction.D, this));
}
}
//把容器里面的子弹都画出来
for(int i=0;i<missiles.size();i++)
{
Missile m=missiles.get(i);
m.hitTanks(tanks);//添加子弹打坦克的方法
m.hitTank(myTank);
m.hitTank(BossTank);
m.hitWall(w1);
m.hitWall(w2);
m.hitMissiles(missiles);//调用子弹撞子弹方法
m.draw(g);
}
//把容器里面的爆炸全部画出来
for(int i=0; i<explodes.size(); i++) {
Explode e = explodes.get(i);
e.draw(g);
//e.ExplodehitTanks(tanks);
//e.ExplodehitTank(BossTank);
}
//把容器里面的坦克全部画出来
for(int i=0; i<tanks.size(); i++) {
Tank t = tanks.get(i);
t.collidesWithWall(w1);
t.collidesWithWall(w2);
t.collidesWithTanks(tanks);
t.draw(g);
}
b.draw(g);//血块画出来
BossTank.draw(g);
BossTank.collidesWithWall(w1);
BossTank.collidesWithWall(w2);
BossTank.collidesWithTanks(tanks);
//我方坦克画出来并添加方法
myTank.draw(g);
myTank.eat(b);
myTank.collidesWithWall(w1);
myTank.collidesWithWall(w2);
//画出墙来
w1.draw(g);
w2.draw(g);
}
//初始化游戏窗口的方法
public void lauchFrame(){
//初始化坦克,添加进容器
for(int i=0;i<10;i++)
{
tanks.add(new Tank(50+40*(i+1),50,false,Tank.Direction.D,this));
}
this.setLocation(300, 100);
this.setSize(GAME_WIDTH,GAME_HEIGHT);
this.setTitle("zz TankWar");
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
this.setBackground(Color.GREEN);
this.setResizable(false);
this.addKeyListener(new KeyMonitor());
this.setVisible(true);
new Thread(new PaintThread()).start();
}
private class PaintThread implements Runnable
{
public void run()
{
while(true)
{
repaint();
try{
Thread.sleep(50);
}catch(InterruptedException e)
{e.printStackTrace();}
}
}
}
private class KeyMonitor extends KeyAdapter {//键盘监听
public void keyReleased(KeyEvent e)
{
myTank.keyReleased(e);
}
public void keyPressed(KeyEvent e) {
myTank.keyPressed(e);
}
}
public static void main(String[] args) {
TankClient t=new TankClient();
t.lauchFrame();
}
}
总结
其实坦克大战这种游戏很简单,只要懂一些基本的数据结构和碰撞检测,还有重绘的方法就能够打造属于自己的坦克大战,给自己童年一个华丽的蜕变。