简单坦克大战
一.游戏思路:
1.首先建立物体的定义类,在建立坦克定义类,子弹定义类,铁,墙,水等定义类分别继承基类物体定义类;建立xml文件用来存储坦克与子弹每种类型的每种等级,同时也存储每个关卡的障碍物;定义类中的数据全都来自xml中存储数据,所以还需要建立相对应的节点类型的类
2,.然后建立物体类,给物体添加所有其他物体子类的所有属性和方法;
每个物体都有位置,定义,方向,场景,类型,队伍;之后创建坦克,
子单等继承物体
3.坦克的移动以及坦克的发射子弹:敌方坦克移动改变方向可以每次move后用随机概率
nextDouble方法来绝定是否该变方向,用随机数来随机该变方向,
我方用键盘来判断是否移动与改变方向,敌方发射子弹用move步数来判断,我方用键盘来判读
4.在建立场景类,场景类是画板,用来画所有物体,同时场景实现接口键盘监听和建立一个线程,在场景里添加一个Vector物体类集合在paint方法中画物体,在线程里面每睡眠40秒后就移动和发射子弹
以及物体与物体之间的碰撞;物体碰撞:以每个物体的中心与中心之间的距离判断,如果在范围类就是碰撞了进行碰撞处理
5.建立窗口,在窗口中添加一个主菜单:”菜单”以及四个分菜单:新游戏,退出游戏,继续游戏,暂停,窗口实现动作监听,用setActionCommand方法来判别4个功能
二.曾经错处:
1.总是坦克,子弹等物体都是分别创建一个集合来遍历,画,让基类物体
几乎成了摆设
public Vector<ObjectScene> list = new Vector<ObjectScene>();
for (int i = 0 ; i < list.size(); i++) {
ObjectScene object = list.get (i);
object .drawColor(g);
object .drawMySelf(g);
for (int i = 0 ; i < list .size(); i++) {
step = 0 ;
ObjectScene objecti = list .get(i);
objecti.launchBullet();
ArrayList<ObjectScene> colision = new ArrayList<ObjectScene>();
for (int j = 0 ; j < list .size(); j++) {
ObjectScene objecctj = list .get(j);
if (objecti != objecctj && judecolision(objecti, objecctj)) {
if (objecti.type == TypeUtil.TANK && objecctj.type == TypeUtil.TANK) {
objecti.move();
}
}
}
objecti.move();
for (int j = 0 ; j < list .size(); j++) {
ObjectScene objecctj = list .get(j);
if (objecti != objecctj && judecolision(objecti, objecctj)) {
colision.add(objecctj);
}
解决问题:在父类里面添加上所有子类的抽象方法,同时在父类里面添加一个int类型的类别变量和一个枚主类型队伍类,这样遍历的时候可以辨别敌我坦克子弹,墙,铁等
2.坦克与坦克碰撞后可以相互穿过去,坦克死亡刚出现新塔克时,相撞在一起不能移动了,只能该变方向
for (int i = 0 ; i < list .size(); i++) {
step = 0 ;
ObjectScene objecti = list .get(i);
objecti.launchBullet();
ArrayList<ObjectScene> colision = new ArrayList<ObjectScene>();
for (int j = 0 ; j < list .size(); j++) {
ObjectScene objecctj = list .get(j);
if (objecti != objecctj && judecolision(objecti, objecctj)) {
if (objecti.type == TypeUtil.TANK && objecctj.type == TypeUtil.TANK) {
objecti.move();
}
}
}
objecti.move();
for (int j = 0 ; j < list .size(); j++) {
ObjectScene objecctj = list .get(j);
if (objecti != objecctj && judecolision(objecti, objecctj)) {
colision.add(objecctj);
}
}
colision(objecti, colision);
public void colision (ObjectScene objecti, ArrayList<ObjectScene> colision) {
for (ObjectScene so : colision) {
switch (objecti.type) {
case TypeUtil.TANK:
switch (so.type) {
case TypeUtil.TANK:
objecti.back();
break ;
case TypeUtil.TIE:
case TypeUtil.QIANG:
case TypeUtil.WATER:
if (step == 0 ) {
objecti.back();
step++;
}
break ;
解决方法:既然有移动的方法那吗就有后退的方法,当坦克碰到障碍物或者坦克的时候就后退到原来位置,先判断如果坦克死了以后重新出来的坦克与一个坦克重合一部份,那么就在前面先判断撞没撞,如果撞了就move一下;判断完后在move
3.键盘控制:因为一个建盘监听按下一个建后在按下另一个建,这样前面一个建就不执行了而是执行下一个建,这样就不能一边走一边发射子弹了
ArrayList<String> set = new ArrayList<>();
@Override
public void keyReleased (KeyEvent e) {
set .remove(e.getKeyCode() + "" );
}
@Override
public void keyPressed (KeyEvent e) {
String str = e.getKeyCode() + "" ;
if (!set .contains(str)) {
set .add(str);
}
}
private void anJianSet () {
String pausex = null ;
String fangxiang = null ;
String zidan = null ;
for (String s : set ) {
switch (s) {
case KeyEvent.VK_A + "" :
case KeyEvent.VK_W + "" :
case KeyEvent.VK_S + "" :
case KeyEvent.VK_D + "" :
fangxiang = s;
case KeyEvent.VK_J + "" :
zidan = s;
case KeyEvent.VK_P + "" :
pausex = s;
}
}
if (zidan != null && !pause) {
anJianZhiXin(zidan);
}
if (fangxiang != null && !pause) {
anJianZhiXin(fangxiang);
} else if (fangxiang == null ) {
ismytankMove = false ;
}
if (pausex != null ) {
anJianZhiXin(pausex);
}
}
private void anJianZhiXin (String s) {
int code = Integer.parseInt(s);
switch (code) {
case KeyEvent.VK_W:
mytank.fx = Fangxiang.Shang;
ismytankMove = true ;
break ;
case KeyEvent.VK_S:
mytank.fx = Fangxiang.Xia;
ismytankMove = true ;
break ;
case KeyEvent.VK_A:
mytank.fx = Fangxiang.Zuo;
ismytankMove = true ;
break ;
case KeyEvent.VK_D:
mytank.fx = Fangxiang.You;
ismytankMove = true ;
break ;
case KeyEvent.VK_J:
ismytankBullet = true ;
break ;
case KeyEvent.VK_P:
pause = !pause;
}
}
解决方法:建立一个ArrayList集合·,当按下一个键盘就添加这个键盘,
当松开这个键盘就删除这个键盘,遍历ArrayList这个集合,这样就可以
多个键盘同时操作,但方向又只能执行一个,所以anJianSet这个方法中
在遍历一次,让只要是控制方向的键盘就只能执行最后一个
4.坦克移动方向的改变,不会如何控制和该变方向
if (RandomUtil.randomjd(0.155 ) && steep >
Config.DITANK_GUAI_WAN_STEEP) {
int r = (int ) (Math.random() * 4 );
fx = Fangxiang.parse(r);
steep = 0 ;
}
public class RandomUtil {
private static final Random rand = new Random();
public static boolean randomjd (double time) {
return rand.nextDouble() <= time;
}
}
public enum Fangxiang {
Shang(0 , 0 , -1 ), Xia(1 , 0 , 1 ), Zuo(2 , -1 , 0 ), You(3 , 1 , 0 );
public int dirction;
public int xsudu;
public int ysudu;
Fangxiang(int dirction, int xsudu, int ysudu) {
this .dirction = dirction;
this .xsudu = xsudu;
this .ysudu = ysudu;
}
public static Fangxiang parse (int id) {
for (Fangxiang f : values()) {
if (f.dirction == id) {
return f;
}
}
return null ;
}
}
解决方法:创建一个随机概率工具,在枚主类型中创建一个方向的办法
它会跟据传入的int而反回相对应的方向类,从而改变敌坦克的方向
5.在坦克类型定义中,每能够实现最后一个子节点中的level对应一个坦克定义
public final int typeId;
public final HashMap<Integer, TankDefine> level_define;
private TankTypeDefine (int typeId, HashMap<Integer, TankDefine> level_define) {
this .typeId = typeId;
this .level_define = level_define;
}
public static TankTypeDefine parse (Element root) {
int typeId = Integer.parseInt(root.getAttribute("typeId" ));
HashMap<Integer, TankDefine> level_defines = new HashMap<>();
ArrayList<Element> proList = XmlReader.parseElements(root, "property" );
for (Element ele : proList) {
TankDefine tankDefine = TankDefine.parse(ele);
level_defines.put(tankDefine.level, tankDefine);
}
return new TankTypeDefine(typeId, level_defines);
}
解决方法:在坦克定义和坦克类型定义中,坦克类型定义和塔克类型中独有的属性必须要对应xml相对应节点的属性,坦克类型定义中HashMap中的建存坦克定义中的level