通过游戏学java(适合初学者)
Robocode(⽤游戏来学习Java技术还是⽤Java来玩游戏?)
⽤你的JAVA编程技术来玩游戏吧!不会JAVA?那就⽤游戏来学习JAVA吧!
什么是Robocode?
其实我对机器⼈⼀直很感兴趣。我记得在我还是初中的时候,就知道 AplleⅡ上有⼀个程序,⽤它来编写简单的机器⼈程序,然后相互作战。当时⾃⼰还完全不懂编程,总是向往着,那神秘的编程⾼⼿玩的游戏是怎样的?
Robocode就是这样⼀个东西,但是更好⼀些。它是⼀个基于Java语⾔的机器⼈作战游戏。其代码的编写和建模都不错,玩起来也很有趣。Robocode是很多"编程游戏"软件中的⼀个,他们共同的特征是在没有⽤户输⼊的状态下许多机器⼈在⼀个及竞技场中⽐赛,⽤户必须编制⼀个⾼效的机器⼈来取胜。Robocode特别的像⼀场机器⼈坦克的⼤混战,它们互相开⽕直到只剩⼀个胜利者。程序完全是由JAVA编写,并且玩家必须要创造⼀个继承⾃Robot类的类。
你希望在玩游戏的过程中、在闪躲炮弹、执⾏精确攻击的演练中学会Java编程的继承、多态性、事件处理以及内部类这些内容
吗?Robocode 这个游戏为全世界的 Java 开发者实现这个愿望,它把游戏风潮变成了教学⼯具,⼈们对它的上瘾程度令⼈吃惊。下⾯,我参考⽹友 Sing Li 以前写的⽂章,让我们⼀起来拆解 Robocode,同时着⼿建造属于⾃⼰的、定制的、⼩⽽精悍的战⽃机器吧!
Robocode 是⼀个很容易使⽤的机器⼈战⽃仿真器,可以在所有⽀持 Java 2 的平台上运⾏。您创建⼀个机器⼈,把它放到战场上,然后让它同其他开发者们创建的机器⼈对⼿拼死战⽃到底。Robocode ⾥有⼀些预先做好的机器⼈对⼿让你⼊门,但⼀旦您不再需要它们,就可以把您⾃⼰创建的机器⼈加⼊到正在世界范围内形成的某个联盟⾥去和世界最强⼿对阵。
每个 Robocode 参加者都要利⽤ Java 语⾔元素创建他或她的机器⼈,这样就使从初学者到⾼级⿊客的⼴⼤开发者都可以参与这⼀娱乐活动。初级的 Java 的开发者们可以学习⼀些基础知识:调⽤ API 代码、阅读 Javadoc、继承、内部类、事件处理等等。⾼级开发者们可以在构建“最优品种”的软件机器⼈全球竞赛中提⾼他们的编程技巧。在本⽂中,我们将介绍 Robocode,并指导您从构建您平⽣第⼀个Robocode 机器⼈开始征服世界。我们还将看⼀下迷⼈的“后台”机制,正是它使得 Robocode 起作⽤。
⾸先当然是下载和安装 Robocode 啦
安装完成后,您也可以通过 shell 脚本(robocode.sh)、批处理⽂件(robocode.bat)或桌⾯上的图标来启动 Robocode 系统。此时,战场将会出现。在此,您可以通过菜单调⽤ Robot Editor 和 compiler。java零基础该怎么学
Robocode 系统组件
当您启动 Robocode 时,将看到两个GUI窗⼝,这两个窗⼝构成了 Robocode 的 IDE:
图 1. Robocode IDE
战场是机器⼈之间进⾏战⽃直⾄分出胜负的场地。主要的仿真引擎被置于其中,并且允许您在这⾥创建战⽃、保存战⽃以及打开新建的或现有的战⽃。通过界⾯区域内的控件,您可以暂停或继续战⽃、终⽌战⽃、消灭任何机器⼈个体或获取任何机器⼈的统计数据。此外,您可以在此屏幕上激活 Robot Editor。
Robot Editor 是⼀个定制的⽂本编辑器,它可以⽤于编辑⽣成机器⼈的 Java 源⽂件。在它的菜单⾥集成了 Java 编译器(⽤于编译机器⼈代码)以及定制的 Robot 打包器。由 Robot Editor 创建并成功编译的所有机器⼈都会处于战场上⼀个部署就绪的位置。
Robocode ⾥的每个机器⼈都由⼀个或多个 Java 类构成。这些类可以被压缩成⼀个 JAR 包。为此,Robocode 的最新版本提供了⼀个可以在战场 GUI 窗⼝中激活的“Robot Packager”。
对 Robocode 机器⼈的详细分析
在写这篇⽂章时,Robocode 机器⼈是⼀个图形化的坦克。图 2 是⼀个典型的 Robocode 机器⼈的图解。
图 2. 对 Robocode 机器⼈的详细分析
请注意,机器⼈有⼀门可以旋转的炮,炮上⾯的雷达也是可以旋转的。机器⼈坦克车(Vehicle)、炮(Gun)以及雷达(Radar)都可以单独旋转,也就是说,在任何时刻,机器⼈坦克车、炮以及雷达都可以转向不同的⽅向。缺省情况下,这些⽅向是⼀致的,都指向坦克车运动的⽅向。
我们先不考虑怎么编程来实现机器⼈战⽃,我们先⽤⾃带的例⼦机器⼈来⼀场战⽃吧
单击菜单上的Battle,然后选New,出现了New Battle对话框
图 3. New Battle 对话框
左边的框是Packages,相当于⼀个⽂件夹,⾥⾯包含多个Robots(机器⼈)
我们选择sample这个包,⾥⾯有Corners、Crazy、Fire等等很多例⼦的机器⼈了
随便选择⼏个你喜欢的,然后按Add添加到Selected Robots框,进了这个框就是准备要上战场的机器⼈了~选择好后,按 StartBattle 开战吧!
现在你已经知道怎样可以使⽤机器⼈去战⽃并且也构建好你的战场了,好,下⾯我们学习怎样来编写属于⾃⼰的战⽃机器⼈!!
战场是机器⼈之间进⾏战⽃直⾄分出胜负的场地。主要的仿真引擎被置于其中,并且允许在这⾥创建战⽃、保存战⽃以及打开新建的或现有的战⽃。通过界⾯区域内的控件,可以暂停或继续战⽃、终⽌战⽃、消灭任何机器⼈个体或获取任何机器⼈的统计数据。此外,我们可以在此屏幕上的Robot菜单打开 Editor,就是我们机器⼈的代码编辑器了!Robot Editor 是⼀个定制的⽂本编辑器,它可以⽤于编辑⽣成机器⼈的 Java 源⽂件。在它的菜单⾥集成了 Java 编译器(⽤于编译机器⼈代码)以及定制的 Robot 打包器。由 Robot Editor 创建并成功编译的所有机器⼈都会处于战场上⼀个部署就绪的位置。我们就是要在这⾥编写机器⼈了。
选择“File”》“New”》“Robot”来新建⼀个机器⼈。它会⾸先要你输⼊这个机器⼈的名字(注意名字⾸字母要⼤写哦),然后要你输⼊包的名字(就是保存这个机器⼈的⽂件夹名称),这样就⽣成了⼀个蠢蠢的机器⼈XForce的代码了~因为我们还没替它加上⼈⼯智能,呵呵!
现在单击菜单的Complie下的Complie进⾏编译,保存好,我们的机器⼈已经⽣产出来咯~
现在关闭Editor,在进⼊New Battle,Pakeage下选择你刚才的包的名字,Robot下就有了我们新建的XForce机器⼈了~添加进去吧,然后选择多⼏个其他的机器⼈,开始战⽃!
看~我们的XForce在战⽃了!
是否觉得它太蠢了点呢?来,继续来学习~~
Robocode 机器⼈是⼀个图形化的坦克,请注意,机器⼈有⼀门可以旋转的炮,炮上⾯的雷达也是可以旋转的。机器⼈坦克车(Vehicle)、炮(Gun)以及雷达(Radar)都可以单独旋转,也就是说,在任何时刻,机器⼈坦克车、炮以及雷达都可以转向不同的⽅向。缺省情况下,这些⽅向是⼀致的,都指向坦克车运动的⽅向。
附:Robot 命令
  Robocode 机器⼈的命令集都收录在 Robocode API Javadoc 中。这些命令都是 robocode.Robot 类的公共⽅法。
