robocode基本原理之坐标锁定
坐標(biāo)基本概念
首先我們還是來看看 Robocode API 中的一段文字翻譯。
All coordinates are expressed as (x,y).
所有的坐標(biāo)都用 x,y 來表示
All coordinates are positive.
所有的坐標(biāo)都為正
The origin (0,0) is at the bottom left of the screen.
坐標(biāo)原點(diǎn) (0,0) 在屏幕的左下角
Positive x is right. X 的右邊為正
Positive y is up. Y 的上面為正
圖 1 顯示了 Robocode 中的坐標(biāo)系統(tǒng),有關(guān)圖的詳細(xì)說明請(qǐng)看我們前面介紹的文章?“Robocode 基本原理之方向剖析”.
圖 1
“動(dòng)靜機(jī)器人”測試法
好了,我們知道了 Robocode 整個(gè)坐標(biāo)系統(tǒng),一切問題都好辦了。先讓我們進(jìn)行一些有趣的實(shí)驗(yàn)。我們?nèi)砸浴眲?dòng)靜機(jī)器人”的方法進(jìn)行測試。這是個(gè)測試機(jī)器人方向,坐標(biāo)參數(shù)的很好辦法。見下說明:
設(shè)計(jì)兩個(gè)機(jī)器人,任意取名為 Geny 和 GenyTrack。Geny 是個(gè)靜止的機(jī)器人,它主要任務(wù)是打印自己的當(dāng)前坐標(biāo),用來驗(yàn)證 GenyTrack 追蹤它的位置是否正確。GenyTrack 顧名思義,它就是我們要研究的追蹤目標(biāo)機(jī)器人了。它在此負(fù)責(zé)鎖定 Geny 的坐標(biāo),距離并打印出探測到的 Geny 機(jī)器人的 X,Y 坐標(biāo)及距離 , 此處使用了 Java.lang 類庫中的 Math.round 方法 , 四舍五入得到的 double 類型的數(shù)據(jù),方便對(duì)比。最后用表格對(duì)比,以此來驗(yàn)證我們使用方法的正確性。
當(dāng)然還有很多有趣的測試方法來等待著你的驗(yàn)證。如測速度,加速度時(shí)我們就可用”龜兔賽跑”的方法;測炮管,雷達(dá)坦克車旋轉(zhuǎn)相互影響度可用”離心重力”的方法。相信從測試方法的名字聰明的你們就知道他的用法了。
在我們開始前,Skyala.Li 建議你們下載源碼 (?resource) 先看看 GenyTrack 的表演。當(dāng)然你也可參考文章內(nèi)附加的輔助說明 Robocode 坐標(biāo)系統(tǒng)的代碼。
Geny: package test; import robocode.*; public class Geny extends AdvancedRobot { public void run () { while (true) { // round 對(duì) get 到的數(shù)據(jù)進(jìn)行四舍五入處理out.println("x:"+Math.round(getX())); out.println("y:"+Math.round(getY())); } } }GenyTrack:
package test; import robocode.*; public class GenyTrack extends AdvancedRobot { public void run () { while (true) { turnRadarRight(400); } } public void onScannedRobot(ScannedRobotEvent e) { double bearing = (getHeading() + e.getBearing()) % 360; double distance = e.getDistance(); bearing = Math.toRadians(bearing); double genyX = getX() + Math.sin(bearing) * distance; double genyY = getY() + Math.cos(bearing) * distance; out.println("genyX:"+ Math.round(genyX)); out.println("genyY:"+ Math.round(genyY)); } }注意這兩個(gè)機(jī)器人我們都使用了 AdvancedRobot 的類,這可是高級(jí)機(jī)器人的說明了。有關(guān)高級(jí)機(jī)器人大家可以查找 Robocode API 的說明,也可看看 Sing Li 的?"Rock 'em, sock 'em Robocode: Round 2".
回頁首
距離探測
要得到目標(biāo)坐標(biāo)我們首先得知道我們和目標(biāo)之間的距離。這里的距離探測很簡單,只要運(yùn)用 GenyTrack 機(jī)器人 ScannedRobotEvent 事件中的 getDistance() 方法我們就可得到 Geny 機(jī)器人和你之間的距離差了。只是要注意一點(diǎn),由于機(jī)器人存在著寬和高 , 可分別用 Robocode API 中的 getWidth() 和 getHeigth() 方法得到。而兩個(gè)機(jī)器人的距離是以雙方的中心點(diǎn)為終點(diǎn)。如圖所示,L 才是它們的距離,A 的距離是錯(cuò)誤的。
圖 2
回頁首
坐標(biāo)探測
知道了對(duì)方的距離,知道了整個(gè)坐標(biāo)系統(tǒng)。我們就來鎖定我們的目標(biāo) Geny. 我們先來看看圖 3 所示:
圖 3
列表 1:
| X:303 | genyX:303 |
| Y:128 | genyY:128 |
列表 1 就是我們用”動(dòng)靜機(jī)器人”測試法得出的數(shù)據(jù)。你將會(huì)驚喜若狂,不錯(cuò),我們成功的探測到了我們可憐的 Geny 的坐標(biāo)。驚喜過后你就會(huì)不明白了 : 我們是怎樣實(shí)現(xiàn)這一切的?為什么代碼中使用到了非 Robocode 中的類庫 Math,還似乎用到了正余弦求解,還有弧度?不錯(cuò),這就是 Robocode: 處處都讓我們驚奇,處處都讓我們學(xué)習(xí)新的知識(shí)。如果你對(duì)中學(xué)時(shí)代的數(shù)學(xué)三角幾何解法已經(jīng)陌生,沒關(guān)系,你將在我們本文最后的?三角函數(shù)基礎(chǔ)中將學(xué)習(xí)到這些。它將勾起你中學(xué)時(shí)代的記憶。
現(xiàn)在讓我們來分析分析我們 GenyTrack 到底做了些什么:
在 GenyTrack 的 ScanndeRobotEvent 事件中我們首先得到 Geny 的絕對(duì)角度 bearing,也即相對(duì)屏幕的角度。并從 ScannedRobotEvent 掃描事件中得到的大量信息分析中提煉出 Geny 和 GenyTrack 的距離為 distance。有了 Geny 的角度 , 有了 Geny 的距離我們?cè)俑鶕?jù)三角學(xué)基礎(chǔ)(詳見文?三角函數(shù)基礎(chǔ))就可求出 Geny 的精確坐標(biāo)了。
又由于 Java 類庫中的正弦函數(shù) sin 余弦函數(shù) cos 是以弧度制(詳見文?三角函數(shù)基礎(chǔ))為角度的參數(shù)。所以我們利用了 Math.toRadians 方法把 Geny 的絕對(duì)角度轉(zhuǎn)化為弧度。見列表 2
列表 2:
double bearing = (getHeading() + e.getBearing()) % 360; double distance = e.getDistance(); bearing = Math.toRadians(bearing); double genyX = getX() + Math.sin(bearing) * distance; double genyY = getY() + Math.cos(bearing) * distance; out.println("genyX:"+ Math.round(genyX)); out.println("genyY:"+ Math.round(genyY));注意三角函數(shù)的基礎(chǔ)中:對(duì)邊長 =sina * 斜邊長,側(cè)邊長 =cosa *斜邊長,但要記住 Robocode 中三角坐標(biāo)系統(tǒng)中的 sin 和 cos 和我們數(shù)學(xué)中的三角坐標(biāo)系統(tǒng)有一定差別,也即上面的 sina 和 cosa 要對(duì)換,對(duì)邊長 =cosa* 斜邊長。圖 4 畫出了 Geny 和 GenyTrack 之間角度和距離的關(guān)系以及 Robocode 所采用的三角坐標(biāo)系統(tǒng)。
圖 4
黑線條為 GenyTrack 的 X,Y 坐標(biāo) , 藍(lán)線條以 Geny 的距離 distance 和絕對(duì)角度 bear 求得的 X,Y 坐標(biāo) , 兩者相加得到的就是 Geny 的 X 和 Y 坐標(biāo)。
至于 Math 類庫的使用,我們就不詳細(xì)說明了。讀者也可從下面的 IBM Java 專區(qū)鏈接中找到很多有關(guān)的知識(shí),也可參考一些 Java 類庫書籍說明。當(dāng)你設(shè)計(jì)高級(jí) Robocode 機(jī)器人時(shí)你會(huì)發(fā)現(xiàn),Math 類庫是你不可缺少的一部分知識(shí)。此處我們只簡單的介紹正弦函數(shù)及余弦函數(shù)的使用。
Sin public static double sin(double a) Returns the trigonometric sine of an angle. Parameters: a - an angle, in radians. Returns: the sine of the argumentSin 函數(shù)返回三角的正弦函數(shù),參數(shù) a 是一個(gè)以 double 類型以弧度表示的角度值,返回類型為 double.
cos public static double cos(double a) Returns the trigonometric cosine of an angle. Parameters: a - an angle, in radians. Returns: the cosine of the argumentCos 函數(shù)返回三角的余弦函數(shù),參數(shù) a 是一個(gè)以 double 類型的弧度表示的角值 , 返回類型為 double.
有人會(huì)問為什么不使用 ScanndeRobot 事件中的 getRadarHeadingRadians() 方法直接得到弧度。哦,你來看看?Robocode 中華聯(lián)盟 iiley的一段說明:
public void onScannedRobot(ScannedRobotEvent event) { enemyX=Math.sin(Util.standardMathDirRadians(getRadarHeadingRadians()))*event.getDistance(); enemyY=Math.cos(Util.standardMathDirRadians(getRadarHeadingRadians()))*event.getDistance(); }看起來好像正確的,但是你實(shí)踐一下會(huì)發(fā)現(xiàn)他很不準(zhǔn)確,為什么呢?原因在于 getRadarHeadingRadians() 函數(shù),當(dāng)你調(diào)用此函數(shù)的時(shí)候?qū)嶋H上雷達(dá)已經(jīng)不在剛剛掃描到敵人的那個(gè)角度了,他已經(jīng)轉(zhuǎn)過了十幾度甚至更多。雷達(dá)默認(rèn)轉(zhuǎn)動(dòng)速度是 45 度 /robocode 單位時(shí)間,實(shí)際上一般來說你用 getRadarHeadingRadians() 得到的值總是 45 度的整數(shù)倍。(一些情況除外,比如說你用了 turnRadarLeft(11) 類似的語句以后)。
Robocode 也遵循數(shù)學(xué)應(yīng)用中的基本法則用兩種方法來表示方向的角度:角度制和弧度制,本文的代碼及以前文章中的代碼我們一直用的是角度制。另外一種方法就是利用 ScannedRobotEvent.getBearingRadians()+robot.getHeadingRadians() 得到敵人以弧度表示的方向,這個(gè)方法在本文章中沒有說明了,有興趣的朋友可以自己試試用 Java.util 類庫來實(shí)現(xiàn) . 也可參考文檔?"精確計(jì)算敵人的坐標(biāo)"。大家也可比較兩種方法各自特點(diǎn),這將是個(gè)很有意思的過程。
回頁首
移動(dòng)鎖定
當(dāng)然,即使是最簡單的機(jī)器人也不會(huì)坐在那一動(dòng)不動(dòng)等著你來消滅。它會(huì)躲避你的進(jìn)攻以及掃描,當(dāng)你向它原來坐標(biāo)處開火,說不定它已經(jīng)跑得老遠(yuǎn)了,當(dāng)然這一切都不是我們所希望看到的。 我們的目的是要消滅它 : 不管他是移動(dòng)或靜止的。下面我們就結(jié)合方向系統(tǒng)與坐標(biāo)系統(tǒng),來鎖定我們移動(dòng)的目標(biāo)。創(chuàng)造一個(gè)我們自己的高級(jí)掃描機(jī)器人。建議你在此處下載源代碼 (?resource) 并看看演示效果再回到我們的文章中來。顯示如圖 5:
圖 5
對(duì)比一下上面的數(shù)據(jù),不管目標(biāo) GenyMove 在哪 GenyRadar 都能得到它精確的坐標(biāo)。是不是有一種成就感!是的,敵人已經(jīng)完全在我們的掌握之中。即使它在移動(dòng)中也無法擺脫我們雷達(dá)的掃描控制。這里只是很簡單舉了一些例子,GenyMove 在每一個(gè)時(shí)間周期(有關(guān)時(shí)間周期的說明見的?Rock 'em, sock 'em Robocode: Round2)移動(dòng)自己的位置并打印出移動(dòng)后的坐標(biāo),而 GenyRadar 掃描系統(tǒng)不停的掃描目標(biāo),并一直追蹤,同時(shí)打印出掃描到的 GenyMove 方位。關(guān)鍵部分在我們的 ScannedRobotEvent 事件如列表 3
列表 3:
public void onScannedRobot( ScannedRobotEvent e ) { double heading = e.getBearing() +getHeading(); double distance = e.getDistance(); // 求得距離double ager_bearing = Math.toRadians(heading % 360); // 角度轉(zhuǎn)為弧度double genyX = getX() + Math.sin(ager_bearing) * distance; double genyY = getY() + Math.cos(ager_bearing) * distance; out.println("genyX:"+ Math.round(genyX)); out.println("genyY:"+ Math.round(genyY)); if( heading >= 360 ) heading = heading - 360; if( heading < 0 ) heading = heading +360; double bearing = getRadarHeading() - heading; double radar_degree; boolean radar_direction; if( 0 <= bearing && bearing <= 180 ) { radar_direction = LEFT; } else if( bearing <= -180 ) { radar_direction = LEFT; bearing = ( 360 + bearing ); } else if( bearing < 0 ) { radar_direction = RIGHT; bearing =( -bearing ); } else { radar_direction = RIGHT; bearing = (360 - bearing); } radar_degree = bearing * 1.3 ; // 加大每一時(shí)間周期 (tick) 的掃描范圍if( radar_direction == RIGHT ) { setTurnRadarRight( radar_degree ); execute(); } else { setTurnRadarLeft( radar_degree ); execute(); }我們?cè)诖a中首先求得 GenyMove 的絕對(duì)角度,然后用掃描時(shí)雷達(dá)的絕對(duì)角度減去目標(biāo) GenyMove 的角度求得兩者的角度差也即我們雷達(dá)要旋轉(zhuǎn)的角度。最后利用一個(gè)小技巧 radar_degree = bearing * 1.3 使雷達(dá)在目標(biāo)的范圍左右擺動(dòng)以擴(kuò)大雷達(dá)掃描區(qū)域 . 這樣不管目標(biāo)往哪邊移動(dòng)都在自己的雷達(dá)掃描區(qū)內(nèi)。
在此沒有進(jìn)行很詳細(xì)的講解了,我想憑你學(xué)到的方向及坐標(biāo)知識(shí)很快能明白個(gè)中原理并設(shè)計(jì)出自己的高級(jí)掃描機(jī)器人來。 聰明的你可能會(huì)高興的想,哈,我的炮管用相同的辦法鎖定目標(biāo),這樣敵人不就沒辦法跑了,被我追著打。答案是錯(cuò)誤的,雷達(dá)的掃描是條長線能直接定位到目標(biāo)上 ,它到目標(biāo)的時(shí)間差幾乎為零,并且雷達(dá)的掃描范圍比炮管大且精確。而炮管每時(shí)間周期只有 20 度,它定位目標(biāo)是依靠著子彈 , 只有子彈打中了目標(biāo),才能說炮管的計(jì)算坐標(biāo)是精確的。但是由于子彈 到達(dá)目標(biāo)位置時(shí)需要一定的時(shí)間差,子彈本身又有速度值(20-3*power),所以要想炮管鎖定目標(biāo)并讓子彈擊中目標(biāo),我們還得經(jīng)過精確的計(jì)算 , 并要預(yù)測目標(biāo)可能的行動(dòng):是直線前進(jìn),還是做圓周運(yùn)動(dòng),還是隨機(jī)運(yùn)動(dòng)等等。 這些都是我們要充分考慮的因素。是不是很有挑戰(zhàn)性 ! 這一切都在 Robocode 的世界中等待著您的創(chuàng)造!
回頁首
三角函數(shù)基礎(chǔ)
下面我們只是很簡單的介紹了一下與 Robocode 相關(guān)的三角函數(shù)知識(shí),要想了解詳細(xì)的,大家可從家中高中代數(shù)與幾何書中得到這一切。
1 .角的概念
在平面內(nèi),角可以看作一條射線繞著它的端點(diǎn)旋轉(zhuǎn)而成的圖形。如圖,一條射線由原來的位置 OA,繞著它的端點(diǎn) O 按逆時(shí)方向旋轉(zhuǎn)到另一位置 OB,就形成角 a. 旋轉(zhuǎn)開始時(shí)的射線 OA 叫做角 a 的始邊,旋轉(zhuǎn)終止時(shí)的射線 OB 叫做角 a 的終邊,射線的端點(diǎn) O 叫做角 a 的頂點(diǎn)。習(xí)慣上,我們把按逆時(shí)針方向旋轉(zhuǎn)而成的角叫做正角;按順時(shí)針方向旋轉(zhuǎn)而成的角叫做負(fù)角 . 所有與 a 終邊相同的角包括 a 在內(nèi),可以用式子表示:a+K*360 度 , 對(duì)應(yīng)到 Robocode 的方向系統(tǒng)中,只要我們以機(jī)器人的 heading 方向做射線,延長到與屏幕交點(diǎn)處的角度就是我們機(jī)器人的 heading 角度。
2. 直角三角函數(shù)
在△ ABC 中,∠ a 為直角,我們把銳角 A 的對(duì)邊與斜邊的比叫做∠ A 的正弦,記作 sina;銳角 a 的鄰邊與斜邊的比叫做∠ a 的余弦,記作 cosa,即
sina= 對(duì)邊 BC/ 斜邊 AB
cosa= 鄰邊 AC/ 斜邊 AB
3. 單位圓和三角函數(shù)線
半徑為 1 的圓叫做單位圓。設(shè)單位圓的圓心與坐標(biāo)原點(diǎn)重合,則單位圓與 x 軸的交點(diǎn)分為別為 A(1,0)、A ′ (-1,0),與 y 軸的交點(diǎn)分別為 B(0,1)、B ′ (0,-1)。設(shè)角 a 的頂點(diǎn)在圓心 O,始點(diǎn)與 x 軸的正半軸重合,終邊與單位圓相交于點(diǎn) P,過點(diǎn) P 作 PM 垂直 x 軸于 M,則由直角三角函數(shù)的定義可知 :OM=cosa,MP=sina ,點(diǎn) P 的坐標(biāo)為 (cosa,sina),即 P(cosa,sina)。其中 cosa = OM*1,sina = MP*1。Robocode 中所有有關(guān)的坐標(biāo)都可用這種方法求得。
4 .弧度制
用度做單位來度量角的制度叫做角度制。數(shù)學(xué)和其他科學(xué)研究中常用另一種度量角的制度―弧度制。以角的頂點(diǎn)為圓心,以任意長的半徑作圓把這個(gè)角所對(duì)的弧長與半徑的比來衡量角的制度叫做弧度制 . 長度等于半徑的弧長叫 1 弧度。這段弧所對(duì)的圓心角的大小也是 1 弧度。通常單位“弧度”省略不寫。例:弧長為 1.3325。單位就是弧度。由角度和弧度兩種單位之間的關(guān)系得到 :2 π弧度 =360 度 ,2/3 π弧度 =270 度 , π弧度 =180 度 ,1/2 π弧度 =90 度 , 并可推出 1 弧度 = 360 度 /2 π = 57 °即 1 弧度 = 角度 *180/Math.PI.
一般規(guī)定:正角的弧度數(shù)為正數(shù),負(fù)角的弧度數(shù)為負(fù)數(shù),零角的弧度數(shù)為零。這樣角的集合與實(shí)數(shù)集合的元素就建立起了“一一對(duì)應(yīng)”的關(guān)系。
總結(jié)
以上是生活随笔為你收集整理的robocode基本原理之坐标锁定的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: table 表格点击当前行按钮隐藏当前的
- 下一篇: github --- 多个项目的管理方