Java实现坦克大战(1990有木有勾起童年回忆)

笔者最近一个星期又抽时间学习了一点点Java多线程编程,结合swing绘图做了一个特别简单版的坦克大战游戏。

功能说明:

1.仅仅有一关;英雄坦克(自己)只有一条命--不过可以很好的扩展,只要把MyPanel中的SuperTank做成跟敌人坦克一样的Vector即可;

2.右侧的调速面板仅可以调整我军坦克速度;子弹速度目前不可面板自动调整;

3.敌军总共20辆坦克,同时显示的有3辆;击毁一辆会在左上角自动增加一辆,直到上限20辆;

4.目前敌军坦克的移动完全是随机的,没有加入敌军向目标攻击的概率函数控制(后续优化);

 

代码说明:

1.我本机是编写了三个*.java文件,当然拷贝后,完全可以直接在一个文件中粘贴并编译;

 

游戏截图:

Java实现坦克大战(1990有木有勾起童年回忆)_第1张图片

 

/**
 * 坦克大战
 * 1.Created by Light on 2014-8-1  画坦克
 * 2.Modified by Light on 2014-8-1 让坦克按照键盘的方向键实现移动
 * 3.Modified by Light on 2014-8-1 让坦克发子弹,并且让子弹飞
 * 4.Modified by Light on 2014-8-4 让敌人坦克自由行动,让子弹打到坦克时,坦克和子弹都消失,让坦克少于三个时自动增加
 * 5.Modified by Light on 2014-8-4 优化敌军坦克行动代码和让敌军坦克发射子弹
 * 6.Modified by Light on 2014-8-5 让敌军的坦克发射子弹也能击毁英雄坦克
 */
package com.firstversion;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


import javax.swing.*;
public class TankGame extends JFrame implements ActionListener{

	/**
	 * 不知道这是干什么用的,后续补充
	 */
	private static final long serialVersionUID = 1L;
	MyPanel mp ;
	oprationPanel mep;
	JSplitPane jsplit;
	JButton friend, enemy,speedUp,speedDown;
	JLabel speedArea;
	//主函数
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new TankGame();
	}
	//坦克游戏的构造函数
	public TankGame()
	{
		mp = new MyPanel();
		
		mep = new oprationPanel();
		jsplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		//暂时把分隔窗口收起功能注释掉,免的窗口发生变化导致坦克开出边界
		//jsplit.setOneTouchExpandable(true);
		
		friend = new JButton("友军");
		friend.addActionListener(mp);
		friend.setActionCommand("friend");
		friend.addKeyListener(mp);
		//http://blog.csdn.net/hn1232/article/details/4224863
		//因在另外一个Panel上添加button导致键盘监听失效,参考上面的博文将每个Button都加了键盘监听,功能恢复了,但是现在不明白原理
		enemy = new JButton("敌军");
		enemy.addActionListener(mp);
		enemy.setActionCommand("enemy");
		enemy.addKeyListener(mp);
		
		speedUp = new JButton("加速");
		speedUp.addActionListener(this);
		speedUp.addActionListener(mp);
		speedUp.setActionCommand("speedUp");
		speedUp.addKeyListener(mp);
		
		speedDown = new JButton("减速");
		speedDown.addActionListener(this);
		speedDown.addActionListener(mp);
		speedDown.setActionCommand("speedDown");
		speedDown.addKeyListener(mp);
		
		speedArea = new JLabel(String.valueOf(mp.st.getSpeed()));
		
		
		//Layout
		mep.add(friend);
		mep.add(enemy);
		mep.add(speedUp);
		mep.add(speedDown);
		mep.add(speedArea);
		
		jsplit.setLeftComponent(mp);
		jsplit.setRightComponent(mep);
		this.add(jsplit);
		
		//Listener set
		this.addKeyListener(mp);
		
		//启动MyPanel
		Thread t = new Thread(mp);
		t.start();
		
		//设置JFrame
		this.setSize(600, 450);
		this.setResizable(false);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setLocation(350, 180);
		this.setVisible(true);
		jsplit.setDividerLocation(0.88);
		

	}
	
	//写一个方法用于刷新speedArea
	public void refreshSpeed()
	{
		speedArea.setText(String.valueOf(this.mp.st.getSpeed()));
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		this.refreshSpeed();	
		this.repaint();
	}

}


