新手篇:贪吃蛇的编程与代码(Java)年轻的小荣子你要首先懂一些java的基础,就是懂基础就可以了。程序=数据结构+算法;算法考虑通过给定的条件计算出想要的结果,算法首先是要能达到结果,然后在考虑优劣,算法的准确稳定性,是否够优化等。数据结构是算法运算的前提,首先要得先有数据,才能运行算法。而采用什么样的数据结构,也会影响到算法是够容易实现。那么,接下来分析下贪吃蛇应该采用什么样的数据结构:可以看到,贪吃的界面就是一个窗口框,窗口里头包括了一个显示板,用显示板来显示一条蛇和食物,从表面上来看,窗口、显示板、蛇、食物都是内部数据的显示,这里只要会把数据关联到面板就可以了,都不是难事。那么应该采用什么样的数据结构呢?我直接说吧,从显示来看,一个食物就是一个点,一条蛇就是由多个节点连接成的一条线,那么如果我们把这个食物的数据结构设成一个有横纵坐标的点,蛇就是由多个这样的点组成的“数组”,这样我们就完成了数据结构的选定,然后我们可以这样设计各个类:用一个类Cell{}作为点的抽象模型,到时候实例化出来就是一个实在的点了,而一条蛇就是多个点的组合,也就是一个数据Cell[],蛇的长度就是数组的长度,这样,每个点都有自己的坐标x和y,到时候直接打印到面板上就可以了,面板其实就是相当于一个坐标系:所以可以定义一个类Cell:(封装x,y,用get、set方法取值)publicclassCell{privateintx;privateinty;publicintgetX(){returnx;}publicvoidsetX(intx){this.x=x;}publicintgetY(){returny;}publicvoidsetY(inty){this.y=y;}publicCell(intx,inty){//(这里是构造器,构造方法)this.x=x;this.y=y;}publicStringtoString(){//重写toString(),以便测试用return[+x+,+y+];}}然后我们再设置一条蛇类Worm{},在这个类中实例化一个数组,规定数组的长度也就是蛇的长度。如下:publicclassWorm{publicstaticfinalintSNAKE_LENGHT=16;privateCell[]cells;publicWorm(){cells=newCell[SNAKE_LENGHT];for(inti=0;icells.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++;break;caseLEFT:x--;break;caseUP:y--;break;}cells[0]=newCell(x,y);//把新位置赋值给头节点}可以想到,上面计算头节点的算法在判断是够撞到蛇身或墙壁的方法中也需要,我们最好把他独立出来作为一个新方法并封装在类内部即可,如下:privateCellcreateHead(intdirection){intx=cells[0].getX();inty=cells[0].getY();switch(direction){caseDOWN:y++;break;caseRIGHT:x++;break;caseLEFT:x--;break;caseUP:y--;break;}returnnewCell(x,y);}这样无论哪个方法想要知道新头节点的位置只要调用这个方法并给一个方向参数即可,那么我们的爬行方法creep()也就简单多了,改为:publicvoidcreep(intdirection){if(direction+currentDirection==5)return;//判定出为相反方向,直接不做任何动作for(inti=cells.length-1;i=1;i--){cells[i]=cells[i-1];//前进一位}cells[0]=createHead(direction);//直接用这个方法获取头节点}还要增加一个蛇自己走得方法,也就是不给参数,按当前方向currentDIrection走的,改一下,如下:publicvoidcreep(){for(inti=cells.length-1;i=1;i--){cells[i]=cells[i-1];//前进一位}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重合,就代表吃了foodif(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){returnfalse;}Cellhead=createHead(direction);if(head.getX()0||head.getX()=WormStage.COLS||head.getY()0||head.getY()=WormStage.ROWS){returntrue;//撞四周的墙了}for(inti=0;icells.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)returntrue;}returnfalse;}或者你直接把food当做参数也行,内容改改就是了。另外,在面板显示蛇的身体的时候要用到数组cells,但是它是private怎么办,用get方法复制一个出来:publicCell[]getCells(){//copy一个相同长度相同内容的cells作为返回值returnArrays.copyOf(cells,cells.length);}好了,到此,想象一下,如果我们在刚才那个面板的坐标中按照坐标点显示出来,是不是就有一条蛇和一个食物点了,然后调用用时间函数来定时运行creep方法是不是蛇就爬起来了,然后每爬一步之前就用hit来判断撞击,是否就能实现整个贪吃蛇的游戏了?好哒,让我们来做出来吧!!首先说下java.awt,是一个图形化界面的,包含了很多用来图形化的类,然后在javax.swing包中另外有一大堆的类,这些类多继承于java.awt中的类,也就