1. 程式人生 > >追逐演算法之--牛鞭的子彈是怎樣練成的(2)--簡單追逐

追逐演算法之--牛鞭的子彈是怎樣練成的(2)--簡單追逐


        那是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,如果達到設定的幀數,就返回true
	public 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;
        }
	}
	
	@Override
	public void run() {
		while(true){
			//執行一次邏輯處理函式
			this.logical();
			
			//使面板重繪一次,repaint方法會呼叫GamePanel中的paintComponent方法
			GameFrame.gamePanel.repaint();
			
			try {
				//執行緒每隔25ms休眠一次,即每秒執行40次此函式,即FPS=40
				Thread.sleep(1000/FPS);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

   此時執行你應該可以看到如下效果了



相關推薦

追逐演算法--鞭的子彈是怎樣的(2)--簡單追逐

        那是8歲的一個夏天,我剛剛完成了一天的工作,賺了50塊錢,那個時候賺錢之後,唯一的消費場所當然就是遊戲機廳了,從家裡往遊戲機室衝的感覺真是美好。樂極生悲,那天可能過於興奮,穿著一雙拖鞋,往遊戲機室跳奔,誰知到途中的地上豎著一根大鐵釘,我以百米衝刺的速度,將

追逐演算法--鞭的子彈是怎樣的(1)--遊戲主框架

       從小我就知道,我這輩子註定不會有什麼出息,縱觀古今中外大人物的童年,不是像丘吉爾,愛因斯坦這樣的智障腦殘,就是像馬克思,李嘉誠這樣的貧困兒童。而我,從小作為一個公認的神童,幾乎每個見了我的人,都要指著我的鼻子誇個半天。我不禁仰天長嘯,“老天啊,你大爺的為什麼

追逐演算法--鞭的子彈是怎樣的(4)--散射子彈

      松江上死豬的餘香還未散去,昨天所有大型的禽類交易市場都被關閉了,撲殺了n萬隻生雞,哎!雞這種動物在中國真的有夠悲劇,一遇到什麼頭疼腦熱,掃黃打非,最先倒黴的總是這種生物,自己不檢點出了問題,關雞屁事啊!今天又感染了2個,病毒在上海這種密度的地方,都是以指數數量級

C語言演算法將十進位制數轉換二進位制數

導語:在C語言中沒有將其他進位制的數直接輸出為二進位制數的工具或方法,輸出為八進位制數可以用%o,輸出為十六進位制可以用%x,輸出為二進位制就要我們自行解決了。下面給大家講述一下如何程式設計實現將十進位制數轉換成二進位制數。 先將原始碼展示給大家: #include

《資料結構與演算法美》專欄閱讀筆記2——線性表

換個方式來寫筆記,最近啃完了《Thinking in Java》,想要在看專欄的時候多做點擴充套件性的東西,比如把難撩的泛型加進來做實現,程式碼還是要寫起來才曉得怎麼寫更酷。總之最近看書的過程中、搜尋答案的過程中發出了很多“哇~超厲害!超酷!我也要這樣棒棒噠!”的嘆聲。新的開始,

《資料結構與演算法美》專欄閱讀筆記2

換個方式來寫筆記,最近啃完了《Thinking in Java》,想要在看專欄的時候多做點擴充套件性的東西,比如把難撩的泛型加進來做實現,程式碼還是要寫起來才曉得怎麼寫更酷。總之最近看書的過程中、搜尋答案的過程中發出了很多“哇~超厲害!超酷!我也要這樣棒棒噠!

幕,是怎樣的?

天下視訊唯彈幕不破 說起彈幕看過視訊的都不會陌生,那滿屏充滿著飄逸評論的效果,讓人如痴如醉,無法自拔 最近也是因為在學習關於canvas的知識,所以今天就想和大家分享一個關於彈幕的故事 那麼究竟彈幕是怎樣煉成的呢? 我們且往下看(look) 看什麼?看效果 效果圖已經呈

重拾演算法劍指Offier——棧的壓入、出序列

題目描述 輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否為該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注

幕,你知道是怎樣的?

天下視訊唯彈幕不破 說起彈幕看過視訊的都不會陌生,那滿屏充滿著飄逸評論的效果,讓人如痴如醉,無法自拔 最近也是因為在學習關於canvas的知識,所以今天就想和大家分享一個關於彈幕的故事 那麼究竟彈幕是怎樣煉成的呢? 我們且往下看(look) 看什麼?看效果 效果圖

程式設計法面試和演算法心得-1.4字串轉換整數

一、題目描述 輸入一個由數字組成的字串,請把它們轉換成整數並輸出。如輸入字串為“123”,輸出整數123。 python化成整數直接可以int(str),現在是自己實現這個函式。 二、解法 這道題看起來比前面的的幾道題都簡單許多,只需要把輸入字串的各個字元的acsii碼減去‘0’

演算法709. 轉換小寫字母

題目:實現函式 ToLowerCase(),該函式接收一個字串引數 str,並將該字串中的大寫字母轉換成小寫字母,之後返回新的字串。 class Solution { public: strin

【NOI】2971:抓住那頭/ 2.5基本演算法搜尋

2971:抓住那頭牛 檢視 提交 統計 提問 總時間限制:  2000ms   記憶體限制:  65536kB 描述 農夫知道一頭牛的位置,想要抓住它。農夫和牛都位於數軸上,農

《合成孔徑雷達像——演算法與實現》【1】模擬圖2.2

重點在於理解“由於影象扭曲或旋轉帶來的頻域表現形式的變化”。 % SAR_Figure_2_2 % 2016.10.31 clear all;clc;close all; %% 引數設定 M

Java路:第三帖----資料結構與演算法佇列

[toc] # 資料結構與演算法--佇列 ***今天掉了兩根頭髮,摸掉的,記得 別亂摸,很珍貴的!!*** ## 什麼是佇列? 1)佇列是一個有序列表,可以用陣列或是連結串列來實現 2)遵循 ***先入先出*** 的原則。即:先存入佇列的資料,要先取出。後存入的要後取出 3)示意圖:(使用陣列模擬佇

oracle改進將阿拉伯數字轉換中文數字

replace 工作 漢字 spa bsp func ace 延展 char   本博客是自己在學習和工作途中的積累與總結。 將阿拉伯數字轉換成中文漢字,方法自定義函數      create or replace function formate(val in num

Python學習路——第二(認識python)

內容 代碼結構 計算 戰術 個人 方法 十分 現在 目的   第一彈中我是說明了學習python的目的,主要為了自我提升的考慮,那麽為什麽我對python感興趣,python有什麽用了?本章就簡單說明下。   python的用途很廣,而且代碼十分簡潔,不像java、c等其他

慕課網_Java Socket應用---通信是這樣

tostring byte[] out -- 編程 nbsp 用戶 static 簡介 fad 第1章 網絡基礎知識 1-1 網絡基礎簡介 (10:21) 第2章 Java 中網絡相關 API 的應用 2-1 Java 中的 InetAddress 的應用 (08:10)

spring boot 學習路3( 集mybatis )

sys pat min lba asn ria [] system emp 下面就簡單來說一下spring boot 與mybatiis的整合問題,如果你還沒學習spring boot的註解的話,要先去看spring boot的註解 好了,現在讓我們來搞一下與mybat

Java復習整型自動轉換浮點型

自動 string 自動轉換 oat 整型 fop ring java 轉換 class DataCon {   public static void main(String args[])   {     int nop1=2;     float fop2=2.25f;

Unity將Texture保存png

ria edi 圖片 put ont details release direct create http://blog.csdn.net/bingheliefeng/article/details/51177505 using UnityEngine;using Sy