Java小游戏:童年经典游戏坦克大战是这么实现的

一. 程序基本结构

基本结构

二. 实现的功能

1、单人模式

2、双人模式

3、通过广度优先算法实现坦克寻路功能

三. 运行效果

主界面

运行

四. 实现思路

数据存储表示: 在JPanel绘制图像,统一规定各个方块的大小为同一大小(如墙壁,坦克之类,子弹除外),从而方便使用二维数组存储地图的各个元素。

关于检测物体碰撞,这里使用了一个MyImage的父类,将坦克,墙壁

定义为继承这个父类的一个类。

class MyImage {

int width = Game.width;

int height = Game.height;

//二维地图的坐标

Coord coord;

//屏幕上的像素坐标

int x;

int y;

MyImage(Coord coord) {

x = coord.x * width;

y = coord.y * height;

this.coord = coord;

}

private Rectangle getRect() {

return new Rectangle(x, y, width, height);

}

//碰撞检测

boolean isIntersects(MyImage other) {

return other.getRect().intersects(getRect());

}

}

图像打印则借助遍历两个ConcurrentHashMap分别储存坦克和其他类型的方块。将这些方块使用Map而不是使用数组是因为管理起来比较方便,而二维数组则是为了寻路算法而准备的,防止了频繁使用上面的两个Map而导致线程锁的问题。

五. 遇到的问题

java的按键监听在响应按键长按时会有1-2秒的延迟,导致操作手感极差

解决方法:在坦克类中设置一个布尔属性move以及整形变量key储存键入的按键值,创建一个线程来响应按键。

实现代码:

//按键响应的线程类

class MyTankMove implements Runnable{

public void run(){

while(flag){

GetKey(key);

while(move){//决定是否移动

try {

e.printStackTrace();

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

try {

Thread.sleep(10);//防止无按键时陷入死循环导致线程堵塞

} catch (InterruptedException e) {

}

}

}

按键监听事件,即给key和move赋值

//按键监听接口

private class KeyBoradListener extends KeyAdapter{

public void keyPressed(KeyEvent e){

super.keyPressed(e);

int key = e.getKeyCode();

if(key<65){//为了实现双人对战而设置的。。。

if(key!=KeyEvent.VK_SHIFT&&!MyTank.isEmpty()){

MyTank.getFirst().key=key;

MyTank.getFirst().move=true;

}

}

else{

if(key!=KeyEvent.VK_G&&!MyTank.isEmpty()){

switch (key){

case KeyEvent.VK_W:key = KeyEvent.VK_UP;break;

case KeyEvent.VK_A:key = KeyEvent.VK_LEFT;break;

case KeyEvent.VK_S:key = KeyEvent.VK_DOWN;break;

case KeyEvent.VK_D:key = KeyEvent.VK_RIGHT;break;

}

MyTank.getLast().key=key;

MyTank.getLast().move=true;

}

}

}

public void keyReleased(KeyEvent e){

super.keyReleased(e);

int key = e.getKeyCode();

if(key<65){

if(!MyTank.isEmpty()){

if(key!=KeyEvent.VK_SHIFT&&key==MyTank.getFirst().key){//避免了同时按两个以上按键后会卡住

MyTank.getFirst().move=false;

}

else{

MyTank.getFirst().GetKey(key);

}

}

}

else{

switch (key){

case KeyEvent.VK_W:key = KeyEvent.VK_UP;break;

case KeyEvent.VK_A:key = KeyEvent.VK_LEFT;break;

case KeyEvent.VK_S:key = KeyEvent.VK_DOWN;break;

case KeyEvent.VK_D:key = KeyEvent.VK_RIGHT;break;

case KeyEvent.VK_G:key = KeyEvent.VK_SHIFT;break;

}

if(!MyTank.isEmpty()){

if(key!=KeyEvent.VK_SHIFT&&key==MyTank.getLast().key){

MyTank.getLast().move=false;

}

else{

MyTank.getLast().GetKey(key);

}

}

}

}

}

项目内的图片资源如何在项目导出后也能使用

解决方法:假设在项目的scr文件夹中建立img文件夹,在项目的.classpath中加一句<classpathentry kind="src" path="src/img"/>(Eclipse),也可以通过设置项目的Modules,将图像文件夹设置为resources(IDEA),并使用当前类的名.class.getResource("/(文件名)")).getImage()获得图像对象。

寻路的实现

实现思路:通过使用一个存储了地图内各个元素的二维数组Game.map,使用广度优先算法遍历出一条路线,将结果存放于栈之中。

实现代码:

/**

* 使用广度遍历算法,使用队列存储遍历的节点

*

* @return 移动的路径

*/

private Stack<Coord> GetPath() {

Coord target = Game.tanks.get(Game.P1_TAG).coord;

Queue<Coord> d_q = new LinkedBlockingQueue<>();

ArrayList<Coord> IsMove = new ArrayList<>();

d_q.offer(coord);

IsMove.add(coord);

Coord last = null;

boolean flag;

while (!d_q.isEmpty()) {

Coord t = d_q.poll();

int tx = t.x;

int ty = t.y;

int i;

//遍历所有的方向

for (i = 0; i < 4; ++i) {

switch (i) {

case Game.UP:

ty -= 1;

break;

case Game.LEFT:

tx -= 1;

break;

case Game.RIGHT:

tx += 1;

break;

case Game.DOWN:

ty += 1;

break;

}

//判断该点是否可行

flag = true;

Coord z = new Coord(tx, ty);

//检查是否为目标终点

if (z.equals(target)) {

z.per = t;

last = z;

break;

}

//检查该坐标是否已经遍历了

for (Coord c : IsMove) {

if (c.equals(z)) {

flag = false;

break;

}

}

if (flag) {

//检查下一格是否可以抵达

flag = !(Game.map[ty][tx] == Game.BLANK || Game.map[ty][tx] == Game.WALLS);

}

//该点可以用

if (flag) {

//将坐标纳入已经遍历的队列中

d_q.offer(z);

IsMove.add(z);

z.per = t;

last = z;

}

IsMove.add(z);

//重新选择方向遍历

tx = t.x;

ty = t.y;

}

//如果没有四个方向都遍历完就跳出,说明已经找到了终点

if (i != 4) {

break;

}

}

Stack<Coord> coords = new Stack<>();

while (null != last && last.per != null) {

coords.push(last);

last = last.per;

}

return coords;

}

关注小编,回复坦克大战即可获得源码;

本文来源:CSDN-madongyu-的博客

发表评论
留言与评论(共有 0 条评论)
   
验证码:

相关文章

推荐文章

'); })();