(1)移动机器⼈、炮和雷达
  移动机器⼈及其装备的基本命令:
turnRight(double degree) 和 turnLeft(double degree) 使机器⼈转过⼀个指定的⾓度。
ahead(double distance) 和 back(double distance) 使机器⼈移动指定的像素点距离;这两个⽅法在机器⼈碰到墙或另外⼀个机器⼈时即告完成。
turnGunRight(double degree) 和 turnGunLeft(double degree) 使炮可以独⽴于坦克车的⽅向转动。
turnRadarRight(double degree) 和 turnRadarLeft(double degree) 使炮上⾯的雷达转动,转动的⽅向也独⽴于炮的⽅向(以及坦克车的⽅向)。
  这些命令都是在执⾏完毕后才把控制权交还给程序。此外,转动坦克车的时候,除⾮通过调⽤下列⽅法分别指明炮(和雷达)的⽅向,否则炮(和雷达)的指向也将移动。
setAdjustGunForRobotTurn(boolean flag):如果 flag 被设置成 true,那么坦克车转动时,炮保持原来的⽅向。
setAdjustRadarForRobotTurn(boolean flag):如果 flag 被设置成 true,那么坦克车(和炮)转动时,雷达会保持原来的⽅向。
setAdjustRadarForGunTurn(boolean flag):如果 flag 被设置成 true,那么炮转动时,雷达会保持原来的⽅向。⽽且,它执⾏的动作如同调⽤了
setAdjustRadarForRobotTurn(true)。
(2)获取关于机器⼈的信息
getX() 和 getY() 可以捕捉到机器⼈当前的坐标。
getHeading()、getGunHeading() 和 getRadarHeading() 可以得出坦克车、炮或雷达当前的⽅向,该⽅向是以⾓度表⽰的。
getBattleFieldWidth() 和 getBattleFieldHeight() 可以得到当前这⼀回合的战场尺⼨。
(3)射击命令
  ⼀旦掌握了移动机器⼈以及相关的武器装备的⽅法,我们就该考虑射击和控制损害的任务了。每个机器⼈在开始时都有⼀个缺省的“能量级别”,当它的能量级别减⼩到零的时候,我们就认为这个机器⼈已经被消灭了。射击的时候,机器⼈最多可以⽤掉三个能量单位。提供给炮弹的能量越多,对⽬标机器⼈所造成的损害也就越⼤。 fire(double power) 和 fireBullet(double power) ⽤来发射指定能量(⽕⼒)的炮弹。调⽤的 fireBullet() 版本返回 robocode.Bullet 对象的⼀个引⽤,该引⽤可以⽤于⾼级机器⼈。(也就是说,当你确定能击中对⽅,⽕⼒越⼤越好咯^_^)
