那是8岁的一个夏天,我刚刚完成了一天的工作,赚了50块钱,那个时候赚钱之后,唯一的消费场所当然就是游戏机厅了,从家里往游戏机室冲的感觉真是美好。乐极生悲,那天可能过于兴奋,穿着一双拖鞋,往游戏机室跳奔,谁知到途中的地上竖着一根大铁钉,我以百米冲刺的速度,将脚插进铁钉,顿时血流如注,当我把脚拔出来时,我发现脚底被扎出了一个直径1厘米的洞。我知道我要是再纠结我的脚,那么游戏机室的机子就肯定就被人占了,于是我立刻单脚跳到了游戏机室。一个小时后,尽管游戏机室的地面已经被我的鲜血染红,我仍然坐在游戏机上,拼命的砸着按键,大喊“大血给我吃!大血给我吃!我没血了!”老板过来拍拍我,问我是不是撒了什么染料在地上。我才意识到,我有可能先于游戏里的角色game over,这时我的整条腿已经失去了知觉,我赶紧又往家蹦,一离开游戏厅我才感觉到剧痛,说游戏是海洛因真的一点也不假。

到家之后,我肯定是不敢告诉父母的了,因为之前也听说过,有伤口之后要在一定cd内打破伤风针,不然就会死。突然我看到了桌子上的风油精,我拿起来一看说明,“用于蚊虫叮咬及伤风感冒引起的头痛,头晕,晕车不适”,我优秀的语文阅读能力,让我认为,这个东西一定是治破伤风的,只是没有写明是内服还是外服,只好内外双服。然后在伤口上涂满了风油精,之后当然我很快就晕过去了,幸好抢救的及时,不然就要从下半身截肢了。

今天,我们要开始进入正题了,我们首先要搭一个简单射击游戏的框架,并实现一个简单追逐的效果,为后面实现牛鞭子弹做准备。

首先,我们先造一架自己的飞机,并且能够控制它移动,新建一个MyPlanet类,在draw函数中,我们用画笔画一个长50,宽50的正方形,表示我的飞机,你们一定会惊讶你的飞机竟然是正方形的,我知道你们的飞机都是火腿肠形的,只是用画笔画一个火腿肠实在不容易,只好先代替下,后面会修饰。

画完图形之后,我们实现了飞机向4个方向移动的方法,和4个动作状态属性,一旦相应的动作状态被设为true,我的飞机就会执行某一项动作。

public class MyPlanet {//位置坐标private double  x,y;//动作状态public boolean  isLeft,isRight,isUp,isDown,isFire;//移动速度private int speed =10;public MyPlanet(double x,double y){//构造函数,用于传入初始位置this.x =x;this.y = y;}public void draw(Graphics g){//画个正方形表示自己的飞机,这里的画矩形函数只能接受int类型的数,所以这里将//坐标的double类型转为int型g.drawRect((int)this.x, (int)this.y, 50, 50);}public void update(){//处理各方向的移动if(this.isLeft)this.move_left(speed);if(this.isRight)this.move_right(speed);if(this.isUp)this.move_up(speed);if(this.isDown)this.move_down(speed);}private void move_left(int speed) {this.x-=speed;  }private void move_right(int speed) {this.x+=speed;}private void move_up(int speed) {this.y-=speed;}private void move_down(int speed) {this.y+=speed;}
}

那么这里的动作状态是从哪里改变的呢?我们希望是按下w,a,s,d键分别让飞机,上下左右的运动,这个时候,我们只需要在GameHandler中的keypress中把相应的状态改成true就可以了

//按下键盘事件public void keyPressed(KeyEvent e) {//获得按键编号int keyCode = e.getKeyCode();//后面会添加具体按键匹配switch (keyCode) {case KeyEvent.VK_A:myPlane.isLeft=true;break;case KeyEvent.VK_D:myPlane.isRight=true;break;case KeyEvent.VK_W:myPlane.isUp=true;break;case  KeyEvent.VK_S:myPlane.isDown=true;break;}}

这样当你按下s键时,myPlanet的向下移动判断成立并执行,飞机的位移就向下移动了,之后我们要在keyReleaesd 即按键释放函数中,将按键状态重置为false

//释放键盘事件public void keyReleased(KeyEvent e) {//获得按键编号int keyCode = e.getKeyCode();//后面会添加具体按键匹配switch (keyCode) {case KeyEvent.VK_A:myPlane.isLeft=false;break;case KeyEvent.VK_D:myPlane.isRight=false;break;case KeyEvent.VK_W:myPlane.isUp=false;break;case  KeyEvent.VK_S:myPlane.isDown=false;break;}}

