至于Math类库的使用,我们就不详细说明了。读者也可从下面的IBM Java专区链接中找到很多有关的知识,也可参考一些Java类库书籍说明。当你设计高级Robocode机器人时你会发现,Math类库是你不可缺少的一部分知识。此处我们只简单的介绍正弦函数及余弦函数的使用。
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 argument
Sin函数返回三角的正弦函数,参数a是一个以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 argument
Cos函数返回三角的余弦函数,参数a是一个以double类型的弧度表示的角值,返回类型为double.
有人会问为什么不使用ScanndeRobot事件中的getRadarHeadingRadians()方法直接得到弧度。哦,你来看看Robocode中华联盟iiley的一段说明:
public void onScannedRobot(ScannedRobotEvent event) {
enemyX=Math.sin(Util.standardMathDirRadians(getRadarHeadingRadians()))*event.getDistance();
enemyY=Math.cos(Util.standardMathDirRadians(getRadarHeadingRadians()))*event.getDistance();
}
看起来好像正确的,但是你实践一下会发现他很不准确,为什么呢?原因在于getRadarHeadingRadians()函数,当你调用此函数的时候实际上雷达已经不在刚刚扫描到敌人的那个角度了,他已经转过了十几度甚至更多。雷达默认转动速度是45度/robocode单位时间,实际上一般来说你用getRadarHeadingRadians()得到的值总是45度的整数倍。(一些情况除外,比如说你用了turnRadarLeft(11)类似的语句以后)。
Robocode也遵循数学应用中的基本法则用两种方法来表示方向的角度:角度制和弧度制,本文的代码及以前文章中的代码我们一直用的是角度制。另外一种方法就是利用ScannedRobotEvent.getBearingRadians()+robot.getHeadingRadians()得到敌人以弧度表示的方向,这个方法在本文章中没有说明了,有兴趣的朋友可以自己试试用Java.util 类库来实现. 也可参考文档"精确计算敌人的坐标"。大家也可比较两种方法各自特点,这将是个很有意思的过程。
移动锁定 当然,即使是最简单的机器人也不会坐在那一动不动等着你来消灭。它会躲避你的进攻以及扫描,当你向它原来坐标处开火,说不定它已经跑得老远了,当然这一切都不是我们所希望看到的。 我们的目的是要消灭它:不管他是移动或静止的。下面我们就结合方向系统与坐标系统,来锁定我们移动的目标。创造一个我们自己的高级扫描机器人。建议你在此处下载源代码(resource)并看看演示效果再回到我们的文章中来。显示如图5:
图5 对比一下上面的数据,不管目标GenyMove在哪GenyRadar都能得到它精确的坐标。是不是有一种成就感!是的,敌人已经完全在我们的掌握之中。即使它在移动中也无法摆脱我们雷达的扫描控制。这里只是很简单举了一些例子,GenyMove在每一个时间周期(有关时间周期的说明见的Rock 'em, sock 'em Robocode: Round 2)移动自己的位置并打印出移动后的坐标,而GenyRadar扫描系统不停的扫描目标,并一直追踪,同时打印出扫描到的GenyMove方位。关键部分在我们的ScannedRobotEvent事件如列表3
列表3:
public void onScannedRobot( ScannedRobotEvent e )
{
double heading = e.getBearing() +getHeading();
double distance = e.getDistance(); //求得距离
double ager_bearing = Math.toRadians(heading % 360); //角度转为弧度
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 ; //加大每一时间周期(tick)的扫描范围
if( radar_direction == RIGHT )
{
setTurnRadarRight( radar_degree );
execute();
}
else
{
setTurnRadarLeft( radar_degree );
execute();
}
我们在代码中首先求得GenyMove的绝对角度,然后用扫描时雷达的绝对角度减去目标GenyMove的角度求得两者的角度差也即我们雷达要旋转的角度。最后利用一个小技巧radar_degree = bearing * 1.3 使雷达在目标的范围左右摆动以扩大雷达扫描区域.这样不管目标往哪边移动都在自己的雷达扫描区内。
在此没有进行很详细的讲解了,我想凭你学到的方向及坐标知识很快能明白个中原理并设计出自己的高级扫描机器人来。 聪明的你可能会高兴的想,哈,我的炮管用相同的办法锁定目标,这样敌人不就没办法跑了,被我追着打。答案是错误的,雷达的扫描是条长线能直接定位到目标上 ,它到目标的时间差几乎为零,并且雷达的扫描范围比炮管大且精确。而炮管每时间周期只有20度,它定位目标是依靠着子弹,只有子弹打中了目标,才能说炮管的计算坐标是精确的。但是由于子弹 到达目标位置时需要一定的时间差,子弹本身又有速度值(20-3*power),所以要想炮管锁定目标并让子弹击中目标,我们还得经过精确的计算,并要预测目标可能的行动:是直线前进,还是做圆周运动,还是随机运动等等。 这些都是我们要充分考虑的因素。是不是很有挑战性!这一切都在Robocode的世界中等待着您的创造!
三角函数基础
下面我们只是很简单的介绍了一下与Robocode相关的三角函数知识,要想了解详细的,大家可从家中高中代数与几何书中得到这一切。
1.角的概念
在平面内,角可以看作一条射线绕着它的端点旋转而成的图形。如图,一条射线由原来的位置OA,绕着它的端点O按逆时方向旋转到另一位置OB,就形成角a.旋转开始时的射线OA叫做角a的始边,旋转终止时的射线OB叫做角a的终边,射线的端点O叫做角a的顶点。习惯上,我们把按逆时针方向旋转而成的角叫做正角;按顺时针方向旋转而成的角叫做负角.所有与a终边相同的角包括a在内,可以用式子表示:a+K*360度,对应到Robocode的方向系统中,只要我们以机器人的heading方向做射线,延长到与屏幕交点处的角度就是我们机器人的heading角度。
2.直角三角函数
在△ABC中,∠a为直角,我们把锐角A的对边与斜边的比叫做∠A的正弦,记作sina;锐角a的邻边与斜边的比叫做∠a的余弦,记作cosa,即
sina=对边BC/斜边AB
cosa=邻边AC/斜边AB
3.单位圆和三角函数线
半径为1的圆叫做单位圆。设单位圆的圆心与坐标原点重合,则单位圆与x轴的交点分为别为A(1,0)、A′(-1,0),与y轴的交点分别为B(0,1)、B′(0,-1)。设角a的顶点在圆心O,始点与x轴的正半轴重合,终边与单位圆相交于点P,过点P作PM垂直x轴于M,则由直角三角函数的定义可知:OM=cosa,MP=sina ,点P的坐标为(cosa,sina),即P(cosa,sina)。其中cosa=OM*1,sina=MP*1。Robocode中所有有关的坐标都可用这种方法求得。
4.弧度制
用度做单位来度量角的制度叫做角度制。数学和其他科学研究中常用另一种度量角的制度—弧度制。以角的顶点为圆心,以任意长的半径作圆把这个角所对的弧长与半径的比来衡量角的制度叫做弧度制.长度等于半径的弧长叫1弧度。这段弧所对的圆心角的大小也是1弧度。通常单位“弧度”省略不写。例:弧长为1.3325。单位就是弧度。由角度和弧度两种单位之间的关系得到:2π弧度=360度,2/3π弧度=270度,π弧度=180度,1/2π弧度=90度,并可推出1弧度 = 360度/2π = 57°即 1弧度=角度*1