不太会写文章,只是为了记录自己做过的东西
文章目录
前言
想必大家很多人小时候都玩过“坦克大战”,对坦克大战早已再熟悉不过了,甚至想着自己以后也能做这个游戏
本人的游戏按照B站上教程完成
一、大概思路
(1)先创建父类“tank”,不管是敌机还是我方坦克,都是坦克,基本的操作都是一样的,都可以上下左右移动,并且攻击。
(2)坦克除了移动,还要能攻击,“shot”类
二、主要代码
1.Tank.java
代码如下:
package tankGame;
public class Tank {
private int x; // 坦克横坐标
private int y; // 坦克纵坐标
private int direct; // 坦克方向
private int speed;
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 Tank(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int getDirect() {
return direct;
}
public void setDirect(int direct) {
this.direct = direct;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
// 坦克上下左右移动方法
public void moveUp() {
if(getY()>0 )
y-=speed;
}
public void moveDown() {
if(getY()+60<750)
y+=speed;
}
public void moveRight() {
if(getX()+60<1000)
x+=speed;
}
public void moveLeft() {
if(getX()>0)
x-=speed;
}
}
2.Shot.java
代码如下:
package tankGame;
/*
* 射击子弹
*/
public class Shot implements Runnable{
int x;
int y;
int direct=0; //初始化方向朝上
int speed=6; // 初始化子弹速度为5
boolean isLive= true; //判断子弹状态 存活/被销毁
public Shot(int x, int y, int direct) {
this.x = x;
this.y = y;
this.direct = direct;
}
public void run() { // 射击行为,子弹移动
while (true) {
//休眠周期为60毫秒
try {
Thread.sleep(40);
} catch(InterruptedException e) {
e.printStackTrace();
}
// 根据direct 来判断子弹飞行方向
switch(direct) { // 0:上,1:右,2:下,3:左
case 0:
y-=speed;
break;
case 1:
x+=speed;
break;
case 2:
y+=speed;
break;
case 3:
x-=speed;
break;
}
// 子弹销毁条件 / 线程退出
if((x<0 && x>1000 && y<0 && y>750 ) || isLive == false)
{
isLive=false;
break;
}
}
}
}
ps:每次射出一颗子弹都创建一个线程,线程的休眠时间决定了子弹飞行的速度,并且当子弹击中敌方坦克时,线程结束。相同的,敌方子弹击中我方坦克,线程结束,游戏结束。
3. Mypanel.java
package tankGame;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;
import javax.swing.*;
/*
* 坦克大战绘图区域 / 面板
*/
// 为了监听键盘事件,实现KeyListener
public class Mypanel extends JPanel implements KeyListener,Runnable{
/**
*
*/
private static final long serialVersionUID = 7217435523947313004L;
Hero hero = null ;
// 定义敌人坦克
Vector<EnemyTank> enemyTanks = new Vector<>();
Vector<Bomb> bombs = new Vector<>();
// 初始化图片对象
Image image1= null;
Image image2 = null;
Image image3 = null;
public Mypanel() {
// 初始化我方坦克
hero = new Hero(700,500);
hero.setSpeed(5); // 设我方坦克速度
// 初始化敌方坦克
int enemyTanksize = 5;
for (int i =0;i<enemyTanksize;i++)
{
EnemyTank enemytank=new EnemyTank(100*(i+1), 0);
// 将enemytanks设置给enemytank
enemytank.setEnemyTanks(enemyTanks);
enemytank.setDirect(1);
enemytank.setSpeed(2);
//启动敌人坦克线程, 动起来
new Thread(enemytank).start();
enemyTanks.add(enemytank);
// 创建子弹
Shot shot = new Shot (enemytank.getX()+20 , enemytank.getY()+70,enemytank.getDirect());
enemytank.shots.add(shot);
new Thread(shot).start(); // 启动线程
}
//初始化
image1 = Toolkit.getDefaultToolkit().getImage(Mypanel.class.getResource("/1.gif"));
image2 = Toolkit.getDefaultToolkit().getImage(Mypanel.class.getResource("/2.gif"));
image3 = Toolkit.getDefaultToolkit().getImage(Mypanel.class.getResource("/1.gif"));
}
@Override
public void paint(Graphics g) {
// TODO 自动生成的方法存根
super.paint(g);
g.fillRect(0, 0, 1000, 750); // 填充矩形 / 画布
if(hero.shot != null && hero.shot.isLive == true) {
g.draw3DRect(hero.shot.x,hero.shot.y,1,1,true);
}
//画子弹
for(int i= 0;i<hero.shots.size();i++) {
Shot shot = hero.shots.get(i);
if(shot != null && shot.isLive == true) {
g.setColor(Color.green); // 设置子弹颜色
g.draw3DRect(shot.x,shot.y,1,1,true);
}else {
hero.shots.remove(shot);
}
}
//如果bombs集合中有对象,就画炸弹
if(bombs != null)
for(int i= 0;i< bombs.size();i++) {
Bomb bomb= bombs.get(i); // 拿出炸弹
// 根据 bomb 的 life 画
if(bomb.life>8)
g.drawImage(image1, bomb.x, bomb.y, 60, 60,this);
else if(bomb.life>4)
g.drawImage(image2, bomb.x, bomb.y, 60, 60,this);
else if (bomb.life>0)
g.drawImage(image3, bomb.x, bomb.y, 60, 60,this);
bomb.lifeDown();
if(bomb.life == 0)
bombs.remove(bomb);
}
// 画出我方坦克的封装方法
if(hero.isLive)
drawTank(hero.getX(),hero.getY(),g,hero.getDirect(),0);
// 画敌方坦克
for(int i=0;i<enemyTanks.size();i++) {
EnemyTank enemytank=enemyTanks.get(i);
if(enemytank.isLive)
drawTank(enemytank.getX(),enemytank.getY(),g,enemytank.getDirect(),1);
// 遍历画出所有子弹
for(int j = 0;j<enemytank.shots.size();j++) {
Shot shot = enemytank.shots.get(j);
if(shot.isLive == true)
{
g.draw3DRect(shot.x, shot.y, 1, 1, false);
}else
{
enemytank.shots.remove(shot); //子弹被销毁后,从集合中删除
}
}
}
}
// 画坦克
/*
* x , y 坦克左上角坐标
* g 画笔
* direct 坦克方向(上下左右), type 坦克种类
*/
public void drawTank(int x, int y , Graphics g ,int direct ,int type ) {
switch(type) {
// 0 为我方坦克,1为敌方坦克
case 0 :
g.setColor(Color.green);
break;
case 1 :
g.setColor(Color.red);
break;
}
switch(direct) {
case 0: // 方向向上
g.fill3DRect(x, y, 10, 60, false);
g.fill3DRect(x+30, y, 10, 60, false);
g.fill3DRect(x+10, y+10, 20, 40, false);
g.fillOval(x+10, y+20, 20, 20);
g.drawLine(x+20, y-10, x+20, y+30);
break;
case 1: // 方向向右
g.fill3DRect(x, y, 60, 10, false);
g.fill3DRect(x, y+30, 60, 10, false);
g.fill3DRect(x+10, y+10, 40, 20, false);
g.fillOval(x+20, y+10, 20, 20);
g.drawLine(x+30, y+20, x+70, y+20);
break;
case 2: // 方向向下
g.fill3DRect(x, y, 10, 60, false);
g.fill3DRect(x+30, y, 10, 60, false);
g.fill3DRect(x+10, y+10, 20, 40, false);
g.fillOval(x+10, y+20, 20, 20);
g.drawLine(x+20, y+70, x+20, y+30);
break;
case 3: //方向向左
g.fill3DRect(x, y, 60, 10, false);
g.fill3DRect(x, y+30, 60, 10, false);
g.fill3DRect(x+10, y+10, 40, 20, false);
g.fillOval(x+20, y+10, 20, 20);
g.drawLine(x+30, y+20, x-10, y+20);
break;
default:
System.out.println("方向错误");
break;
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO 自动生成的方法存根
}
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_W) {
hero.setDirect(0); // 改变坦克方向
hero.moveUp(); // 向上移动
}else if( e.getKeyCode()==KeyEvent.VK_D) {
hero.setDirect(1);
hero.moveRight(); //向右移动
}else if( e.getKeyCode()==KeyEvent.VK_S) {
hero.setDirect(2); //改变坦克方向
hero.moveDown(); //向下移动
}else if( e.getKeyCode()==KeyEvent.VK_A) {
hero.setDirect(3); //改变坦克方向
hero.moveLeft(); //向左移动
}
if (e.getKeyCode()==KeyEvent.VK_J) { // J 发射子弹
hero.shotEnemy();
}
this.repaint();// 重绘
}
@Override
public void keyReleased(KeyEvent e) {
// TODO 自动生成的方法存根
}
// 敌人子弹击中我们
public void hithero() {
for(int i=0;i<enemyTanks.size();i++) {
EnemyTank enemytank = enemyTanks.get(i);
for(int j=0;j<enemytank.shots.size();j++) {
Shot s = enemytank.shots.get(j);
if(hero.isLive && s!=null &&s.isLive) {
hitTank(s,hero);
if(s.isLive == false)
enemytank.shots.remove(s);
}
}
}
if (!hero.isLive) {
JOptionPane.showMessageDialog(null, "你输了");
System.exit(0);
}
}
public void hitenemytank() {
for(int j= 0;j <hero.shots.size();j++) {
Shot shot = hero.shots.get(j);
//判断子弹是否击中
if (shot !=null && shot.isLive)
{
for(int i = 0;i< enemyTanks.size();i++)
{
EnemyTank enemyTank = enemyTanks.get(i);
if(enemyTank.isLive)
hitTank(hero.shot,enemyTank);
}
}
}
if(enemyTanks.size()==0) {
JOptionPane.showMessageDialog(null, "你赢了");
System.exit(0);
}
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(40);
}catch (InterruptedException e) {
e.printStackTrace();
}
hithero();
hitenemytank();
this.repaint();
}
}
//编写方法判断子弹是否击中敌方坦克的方法
public void hitTank(Shot s,Tank enemytank){
switch(enemytank.getDirect()) {
case 0:
case 2:
if(s.x>enemytank.getX() && s.x<enemytank.getX()+40 && s.y > enemytank.getY() && s.y < enemytank.getY()+60) {
s.isLive = false;
enemytank.isLive = false;
// 当子弹击中坦克,创建一个炸弹
Bomb bomb= new Bomb(enemytank.getX(),enemytank.getY());
enemyTanks.remove(enemytank);
s.isLive=false;
bombs.add(bomb);
}
break;
case 1:
case 3:
if(s.x>enemytank.getX() && s.x<enemytank.getX()+60 && s.y > enemytank.getY() && s.y < enemytank.getY()+40) {
s.isLive = false;
enemytank.isLive = false;
// 当子弹击中坦克,创建一个炸弹
Bomb bomb=
new Bomb(enemytank.getX(),enemytank.getY());
enemyTanks.remove(enemytank);
bombs.add(bomb);
}
break;
}
}
}
这个类主要是绘制坦克、子弹与坦克爆炸效果,
4.Hero.java
package tankGame;
/*
* 我方坦克
*/
import java.util.Vector;
public class Hero extends Tank {
public Hero(int x, int y) {
super(x, y);
// TODO 自动生成的构造函数存根
}
Shot shot = null;
//可以发射多颗子弹
Vector<Shot> shots = new Vector<>();
public void shotEnemy() { //设计敌人, 创建子弹对象,将炮筒位置赋给子弹初值
switch(getDirect()) {
case 0:
shot = new Shot(getX()+20,getY()-10,0);
break;
case 1:
shot = new Shot(getX()+70,getY()+20,1);
break;
case 2:
shot = new Shot(getX()+20,getY()+70,2);
break;
case 3:
shot = new Shot(getX()-10,getY()+20,3);
break;
}
shots.add(shot);
new Thread(shot).start(); // 线程启动
}
}
我方坦克,当进行射子弹这个操作的时候,创建子弹对象,启动线程
5.Enempy.java
package tankGame;
import java.util.Vector;
public class EnemyTank extends Tank implements Runnable{
boolean isLive = true;
Vector<Shot>shots = new Vector<>();
// 增加一个成员,可以得到敌人坦克vector;
Vector<EnemyTank>enemyTanks = new Vector<>();
// 使用vector 保存多个Shot
//Vector<Shot>shots=new Vector<>();
public EnemyTank(int x, int y ) {
super(x,y);
}
// 设置一个方法,可以将Mypanel成员设置到Enemytanks
public void setEnemyTanks(Vector<EnemyTank> enemyTanks) {
this.enemyTanks=enemyTanks;
}
//编写方法, 判断当前坦克和enemytanks中的其余坦克是否重叠,或者碰撞
public boolean isTouchEnemyTank() {
// 判断当前敌人坦克的方向
switch(getDirect()) {
case 0:
for (int i = 0;i<enemyTanks.size();i++) {
// 遍历 从vector中取出一个敌人坦克,判断是否重叠
EnemyTank enemyTank = enemyTanks.get(i);
// 先取出‘自己’,自己不和自己比较
if(enemyTank != this) {
// 如果敌人坦克是 上下方向
if(enemyTank.getDirect() == 0|| enemyTank.getDirect() == 2) {
if(this.getX()>=enemyTank.getX() && this.getX()<=enemyTank.getX()+40 && this.getY()>=enemyTank.getY()&& this.getY()<=enemyTank.getY()+60) {
return true;
}
if(this.getX()+40>=enemyTank.getX() && this.getX()+40<=enemyTank.getX()+40 && this.getY()>=enemyTank.getY()&& this.getY()<=enemyTank.getY()+60) {
return true;
}
}
// 如果敌人坦克方向是左右方向
if(enemyTank.getDirect() == 1|| enemyTank.getDirect() == 3) {
if(this.getX()>=enemyTank.getX() // 当前坦克左上角坐标为getx,gety
&& this.getX()<=enemyTank.getX()+60
&& this.getY()>=enemyTank.getY()
&& this.getY()<=enemyTank.getY()+40) {
return true;
}
if(this.getX()+40>=enemyTank.getX() //右上角坐标为getx+40,gety
&& this.getX()+40<=enemyTank.getX()+60
&& this.getY()>=enemyTank.getY()
&& this.getY()<=enemyTank.getY()+40) {
return true;
}
}
}
}break;
case 1:
for (int i = 0;i<enemyTanks.size();i++) {
// 遍历 从vector中取出一个敌人坦克,判断是否重叠
EnemyTank enemyTank = enemyTanks.get(i);
// 先取出‘自己’,自己不和自己比较
if(enemyTank != this) {
// 如果敌人坦克是 上下方向
if(enemyTank.getDirect() == 0|| enemyTank.getDirect() == 2) {
// 当前坦克右上角坐标getx+60,getY
if(this.getX()+60>=enemyTank.getX()
&& this.getX()+60<=enemyTank.getX()+40
&& this.getY()>=enemyTank.getY()
&& this.getY()<=enemyTank.getY()+60) {
return true;
}
// getx +60 ,get y+40
if(this.getX()+60>=enemyTank.getX() && this.getX()+60<=enemyTank.getX()+40 && this.getY()+40>=enemyTank.getY()&& this.getY()+40<=enemyTank.getY()+60) {
return true;
}
}
// 如果敌人坦克方向是左右方向
if(enemyTank.getDirect() == 1|| enemyTank.getDirect() == 3) {
if(this.getX()+60>=enemyTank.getX() // 当前坦克左上角坐标为getx,gety
&& this.getX()+60<=enemyTank.getX()+60
&& this.getY()>=enemyTank.getY()
&& this.getY()<=enemyTank.getY()+40) {
return true;
}
if(this.getX()+60>=enemyTank.getX() //右上角坐标为getx+40,gety
&& this.getX()+60<=enemyTank.getX()+60
&& this.getY()+40>=enemyTank.getY()
&& this.getY()+40<=enemyTank.getY()+40) {
return true;
}
}
}
}
break;
case 2:
for (int i = 0;i<enemyTanks.size();i++) {
// 遍历 从vector中取出一个敌人坦克,判断是否重叠
EnemyTank enemyTank = enemyTanks.get(i);
// 先取出‘自己’,自己不和自己比较
if(enemyTank != this) {
// 如果敌人坦克是 上下方向
if(enemyTank.getDirect() == 0|| enemyTank.getDirect() == 2) {
// 当前坦克左下角getx,gety+60
if(this.getX()>=enemyTank.getX()
&& this.getX()<=enemyTank.getX()+40
&& this.getY()+60>=enemyTank.getY()
&& this.getY()+60<=enemyTank.getY()+60) {
return true;
}
// getx +40 ,get y+60
if(this.getX()+40>=enemyTank.getX() && this.getX()+40<=enemyTank.getX()+40 && this.getY()+60>=enemyTank.getY()&& this.getY()+60<=enemyTank.getY()+60) {
return true;
}
}
// 如果敌人坦克方向是左右方向
if(enemyTank.getDirect() == 1|| enemyTank.getDirect() == 3) {
if(this.getX()>=enemyTank.getX() // 当前坦克左上角坐标为getx,gety
&& this.getX()<=enemyTank.getX()+60
&& this.getY()+60>=enemyTank.getY()
&& this.getY()+60<=enemyTank.getY()+40) {
return true;
}
if(this.getX()+40>=enemyTank.getX() //右上角坐标为getx+40,gety
&& this.getX()+40<=enemyTank.getX()+60
&& this.getY()+60>=enemyTank.getY()
&& this.getY()+60<=enemyTank.getY()+40) {
return true;
}
}
}
}
break;
case 3: // 左上角getx,gety,左下角getx,gety
for (int i = 0;i<enemyTanks.size();i++) {
// 遍历 从vector中取出一个敌人坦克,判断是否重叠
EnemyTank enemyTank = enemyTanks.get(i);
// 先取出‘自己’,自己不和自己比较
if(enemyTank != this) {
// 如果敌人坦克是 上下方向
if(enemyTank.getDirect() == 0|| enemyTank.getDirect() == 2) {
// 当前坦克右上角坐标getx+60,getY
if(this.getX()>=enemyTank.getX()
&& this.getX()<=enemyTank.getX()+40
&& this.getY()>=enemyTank.getY()
&& this.getY()<=enemyTank.getY()+60) {
return true;
}
// getx +60 ,get y+40
if(this.getX()>=enemyTank.getX() && this.getX()<=enemyTank.getX()+40 && this.getY()+40>=enemyTank.getY()&& this.getY()+40<=enemyTank.getY()+60) {
return true;
}
}
// 如果敌人坦克方向是左右方向
if(enemyTank.getDirect() == 1|| enemyTank.getDirect() == 3) {
if(this.getX()>=enemyTank.getX() // 当前坦克左上角坐标为getx,gety
&& this.getX()<=enemyTank.getX()+60
&& this.getY()>=enemyTank.getY()
&& this.getY()<=enemyTank.getY()+40) {
return true;
}
if(this.getX()>=enemyTank.getX() //右上角坐标为getx+40,gety
&& this.getX()<=enemyTank.getX()+600
&& this.getY()+40>=enemyTank.getY()
&& this.getY()+40<=enemyTank.getY()+40) {
return true;
}
}
}
}
break;
}
return false;
}
@Override
public void run() {
while (true) {
// 发射子弹,
if(isLive && shots.size()<100) {
Shot s= null;
switch(getDirect()) {
case 0:
s= new Shot(getX()+20,getY()-10,0);
break;
case 1:
s=new Shot(getX()+70,getY()+20,1);
break;
case 2:
s=new Shot(getX()+20,getY()+70,2);
break;
case 3:
s=new Shot (getX()-10,getY()+20,3);
break;
}
shots.add(s);
new Thread(s).start();
}
switch(getDirect()) {
case 0:
for(int i=0;i<30;i++) {
if(!isTouchEnemyTank())
moveUp();
try {
Thread.sleep(50);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 1:
for(int i=0;i<30;i++) {
if(!isTouchEnemyTank())
moveRight();
try {
Thread.sleep(50);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 2:
for(int i=0;i<30;i++) {
if(!isTouchEnemyTank())
moveDown();
try {
Thread.sleep(50);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 3:
for(int i=0;i<30;i++) {
if(isTouchEnemyTank())
moveLeft();
try {
Thread.sleep(50);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
setDirect((int)(Math.random()*4));
if( ! isLive)
break; // 当死亡后,退出线程
}
}
}
敌方坦克类,首先先让地方坦克动起来,接着让敌方坦克可以发射子弹,最后确保敌方坦克不会重叠,
6.Bomb.java
package tankGame;
/*
* 炸弹
*/
public class Bomb {
int x,y;
int life = 12;
boolean isLive = true ;
public Bomb(int x, int y) {
this.x = x;
this.y = y;
}
public void lifeDown() {
if(life>0)
life--;
else
isLive=false;
}
}
当子弹击中坦克的时候,创建炸弹对象,在通过切换图片来实现炸弹爆炸的特效。
7.Tankgame.java
package tankGame;
/*
* 画坦克区域
*/
import javax.swing.*;
public class Tankgame extends JFrame{
// 定义Mypanel
Mypanel mp = null;
public static void main(String[] args) {
new Tankgame();
}
public Tankgame() {
mp = new Mypanel();
// 将mp放入Thread,并启动
Thread thread = new Thread(mp);
thread.start();
this.add(mp); // 把面板也就是游戏绘图区
this.setSize(1000,785); //设置窗口大小
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 点×能够关闭程序
this.setVisible(true); // 可以被看见
this.setResizable(false); // 不允许用户修改窗口大小
this.addKeyListener(mp);
this.setTitle("坦克大战之坦克乱斗");
}
}
附:俩张坦克爆炸图片


三张图片产生的图片效果更好,我这边为了方便,只用了俩张,记得图片一定要放在这个tankgane目录下
例如我这边的路径是: file:///D:/eclipse/tankGame/src/tankGame/1.gif
总结
心得记录
这个游戏是当时java课程设计老师布置的作业,本来是要三个人做,我为了方便,选择自己一个做,当然工作量大了点,且还需要很多细节需要完善。
bug记录:
当时跟着那个视频做的过程中,有一个方法(获取图片那个方法)因为jdk版本不一样,用不了了,当时一度以为自己哪里操作不对,后来查了资料才知道。
希望努力学算法可以找到一个好的实习,加油,冲!!!
独自坦克大战:自造Java版坦克游戏开发记
&spm=1001.2101.3001.5002&articleId=124199929&d=1&t=3&u=a361c3a5b66942ed99a4c62e216ff93b)
4838