看到这里,有的码农朋友肯定要骂了,你这不闲的蛋疼嘛,你直接在keypress中这样写

        case  KeyEvent.VK_S:myPlane.y+=myPlane.speed;break;

这样不就行了?连KeyReleased都不用写,简单快捷!

其实这里我主要是出于,游戏操作的手感才这样写的,你们可以打开记事本,然后在输入区域,按住一个键输入字母,你会发现,当你刚刚按住一个键的时候,会出一个字母,但是要等接近0.5s之后,才会接着出现字母,这对飞机游戏的手感有着毁灭性的打击。

这个时候我们还不能让飞机运动,因为我们还没在逻辑循环,和绘图循环中加入myPlanet的处理。

public class GameHandler implements Runnable {//游戏帧率public static final int FPS =40 ;//定义全局常量,游戏窗口的宽度和高度public static final int FRAME_WIDTH = 600;public static final int FRAME_HEIGHT = 700;//我的飞机public static MyPlanet myPlane = new MyPlanet(200,630) ;//渲染主循环,处理每一帧的画图动作public void draw(Graphics g){g.setColor(Color.WHITE);//绘制我的飞机myPlane.draw(g);}//逻辑主循环,处理每一帧的逻辑处理public void logical(){myPlane.update();}

如上添加后,你可以运行程序了,此时你应该可以用按键操控飞机的四方向移动了,飞机移动的是挺流畅,但是世界上最悲剧的事情就是拥有飞机,却打不了炮,我们赶紧给我们的飞机添加上发射子弹的功能。

我们希望我们的飞机在按下空格的情况下一秒钟内,能够发射5发子弹,因此我们这里需要一个计时器,也就是每隔200ms才允许飞机打出一发子弹。

要打炮,首先得有子弹,我们创建一个子弹类,我们画一个圆形的子弹,由于子弹在游戏中不止一个,我们用一个List集合来统一管理,并在子弹创建时,将自己加入到集合。

package planet;import java.awt.Graphics;
public class Bullet {private double x,y;private int speed =15;//子弹直径private int rdius =15;public Bullet(double x,double y){this.x =x;this.y = y;//将此类加入GameHandler的子弹集合GameHandler.bulletList.add(this);}public void draw(Graphics g){//绘制一个圆形 g.fillOval((int)this.x,(int)this.y, rdius, rdius);}public void update(){//每帧让子弹上升this.y-=speed;}}

这里的画圆方法是以正方形的最大内切圆来计算的,所以这里的直径长宽相同时,画出来的就是圆。

接下来我们在GameHandler中创立子弹集合,并在主循环中遍历调用每一个子弹的draw()和logical()

 //子弹集合public static ArrayList<Bullet> bulletList = new ArrayList<Bullet>();//渲染主循环,处理每一帧的画图动作public void draw(Graphics g){g.setColor(Color.WHITE);//绘制我的飞机myPlane.draw(g);//遍历绘制子弹集合for(Bullet e :GameHandler.bulletList){e.draw(g);}}//逻辑主循环,处理每一帧的逻辑处理public void logical(){//更新我的飞机的逻辑函数myPlane.update();//遍历更新子弹集合的逻辑函数for(Bullet e :GameHandler.bulletList){e.update();}}

接下来,我们写一个极其简单的计时器类Timer

package planet;public class Timer {//用于计时private int currentIndex;//计时的长度private int endIndex;public Timer(int frame){//构造函数,传入需要计时的时间this.endIndex = frame;}//开始计时,让计数帧加1,如果达到设定的帧数,就返回truepublic boolean act(){this.currentIndex++;if(this.currentIndex==this.endIndex){//一旦到达计数时间,就把当前计数帧重置,这样就可以达到重复计时的效果了this.reset();return true;}return false;}//重置计时器private void reset(){this.currentIndex =0;}
}

下面我们看一下怎么来用这个计时器

我们首先在MyPlanet类中创建一个计时器和一个开火状态

          //动作状态public boolean  isLeft,isRight,isUp,isDown,isFire;
         //子弹发射的cd时间,这里设置一秒钟产生5颗子弹private Timer fireCDTimer =new Timer(GameHandler.FPS/5);

然后我们增加相应的开火方法,之后在GameHandler中的keypress和keyreleased中添加相应的状态

