`
Coco_young
  • 浏览: 120669 次
  • 性别: Icon_minigender_1
  • 来自: 湖南长沙
社区版块
存档分类
最新评论

我的多线程小游戏——坦克大战

阅读更多

经过一段时间的小奋斗,终于做出了一个还能玩的多线程小游戏啦!

 

1:游戏介绍:
游戏一共3关,每关的不同点是敌方坦克的强度不同,玩家坦克用键盘操控(按键说明已写在附件中),每隔一段时间还会出现道具,其余游戏说明也已经写在附件中。

(开始界面)


(游戏运行界面)
 

2:主要技术要点和学到的经验

    (1):技术要点:

           技术要点主要有两个:一是多线程的应用,二就是双缓冲技术。关于线程的使用,在这里,我并没有去考虑机器的承受能力(做的比较猥琐),每个坦克一个线程,每个子弹一个线程,每次出现的爆炸效果也是一个线程,同时还有一个局面的监控线程(主要是负责判断游戏是否结束,是否进入下一关,和道具出现),线程之间的关系是:坦克线程(坦克线程在运行过程中判断是否捡到道具,是否可移动)在运行过程中调用开火方法启动一个子弹线程,子弹线程在运行过程中判断是否应该爆炸(是否打中了东西),如果爆炸的话,这启动爆炸效果线程,同时结束自己的生命。下面是关键代码:

// 坦克的run()
	public void run() {
		while (true) {
			setBody();// 设置坦克属性值
			if (getProperty() == 0) {
				if (level < 2) {
					level++;
				}// 最高只能生到2级
				setBody();
				count = 0;
				TankClient.properties.remove(0);
			} else if (getProperty() == 1) {// 减少玩家剩余生命次数
				TankClient.lifeTime--;
				TankClient.lab.setText("剩余生命次数:" + TankClient.lifeTime);
				TankClient.properties.remove(0);
			} else if (getProperty() == 2) {// 炸掉玩家一架坦克
				if (TankClient.Client.size() > 0) {
					Bang bang = new Bang(TankClient.Client.get(0).getX(),
							TankClient.Client.get(0).getY());
					TankClient.Visual.add(bang);
					bang.start();
					TankClient.Client.remove(0);
					TankClient.properties.remove(0);
				}
			}
			if (TankClient.tanks.size() == 0) {
				break;
			}
			TankMove();
			count++;
			if (count == SHOOT) {
//每循环一次,计数变量加一,当等于开火次数时,开火。
				Fire();
				count = 0;
			}
			if (Ruined) {
				break;
			}
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

 

      

// 子弹的run
	public void run() {
		while (true) {
			if (indoor()) {
				if (TankClient.bullets.size() == 0) {
					break;
				}
				Fly();
				if (shoot_bride()) {//如果击中砖头
					Bang bang = new Bang(this.getX(), this.getY());//启动爆炸效果线程
					TankClient.Visual.add(bang);
					bang.start();
					TankClient.bullets.remove(this);
					break;
				}
				if (shoot_fe()) {//如果击中铁
					Bang bang = new Bang(this.getX(), this.getY());//启动爆炸效果线程
					TankClient.Visual.add(bang);
					bang.start();
					TankClient.bullets.remove(this);
					break;
				}
				if (Succeed()) {// 如果击中目标
					Bang bang = new Bang(this.getX(), this.getY());//启动爆炸效果线程
					TankClient.Visual.add(bang);
					bang.start();
					TankClient.bullets.remove(this);
					// 如果到达死亡水平
					if (TankClient.Client.get(0).getLevel() == -1) {
						TankClient.Client.remove(0);
					}
					break;
				}
			} else {
				Bang bang = new Bang(this.getX(), this.getY());//启动爆炸效果线程
				TankClient.Visual.add(bang);
				bang.start();
				TankClient.bullets.remove(this);
				break;
			}
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

 
 

 

//爆炸效果run(setNum是控制绘制第几张爆炸效果图片,以形成动态的效果)
	public void run(){
		int i = 0;
		setNum(i++);
		try {
			Thread.sleep(TankClient.FRANK-30);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		setNum(i++);
		try {
			Thread.sleep(TankClient.FRANK-30);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		setNum(i++);
		try {
			Thread.sleep(TankClient.FRANK-30);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		setNum(i++);
		try {
			Thread.sleep(TankClient.FRANK-30);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		setNum(i++);
		try {
			Thread.sleep(TankClient.FRANK-30);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		setNum(i++);
		try {
			Thread.sleep(TankClient.FRANK-30);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		TankClient.Visual.remove(this);
	}
}

 

   这个是主监控线程的代码:

 

package TankWarv10;

import java.util.Random;

/**
 * 监控线程:监控局面是否结束
 * 
 * @author dell
 * 
 */
public class Monitor extends Thread {

	private int Count = 0;// 计数器,每数100次出现一个道具
	private Random ran = new Random();// 随机算子,产生随机道具

	public void run() {
		while (true) {
			Count++;

			if (Count == 100) {// 每数100次出现一个道具
				int x, y;// 随机坐标
				x = ran.nextInt(430) + 50;
				y = ran.nextInt(430) + 50;
				property tmp = new property(x,y);
				int kind = ran.nextInt(3);
				tmp.setKind(kind);// 设置道具种类
				tmp.start();// 启动道具线程
				TankClient.properties.add(tmp);
				Count = 0;
			}
			// 如果我方坦克被摧毁,则游戏结束,清空所有局面
			if (TankClient.Client.size() == 0) {
				if (TankClient.lifeTime <= 0) {
					TankClient.bullets.clear();
					TankClient.pb.clear();
					TankClient.tanks.clear();
					TankClient.Visual.clear();
					TankClient.stop = true;
					break;
				} else {
					// 加入我的坦克
					TankClient.Client.add(new PlayerTank(250, 480));
					TankClient.Client.get(0).start();
					TankClient.lifeTime--;
					TankClient.lab.setText("剩余生命次数:" + TankClient.lifeTime);
				}
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// 如果敌方坦克被摧毁完毕,则游戏结束,清空所有局面
			switch (TankClient.mission) {
			case 1://如果是第一关
				if (TankClient.tanks.size() == 0) {
					TankClient.bullets.clear();
					TankClient.pb.clear();
					TankClient.Visual.clear();
					TankClient.clear();
					TankClient.mission++;
					TankClient.mis.setText("当前关卡:"+TankClient.mission);
					TankClient.initBackground();
				}break;
			case 2://如果是第二关
				if (TankClient.Sec_tanks.size() == 0) {
					TankClient.bullets.clear();
					TankClient.pb.clear();
					TankClient.Visual.clear();
					TankClient.clear();
					TankClient.mission++;
					TankClient.mis.setText("当前关卡:"+TankClient.mission);
					TankClient.initBackground();
				}break;
			case 3://如果是第三关
				if (TankClient.Tri_tanks.size() == 0) {
					TankClient.bullets.clear();
					TankClient.pb.clear();
					TankClient.Visual.clear();
					TankClient.clear();
					TankClient.Client.clear();
					TankClient.properties.clear();
					TankClient.stop = true;
				}break;
			}

				
		}
	}
}

 

 关于双缓冲技术的使用:双缓冲技术我理解也不能算十分深刻,在参见人家的代码以后,还算是能够做到吧。

 我自己的理解:双缓冲主要就是去重写update方法,在更新窗体时把下一个要显示的图片整体的画到窗体上去。

 下面是引用stchou博客中对闪屏的解释,和对双缓冲的解释:

paint闪烁的原因:

每一个paint的过后,程序会自行的调用repaint的方法,但是repaint方法中的绘制有的分配一个与原来窗口一样的的内存空间,但里面是没有存储东西的,所以一次次的paint,repaint的交替就会产生闪烁

 

解决方法:双缓冲技术的工作原理:先在内存中分配一个和窗口一样大的空间(在内存中的空间我门是看不到的),然后利用getGraphics()方法去获得该空间并将它全部一次性的显示到屏幕上.这样显示出来就非常的流畅了.避免了闪烁效果.

双缓冲的原理:
 1.建立一个Image对象DbBuffer,通过DbBuffer=createrImage(int width,int height)来在内存中开辟一个长为width 宽为heithr空间.次空间的大小可以和你动画窗口的大小保持一致,也可以利用getwidth()和getheight()来获得动画窗口的大小.
 2.建立一个Graphics 对象GraImage通过GraImage=DbBuffer.getGraphics();去把要绘制的对象并存放到分配好的内存空间中.
 3.利用paint(GraImage);将其全部绘制带内存之中,最后调用我门的paint(Graphics g)方法中的g.drawImage(DbBuffer,0,0,null)将DbBuffer全部一次性的绘制到我门的动画窗口,然后把我门内存中分配的空间窗口关闭调用dispose()方法.

下面是关键代码:

 

// 重写paint()
	public void paint(Graphics g) {
		super.paint(g);

		if (start) {
			// 我方tank
			if (Client.size() != 0) {
				Client.get(0).drawTank(g);
			}
			// 敌方坦克
			for (int i = 0; i < tanks.size(); i++) {
				tanks.get(i).drawTank(g);
			}
			for (int i = 0; i < Sec_tanks.size(); i++) {
				Sec_tanks.get(i).drawTank(g);
			}
			for (int i = 0; i < Tri_tanks.size(); i++) {
				Tri_tanks.get(i).drawTank(g);
			}

			// 画河流
			for (int i = 0; i < rivers.size(); i++) {
				rivers.get(i).drawBride(g);
			}
			// 敌方子弹
			for (int i = 0; i < bullets.size(); i++) {
				bullets.get(i).drawBullet(g);
			}
			// 我方子弹
			for (int i = 0; i < pb.size(); i++) {
				pb.get(i).drawBullet(g);
			}

			// 命中效果
			for (int i = 0; i < Visual.size(); i++) {
				Visual.get(i).drawBang(g);
			}
			// 画草
			for (int i = 0; i < Grass.size(); i++) {
				Grass.get(i).drawGrass(g);
			}
			// 画铁
			for (int i = 0; i < Fes.size(); i++) {
				Fes.get(i).drawFe(g);
			}
			// 画砖头
			for (int i = 0; i < brides.size(); i++) {
				brides.get(i).drawBride(g);
			}
			// 遍历道具队列
			for (int i = 0; i < properties.size(); i++) {
				properties.get(i).drawProperty(g);
			}
			if (stop) {
				new GameConsole().drawImage(g);// 画出游戏结束图片
			}
		} else {
			new GameConsole().drawStart(g);// 画出游戏开始图片
		}
	}

	// 重写update() 实现双缓冲
	public void update(Graphics g) {
		// super.update(g);
		if (buf_image == null) {
			buf_image = this.createImage(WINDOW_WHITH, WINDOW_HEIGHT);
		}
		Graphics gImage = buf_image.getGraphics();
		Color c = gImage.getColor();
		gImage.setColor(Color.BLACK);
		gImage.fillRect(0, 0, WINDOW_WHITH, WINDOW_HEIGHT);
		gImage.setColor(c);
		paint(gImage);
		g.drawImage(buf_image, 0, 0, null);
	}

	// 重绘线程
	class PaintThread extends Thread {
		private int Count = 0;// 计数变量(有时候会出现爆炸效果不消失现象,在此暂时解决);

		public void run() {
			while (true) {
				if (stop) {
					repaint();
					break;
				}
				Count++;
				if (Count == 50) {
					Visual.clear();
					Count = 0;
				}
				repaint();
				try {
					Thread.sleep(FRANK);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
			}
		}
	}

 

 经过这次坦克大战游戏的编写我所获得的经验:
 (1):每个软件的编写都是徐徐渐进的,先实现简单的功能,在逐步逐步加入新的功能,最后达到质变的阶段.

  附上几张小图:(版本的进程和最初版本的效果)

 


(2):做软件的过程中难免会碰到瓶颈,这时候要主动查询资料,和请教别人。
(3):做软件并不是随随便便,三下五除二就搞定的,它需要我们长时间的琢磨,无论是我们的UI还是AI,打造一个精美的软件是很有挑战性的。


最后,在附件中附上我的这个小游戏和源代码(在jar包中解压即可)。 感谢大家~~~~

  • 大小: 20.7 KB
  • 大小: 15.5 KB
  • 大小: 5.2 KB
  • 大小: 4.1 KB
7
5
分享到:
评论
2 楼 igdnss 2012-05-09  
static List<Home> symbol=new ArrayList<Home>();//司令部

//加入司令部

Home home = new Home(250,465);
  symbol.add(home);



//画司令部
   symbol.get(0).drawHome(g);
//击中司令部让司令部消失

private boolean shoot_home()
{  
          int symbol_x = TankClient.symbol.get(0).getX();
          int symbol_y = TankClient.symbol.get(0).getY();

          if(x>symbol_x&&x<symbol_x+75&&y>symbol_y&&y<symbol_y+50)
          {
           TankClient.symbol.remove(0);
           return true;
          }
          

return false;

}

我是学着你写的,自己会的不多,希望谅解。不知道为什么,总出现越界问题,却找不出错误来,谢谢你的帮助。链表里面只有一个司令部。用下标从0开始应该没有什么问题啊
1 楼 yinger_fei 2011-11-04  
赞一个!!

相关推荐

    坦克大战——C++游戏代码

    功能方面,游戏参考于80年代任天堂红白机(FC/FamilyComputer)上的游戏坦克大战(Battle City),包括地图,游戏模式等等(当时的游戏直接烧在电路板上)。所以游戏平衡方面已经有了很好的参考,无需再花大量时间测试平衡...

    Android应用源码安卓源码(172个合集).zip

    android多线程断点下载.rar Android手机一键Root原理分析.zip Android手机的VoIP客户端 Sipdroid.rar Android有未接来电后处理(判断未接来电).rar Android模仿乐淘的应用程序分析源码.zip Android游戏源码——忍者快...

    java源码包---java 源码 大量 实例

     JAVA编写的吃豆子游戏,类似疯狂坦克一样,至少界面有点像。大家可以看截图。 Java从网络取得文件 1个目标文件 简单 Java从压缩包中提取文件 1个目标文件 简单 Java存储与读取对象 1个目标文件 如题 Java调色...

    java源码包2

     JAVA编写的吃豆子游戏,类似疯狂坦克一样,至少界面有点像。大家可以看截图。 Java从网络取得文件 1个目标文件 简单 Java从压缩包中提取文件 1个目标文件 简单 Java存储与读取对象 1个目标文件 如题 Java...

    JAVA上百实例源码以及开源项目源代码

     JAVA编写的吃豆子游戏,类似疯狂坦克一样,至少界面有点像。大家可以看截图。 Java从网络取得文件 1个目标文件 简单 Java从压缩包中提取文件 1个目标文件 简单 Java存储与读取对象 1个目标文件 如题 Java调色...

    java源码包3

     JAVA编写的吃豆子游戏,类似疯狂坦克一样,至少界面有点像。大家可以看截图。 Java从网络取得文件 1个目标文件 简单 Java从压缩包中提取文件 1个目标文件 简单 Java存储与读取对象 1个目标文件 如题 Java...

    java源码包4

     JAVA编写的吃豆子游戏,类似疯狂坦克一样,至少界面有点像。大家可以看截图。 Java从网络取得文件 1个目标文件 简单 Java从压缩包中提取文件 1个目标文件 简单 Java存储与读取对象 1个目标文件 如题 Java...

    JAVA上百实例源码以及开源项目

     JAVA编写的吃豆子游戏,类似疯狂坦克一样,至少界面有点像。大家可以看截图。 Java从网络取得文件 1个目标文件 简单 Java从压缩包中提取文件 1个目标文件 简单 Java存储与读取对象 1个目标文件 如题 Java调色...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Java吃豆子游戏源代码 6个目标文件 内容索引:JAVA源码,游戏娱乐,JAVA游戏源码 JAVA编写的吃豆子游戏,类似疯狂坦克一样,至少界面有点像。大家可以看截图。 Java从网络取得文件 1个目标文件 简单 Java从压缩包中...

    成百上千个Java 源码DEMO 3(1-4是独立压缩包)

    Java吃豆子游戏源代码 6个目标文件 内容索引:JAVA源码,游戏娱乐,JAVA游戏源码 JAVA编写的吃豆子游戏,类似疯狂坦克一样,至少界面有点像。大家可以看截图。 Java从网络取得文件 1个目标文件 简单 Java从压缩包中...

Global site tag (gtag.js) - Google Analytics