《Java小游戏实现》:坦克大战(续一)
来源:互联网 发布:爱的算法免费下载 编辑:程序博客网 时间:2024/05/13 17:33
《Java小游戏实现》:坦克大战(续一)
上篇博文(http://blog.csdn.net/u010412719/article/details/51712663)只介绍到了能够控制一个坦克在4个方向上移动就结束了,今天就在此基础上继续完成一个一个的小功能。
完成的功能:根据键盘的按键控制一个坦克在8个方向上移动
要完成这个功能,我们要做一下几个事情
1、记录下键盘的按键情况,即要重写键盘按下和抬起的监听事件
采用4个boolean变量来记录,按下为true,抬起为false
具体实现的代码如下:
//定义四个布尔类型变量来记录按键的情况,默认状态下为false,表示没有键按下 private boolean b_L,b_U,b_R,b_D; //记录键盘的按键情况 public void keyMonitor(KeyEvent e){ int key=e.getKeyCode(); switch(key){ 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 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; } }
2、根据1中所记录的按键情况来确定坦克的运行方向
8个运行方向采用一个枚举类型来保存。即 private enum Direction{ L,LU,U,RU,R,RD,D,LD,STOP } //例如:如果键盘的左键和下键被按下,则运行方向dir=Direction.LD. //定义一个变量来表示坦克要运行的方向,初始状态为STOP private Direction dir = Direction.STOP; //根据键盘的按键情况来确定坦克的运行方向 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; } }
3、根据2中的运动方向来确定具体的运行快慢等情况,例如,如果是像左下方向运动,则坦克目前所在的位置(x,y)—->(x-XSPEED,y+YSPEED);其中XSPEED、YSPEED为x、y轴的运行速度。
//上面有运行方向,但是还缺少具体的运行细节,例如:假设是按下了右键,则应该横坐标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 } }
上面三步完整代码代码如下:
public class Tank { //坦克所在的位置坐标 private int x; private int y; //定义两个常量,表示运动的速度 private static final int XSPEED = 5; private static final int YSPEED = 5; //定义四个布尔类型变量来记录按键的情况,默认状态下为false,表示没有键按下 private boolean b_L,b_U,b_R,b_D; //定义一个枚举类型来表示运行的方向 private enum Direction{ L,LU,U,RU,R,RD,D,LD,STOP } //定义一个变量来表示坦克要运行的方向,初始状态为STOP private Direction dir = Direction.STOP; public Tank(int x, int y) { super(); this.x = x; this.y = y; } 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 void draw(Graphics g){ Color c = g.getColor(); g.setColor(Color.RED); g.fillOval(x, y, 30, 30); g.setColor(c); move();//根据键盘按键的结果改变坦克所在的位置 } //记录键盘的按键情况 public void keyMonitor(KeyEvent e){ int key=e.getKeyCode(); switch(key){ 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 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; } } //上面有运行方向,但是还缺少具体的运行细节,例如:假设是按下了右键,则应该横坐标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 } } }
而在TarkClient.java文件中主要修改的地方重写了一个按键抬起的方法。
private class KeyMonitor extends KeyAdapter{ @Override public void keyPressed(KeyEvent e) { tk.keyMonitor(e); } @Override public void keyReleased(KeyEvent e) { tk.keyReleased(e); } }
以上就完成了控制坦克在8个方向上的运行。
完成功能:坦克可以打出子弹
既然是坦克大战,必须要可以打出子弹,是吧,否则,多没意思呀。
首先,我们建立一个子弹类,子弹类中也应该有位置和方向属性,以及draw方法和move方法。
代码如下:
public class Missile { //定义两个常量,表示运动的速度 private static final int XSPEED = 20; private static final int YSPEED = 20; //子弹所在的位置 private int x; private int y; //子弹的运行方向 private Direction dir; public Missile(int x, int y, Direction dir) { this.x = x; this.y = y; this.dir = dir; } public void draw(Graphics g){ Color c = g.getColor(); g.setColor(Color.YELLOW); g.fillOval(x, y, 5, 5); 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; } } }
然后,我们来测试下,在界面中画出一颗不受控制的子弹出来,
在TankClient类中,有一个Missile对象,然后在paint中调用Missile的draw画出来即可。
public class TankClient extends Frame{ //....无关代码没有显示 private Missile ms =new Missile(50,50,Direction.D);//一个子弹对象 @Override public void paint(Graphics g) { //直接调用坦克至圣的draw方法 tk.draw(g); if(ms!=null){ ms.draw(g); } } //....无关代码没有显示 }
现在我们来实现子弹是按下Ctrl键由坦克发射出来。既然是坦克发出一颗子弹,那么在坦克类中就应该有一个发子弹的方法,取名为fire(),返回值为一个子弹对象。子弹对象有坦克当前的位置以及方向来进行初始化。
public Missile fire(){ Missile ms = new Missile(x,y,dir); return ms; }
我们要按下Ctrl键时,发射一颗子弹。
因此,在坦克类中的keyPressed方法中,添加键值是ctrl的情况。由于fire()返回的Missile对象需要用来初始化TankClient类中的Missile引用。因此,在Tank类中,需要持有一个TankClient的引用,借此访问TankClient中的Missile引用。
private TankClient tc; public Tank(int x, int y) { this.x = x; this.y = y; } public Tank(int x, int y, TankClient tc) { this(x,y); this.tc = tc; } //记录键盘的按键情况 public void keyPressed(KeyEvent e){ int key=e.getKeyCode(); //System.out.println(key); switch(key){ case 17: //按下Ctrl键时的处理情况,tc是一个TankClient对象,里面有一个子弹的引用 tc.setMs(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(); }
而TankClient.java类中的代码如下:
public class TankClient extends Frame{ private final static int GAME_WIDTH=600; private final static int GAME_HEIGHT=600; private Tank tk=new Tank(50,50,this);//将this穿进去初始化TankClient private Missile ms ;//持有一个Missile的引用 public Missile getMs() { return ms; } public void setMs(Missile ms) { this.ms = ms; } 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); if(ms!=null){ 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); } } }
以上就实现了一个坦克按下Ctrl键就发射一颗子弹。
虽然上面坦克能够发送一颗子弹了,但是坦克发射一颗子弹还存在有两个小问题
1、发射位置不是在坦克的中心。
2、当坦克处于暂定状态时,不能发送子弹。
先解决第一个问题:使的子弹发送的位置在坦克的中心。
下图画出了如何来根据坦克的位置(X,Y)、高度宽度以及子弹的高度和宽度来求得子弹应该所在的位置。
有了求和子弹位置的表达式,在代码实现中就只需要修正一下Tank类中的fire()方法即可。
public Missile fire(){ 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,dir); return ms; }
先解决第二个问题:当坦克处于暂定状态时,不能发送子弹。
为解决这个问题,引入了炮筒方向,当坦克运动时炮筒方向与坦克方向一致,而当坦克停止时,则炮筒就保持坦克停止前最后的运动方向。而子弹的发射方向为炮筒的指向。
实现代码如下:
//引入一个代表炮筒方向的属性 private Direction ptDir = Direction.D; //并根据炮筒的方向在坦克中画出炮筒 public void draw(Graphics g){ Color c = g.getColor(); g.setColor(Color.RED); g.fillOval(x, y, 30, 30); 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(dir==Direction.LU){ g.drawLine(centerX, centerY, x, y ); } else if(dir==Direction.U){ g.drawLine(centerX, centerY, x+ WIDTH/2, y ); } else if(dir==Direction.RU){ g.drawLine(centerX, centerY, x + WIDTH, y ); } else if(dir==Direction.R){ g.drawLine(centerX, centerY, x+ WIDTH, y + HEIGHT/2); } else if(dir==Direction.RD){ g.drawLine(centerX, centerY, x+ WIDTH, y + HEIGHT); } else if(dir==Direction.D){ g.drawLine(centerX, centerY, x+ WIDTH/2, y + HEIGHT); } else if(dir==Direction.LD){ g.drawLine(centerX, centerY, x, y + 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; }
以上就解决了当坦克停止时不能发射子弹的问题。
Tank.java完整代码如下:
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; //定义一个枚举类型来表示运行的方向 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) { this.x = x; this.y = y; } public Tank(int x, int y, TankClient tc) { this(x,y); this.tc = tc; } public void draw(Graphics g){ Color c = g.getColor(); g.setColor(Color.RED); g.fillOval(x, y, 30, 30); 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(dir==Direction.LU){ g.drawLine(centerX, centerY, x, y ); } else if(dir==Direction.U){ g.drawLine(centerX, centerY, x+ WIDTH/2, y ); } else if(dir==Direction.RU){ g.drawLine(centerX, centerY, x + WIDTH, y ); } else if(dir==Direction.R){ g.drawLine(centerX, centerY, x+ WIDTH, y + HEIGHT/2); } else if(dir==Direction.RD){ g.drawLine(centerX, centerY, x+ WIDTH, y + HEIGHT); } else if(dir==Direction.D){ g.drawLine(centerX, centerY, x+ WIDTH/2, y + HEIGHT); } else if(dir==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: tc.setMs(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 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 } } 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; } }
未完,余下的功能见下篇博文
- 《Java小游戏实现》:坦克大战(续一)
- 《Java小游戏实现》:坦克大战(续2)
- 《Java小游戏实现》:坦克大战(续三)
- 《Java小游戏实现》:坦克大战(续四)
- 《Java小游戏实现》:坦克大战
- 坦克大战小游戏(Java)
- java swing实现坦克大战小游戏源码
- 射击类小游戏——坦克大战(java实现)
- 【Java_项目篇<1>】--JAVA实现坦克大战游戏--画出坦克(一)
- HTML5+JS实现坦克大战小游戏
- java之坦克大战(一)
- java基础--坦克大战(一)
- 坦克大战(一)
- 坦克大战(一)
- java小游戏——坦克大战
- java小游戏-坦克大战,事后总结
- 《Java小游戏实现》:坦克大战(最后一点附加功能的实现)
- unity坦克大战(一)
- 快学Scala第8章----继承
- JAVA中native方法调用C语言实现学习
- 操作系统:进程管理
- 堆(heap)和栈(stack)有什么区别
- Android 平滑图片加载和缓存库 Glide
- 《Java小游戏实现》:坦克大战(续一)
- 使用C++网络库libcurl
- leetcode 203 Remove-Linked-List-Elements
- 登山-C#-两个小知识点,字符串补全,在TextBox中捕获回车键
- 使用主机putty登陆vmware虚拟机里的suse系统
- HDOJ 2544 最短路(四种做法)
- 信号(signal)介绍
- SVN知识整理[iOS]
- 二维数组中的查找