GUI
图形用户界面需要用到抽象窗口工具集即AWT和Swing来设计实现,由于游戏需要有功能按键来控制游戏的重新开始,退出,暂停,帮助等,所以在实现过程中,设计菜单项。
坦克,河流,树,家,墙等都是调用各自类里面的draw函数来画出来,最后new一个图形面板出来,把所有的元素都add进面板就可以了。界面的构造在程序中由TankClient类的构造方法来完成,即没构造一个用户类都会产生这样一个包含各种游戏元素在内的界面。由于画面是静止的,而我们游戏的画面要求是动态的,游戏中的坦克和子弹都是在不断的动,可穿透的普通墙体也在“动”,所以对于画面,必须要不断的更新,重画才能产生动态的效果。
Elementos en la interfaz
界面中的元素都是在构造界面的时候用界面实例对象的add方法加入进去的,且“动”的元素要不断更新重画。
Tipo de tanque
坦克主要由Tank类来实现。坦克的属性:速度,坦克大小,坦克所在新的位置(X坐标和Y坐标),坦克是否还活着,行动方向等。所有的这些属性都有一个初始化值,从而在游戏一开始就可以运行。
在设计过程中,坦克出现的位置由坐标而定。用户方之后的位置由键盘监听方向,按指定方向以恒定的速度前进——此速度为全局静态变量,当没有接受到键盘的控制时,则保持静止。敌方的坦克则是根据随机数来控制随机的方向和路径的,当敌方坦克撞到阻碍物时,会转回到前一步的位置,从而解决了坦克撞到阻碍物不回头的问题。
坦克的方向和子弹发射以及游戏的重新开始都是由键盘来控制的,所以在Tank类里面必须要实现这些功能。
Tank类的keyPressed()方法用于接受键盘的按键监听,接收到相应的键盘信息后,如接收到F,则表示发射子弹,所以此时要调用Tank类里的fire()方法,fire()方法不带传递参数,因为子弹的方向总是从属于坦克的方向和位置。
由于坦克在碰到墙,界面边界和“家”等阻碍物时要改变方向,所以在Tank里面必须对于每一种阻碍物要设计一个方法来作为应对策略,当然解决方法是把下一步的位置调整到上一步的位置。
用户方Tank还可以吃红心来增加自己的生命值,得到一个红心,增加100生命值。所以在程序中坦克必须要有一个方法来判断当坦克接触到红心时,生命值增加的方法。程序中用eat()方法来实现“吃”红心并且增加生命值,当然,这其中要使得坦克的生命值不会超过自己生命的极限值200,所以判断的时候,当生命小于等于100时,直接加100生命值,但是当生命值大于100时,就只能使生命值加满到极限值200.当然,说到生命值,一定要在图形用户界面中显示出来,所以在Tank类里面一定要设计一个方法,来画出生命的增减过程,在Tank类里面是用DrawBloodbBar()来刻画。
bosque
树林主要是用来做修饰物体的,具有不透明性。由一个Tree类来描述,Tree有两个属性,位置和长宽。树林的类里有一个自己的构造方法,必不可少的draw()方法。
base)
家(基地)是由Home类来抽象的,具体的属性有:家(基地)的大小,家(基地)的位置,家(基地)的存活状态,与树林和河流一样,家(基地)还有draw()和自己的构造方法,作用也和前面的一样,但是家(基地)还有一些新的方法,因为可以在游戏中重新开始游戏,所以要有设置方法让家(基地)重新“活”起来,当然,有时候要判断家(基地)现在的存活状态,所以就必须要isLive()和setLive()两个方法了。除此之外,还要有一个游戏结束界面的清理工作和提示工作,gameover()方法就是解决这个问题的。
muro
墙体分为普通墙(砖墙)和铁墙。普通墙(砖墙)可以被子弹损坏,而铁墙则不会。
普通墙(砖墙):
普通墙(砖墙)有以下几个属性:墙的固定长度和宽度,墙的位置坐标,墙体是由图片加入到图形用户界面来表示的。程序中用CommonWall类来描述普通墙(砖墙),此类里有一个构造方法,用于传递参数,还有一个draw()方法,来画指定位置的墙,另外还有个getRect()方法来构造长方形实例。
金属墙:
金属墙的参数和普通墙完全类似,只不过金属墙不能被子弹穿破,但这个属性在接下来讨论的子弹的属性里面,和普通墙的属性一样。
bala
子弹的属性:子弹前进的X轴和Y轴的速度——初始速度都为10,子弹的长度和宽度——初始的长宽都为10,子弹的位置,子弹的方向,子弹是否live等。由于不同方向的子弹其实是图片,所以这里要考虑不同图片的选择用对应的方向来指定,所以要建立Map键值对,用String属性的方向来指定不同的图片。如:imgs.put("L", bulletImages[0]);
类里面惯例有个构造方法,用来默认传递位置和方向,当然这里用到了同构,另外一个构造函数来取得子弹的状态和界面。darw()方法和move()方法来控制画子弹和移动子弹,其次子弹打到其他坦克上时用hitTank(Tank t) 方法,子弹打到墙上时用hitWall(CommonWall w)方法,打到金属墙上hitWall(MetalWall w),打到家hitHome(),当然在这些方法里,都要作相应的操作来表示接受到子弹的后果,普通墙要移除对应的位置,所以在方法里面又要调用remove()方法来移除,如打到家上,就要吧home的生命设置为false,从而结束游戏。实现如下:
public boolean hitHome() { //当子弹打到家时
if (this.live && this.getRect().intersects(tc.home.getRect())) {
this.live = false;
this.tc.home.setLive(false); //当家接受一枪时就死亡
return true;
}
return false;
}
还有,当子弹射击到对方(用户方对敌方)时,当敌方射击到用户方,用户方要减少生命值,每接受一枪,就执行 t.setLife(t.getLife() - 50); // 受一粒子弹寿命减少50,接受4枪就死,总生命值200,方法里面还要做其他很多的判断,如生命值是否小于0,如果是的话就执行t.setLive(false);从而结束游戏。
explotar
坦克的爆炸效果独立出来用一个类来描述,爆炸的属性:位置和存活状态,另外,画爆炸效果的时候要取得用户界面控制,所以要定义private static Toolkit tk = Toolkit.getDefaultToolkit();
Implementación de clase de tanque.
Método de construcción
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class Tank {
public static int speedX = 6, speedY =6; // 静态全局变量速度---------可以作为扩张来设置级别,速度快的话比较难
public static int count = 0;
public static final int width = 35, length = 35; // 坦克的全局大小,具有不可改变性
private Direction direction = Direction.STOP; // 初始化状态为静止
private Direction Kdirection = Direction.U; // 初始化方向为向上
TankClient tc;
private boolean good;
private int x, y;
private int oldX, oldY;
private boolean live = true; // 初始化为活着
private int life = 200; // 初始生命值
private static Random r = new Random();
private int step = r.nextInt(10)+5 ; // 产生一个随机数,随机模拟坦克的移动路径
private boolean bL = false, bU = false, bR = false, bD = false;
private static Toolkit tk = Toolkit.getDefaultToolkit();// 控制面板
private static Image[] tankImags = null; // 存储全局静态
static {
tankImags = new Image[] {
tk.getImage(BombTank.class.getResource("Images/tankD.gif")),
tk.getImage(BombTank.class.getResource("Images/tankU.gif")),
tk.getImage(BombTank.class.getResource("Images/tankL.gif")),
tk.getImage(BombTank.class.getResource("Images/tankR.gif")), };
}
public Tank(int x, int y, boolean good) {
// Tank的构造函数1
this.x = x;
this.y = y;
this.oldX = x;
this.oldY = y;
this.good = good;
}
public Tank(int x, int y, boolean good, Direction dir, TankClient tc) {
// Tank的构造函数2
this(x, y, good);
this.direction = dir;
this.tc = tc;
}
public void draw(Graphics g) {
if (!live) {
if (!good) {
tc.tanks.remove(this); // 删除无效的
}
return;
}
if (good)
new DrawBloodbBar().draw(g); // 创造一个血包
switch (Kdirection) {
//根据方向选择坦克的图片
case D:
g.drawImage(tankImags[0], x, y, null);
break;
case U:
g.drawImage(tankImags[1], x, y, null);
break;
case L:
g.drawImage(tankImags[2], x, y, null);
break;
case R:
g.drawImage(tankImags[3], x, y, null);
break;
}
move(); //调用move函数
}
void move() {
this.oldX = x;
this.oldY = y;
switch (direction) {
//选择移动方向
case L:
x -= speedX;
break;
case U:
y -= speedY;
break;
case R:
x += speedX;
break;
case D:
y += speedY;
break;
case STOP:
break;
}
if (this.direction != Direction.STOP) {
this.Kdirection = this.direction;
}
if (x < 0)
x = 0;
if (y < 40) //防止走出规定区域
y = 40;
if (x + Tank.width > TankClient.Fram_width) //超过区域则恢复到边界
x = TankClient.Fram_width - Tank.width;
if (y + Tank.length > TankClient.Fram_length)
y = TankClient.Fram_length - Tank.length;
if (!good) {
Direction[] directons = Direction.values();
if (step == 0) {
step = r.nextInt(12) + 3; //产生随机路径
int rn = r.nextInt(directons.length);
direction = directons[rn]; //产生随机方向
}
step--;
if (r.nextInt(40) > 38)//产生随机数,控制敌人开火
this.fire();
}
}
private void changToOldDir() {
x = oldX;
y = oldY;
}
public void keyPressed(KeyEvent e) {
//接受键盘事件
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_R: //当按下R时,重新开始游戏
tc.tanks.clear(); //清理
tc.bullets.clear();
tc.trees.clear();
tc.otherWall.clear();
tc.homeWall.clear();
tc.metalWall.clear();
tc.homeTank.setLive(false);
if (tc.tanks.size() == 0) {
//当在区域中没有坦克时,就出来坦克
for (int i = 0; i < 20; i++) {
if (i < 9) //设置坦克出现的位置
tc.tanks.add(new Tank(150 + 70 * i, 40, false,
Direction.R, tc));
else if (i < 15)
tc.tanks.add(new Tank(700, 140 + 50 * (i -6), false,
Direction.D, tc));
else
tc.tanks.add(new Tank(10, 50 * (i - 12), false,
Direction.L, tc));
}
}
tc.homeTank = new Tank(300, 560, true, Direction.STOP, tc);//设置自己出现的位置
if (!tc.home.isLive()) //将home重置生命
tc.home.setLive(true);
new TankClient(); //重新创建面板
break;
case KeyEvent.VK_RIGHT: //监听向右键
bR = true;
break;
case KeyEvent.VK_LEFT://监听向左键
bL = true;
break;
case KeyEvent.VK_UP: //监听向上键
bU = true;
break;
case KeyEvent.VK_DOWN://监听向下键
bD = true;
break;
}
decideDirection();//调用函数确定移动方向
}
void decideDirection() {
if (!bL && !bU && bR && !bD) //向右移动
direction = Direction.R;
else if (bL && !bU && !bR && !bD) //向左移
direction = Direction.L;
else if (!bL && bU && !bR && !bD) //向上移动
direction = Direction.U;
else if (!bL && !bU && !bR && bD) //向下移动
direction = Direction.D;
else if (!bL && !bU && !bR && !bD)
direction = Direction.STOP; //没有按键,就保持不动
}
public void keyReleased(KeyEvent e) {
//键盘释放监听
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_F:
fire();
break;
case KeyEvent.VK_RIGHT:
bR = false;
break;
case KeyEvent.VK_LEFT:
bL = false;
break;
case KeyEvent.VK_UP:
bU = false;
break;
case KeyEvent.VK_DOWN:
bD = false;
break;
}
decideDirection(); //释放键盘后确定移动方向
}
public Bullets fire() {
//开火方法
if (!live)
return null;
int x = this.x + Tank.width / 2 - Bullets.width / 2; //开火位置
int y = this.y + Tank.length / 2 - Bullets.length / 2;
Bullets m = new Bullets(x, y + 2, good, Kdirection, this.tc); //没有给定方向时,向原来的方向发火
tc.bullets.add(m);
return m;
}
public Rectangle getRect() {
return new Rectangle(x, y, width, length);
}
public boolean isLive() {
return live;
}
public void setLive(boolean live) {
this.live = live;
}
public boolean isGood() {
return good;
}
public boolean collideWithWall(CommonWall w) {
//碰撞到普通墙时
if (this.live && this.getRect().intersects(w.getRect())) {
this.changToOldDir(); //转换到原来的方向上去
return true;
}
return false;
}
public boolean collideWithWall(MetalWall w) {
//撞到金属墙
if (this.live && this.getRect().intersects(w.getRect())) {
this.changToOldDir();
return true;
}
return false;
}
public boolean collideRiver(River r) {
//撞到河流的时候
if (this.live && this.getRect().intersects(r.getRect())) {
this.changToOldDir();
return true;
}
return false;
}
public boolean collideHome(Home h) {
//撞到家的时候
if (this.live && this.getRect().intersects(h.getRect())) {
this.changToOldDir();
return true;
}
return false;
}
public boolean collideWithTanks(java.util.List<Tank> tanks) {
//撞到坦克时
for (int i = 0; i < tanks.size(); i++) {
Tank t = tanks.get(i);
if (this != t) {
if (this.live && t.isLive()
&& this.getRect().intersects(t.getRect())) {
this.changToOldDir();
t.changToOldDir();
return true;
}
}
}
return false;
}
public int getLife() {
return life;
}
public void setLife(int life) {
this.life = life;
}
private class DrawBloodbBar {
public void draw(Graphics g) {
Color c = g.getColor();
g.setColor(Color.RED);
g.drawRect(375, 585, width, 10);
int w = width * life / 200;
g.fillRect(375, 585, w, 10);
g.setColor(c);
}
}
public boolean eat(GetBlood b) {
if (this.live && b.isLive() && this.getRect().intersects(b.getRect())) {
if(this.life<=100)
this.life = this.life+100; //每吃一个,增加100生命点
else
this.life = 200;
b.setLive(false);
return true;
}
return false;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
Balas
import java.awt.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Bullets {
public static int speedX = 10;
public static int speedY = 10; // 子弹的全局静态速度
public static final int width = 10;
public static final int length = 10;
private int x, y;
Direction diretion;
private boolean good;
private boolean live = true;
private TankClient tc;
private static Toolkit tk = Toolkit.getDefaultToolkit();
private static Image[] bulletImages = null;
private static Map<String, Image> imgs = new HashMap<String, Image>(); // 定义Map键值对,是不同方向对应不同的弹头
static {
bulletImages = new Image[] {
// 不同方向的子弹
tk.getImage(Bullets.class.getClassLoader().getResource(
"images/bulletL.gif")),
tk.getImage(Bullets.class.getClassLoader().getResource(
"images/bulletU.gif")),
tk.getImage(Bullets.class.getClassLoader().getResource(
"images/bulletR.gif")),
tk.getImage(Bullets.class.getClassLoader().getResource(
"images/bulletD.gif")),
};
imgs.put("L", bulletImages[0]); // 加入Map容器
imgs.put("U", bulletImages[1]);
imgs.put("R", bulletImages[2]);
imgs.put("D", bulletImages[3]);
}
public Bullets(int x, int y, Direction dir) {
// 构造函数1,传递位置和方向
this.x = x;
this.y = y;
this.diretion = dir;
}
// 构造函数2,接受另外两个参数
public Bullets(int x, int y, boolean good, Direction dir, TankClient tc) {
this(x, y, dir);
this.good = good;
this.tc = tc;
}
private void move() {
switch (diretion) {
case L:
x -= speedX; // 子弹不断向左进攻
break;
case U:
y -= speedY;
break;
case R:
x += speedX; // 字段不断向右
break;
case D:
y += speedY;
break;
case STOP:
break;
}
if (x < 0 || y < 0 || x > TankClient.Fram_width
|| y > TankClient.Fram_length) {
live = false;
}
}
public void draw(Graphics g) {
if (!live) {
tc.bullets.remove(this);
return;
}
switch (diretion) {
// 选择不同方向的子弹
case L:
g.drawImage(imgs.get("L"), x, y, null);
break;
case U:
g.drawImage(imgs.get("U"), x, y, null);
break;
case R:
g.drawImage(imgs.get("R"), x, y, null);
break;
case D:
g.drawImage(imgs.get("D"), x, y, null);
break;
}
move(); // 调用子弹move()函数
}
public boolean isLive() {
// 判读是否还活着
return live;
}
public Rectangle getRect() {
return new Rectangle(x, y, width, length);
}
public boolean hitTanks(List<Tank> tanks) {
// 当子弹打到坦克时
for (int i = 0; i < tanks.size(); i++) {
if (hitTank(tanks.get(i))) {
// 对每一个坦克,调用hitTank
return true;
}
}
return false;
}
public boolean hitTank(Tank t) {
// 当子弹打到坦克上
if (this.live && this.getRect().intersects(t.getRect()) && t.isLive()
&& this.good != t.isGood()) {
BombTank e = new BombTank(t.getX(), t.getY(), tc);
tc.bombTanks.add(e);
if (t.isGood()) {
t.setLife(t.getLife() - 50); // 受一粒子弹寿命减少50,接受4枪就死,总生命值200
if (t.getLife() <= 0)
t.setLive(false); // 当寿命为0时,设置寿命为死亡状态
} else {
t.setLive(false);
}
this.live = false;
return true; // 射击成功,返回true
}
return false; // 否则返回false
}
public boolean hitWall(CommonWall w) {
// 子弹打到CommonWall上
if (this.live && this.getRect().intersects(w.getRect())) {
this.live = false;
this.tc.otherWall.remove(w); // 子弹打到CommonWall墙上时则移除此击中墙
this.tc.homeWall.remove(w);
return true;
}
return false;
}
public boolean hitWall(MetalWall w) {
// 子弹打到金属墙上
if (this.live && this.getRect().intersects(w.getRect())) {
this.live = false;
//this.tc.metalWall.remove(w); //子弹不能穿越金属墙
return true;
}
return false;
}
public boolean hitHome() {
// 当子弹打到家时
if (this.live && this.getRect().intersects(tc.home.getRect())) {
this.live = false;
this.tc.home.setLive(false); // 当家接受一枪时就死亡
return true;
}
return false;
}
}
Hogar
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
public class Home {
private int x, y;
private TankClient tc;
public static final int width = 30, length = 30; // 全局静态变量长宽
private boolean live = true;
private static Toolkit tk = Toolkit.getDefaultToolkit(); // 全局静态变量
private static Image[] homeImags = null;
static {
homeImags = new Image[] {
tk.getImage(CommonWall.class
.getResource("Images/home.jpg")), };
}
public Home(int x, int y, TankClient tc) {
// 构造函数,传递Home的参数并赋值
this.x = x;
this.y = y;
this.tc = tc; // 获得控制
}
public void gameOver(Graphics g) {
tc.tanks.clear();// 作清理页面工作
tc.metalWall.clear();
tc.otherWall.clear();
tc.bombTanks.clear();
tc.theRiver.clear();
tc.trees.clear();
tc.bullets.clear();
tc.homeTank.setLive(false);
Color c = g.getColor(); // 设置参数
g.setColor(Color.green);
Font f = g.getFont();
g.setFont(new Font(" ", Font.PLAIN, 40));
g.drawString("你输了!", 220, 250);
g.drawString(" 游戏结束! ", 220, 300);
g.setFont(f);
g.setColor(c);
}
public void draw(Graphics g) {
if (live) {
// 如果活着,则画出home
g.drawImage(homeImags[0], x, y, null);
for (int i = 0; i < tc.homeWall.size(); i++) {
CommonWall w = tc.homeWall.get(i);
w.draw(g);
}
} else {
gameOver(g); // 调用游戏结束
}
}
public boolean isLive() {
// 判读是否还活着
return live;
}
public void setLive(boolean live) {
// 设置生命
this.live = live;
}
public Rectangle getRect() {
// 返回长方形实例
return new Rectangle(x, y, width, length);
}
}
tanque de bombas
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
public class BombTank {
private int x, y;
private boolean live = true; // 初始状态为活着的
private TankClient tc;
private static Toolkit tk = Toolkit.getDefaultToolkit();
private static Image[] imgs = {
// 存储爆炸图片 从小到大的爆炸效果图
tk.getImage(BombTank.class.getClassLoader().getResource(
"images/1.gif")),
tk.getImage(BombTank.class.getClassLoader().getResource(
"images/2.gif")),
tk.getImage(BombTank.class.getClassLoader().getResource(
"images/3.gif")),
tk.getImage(BombTank.class.getClassLoader().getResource(
"images/4.gif")),
tk.getImage(BombTank.class.getClassLoader().getResource(
"images/5.gif")),
tk.getImage(BombTank.class.getClassLoader().getResource(
"images/6.gif")),
tk.getImage(BombTank.class.getClassLoader().getResource(
"images/7.gif")),
tk.getImage(BombTank.class.getClassLoader().getResource(
"images/8.gif")),
tk.getImage(BombTank.class.getClassLoader().getResource(
"images/9.gif")),
tk.getImage(BombTank.class.getClassLoader().getResource(
"images/10.gif")), };
int step = 0;
public BombTank(int x, int y, TankClient tc) {
// 构造函数
this.x = x;
this.y = y;
this.tc = tc;
}
public void draw(Graphics g) {
// 画出爆炸图像
if (!live) {
// 坦克消失后删除爆炸图
tc.bombTanks.remove(this);
return;
}
if (step == imgs.length) {
live = false;
step = 0;
return;
}
g.drawImage(imgs[step], x, y, null);
step++;
}
}
Resumir
Hay muchos códigos y no los enumeraré en detalle. Este es el diseño del curso que hice. Si necesita referencia, agregue QQ1451563342