package com.firstversion;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.*;
import javax.swing.JPanel;
import java.lang.Thread;


//个人Panel,用于重写paint方便自定义
class MyPanel extends JPanel implements KeyListener,ActionListener,Runnable
{
	/**
	 * 暂时不清楚这个是干什么用的
	 */
	private static final long serialVersionUID = 1L;
	//英雄坦克
	SuperTank st = null;	
	//敌军坦克,使用Vector来装敌军坦克群
	int enemyNum = 3;
	Vector enemyTanks = null;
	//个人Panel构造方法
	
	public MyPanel()
	{
		//初始化英雄坦克-->位置在底层中央,方向朝上
		st = new SuperTank(250,350);
		//初始化敌人坦克,并为每个坦克启动一个线程-->在上层,方向朝下
		enemyTanks = new Vector();
		for(int i=0;i参数:初始横坐标/初始纵坐标/画笔/坦克类型/坦克方向	
	public void createTank(int x,int y,Graphics g,int tankType, int direction)
	{
		//先根据tankType判断坦克类型
		switch(tankType)
		{//0-->自己; 1--> 敌军
		case 0: g.setColor(Color.YELLOW); break; //暂时使用颜色来标识tankType
		case 1: g.setColor(Color.CYAN); break;
		}
		//接着根据direction判断坦克朝向,因为坦克朝不同的方向画法可能不一样,比如炮筒的画法和旗帜的画法肯定不一样
		switch(direction)
		{//0-->right; 1-->left; 2-->up; 3-->down;
		case 0: //认为是朝右
			//画两条履带
			g.fill3DRect(x, y, 60, 10, false);
			g.fill3DRect(x, y+30, 60, 10, false);
			//画坦克体
			g.fill3DRect(x+10, y+10, 30, 20, false);
			//画盖子
			g.fillOval(x+18, y+13, 14, 14);
			//画炮筒+炮筒头(略粗一点点)
			g.drawLine(x+25, y+20, x+60, y+20);
			g.drawLine(x+55, y+19, x+60, y+19);
			g.drawLine(x+55, y+21, x+60, y+21);
			break;
		case 1://朝左-->由朝右掉头
			//画两条履带
			g.fill3DRect(x, y, 60, 10, false);
			g.fill3DRect(x, y+30, 60, 10, false);
			//画坦克体
			g.fill3DRect(x+20, y+10, 30, 20, false);
			//画盖子
			g.fillOval(x+28, y+13, 14, 14);
			//画炮筒+炮筒头(略粗一点点)
			g.drawLine(x+35, y+20, x, y+20);
			g.drawLine(x+5, y+19, x, y+19);
			g.drawLine(x+5, y+21, x, y+21);
			break;
		case 2://朝上-->由朝右转头
			//画两条履带
			g.fill3DRect(x, y, 10, 60, false);
			g.fill3DRect(x+30, y, 10, 60, false);
			//画坦克体
			g.fill3DRect(x+10, y+20, 20, 30, false);
			//画盖子
			g.fillOval(x+13, y+28, 14, 14);
			//画炮筒+炮筒头(略粗一点点)
			g.drawLine(x+20, y, x+20, y+35);
			g.drawLine(x+19, y, x+19, y+5);
			g.drawLine(x+21, y, x+21, y+5);
			break;
		case 3://朝下-->由朝上掉头
			//画两条履带
			g.fill3DRect(x, y, 10, 60, false);
			g.fill3DRect(x+30, y, 10, 60, false);
			//画坦克体
			g.fill3DRect(x+10, y+10, 20, 30, false);
			//画盖子
			g.fillOval(x+13, y+18, 14, 14);
			//画炮筒+炮筒头(略粗一点点)
			g.drawLine(x+20, y+10, x+20, y+60);
			g.drawLine(x+19, y+55, x+19, y+60);
			g.drawLine(x+21, y+55, x+21, y+60);
			break;
		}
		
	}
	
	//写一个方法或者当前Panel中存活的敌军坦克数
	public int getAliveTanks()
	{
		int aliveTankNum = 0;
		for(int i=0;iet.getX() && b.getX() < et.getX()+56 && b.getY()>et.getY() && b.getY()et.getX() && b.getX()et.getY() && b.getY()st.getX() && b.getX() < st.getX()+56 && b.getY()>st.getY() && b.getY()st.getX() && b.getX()st.getY() && b.getY()0)
			{
				this.st.setSpeed(this.st.getSpeed()-1);
			}else{
				this.st.setSpeed(1);
			}
			
		}else{
			System.out.println("真神奇,你竟然能让程序跑到这个逻辑来!");
		}
		this.repaint();
	}

	@Override
	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub
		if(e.getKeyCode()==KeyEvent.VK_S)
		{
			this.st.setDirection(3);//控制方向
			this.st.move(this.st.getDirection());
		}else if(e.getKeyCode()==KeyEvent.VK_W)
		{
			this.st.setDirection(2);
			this.st.move(this.st.getDirection());
		}else if(e.getKeyCode()==KeyEvent.VK_A)
		{
			this.st.setDirection(1);
			this.st.move(this.st.getDirection());
		}else if(e.getKeyCode()==KeyEvent.VK_D)
		{
			this.st.setDirection(0);
			this.st.move(this.st.getDirection());
		}
		//判断发射子弹
		if(e.getKeyCode()==KeyEvent.VK_J)
		{
			this.st.fair();
		}
		
		this.repaint();
	}

