如何写贪吃蛇Word下载.docx
《如何写贪吃蛇Word下载.docx》由会员分享,可在线阅读,更多相关《如何写贪吃蛇Word下载.docx(13页珍藏版)》请在冰豆网上搜索。
publicCell(intx,inty){//(这里是构造器,构造方法)
publicStringtoString(){//重写toString(),以便测试用
return"
["
+x+"
"
+y+"
]"
;
}
然后我们再设置一条蛇类Worm{},在这个类中实例化一个数组,规定数组的长度也就是蛇的长度。
如下:
publicclassWorm{
publicstaticfinalintSNAKE_LENGHT=16;
privateCell[]cells;
publicWorm(){
cells=newCell[SNAKE_LENGHT];
for(inti=0;
i<
cells.length;
i++){
cells[i]=newCell(i,0);
}
上面直接在构造方法中建立了一条蛇,当然,蛇一开始的位置是由自己设定的,我用for循环设定蛇的所有点的y坐标为0,横坐标为i(0~15),所以我的蛇一旦被实例化,就会首先出现在第一行中,并且它的头部是点cells[0]->
(0,0).
至此,我们的数据结构这方面基本算是搞定,这样我们就把现实世界中的实体抽象为我们的数据了,而接下来就是如何操作我们的数据,使得按我们预想的发展。
所以,继续来看下在贪吃蛇运行过程中的算法。
根据需要,比如,贪吃蛇的走一步,我们就要设置一个方法,用来实现走一步这个功能,爬的英文是creep,所以我们设定这个方法为publicvoidcreep(){},这个方法用来实现蛇走一步后数据(位置)怎么变化,然后我们还需要一个判断是够撞墙或者撞到自己的方法:
publicbooleanhit(){},设定返回值类型是布尔型(trueorfalse),这样我们一调用这个方法,就能根据返回值判断是够撞到。
当然,一个类中是不止这几个方法的,根据需要自己设定就可以了,比如我们在做到一半要测试我们的方法是够能够正确执行,要输出一些信息,就可以重写toString()方法,然后这样:
publicStringtoString(){
returnArrays.toString(cells);
比如要测试creep()是够真的走了一步,在调用creep()前后各调用一次toString()方法来输出蛇的位置(数组cells),以便判断。
好了,搞定了数据结构,设定了方法,那么如何实现呢?
这就要用到我们所说的算法了,怎么去实现走一步,怎么去判断是够撞到,这个过程就是把这些方法的方法体写出来。
在开始写方法体之前,先来统筹兼顾我们方法中将会用到的各种属性,然后统一设定。
比如你用creep方法,那首先得有个方向啊,到底是向哪个方向爬呢,所以我们可以这样,设定creep()的参数为intdirection,也就是creep(intdirection),也就是说要想爬先传给我一个方向参数,那如果是按当前方向爬呢,我们就设一个privateintcurrentDirection;
作为当前的默认方向,如果没有键盘来改变方向就按当前方向继续(current->
当前的)。
然后我们给每个方向设一个常量,这样到时候可以用switch(){}来匹配,并且上下之和为5,左右之和也为5,这样设的好处是到时候如果你传递进来的方向参数和当前一相加如果是5,就说明这个参数无效(与当前方向相反):
publicstaticfinalintUP=4;
publicstaticfinalintLEFT=3;
publicstaticfinalintRIGHT=2;
publicstaticfinalintDOWN=1;
万事俱备,来写第一个方法,爬一步,头节点的位置变成了新的那个点,后面的所以点都向前移动一步,所以:
publicvoidcreep(intdirection){
if(direction+currentDirection==5)
return;
//判定出为相反方向,直接不做任何动作
for(inti=cells.length-1;
i>
=1;
i--){
cells[i]=cells[i-1];
//前进一位
//下面是计算头节点的新位置
intx=cells[0].getX();
inty=cells[0].getY();
switch(direction){
caseDOWN:
y++;
break;
caseRIGHT:
x++;
caseLEFT:
x--;
caseUP:
y--;
cells[0]=newCell(x,y);
//把新位置赋值给头节点
可以想到,上面计算头节点的算法在判断是够撞到蛇身或墙壁的方法中也需要,我们最好把他独立出来作为一个新方法并封装在类内部即可,如下:
privateCellcreateHead(intdirection){
returnnewCell(x,y);
这样无论哪个方法想要知道新头节点的位置只要调用这个方法并给一个方向参数即可,那么我们的爬行方法creep()也就简单多了,改为:
cells[0]=createHead(direction);
//直接用这个方法获取头节点
还要增加一个蛇自己走得方法,也就是不给参数,按当前方向currentDIrection走的,改一下,如下:
publicvoidcreep(){
cells[0]=createHead(currentDirection);
//currentDirection是全局变量,一个实例从头到尾都存在的,没有被改变
//自己的值就不会变也不会消失
然后,我们再增加一个有改变方向和提供食物的爬行方法:
publicbooleancreep(intdirection,Cellfood){
if(currentDirection+direction==5){
returnfalse;
currentDirection=direction;
//把改变的方向作为新的当前方向
Cellhead=createHead(direction);
booleaneat=(head.getX()==food.getX()&
&
head.getY()==food.getY());
//如果和food重合,就代表吃了food
if(eat){
//Arrays.copuOf()是刷新数组的长度,第二个参数自己
//规定长度,这里我们让长度+1(前面的内容保持不变的)
cells=Arrays.copyOf(cells,cells.length+1);
for(inti=cells.length-1;
i>
=1;
i--){
cells[i]=cells[i-1];
cells[0]=createHead(currentDirection);
returneat;
//之所以要返回是否吃了,是因为吃了的话要新new一个点food出来
同样的,我们创建撞击方法的时候也是判断新出来头节点是够在蛇身上或者在墙壁上,有重合就返回true值,就不一一说了,直接上代码:
publicbooleanhit(intdirection){
if(currentDirection+direction==0){
if(head.getX()<
0||head.getX()>
=WormStage.COLS||
head.getY()<
0||head.getY()>
=WormStage.ROWS){
returntrue;
//撞四周的墙了
for(inti=0;
i<
cells.length-1;
i++){
Cellnode=cells[i];
if(node.getX()==head.getX()&
node.getY()==head.getY()){
returntrue;
//撞到蛇身体了
}
returnfalse;
//都没有撞到?
好吧,返回没有撞到
再加个没有改变方向的hit()方法:
publicbooleanhit(){
returnhit(currentDirection);
//调用上面方法,参数是当前方向
//然后就实现了方向不变的hit判断
顺便一起说了,等下在面板中显示的时候,食物food被吃了之后,是要food=newCell(x,y)的,但是如果new出来的food在蛇身上,就要重新new一次吧,在这里先弄个判断这个食物是否在蛇身上的方法:
publicbooleancontains(intx,inty){
for(Cellc:
cells){
if(c.getX()==x&
c.getY()==y)
或者你直接把food当做参数也行,内容改改就是了。
另外,在面板显示蛇的身体的时候要用到数组cells,但是它是private怎么办,用get方法复制一个出来:
publicCell[]getCells(){
//copy一个相同长度相同内容的cells作为返回值
returnArrays.copyOf(cells,cells.length);
好了,到此,想象一下,如果我们在刚才那个面板的坐标中按照坐标点显示出来,是不是就有一条蛇和一个食物点了,然后调用用时间函数来定时运行creep方法是不是蛇就爬起来了,然后每爬一步之前就用hit来判断撞击,是否就能实现整个贪吃蛇的游戏了?
好哒,让我们来做出来吧!
!
首先说下java.awt,是一个图形化界面的,包含了很多用来图形化的类,然后在javax.swing包中另外有一大堆的类,这些类多继承于java.awt中的类,也就是实现同样的图形的东西,swing也自成了自己的一个小体系,所以接下来直接引入swing包,用里面的类JFrame(窗框),JPanel(面板),然后使用窗框和面板的方法去绘图paint()。
还有一个时间控制类Timer和Timertask,都在java.util包里头,在IDE工具里可以看到Timer类的实例要求四个参数,
Timertimer=newTimer();
//新建实例
timer.schedule(Timertasktask,firstTime,period);
schedule()方法第一个参数是传一个任务进来,第二个参数是隔多久开始运行这个任务,第三个参数表示多久执行一次。
Timertask是一个抽象类,里面有个publicabstractvoidrun();
,表示必须要在这个任务里重写run()方法,方法体就是你要执行的任务的内容。
publicclass面板extendsJPanel{
//这里等下要插入一些用到的方法,先做个标志
publicstaticvoidmain(String[]args){
//执行main方法,开始
JFrameframe=newJFrame("
贪吃蛇"
);
面板pane=new面板();
frame.setLayout(null);
//取消窗口的默认布局
frame.add(pane);
//在把pane面板置于frame窗框里头
pane.setSize(30,30);
//面板大小
pane.setLocation(4,4);
//pane(左上角坐标)
//在frame的位置
frame.setSize(40,48);
//窗框大小
frame.setLocationRelativeTo(null);
//中间显示窗框
frame.setResizable(false);
//设置不可拖放大小
frame.setVisible(true);
//设这个窗框变为可见
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//close窗框就退出程序
pane.action();
//这个方法是用来
//开始执行面板pane的绘制,等下重写
以上创建类“面板”,继承于JPanel,那么这个类就应可以重写JPanel类的方法paint(),等下把重写的方法放进去面板类的标志处:
//重写JPanel的paint方法
publicvoidpaint(Graphicsg){
g.setColor(Color.RED);
g.fill3DRect(food.getX()*1,food.getY()*1,
1,1,true);
Cell[]cells=worm.getCells();
g.setColor(Color.BLUE);
g.fill3DRect(cells[i].getX(),
cells[i].getY(),1,1,true);
}}
解析下,Graphicsg是给paint方法创建了一个绘图工具,g有很多方法,比如g.setColor(GREEN)就是把绘笔设置为绿色,接下来就g.fillRect(x,y,width,height)就是绘制一个矩形区域,起始坐标也就是矩形的左上角是(x,y),矩形宽和高是width和height,对此,fill3DRect的区别是增加了一个参数,true表示凸的3D效果,false表示凹的效果以上用的是3D而不是平面效果绘制的食物和蛇。
继续,在面板类的标志处插入重写的action方法:
publicvoidaction(){
Timertimer=newTimer();
timer.schedule(Timertasktask,0,200);
其实task还没实例化,我们直接用内部类替换task部分,同时重写run方法:
然后摇身一变成了下面的样子:
(注意内部类的task实例化部分很长)
timer.schedule(newTimerTask(){
publicvoidrun(){
if(worm.hit()){
worm=newWorm();
food=createFood();
}else{
booleaneat=worm.creep(food);
if(eat){
food=createFood();
}
}
repaint();
},0,200);
repaint()方法是重新调用paint重新绘制蛇的位置和食物的位置,整体起来就是隔200mscreep()一下,然后paint一下,这样蛇和食物才能更新。
我们看到上面调用了createFood方法,就是以上我们说过的创建一个食物的方法,来写一下:
privateCellcreateFood(){
intx;
inty;
Randomr=newRandom();
do{
x=r.nextInt(29);
y=r.nextInt(29);
}while(worm.contains(x,y));
Random方法是java.util包下的随机返回一个数字方法,方法中的29表示范围在0到29之间,因为食物的出现要在面板内,然后用contains()方法判断直到不出现在蛇身上。
上面action()方法中只是用到了定时timer来爬行,如果需要键盘控制,还要在action(){}里头增加一个键盘监听事件,实现除了timer能控制蛇走之外,还能键盘控制方向:
方法如下:
先:
importjava.awt.event.KeyAdapter;
importjava.awt.event.KeyEvent;
//键盘事件
然后:
this.requestFocus();
this.addKeyListener(newKeyAdapter(){
publicvoidkeyPressed(KeyEvente){
intkey=e.getKeyCode();
switch(key){
caseKeyEvent.VK_UP:
creepTo(Worm.UP);
caseKeyEvent.VK_DOWN:
creepTo(Worm.DOWN);
caseKeyEvent.VK_LEFT:
creepTo(Worm.LEFT);
caseKeyEvent.VK_RIGHT:
creepTo(Worm.RIGHT);
});
this.requestFocus()表示当前对象要求一个焦点,this.addKeyListener(键盘事件类实例),KeyEvent键盘事件实例化成为e,用e调用getKeyCode方法获取输入键的int值,然后用switch匹配上下左右,匹配之后调用creepTo方法,跟时间间隔那个很像,只是改变方向而已,下面在面板类中增加这个方法给于调用:
privatevoidcreepTo(intdirection){
if(worm.hit(direction)){
worm=newWorm();
food=createFood();
}else{
booleaneat=worm.creep(direction,food);
if(eat){
food=createFood();
最后,给这个面板类增加一个构造方法:
public面板(){
worm=newWorm();
food=createFood();
至此,一个贪吃蛇的代码书写完毕,创建了三个publicclass,分别是Cell(soeasy),Worm(包含自己的行为方法),面板(自己改名字吧),整个实现过程理解清楚后,我可能还有漏讲一些地方或者缺少一些变量和方法,需要的自己动手添加,也可以提高自己能力,还有上面一个点是一个像素吧,如果想要显示大一点,每个点乘以一个长度即可,然后宽和高都改成这个长度。
贪吃蛇的实现有很多种,方法也不是固定的,以上作为一种参考,如果有更为简便的方式或者更加优异的实现方式。
思想先行,代码随后。
以上。