(4)事件
  每当机器⼈在移动或转动时,雷达⼀直处于激活状态,如果雷达检测到有机器⼈在它的范围内,就会触发⼀个事件。作为机器⼈创建者,我们有权选择处理可能在战⽃中发⽣的各类事件。基本的 Robot 类中包括了所有这些事件的缺省处理程序。但是,们可以覆盖其中任何⼀个“什么也不做的”缺省处理程序,然后实现⾃⼰的定制⾏为。下⾯是⼀些较为常⽤的事件:
ScannedRobotEvent。通过覆盖 onScannedRobot() ⽅法来处理 ScannedRobotEvent;当雷达检测到机器⼈时,就调⽤该⽅法。
HitByBulletEvent。通过覆盖 onHitByBullet() ⽅法来处理 HitByBulletEvent;当机器⼈被炮弹击中时,就调⽤该⽅法。
HitRobotEvent。通过覆盖 onHitRobot() ⽅法来处理 HitRobotEvent;当您的机器⼈击中另外⼀个机器⼈时,就调⽤该⽅法。
HitWallEvent。通过覆盖 onHitWall() ⽅法来处理 HitWallEvent;当您的机器⼈撞到墙时,就调⽤该⽅法。
很多研究Robocode的玩家都被其中的⽅向及坐标弄糊涂了。整个屏幕哪个是0度⾓,整个是坐标原点呢?顺时针与逆时针的⽅向如何区分?
⼀段英⽂的翻译及说明:
heading - absolute angle in degrees with 0 facing up the screen, positive clockwise. 0 <= heading < 360.
bearing - relative angle to some object from your robots heading, positive clockwise. -180 < bearing <= 180
heading:是机器⼈⽅向与屏幕正上⽅的⾓度差,⽅向在0到360之间.
bearing:是机器⼈的某个部件如雷达发现的⽬标与⽅向的⾓度差,顺时针为正⾓度在-180到180之间
⼏个在Robocode中很重要的概念:
坐标系:Robocode整个坐标系都是战场屏幕以左下⾓为原点
绝对⽅向系:Robocode中不管机器⼈在哪个⽅向都是以静态战场屏幕为参照的绝对⾓度(也即⼤家说的Heading),正上⽅为0度⾓。也即不管是Robot,Gun,Radar向北为0,向东为90,向南为180,向西为270。
相对⽅向系:相对⽅向是Robot,Gun,Radar以机器⼈的动态heading⾓度为参照的⾓度差不再以整个静态屏幕为参照了,叫它相对因为机器⼈的heading是随着机器⼈移动⽽不停的在改变,heaing只是个相对物体。
顺时针和逆时针是看另⼀机器⼈是在你的Heading⾓度的(0,180)还是(-180,0)之间。
  再次提醒:Heading是个静态⾓度,正上⽅总为0.不管是取Heading,还是取⽅向。Bearing是个⾓度差值,是由参照的Heading和发现时的Heading的差值。⽅向的问题就说到这,欢迎⼤家讨论。
