《Java小游戏实现》:坦克大战(续2)
Posted HelloWorld_EE
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java小游戏实现》:坦克大战(续2)相关的知识,希望对你有一定的参考价值。
《Java小游戏实现》:坦克大战(续2)
相关博文:
《Java小游戏实现》:坦克大战http://blog.csdn.net/u010412719/article/details/51712663
《Java小游戏实现》:坦克大战(续一):http://blog.csdn.net/u010412719/article/details/51723570
博文《Java小游戏实现》:坦克大战(续1)中已经实现到了坦克可以发射一颗子弹了。这篇博文在此基础上继续实现更多的功能。
完成功能:坦克发射多颗子弹
有了坦克发射一颗子弹的基础,发射多颗子弹就相当简单的,只需要在TankClien类中加入一个容器来存放多枚子弹即可。由于容器的容量也是有限的,我们不能一直往里面装。因此,在子弹类中,我们为子弹对象引入了一个live属性,用来判断这个子弹是否还存活。判断子弹是否还存活是根据子弹的位置是否出界。
下面只贴出完成这个功能相关代码
TankClient.java
private List<Missile> missiles = new ArrayList<Missile> ();
public List<Missile> getMissiles() {
return missiles;
}
@Override
public void paint(Graphics g) {
//直接调用坦克类的draw方法
tk.draw(g);
//画子弹
for(int i=0;i<missiles.size();i++){
Missile ms = missiles.get(i);
//判断子弹是否还存活在,如果不是存活的,则移除
if(!ms.isLive()){
missiles.remove(ms);
}
else{
ms.draw(g);
}
}
}
Missile.java类
在move方法中根据子弹所在的位置判断子弹是否还存活。
public class Missile {
private void move() {
if(dir==Direction.L){//L,LU,U,RU,R,RD,D,LD,STOP
x -= XSPEED;
}
else if(dir==Direction.LU){
x -= XSPEED;
y -= YSPEED;
}
else if(dir==Direction.U){
y -= YSPEED;
}
else if(dir==Direction.RU){
x += XSPEED;
y -= YSPEED;
}
else if(dir==Direction.R){
x += XSPEED;
}
else if(dir==Direction.RD){
x += XSPEED;
y += YSPEED;
}
else if(dir==Direction.D){
y += YSPEED;
}
else if(dir==Direction.LD){
x -= XSPEED;
y += YSPEED;
}
//根据子弹所在的位置x,y来判断子弹是否还存活在
if(x<0||x>TankClient.GAME_WIDTH||y<0||y>TankClient.GAME_HEIGHT){
live = false;
}
}
public boolean isLive() {
return live;
}
}
Tank.java文件内容如下:
在坦克类中,在keyPressed方法中,每按一次Ctrl键,就发射一颗子弹。
public class Tank {
//记录键盘的按键情况
public void keyPressed(KeyEvent e){
int key=e.getKeyCode();
//System.out.println(key);
switch(key){
case 17:
tc.getMissiles().add(fire());
break;
case KeyEvent.VK_LEFT:
b_L=true;
break;
case KeyEvent.VK_UP:
b_U=true;
break;
case KeyEvent.VK_RIGHT:
b_R=true;
break;
case KeyEvent.VK_DOWN:
b_D=true;
break;
}
//根据上面的按键情况,确定坦克即将要运行的方向
moveDirection();
}
}
完整代码下载链接:http://download.csdn.net/detail/u010412719/9555292
完成功能:解决坦克越界问题
坦克越界问题比较简单,只需要判断坦克所在的位置是否越界,如果越界,则将其位置设置为边界位置即可。
实现代码如下:
/*
* 函数功能:处理坦克越界问题
* */
private void dealTankBorder() {
if(x<0){
x = 0;
}
else if(x > TankClient.GAME_WIDTH-this.WIDTH){
x = TankClient.GAME_WIDTH-this.WIDTH ;
}
if(y<0){
y = 0;
}
else if(y>TankClient.GAME_WIDTH - this.HEIGHT){
y = TankClient.GAME_WIDTH - this.HEIGHT;
}
}
上面函数在move()方法最后进行调用即可。
完成功能:加入敌人的坦克
前面实现的所用功能是自己一个坦克在界面上面运动呀、发子弹呀等等。
下面我们完成在界面上加入一个敌人坦克。
实现敌人坦克有两种方式
1、第一种是再新建一个EnemyTank类,
2、第二种是在原来的Tank类中加入一个属性来表示此坦克的类型。
由于我们有一个坦克类了,为方便起见,这里采用第二种方式:在原来的Tank类中加入一个属性来表示此坦克的类型。
Tank.java中新增加的内容主要有
//添加一个属性,表示此坦克是好还是坏
private boolean good;
//更改下构造方法
public Tank(int x, int y,boolean good) {
this.x = x;
this.y = y;
this.good = good;
}
public Tank(int x, int y,boolean good, TankClient tc) {
this(x,y,good);
this.tc = tc;
}
//根据坦克的类型给出不同的颜色
public void draw(Graphics g){
Color c = g.getColor();
if(good){
g.setColor(Color.RED);
}
else{
g.setColor(Color.BLUE);
}
g.fillOval(x, y, WIDTH, HEIGHT);
g.setColor(c);
//画一个炮筒
drawGunBarrel(g);
move();//根据键盘按键的结果改变坦克所在的位置
}
Missile类没有任何变动。
总管家TankClient.java中需要添加的内容有:
1、new 出两个坦克对象
private Tank tk=new Tank(50,50,true,this);
private Tank enemy = new Tank(100,100,false,this);
2、在paint方法中画出两个坦克
@Override
public void paint(Graphics g) {
//直接调用坦克类的draw方法
tk.draw(g);
enemy.draw(g);
//画子弹
for(int i=0;i<missiles.size();i++){
Missile ms = missiles.get(i);
//判断子弹是否还存活在,如果不是存活的,则移除
if(!ms.isLive()){
missiles.remove(ms);
}
else{
ms.draw(g);
}
}
}
以上就实现了添加敌对坦克这一功能,但是还没有对其添加随机运动。
完成的功能:子弹打死敌人坦克
经过上面的实现,我们有了一个不会动且不会发子弹的傻傻的敌对坦克,但是我们也不能打死它,下面我们就来实现打死坦克。哈哈,是不是稍微变得有趣一点了。
分析:
1、由于是子弹打死敌人坦克,根据面向对象的思想,在子弹类中,加入一个hitTank方法。
public boolean hitTank(Tank t){
}
2、那么hitTank这个方法应该如何来判断是否子弹打中了该坦克呢??这就涉及到一个碰撞的问题。由于我们的子弹和坦克都是矩形,因此碰撞问题就得到了很好的简化,只是判断两个矩形是否有重叠的部分。如果有,则就判断发生了碰撞,子弹就打中了坦克。
因此,在Missile类和Tank类中,,均添加一个getRect()方法,返回子弹和坦克所在的矩形区域对象。
public Rectangle getRect(){
return new Rectangle(x, y, WIDTH, HEIGHT);
}
3、当子弹打中了坦克,则子弹和坦克就应该都消失。因此,在坦克中需要引入一个布尔变量来判断坦克是否存活
public boolean hitTank(Tank t){
//首先判断此坦克是否是存活的,如果是死的,就不打了
if(!t.isLive()){
return false;
}
if(this.getRect().intersects(t.getRect())){//判断是否有碰撞
//碰撞之后,子弹和该坦克就应该都死了
this.live = false;//子弹死了
t.setLive(false);//坦克死了
return true;
}
else{
return false;
}
}
无论是子弹消失,在前面的版本中有处理,但是本版本上的坦克消失,目前的处理方法就是不绘画出来,即在draw方法中,首先判断一下,看此对象是否存活,如果存活,则绘画出来。
代码如下:
public void draw(Graphics g){
if(!live){ //判断坦克是否存活,如果死了,则不绘画出来,直接返回
return ;
}
Color c = g.getColor();
if(good){
g.setColor(Color.RED);
}
else{
g.setColor(Color.BLUE);
}
g.fillOval(x, y, WIDTH, HEIGHT);
g.setColor(c);
//画一个炮筒
drawGunBarrel(g);
move();//根据键盘按键的结果改变坦克所在的位置
}
最后,贴下Tank类、Missile类、TankClient类的完整代码:
Tank类
public class Tank {
//坦克所在的位置坐标
private int x;
private int y;
//坦克的高度和宽度
private static final int WIDTH = 30;
private static final int HEIGHT = 30;
//定义两个常量,表示运动的速度
private static final int XSPEED = 5;
private static final int YSPEED = 5;
//定义四个布尔类型变量来记录按键的情况,默认状态下为false,表示没有键按下
private boolean b_L,b_U,b_R,b_D;
//添加一个属性,表示此坦克是好还是坏
private boolean good;
public boolean isGood() {
return good;
}
//用来标识此坦克对象是否存活
private boolean live =true;
public boolean isLive() {
return live;
}
public void setLive(boolean live) {
this.live = live;
}
//定义一个枚举类型来表示运行的方向
public enum Direction{
L,LU,U,RU,R,RD,D,LD,STOP
}
//定义一个变量来表示坦克要运行的方向,初始状态为STOP
private Direction dir = Direction.STOP;
//炮筒方向
private Direction ptDir = Direction.D;
private TankClient tc;
public Tank(int x, int y,boolean good) {
this.x = x;
this.y = y;
this.good = good;
}
public Tank(int x, int y,boolean good, TankClient tc) {
this(x,y,good);
this.tc = tc;
}
public void draw(Graphics g){
if(!live){ //判断坦克是否存活,如果死了,则不绘画出来,直接返回
return ;
}
Color c = g.getColor();
if(good){
g.setColor(Color.RED);
}
else{
g.setColor(Color.BLUE);
}
g.fillOval(x, y, WIDTH, HEIGHT);
g.setColor(c);
//画一个炮筒
drawGunBarrel(g);
move();//根据键盘按键的结果改变坦克所在的位置
}
private void drawGunBarrel(Graphics g) {
int centerX = this.x + this.WIDTH/2;
int centerY = this.y + this.HEIGHT/2;
if(ptDir==Direction.L){//L,LU,U,RU,R,RD,D,LD,STOP
g.drawLine(centerX, centerY, x, y + HEIGHT/2);
}
else if(ptDir==Direction.LU){
g.drawLine(centerX, centerY, x, y );
}
else if(ptDir==Direction.U){
g.drawLine(centerX, centerY, x+ WIDTH/2, y );
}
else if(ptDir==Direction.RU){
g.drawLine(centerX, centerY, x + WIDTH, y );
}
else if(ptDir==Direction.R){
g.drawLine(centerX, centerY, x+ WIDTH, y + HEIGHT/2);
}
else if(ptDir==Direction.RD){
g.drawLine(centerX, centerY, x+ WIDTH, y + HEIGHT);
}
else if(ptDir==Direction.D){
g.drawLine(centerX, centerY, x+ WIDTH/2, y + HEIGHT);
}
else if(ptDir==Direction.LD){
g.drawLine(centerX, centerY, x, y + HEIGHT);
}
}
//记录键盘的按键情况
public void keyPressed(KeyEvent e){
int key=e.getKeyCode();
//System.out.println(key);
switch(key){
// case 17://避免因Ctrl一直按下,一直发射子弹,因此将这一功能放入keyReleased方法中了
// tc.getMissiles().add(fire());
// break;
case KeyEvent.VK_LEFT:
b_L=true;
break;
case KeyEvent.VK_UP:
b_U=true;
break;
case KeyEvent.VK_RIGHT:
b_R=true;
break;
case KeyEvent.VK_DOWN:
b_D=true;
break;
}
//根据上面的按键情况,确定坦克即将要运行的方向
moveDirection();
}
//键盘按键松下时,也要进行记录
public void keyReleased(KeyEvent e) {
int key=e.getKeyCode();
switch(key){
case 17:
tc.getMissiles().add(fire());
break;
case KeyEvent.VK_LEFT:
b_L=false;
break;
case KeyEvent.VK_UP:
b_U=false;
break;
case KeyEvent.VK_RIGHT:
b_R=false;
break;
case KeyEvent.VK_DOWN:
b_D=false;
break;
}
}
//根据键盘的按键情况来确定坦克的运行方向
private void moveDirection() {//L,LU,U,RU,R,RD,D,LD,STOP
if(b_L&&!b_U&&!b_R&&!b_D){
dir = Direction.L;
}
else if(b_L&&b_U&&!b_R&&!b_D){
dir = Direction.LU;
}
else if(!b_L&&b_U&&!b_R&&!b_D){
dir = Direction.U;
}
else if(!b_L&&b_U&&b_R&&!b_D){
dir = Direction.RU;
}
else if(!b_L&&!b_U&&b_R&&!b_D){
dir = Direction.R;
}
else if(!b_L&&!b_U&&b_R&&b_D){
dir = Direction.RD;
}
else if(!b_L&&!b_U&&!b_R&&b_D){
dir = Direction.D;
}
else if(b_L&&!b_U&&!b_R&&b_D){
dir = Direction.LD;
}
else{//其它所有情况,都是不动
dir = Direction.STOP;
}
//将坦克方向赋值给炮筒方向
if(dir!=Direction.STOP){
ptDir = dir;
}
}
//上面有运行方向,但是还缺少具体的运行细节,例如:假设是按下了右键,则应该横坐标x+=XSPEED;
private void move(){
if(dir==Direction.L){//L,LU,U,RU,R,RD,D,LD,STOP
x -= XSPEED;
}
else if(dir==Direction.LU){
x -= XSPEED;
y -= YSPEED;
}
else if(dir==Direction.U){
y -= YSPEED;
}
else if(dir==Direction.RU){
x += XSPEED;
y -= YSPEED;
}
else if(dir==Direction.R){
x += XSPEED;
}
else if(dir==Direction.RD){
x += XSPEED;
y += YSPEED;
}
else if(dir==Direction.D){
y += YSPEED;
}
else if(dir==Direction.LD){
x -= XSPEED;
y += YSPEED;
}
else if(dir==Direction.STOP){
//... nothing
}
//处理坦克越界问题
dealTankBorder();
}
/*
* 函数功能:处理坦克越界问题
* */
private void dealTankBorder() {
if(x<0){
x = 0;
}
else if(x > TankClient.GAME_WIDTH-this.WIDTH){
x = TankClient.GAME_WIDTH-this.WIDTH ;
}
if(y<0){
y = 0;
}
else if(y>TankClient.GAME_WIDTH - this.HEIGHT){
y = TankClient.GAME_WIDTH - this.HEIGHT;
}
}
public Missile fire(){
//计算子弹的位置,并利用炮筒的方向来new一个子弹对象
int x = this.x +(this.WIDTH)/2 - (Missile.WIDTH)/2;
int y = this.y + (this.HEIGHT)/2 -(Missile.HEIGHT)/2;
Missile ms = new Missile(x,y,this.ptDir);
return ms;
}
/*
* 函数功能:得到坦克所在位置的矩形框
* */
public Rectangle getRect(){
return new Rectangle(x, y, WIDTH, HEIGHT);
}
}
Missile类
代码如下
public class Missile {
//定义两个常量,表示运动的速度
private static final int XSPEED = 10;
private static final int YSPEED = 10;
//子弹所在的位置
private int x;
private int y;
//坦克的高度和宽度
public static final int WIDTH = 10;
public static final int HEIGHT = 10;
//子弹的运行方向
private Direction dir;
private boolean live = true;
public Missile(int x, int y, Direction dir) {
this.x = x;
this.y = y;
this.dir = dir;
}
public void draw(Graphics g){
//如果该子弹不是存活的,则不进行绘图
if(!live){
return ;
}
Color c = g.getColor();
g.setColor(Color.YELLOW);
g.fillOval(x, y, WIDTH, HEIGHT);
g.setColor(c);
move();
}
private void move() {
if(dir==Direction.L){//L,LU,U,RU,R,RD,D,LD,STOP
x -= XSPEED;
}
else if(dir==Direction.LU){
x -= XSPEED;
y -= YSPEED;
}
else if(dir==Direction.U){
y -= YSPEED;
}
else if(dir==Direction.RU){
x += XSPEED;
y -= YSPEED;
}
else if(dir==Direction.R){
x += XSPEED;
}
else if(dir==Direction.RD){
x += XSPEED;
y += YSPEED;
}
else if(dir==Direction.D){
y += YSPEED;
}
else if(dir==Direction.LD){
x -= XSPEED;
y += YSPEED;
}
//根据子弹所在的位置x,y来判断子弹是否还存活在
if(x<0||x>TankClient.GAME_WIDTH||y<0||y>TankClient.GAME_HEIGHT){
live = false;
}
}
public boolean isLive() {
return live;
}
public Rectangle getRect(){
return new Rectangle(x, y, WIDTH, HEIGHT);
}
public boolean hitTank(Tank t){
//首先判断此坦克是否是存活的,如果是死的,就不打了
if(!t.isLive()){
return false;
}
if(this.getRect().intersects(t.getRect())){//判断是否有碰撞
//碰撞之后,子弹和该坦克就应该都死了
this.live = false;//子弹死了
t.setLive(false);//坦克死了
return true;
}
else{
return false;
}
}
TankClient类代码如下:
/*
* 此版本添加了子弹打死敌对坦克
* */
public class TankClient extends Frame{
public final static int GAME_WIDTH=600;
public final static int GAME_HEIGHT=600;
private Tank tk=new Tank(50,50,true,this);
private Tank enemy = new Tank(100,100,false,this);
private List<Missile> missiles = new ArrayList<Missile> ();
public List<Missile> getMissiles() {
return missiles;
}
private Image offScreenImage = null;
public static void main(String[] args) {
new TankClient().launchFrame();
}
@Override
public void update(Graphics g) {
if (offScreenImage == null) {
offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
}
Graphics goffScreen = offScreenImage.getGraphics();// 重新定义一个画虚拟桌布的画笔//
Color c = goffScreen.getColor();
goffScreen.setColor(Color.darkGray);
goffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
goffScreen.setColor(c);
paint(goffScreen);
g.drawImage(offScreenImage, 0, 0, null);
}
@Override
public void paint(Graphics g) {
//直接调用坦克类的draw方法
tk.draw(g);
enemy.draw(g);
//画子弹
for(int i=0;i<missiles.size();i++){
Missile ms = missiles.get(i);
//判断子弹是否还存活在,如果不是存活的,则移除
if(!ms.isLive()){
missiles.remove(ms);
}
else{
ms.hitTank(enemy);
ms.draw(g);
}
}
}
public void launchFrame(){
this.setTitle("坦克大战");
this.setLocation(300, 400);
this.setSize(GAME_WIDTH, GAME_HEIGHT);
this.setBackground(Color.GRAY);
//为关闭窗口添加响应
this.addWindowListener(new WindowAdapter(){
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
//设置是否允许用户改变窗口的大小,这里限制下,不允许
this.setResizable(false);
this.setVisible(true);
new Thread(new MyRepaint()).start();
this.addKeyListener(new KeyMonitor());
}
private class MyRepaint implements Runnable{
@Override
public void run() {
while(true){
//每50ms重画一次
repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private class KeyMonitor extends KeyAdapter{
@Override
public void keyPressed(KeyEvent e) {
tk.keyPressed(e);
}
@Override
public void keyReleased(KeyEvent e) {
tk.keyReleased(e);
}
}
}
以上就完成了坦克发射子弹可以击打敌对坦克的功能。
未完,剩余功能见下篇博文
以上是关于《Java小游戏实现》:坦克大战(续2)的主要内容,如果未能解决你的问题,请参考以下文章