         public void fire(){//产生一颗子弹,位置就在自己飞机的正前方if(this.fireCDTimer.act())//这里的+20和-5用来调整子弹的初始位置,让它从飞机的正前方打出来GameHandler.bulletList.add(new Bullet(this.x+20,this.y-5));}public void update(){//处理各方向的移动if(this.isLeft)this.move_left(speed);if(this.isRight)this.move_right(speed);if(this.isUp)this.move_up(speed);if(this.isDown)this.move_down(speed);//按下space键时处理开火if(this.isFire)this.fire();}

现在我们在运行一下,当你按住空格键时,你应该可以随意的打出子弹了!!

接下来就是我们今天的重中之重,实现自杀式敌机,撞击我的飞机,并实现简单追逐算法!

我们希望的是每隔2秒钟,在窗口上方随机产生一架敌机,并超着我飞机的位置飞过来!

首先我们先创建一个Enemy敌机类,由于敌机也不止一个,我们参照子弹类的方法,也在GameHandler中创建一个敌机类的集合,并创建一个每隔2s触发一次的计时器,用来产生敌机,这里我们还使用到了随机生成函数Random类,产生一个从0到窗口宽度之间的整数,作为敌机创建时的横坐标。

package planet;import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Random;public class Enemy {private double  x,y;private int speed =3;public Enemy(){//创建一个随机数生成器Random r = new Random();//随机产生一个从0到窗口宽度之间的整数double x=r.nextInt(GameHandler.FRAME_WIDTH);this.x =x;this.y =-10;//将此类加入GameHandler的敌机集合GameHandler.enemyList.add(this);}public void draw(Graphics g){//画一个矩形表示敌机g.drawRect((int)this.x, (int)this.y, 50, 50);}public void update(){//每帧追踪敌机this.trackMyPanel();}//自杀式追踪敌机private void trackMyPanel(){double mx =GameHandler.myPlane.getX();double my =GameHandler.myPlane.getY();//这里就是简单追踪的核心if(this.x<mx){this.x+=speed;}else{this.x-=speed;}this.y =this.y+speed;}
}

所谓简单追踪的算法,就是当目标在我的左边的时候,我就将横坐标向左移,当目标在我右边的时候,我就将横坐标向右移,虽然够简单,但是效果还是挺智能的!

package planet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.util.ArrayList;public class GameHandler implements Runnable {//游戏帧率public static final int FPS =40 ;//定义全局常量,游戏窗口的宽度和高度public static final int FRAME_WIDTH = 600;public static final int FRAME_HEIGHT = 700;//我的飞机public static MyPlanet myPlane = new MyPlanet(200,630) ;//敌机集合public static ArrayList<Enemy> enemyList = new ArrayList<Enemy>();//子弹集合public static ArrayList<Bullet> bulletList = new ArrayList<Bullet>();//产生敌机计时器private static Timer makeEnemyTimer = new Timer(GameHandler.FPS*2);//渲染主循环,处理每一帧的画图动作public void draw(Graphics g){g.setColor(Color.WHITE);//绘制我的飞机myPlane.draw(g);//遍历绘制敌机集合for(Enemy e :GameHandler.enemyList){e.draw(g);}//遍历绘制子弹集合for(Bullet e :GameHandler.bulletList){e.draw(g);}}//逻辑主循环,处理每一帧的逻辑处理public void logical(){//产生敌机函数makeEnemy();//更新我的飞机的逻辑函数myPlane.update();//遍历更新敌机集合的逻辑函数for(Enemy e :GameHandler.enemyList){e.update();}//遍历更新子弹集合的逻辑函数for(Bullet e :GameHandler.bulletList){e.update();}}//产生敌人public static void makeEnemy(){//每隔2秒产生一个敌人if(makeEnemyTimer.act())new Enemy();}//按下键盘事件public void keyPressed(KeyEvent e) {//获得按键编号int keyCode = e.getKeyCode();//后面会添加具体按键匹配switch (keyCode) {case KeyEvent.VK_SPACE:myPlane.isFire=true;break;case KeyEvent.VK_A:myPlane.isLeft=true;break;case KeyEvent.VK_D:myPlane.isRight=true;break;case KeyEvent.VK_W:myPlane.isUp=true;break;case  KeyEvent.VK_S:myPlane.isDown=true;break;}}//释放键盘事件public void keyReleased(KeyEvent e) {//获得按键编号int keyCode = e.getKeyCode();//后面会添加具体按键匹配switch (keyCode) {case KeyEvent.VK_SPACE:myPlane.isFire=false;break;case KeyEvent.VK_A:myPlane.isLeft=false;break;case KeyEvent.VK_D:myPlane.isRight=false;break;case KeyEvent.VK_W:myPlane.isUp=false;break;case  KeyEvent.VK_S:myPlane.isDown=false;break;}}@Overridepublic void run() {while(true){//执行一次逻辑处理函数this.logical();//使面板重绘一次,repaint方法会调用GamePanel中的paintComponent方法GameFrame.gamePanel.repaint();try {//线程每隔25ms休眠一次,即每秒执行40次此函数,即FPS=40Thread.sleep(1000/FPS);} catch (InterruptedException e) {e.printStackTrace();}}}
}

此时运行你应该可以看到如下效果了

源码地址:http://download.csdn.net/detail/azhangzhengtong/5221655

追逐算法之--牛鞭的子弹是怎样练成的(2)--简单追逐相关推荐

  1. 追逐算法之--牛鞭的子弹是怎样练成的(5)--牛鞭终养成

    从3号开博到现在已经整整一个星期了,偶博客的访问量只有600多人,这得到什么时候才能达到100w啊....我在这里郑重起誓,等我的博客访问量达到100w的时候,我就现场直播,剖腹,挖眼,割耳,掏心,想 ...

  2. 追逐算法之--牛鞭的子弹是怎样练成的(6)--贝塞尔强化

    水一滴一滴的滴在地上,此刻的时间仿佛已经凝固... "一根,两根,三根..."今天又整整掉了10根头发...一个健康的成年男性的大约有15w根头发,如果我每天掉10根,并以每年10 ...

  3. 云存储,甩动数字时代的“牛鞭”

    文|智能相对论 作者|陈选滨 不知道大家有没有发现,数据存储在我们这个时代,已经不知不觉发展成为一件头等大事.不管是个人用户,还是企业用户,都在为解决数据存储这件事情而烦恼着. 此前,#微信拟推出聊天 ...

  4. 牛鞭效应matlab代码,牛鞭效应在啤酒游戏中的运用.doc

    ____________________________________________________________________________________________________ ...

  5. 基于simulink的牛鞭效应模型建模与仿真

    欢迎订阅<FPGA学习入门100例教程>.<MATLAB学习入门100例教程> 目录 一.理论基础 二.核心程序 三.测试结果 一.理论基础 牛鞭效应,是经济学中的一个术语,它 ...

  6. 啤酒游戏的牛鞭效应之VMI策略减弱

    采用了VMI策略的啤酒游戏,具体如下: (01) FINAL TIME = 100 Units: Month The final time for the simulation. (02) INITI ...

  7. 啤酒游戏及其牛鞭效应的模拟之二级模式

    将三级模式更改为2级模式,即只存在生产商和零售商,如下: (01) FINAL TIME = 100 Units: Month The final time for the simulation. ( ...

  8. 啤酒游戏的牛鞭效应分析之供应链4层模式

    在原有三级模式的基础上将供应链变为4层,包括:生产商.批发商.分销商.零售商. (01) FINAL TIME = 100 Units: Month The final time for the si ...

  9. 牛鞭效应及其在ERP系统中的应用

    "牛鞭效应"(Bullwhip Effect)指的是供应链中需求信号的扩大逐渐增大的现象.在供应链中,小幅的需求波动会在上游逐渐扩大,导致供应链中的库存和生产计划波动更加剧烈.这种 ...

最新文章

  1. Gartner最新发布十大战略预测
  2. 2018-3-23Markov 链(笔记一)定义
  3. 成功人士不用睡觉吗?
  4. Java经典面试题详解:Java字符串转成list
  5. Qt Creator设置3D组件属性
  6. 优秀的基数统计算法——HyperLogLog
  7. C++ 读取单个字符
  8. python网络爬虫_一篇文章教会你利用Python网络爬虫获取穷游攻略
  9. Python中的常用模块
  10. JavaScript Array对象 知识点总结
  11. 在线CSV转SQL工具
  12. JavaScript按概率随机生成事件
  13. 小甲鱼python学习笔记1
  14. mac如何设置默认输入法
  15. 狗熊会python培训班
  16. COMPA: Detecting Compromised Accounts on Social Networks 论文分析
  17. Mysql查看数据库和表占用空间
  18. 强化学习—— Target Network Double DQN(解决高估问题,overestimate)
  19. Vertu TI root教程_方法
  20. 《Python程序设计与算法基础教程(第二版)》江红 余青松,第九章课后习题答案

热门文章

  1. java-swing做的客户端聊天软件
  2. mysql中主键约束和唯一约束的区别_主键约束和唯一性约束的区别
  3. 攻防世界-easy_RSA
  4. Windows10安装Kafka教程
  5. 微信小程序循环出来的内容设置不同的样式
  6. PS学习记录-图像【像素】与【分辨率】的说明
  7. 手机日程提醒在哪里设置?
  8. LeetCode OJ 之 House Robber(抢劫犯)
  9. 使用slf4j的时候一个错误Failed to load class org.slf4j.impl.StaticLoggerBinder。
  10. 【回顾】对话巨杉数据库核心研发团队:分布式数据库自研之路