我看了Robocode的基础知识,⾃⼰写了个bot,放到BattleField上却是屡战屡败……伤⼼ing。
  Bot对于周围环境的了解⾮常有限。它可以知道其它机器⼈的距离、⽅位、⽅向、速度和能量等级。但是,它看不到⼦弹。怎么才可以有效的躲避对⽅的⼦弹呢?
  Bot虽然看不到⼦弹,但是对⽅的能量等级还是可以scan到了。对⽅只要发射⼦弹就会耗损能量,并且耗损的能量介于0和3之间。根据这些线索,如何发现其它机器⼈正向它开炮对于“笨笨”的Bot不就易如反掌了? ^_^
  当Bot检测到对⽅发射⼦弹的信息时,向左或向右移动⼀⼩步,嘿嘿,⼦弹就打不到咯~并且⼤多数Bot的瞄准⽅法是要么直接向⽬标开炮,要么试着根据Bot的速度和⽅向来推算位置。如果我的Bot不移动,两种算法都会正好冲着这个Bot的当前位置开炮。哈哈哈,这时我的Bot再移动,不就全部都打不到啦。(是不是颇有武侠⼩说⾥以静制动的⾼⼿味道?^_^)
  下⾯是部分代码和注释:
double previousEnergy = 100; //初始状态对⽅能量为100
int movementDirection = 1; //移动⽅向
int gunDirection = 1; //炮管⽅向
/**
* 当检测到对⽅Bot,触发事件
* @param e
*/
public void onScannedRobot(ScannedRobotEvent e) {
//调整⾃⼰和对⽅之间的⾓度
Bearing()+90-30*movementDirection);
//如果对⽅的能量损耗⼀定值,进⾏躲避动作
double changeInEnergy = previousEnergy - e.getEnergy();
if (changeInEnergy>0 && changeInEnergy<=3) {
//躲避!
movementDirection = -movementDirection; //和上次的躲避⽅向相反
setAhead((e.getDistance()/4+25)*movementDirection);
}
//将炮管指向对⽅当前位置
gunDirection = -gunDirection;
setTurnGunRight(99999*gunDirection);
//射击
fire(1);
//重新设置对⽅能量
previousEnergy = e.getEnergy();
}
  是不是很简单?这个技巧还存在问题。⼦弹⼀发射,我的Bot就移动,所以它最终可能会移回炮弹轨迹之内。最好是在估计⼦弹要到达时再移动。
  我有个更⼤胆的假设:因为现在我的Bot命中率还不⾼,那么如果我的Bot⼀直不开⽕,只是躲避对⽅的⼦弹的话,能不能拖到对⽅的能量为0呢?确实存在⼀点问题。对⽅⼦弹⼀发射,我的Bot就移动,并且这个移动是规律的来回移动。如果移动距离短了,就可能在回来的时候撞到对⽅的⼦弹;如果移动距离长了,就等于做⼀个直线运动,对⽅很容易计算得到Bot的运动轨迹。还有⼀个问题,躲避的时候很有可能撞到墙上……(撞墙是要减energy的:~()
  针对以上的问题,我另写了⼀个Bot。代码如下:
import robocode.*;
public class HanicBot extends AdvancedRobot{
private double eDist; //对⽅的距离
private double move; //移动的距离
private double radarMove = 45; //雷达移动的⾓度
private double dFirePower; //⽕⼒
/**
* main func run()
*/
public void run() {
eDist = 300;
while(true){
//每过⼀个周期,运动随机的距离
double period = 4*((int)(eDist/80)); //周期;敌⼈越接近,周期越短,移动越频繁//周期开始,则移动
if(getTime()%period == 0){
move = (Math.random()*2-1)*(period*8 - 25);
setAhead(move + ((move >= 0) ? 25: -25));
}
//避免撞墙
double heading = getHeadingRadians(); //取得bot⽅向的弧度数
double x = getX() + move*Math.sin(heading); //移动move后将要达到的x坐标double y = getY() + s(heading); //移动move后将要达到的y坐标double dWidth = getBattleFieldWidth(); //战场的宽度
double dHeight = getBattleFieldHeight(); //战场的长度
//当(x,y)超过指定的范围,则反向移动move
if(x < 30 || x > dWidth-30 || y < 30 || y > dHeight-30){
setBack(move);
}
turnRadarLeft(radarMove); //转动雷达
}
}//end run()

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。