	@Override//释放键盘
	public void keyReleased(KeyEvent ke) {
		// TODO Auto-generated method stub
		
	}

	@Override//打印字符
	public void keyTyped(KeyEvent ke) {
		// TODO Auto-generated method stub
		
	}

	
	@Override
	public void run() {
		// 重写run()方法,刷新整个MyPanel
		while(true)
		{
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//System.out.println("刷新了一次!");//测试使用
			//判断子弹是否击中敌军坦克
			for(int i=0;i对每辆坦克的每颗子弹进行判断
			for(int i=0;i bulletVector = null;//使用Vector是为了使每个打出去的子弹都是一个线程,同时每个坦克可以打出多个子弹
	//成员属性访问和设置方法群
	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 getDirection() {
		return direction;
	}
	public void setDirection(int direction) {
		this.direction = direction;
	}
	public int getSpeed() {
		return speed;
	}
	public void setSpeed(int speed) {
		this.speed = speed;
	}
	public int getTankType() {
		return tankType;
	}
	public void setTankType(int tankType) {
		this.tankType = tankType;
	}
	public boolean isLive() {
		return isLive;
	}
	public void setLive(boolean isLive) {
		this.isLive = isLive;
	}


	
	//坦克构造函数,很重要,对于需要在其他位置调用的成员属性,在此都需要初始化,不然会报空指针
	public Tank(int x, int y)
	{
		this.x=x;
		this.y=y;
		//这句话初始化Vector一定要有的,不然会在MyPanel中调用出现空指针错误
		bulletVector = new Vector();
		
	}
	//坦克移动的能力方法,以方向为入参
	public void move(int direction)
	{//0-->right; 1-->left; 2-->up; 3-->down;
		switch(direction)
		{
		case 0: if(x+speed<455) x+=speed; break;
		case 1: if(x-speed>0) x-=speed; break;
		case 2: if(y-speed>0) y-=speed; break;
		case 3: if(y+speed<362) y+=speed; break;
		default: break;
		}
		//System.out.println("跑了一次!");
	}

	//判断当前坦克存活子弹数
	public int getAliveBullets()
	{
		int aliveBulletNum =0 ;
		for(int i=0;i0)
		{
			this.fairController--;
			//System.out.println("不发射子弹");
		}else
		{
			//System.out.println("发射子弹");
			switch(this.getDirection())
			{
			case 0: bulletVector.addElement(new Bullet(this.getX()+60,this.getY()+18,this.getDirection())); break;
			case 1: bulletVector.addElement(new Bullet(this.getX()-4,this.getY()+18,this.getDirection())); break;
			case 2: bulletVector.addElement(new Bullet(this.getX()+18,this.getY()-4,this.getDirection())); break;
			case 3: bulletVector.addElement(new Bullet(this.getX()+18,this.getY()+60,this.getDirection()));break;
			}
			//在每次发射动作之后,就要启动这个线程
			Thread t = new Thread(bulletVector.lastElement());
			t.start();
			this.setFairController(25);
		}
		
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		int i =500;
		int j =3;
		while(true)
		{
			//休眠
			try {
				Thread.sleep(i);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//随机秒	
			i = (int)(Math.random()*5000);
			//开始计时毫秒级时间
			Date d = new Date();
			long longtime = d.getTime();
			//测试打印代码
			//System.out.println(longtime);
			//System.out.println(longtime+i);
			//System.out.println((new Date()).getTime());
			
			//随机方向
			j = ((int)(Math.random()*100))%4;
			//一定要按照随机的方向给敌军坦克设置方向,不能只定义移动方向,不然你会看到坦克横着开
			this.setDirection(j);
			//休眠结束时,将坦克置为移动状态
			this.setMoving(true);
			//在随机数期间保持运动
			while(true)
			{
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Date d2 = new Date();
				//System.out.println("d2:"+d2.getTime());
				if(longtime+i>d2.getTime())
				{
					//调用移动方法
					this.move(j);
				}else{
					//停止时发射一个子弹
					this.fair();
					break;
				}
			}
			
			//退出循环时,将坦克置为静止
			this.setMoving(false);
			//在坦克静止时,每隔2~3s发射一颗子弹
			//判断线程退出
			if(this.isLive()==false)
			{
				break;
			}
		}
	}
}



//子弹类,作为线程处理
class Bullet implements Runnable
{
	private int x;
	private int y;
	private int speed = 5;//妈的这个地方坑死老子了,打了子弹就是不走,最后找到初始速度尼玛默认为0了
	private int direction;
	private boolean isLive = true;//默认子弹是激活的
	
	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 getSpeed() {
		return speed;
	}

	public void setSpeed(int speed) {
		this.speed = speed;
	}

	public int getDirection() {
		return direction;
	}

	public void setDirection(int direction) {
		this.direction = direction;
	}

	public boolean isLive() {
		return isLive;
	}

	public void setLive(boolean isLive) {
		this.isLive = isLive;
	}

	//Bullet构造方法
	public Bullet(int x, int y, int direction)
	{
		this.x=x;
		this.y=y;
		this.direction=direction;
	}
	
	@Override//重写run()方法
	public void run(){
		//在fair()的方法中,会调用start(),进而似的run()得以执行,所以在run()中需要包含当前子弹线程结束的逻辑判断
		//需要进行当前子弹的方向的判断
		while(true)
		{
			try {
				Thread.sleep(100);//每100毫秒变化一次初始位置,这样在MyPanel中进行repaint()就可以实现让子弹飞了
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			switch(this.direction)
			{
			case 0: x+=this.speed; break;
			case 1: x-=this.speed; break;
			case 2: y-=this.speed; break;
			case 3: y+=this.speed; break;
			}
			
			//System.out.println("子弹坐标:["+this.x+","+this.y+"]");//测试使用
			//判断子弹到边界自动退出线程
			if(x<0||y<0||x>600||y>450)
			{
				this.setLive(false);//将子弹状态置为非激活,作为后面在画子弹时需要判断的条件
				break;
				//这里不知道怎么在退出线程时,清除Vector中已经消亡的子弹;初步考虑应该在外部按照时间和子弹状态来清除子弹
			}else if(this.isLive==false){
				break;
			}
				
		}

	}
	
}


 

 

 

 

你可能感兴趣的:(Java)