Java之贪吃蛇游戏的开发
2017-05-30 22:51
267 查看
额,端午三天假,一堆的作业,但手贱的我并没有兴趣去写什么高数作业,而是写了一个贪吃蛇游戏。界面版的,扩展性比较好,地图可以按自己的喜欢去重新做(有关扩展性后面会说),我只写了两个简单的地图。因为代码比较长(400行左右,不连扩展的地图),所以下面我只说思路做法,必要时我会以代码为示例,不在粘全部代码。
好了,先来看看,这个游戏的截图。(滑稽)
这里可以自定义难度系数(其实就是蛇自己移动的速度),共分10级。这里后面我会说实现方法,这都可以改的。
这里是初始的状态,食物是随机生成的,蛇位于地图的中央。其中灰色代表的是石头,白色是空地,蓝色是食物,红色是蛇头,绿色是蛇身。
这里,你可以用键盘的方向键来控制蛇的移动,但遇到上面俩种情况(蛇撞墙或蛇咬到自己)就会死亡。
这是一个扩展的地图的例子。
好了下面说实现的方法:
首先,我们知道java是面向对象编程方法,所以实现过程也肯定是面向对象的。其次,我们要明白“数据”与“界面”分离的思想,做界面不是难事,只要用的熟练就好,难的是核心的算法的实现。所以看到贪吃蛇这个游戏,我们首先想的是他的业务逻辑是什么,而不是这界面是咋做的,其实说真的,我的代码里,有关做图形界面的代码只有50行左右,而全部代码是400行左右,可见做界面之占1/8的任务量。
第一步:
我们先分析,在这个游戏里我们看到了那些对象或者说是变量。
1、地图 2、蛇 3、食物 (你可能还会说“石头”,但石头其实包含在地图里,因为地图就是由石头和空地组成的呀)(滑稽)
第二步:
下面我们一个一个的说:
1、地图如何实现?
首先,想用什么来储存地图的信息,呐,我们可以这样,地图由石头和空地组成,那么我们可以用字符 * 来表示石头,用空字符(就是一个空格键)来表示空地 ,这样我们就可以用char型的数组来存储地图。
然后就是,如何初始化地图,我们可以写一个intiMap()的方法来初始化地图(具体看代码,这里只粘部分代码,关键是思想)
2、蛇如何实现?
蛇分为蛇头和蛇身,我们可以用字符 表示蛇头,用字符 # 表示蛇身。呐,是不是我们就也用字符数组来储存蛇,NO,蛇是会动的,蛇移动时蛇头或蛇身的字符是不变的。用 和 # 只是为了表示蛇,但不能用来储存蛇的信息。我们可以这样,蛇是在地图上动的,如果地图有坐标,我们就可以用坐标来储存蛇的每一个状态。因为一条蛇是由多个节点组成的,即由多个坐标组成,所以我们可以用链表容器来存储Point类型的节点。
呐,我们接下来就是初始化蛇,这个好办,因为初始的时候蛇是静止的,在最中央。写个方法就OK了。
难的来了,就是蛇的移动,我们如何让蛇在地图上移动?
其实也不难,蛇的移动我们可以看作链表添加节点和删除节点的操作,什莫意思呐,就是如果我们给蛇添加一个头,再删一个尾,不就相当于蛇走了吗。
因为 蛇有四个方向,所以可以定义四个常量表示四个方向(看代码,这里的代码是截取的,所以会用一些整体的方法,还是主要看思想,不用太在意代码)。
3、食物如何实现?
这个相比蛇就很容易了,首先食物虽然不会动,但他是随机在地图中生成的,所以我们用Point类,即用坐标来储存食物信息,并用 字符@来表示食物。
初始化食物,我们要明白一点,食物既不能长在墙上也不能长在蛇身上,所以在初始化食物的时候,要避开这些位置。并且,还有一点食物是随机的,这个好办,产生随机数就好了。(看代码)
第三步:
我们在上面已经定义好了,地图、蛇、食物,三个事物,并一一初始化。下面是如何讲这三样事物之间起建立联系。这个很重要,就是如何把蛇的信息、把食物的信息反馈给地图,以实现互动。
我们可以通过两个函数,showSnake() 和 showFood() 来实现,如何做呐?
其实,不难,我们只要将蛇 或 食物 的位置即坐标 反馈给地图就好了呀,然后让地图去显示他们就好了。
第四步:
好了,现在你的蛇和食物,可以在地图上显示了,并且蛇也可以在地图上自由的动了。但蛇还吃不了食物呀,还有蛇不会死呀。
如何让蛇吃到食物呐,我们这样,我们写个函数eatFood()返回值为boolean ,当蛇头的坐标和食物重合是返回真,否则假。然后,在第二步中的蛇的移动函数move()中判断eatFood(), 如果吃到了则就步删除尾结点了,不就好了吗。这样蛇吃了食物并且变长了。美滋滋(滑稽)
还有咋判断蛇死了? 我们还是写一个函数叫 Isover() ,在定义Game Over的布尔常量,当蛇头对应坐标下的地图坐标的值为 字符 * 或 # 时 ,就说明蛇撞墙了 或 咬到自己了 。然后在主函数中判断Game Over的真假,一旦为真,就结束。
哈,有了以上四步,一个贪吃蛇的雏形就大概好了,具体的实现自己琢磨。下面讲一下如何画图,并把数据给图形。
添加事件监听器,要加一个keyListener,并重写keypressed方法,来实现用方向键来控制蛇的移动
这里要定义一个用来改变方向的函数,就是通过方向键来改变蛇的方向,这里我们可以用到之前的方向常量了,具体看代码
利用数据画图,其实不难,比如地图,你把地图的宽(横向的方格数)和高(竖向的方格数)的参数传给画笔,让画笔画就好了。再比如石头,食物,蛇,当坐标位置的值为 *时 用灰色画笔画,为 $ 时用红笔,为 #时用绿笔,为 @时蓝笔,就好了。
下面只展示重写的JPanel类的paint(Graphics g)方法。
最后提一下,常出现的难题:
1、数组越界的问题,因为键盘可以控制一直向某个方向,所以很容易越界。2、要实现蛇自己动,有点难度,我可想了好一会儿(滑稽)。
对了,有关扩展性是因为,你可以有继承,把你的贪吃蛇的类继承一下,然后重写 intiMap()方法就好呀,其他的不用重写。是不是扩展性很好呐(滑稽)
如果想看源码的童鞋,可以点击下面的连接:
http://blog.csdn.net/angellover2017/article/details/72861992
好了,先来看看,这个游戏的截图。(滑稽)
这里可以自定义难度系数(其实就是蛇自己移动的速度),共分10级。这里后面我会说实现方法,这都可以改的。
这里是初始的状态,食物是随机生成的,蛇位于地图的中央。其中灰色代表的是石头,白色是空地,蓝色是食物,红色是蛇头,绿色是蛇身。
这里,你可以用键盘的方向键来控制蛇的移动,但遇到上面俩种情况(蛇撞墙或蛇咬到自己)就会死亡。
这是一个扩展的地图的例子。
好了下面说实现的方法:
首先,我们知道java是面向对象编程方法,所以实现过程也肯定是面向对象的。其次,我们要明白“数据”与“界面”分离的思想,做界面不是难事,只要用的熟练就好,难的是核心的算法的实现。所以看到贪吃蛇这个游戏,我们首先想的是他的业务逻辑是什么,而不是这界面是咋做的,其实说真的,我的代码里,有关做图形界面的代码只有50行左右,而全部代码是400行左右,可见做界面之占1/8的任务量。
第一步:
我们先分析,在这个游戏里我们看到了那些对象或者说是变量。
1、地图 2、蛇 3、食物 (你可能还会说“石头”,但石头其实包含在地图里,因为地图就是由石头和空地组成的呀)(滑稽)
第二步:
下面我们一个一个的说:
1、地图如何实现?
首先,想用什么来储存地图的信息,呐,我们可以这样,地图由石头和空地组成,那么我们可以用字符 * 来表示石头,用空字符(就是一个空格键)来表示空地 ,这样我们就可以用char型的数组来存储地图。
然后就是,如何初始化地图,我们可以写一个intiMap()的方法来初始化地图(具体看代码,这里只粘部分代码,关键是思想)
//常量,表示地图的宽和高 protected static final int HEIGHT=30; protected static final int WIDTH=30; //char型数组来储存地图 protected char[][] map=new char[HEIGHT][WIDTH]; //初始化Map public void intiMap() { for(int i=0;i<HEIGHT;i++) for(int j=0;j<WIDTH;j++) { if(i==0||(i==HEIGHT-1)){ map[i][j]='*'; } else this.map[i][j]=' '; }
2、蛇如何实现?
蛇分为蛇头和蛇身,我们可以用字符 表示蛇头,用字符 # 表示蛇身。呐,是不是我们就也用字符数组来储存蛇,NO,蛇是会动的,蛇移动时蛇头或蛇身的字符是不变的。用 和 # 只是为了表示蛇,但不能用来储存蛇的信息。我们可以这样,蛇是在地图上动的,如果地图有坐标,我们就可以用坐标来储存蛇的每一个状态。因为一条蛇是由多个节点组成的,即由多个坐标组成,所以我们可以用链表容器来存储Point类型的节点。
呐,我们接下来就是初始化蛇,这个好办,因为初始的时候蛇是静止的,在最中央。写个方法就OK了。
难的来了,就是蛇的移动,我们如何让蛇在地图上移动?
其实也不难,蛇的移动我们可以看作链表添加节点和删除节点的操作,什莫意思呐,就是如果我们给蛇添加一个头,再删一个尾,不就相当于蛇走了吗。
因为 蛇有四个方向,所以可以定义四个常量表示四个方向(看代码,这里的代码是截取的,所以会用一些整体的方法,还是主要看思想,不用太在意代码)。
//常量,表示蛇的方向 protected static final int UP_DIRECTION=1; protected static final int DOWN_DIRECTION=-1; protected static final int LEFT_DIRECTION=2; protected static final int RIGHT_DIRECTION=-2; //当前方向,默认为右方 private int currentdirection=RIGHT_DIRECT; //容器,用来储存蛇的坐标信息 private LinkedList<Point> snake=new LinkedList<Point>(); //初始化snake public void intiSnake(){ int x=WIDTH/2; int y=HEIGHT/2; snake.addFirst(new Point(x-1,y)); snake.addFirst(new Point(x, y)); snake.addFirst(new Point(x,y)); } //蛇移动函数 public void move(){ //取蛇头 Point snakehead=snake.getFirst(); //依据方向,移动 switch(currentdirection){ //向上移动 case UP_DIRECTION : //结束的时候不执行添加头结点 if(!GameOver){ snake.addFirst(new Point(snakehead.x,snakehead.y-1)); } break; //向下移动 case DOWN_DIRECTION : //结束的时候不执行添加头结点 if(!GameOver){ snake.addFirst(new Point(snakehead.x,snakehead.y+1)); } break; //向左移动 case LEFT_DIRECTION : if(!GameOver){ if(snakehead.x==0){ snake.addFirst(new Point(snakehead.x-1+WIDTH,snakehead.y)); }else{ snake.addFirst(new Point(snakehead.x-1,snakehead.y)); } } break; //向右移动 case RIGHT_DIRECTION : if(!GameOver){ snake.addFirst(new Point((snakehead.x+1)%WIDTH,snakehead.y)); } break; default : break; } if(eatFood()){ //先刷新,防止出现食物长到身上 repaint(); //重建食物 intiFood(); }else{ //游戏结束的时候不执行删除尾节点 if(!GameOver){ ` //删除蛇尾 snake.removeLast(); } } }
3、食物如何实现?
这个相比蛇就很容易了,首先食物虽然不会动,但他是随机在地图中生成的,所以我们用Point类,即用坐标来储存食物信息,并用 字符@来表示食物。
初始化食物,我们要明白一点,食物既不能长在墙上也不能长在蛇身上,所以在初始化食物的时候,要避开这些位置。并且,还有一点食物是随机的,这个好办,产生随机数就好了。(看代码)
//食物的坐标用Point类来储存 private Point food=new Point(); //初始化食物 public void intiFood(){ while(true){ Random random=new Random(); int x=random.nextInt(WIDTH-1); int y=random.nextInt(HEIGHT-1); if(map[y][x]!='*'&&map[y][x]!='#'&&map[y][x]!='$') { food.x=x; food.y=y; break; } } }
第三步:
我们在上面已经定义好了,地图、蛇、食物,三个事物,并一一初始化。下面是如何讲这三样事物之间起建立联系。这个很重要,就是如何把蛇的信息、把食物的信息反馈给地图,以实现互动。
我们可以通过两个函数,showSnake() 和 showFood() 来实现,如何做呐?
其实,不难,我们只要将蛇 或 食物 的位置即坐标 反馈给地图就好了呀,然后让地图去显示他们就好了。
//在地图上显示蛇 public boolean showSnake(){ { //定位蛇头 int W=snake.getFirst().x; int H=snake.getFirst().y; map[H][W]='$'; //定位蛇身 for(int i=1;i<this.snake.size();i++) { W=snake.get(i).x; H=snake.get(i).y; map[H][W]='#'; } return true; } } //在地图上显示食物 public boolean showFood(){ { map[food.y][food.x]='@'; return true; } }
第四步:
好了,现在你的蛇和食物,可以在地图上显示了,并且蛇也可以在地图上自由的动了。但蛇还吃不了食物呀,还有蛇不会死呀。
如何让蛇吃到食物呐,我们这样,我们写个函数eatFood()返回值为boolean ,当蛇头的坐标和食物重合是返回真,否则假。然后,在第二步中的蛇的移动函数move()中判断eatFood(), 如果吃到了则就步删除尾结点了,不就好了吗。这样蛇吃了食物并且变长了。美滋滋(滑稽)
还有咋判断蛇死了? 我们还是写一个函数叫 Isover() ,在定义Game Over的布尔常量,当蛇头对应坐标下的地图坐标的值为 字符 * 或 # 时 ,就说明蛇撞墙了 或 咬到自己了 。然后在主函数中判断Game Over的真假,一旦为真,就结束。
//蛇吃食物区 public boolean eatFood(){ //取蛇头 Point snakehead=snake.getFirst(); if(snakehead.equals(food)){ return true; } return false; } //用来判断游戏是否结束 protected static boolean GameOver=false; /* 结束游戏区 */ public void IsOver(){ //取蛇头 Point snakehead=snake.getFirst(); //撞墙死 if(map[snakehead.y][snakehead.x]=='*'){ GameOver=true; } //咬到自己死 if(map[snakehead.y][snakehead.xver]=='#'){ GameOver=true; } }
哈,有了以上四步,一个贪吃蛇的雏形就大概好了,具体的实现自己琢磨。下面讲一下如何画图,并把数据给图形。
添加事件监听器,要加一个keyListener,并重写keypressed方法,来实现用方向键来控制蛇的移动
这里要定义一个用来改变方向的函数,就是通过方向键来改变蛇的方向,这里我们可以用到之前的方向常量了,具体看代码
frame1.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { super.keyPressed(e); switch(e.getKeyCode()){ case KeyEvent.VK_UP: IsAuto=false; sg1.changeDirection(UP_DIRECTION); break; case KeyEvent.VK_DOWN: IsAuto=false; sg1.changeDirection(DOWN_DIRECTION); break; case KeyEvent.VK_LEFT: IsAuto=false; sg1.changeDirection(LEFT_DIRECTION); break; case KeyEvent.VK_RIGHT: IsAuto=false; sg1.changeDirection(RIGHT_DIRECTION); break; default: break; } sg1.move(); sg1.IsOver(); sg1.reFresh(); sg1.repaint(); if(SnakeGame_1.GameOver){ sg1.repaint(); /* // System.exit(0); */ } IsAuto=true; } }); //如果IsAuto 为真,则自己走 if(IsAuto)sg1.runAuto();
利用数据画图,其实不难,比如地图,你把地图的宽(横向的方格数)和高(竖向的方格数)的参数传给画笔,让画笔画就好了。再比如石头,食物,蛇,当坐标位置的值为 *时 用灰色画笔画,为 $ 时用红笔,为 #时用绿笔,为 @时蓝笔,就好了。
下面只展示重写的JPanel类的paint(Graphics g)方法。
/* 图形刷新区 */ //重写Paint方法 @Override public void paint(Graphics g) { //画地图 for(int i=0;i<HEIGHT;i++) for(int j=0;j<WIDTH;j++) { if(map[i][j]=='*'){ g.setColor(Color.GRAY); }else{ g.setColor(Color.WHITE); } g.fill3DRect(j*CELL_W, i*CELL_H, CELL_W, CELL_H, true); } //画蛇 //定位蛇头 int W=snake.getFirst().x; int H=snake.getFirst().y; g.setColor(Color.RED); g.fill3DRect(W*CELL_W,H*CELL_H , 20, 20,true); //定位蛇身 for(int t=1;t<this.snake.size();t++) { W=snake.get(t).x; H=snake.get(t).y; g.setColor(Color.GREE c19a N); g.fill3DRect(W*CELL_W,H*CELL_H , 20, 20,true); } //画食物 map[food.y][food.x]='@'; g.setColor(Color.BLUE); g.fill3DRect(food.x*CELL_W, food.y*CELL_H, 20, 20, true); //画字 if(SnakeGame_1.GameOver){ g.setColor(Color.ORANGE); g.setFont(new Font("宋体", Font.BOLD,30 )); g.drawString("GAMW OVER !", CELL_W*(WIDTH/2), CELL_H*(HEIGHT/2)); }
最后提一下,常出现的难题:
1、数组越界的问题,因为键盘可以控制一直向某个方向,所以很容易越界。2、要实现蛇自己动,有点难度,我可想了好一会儿(滑稽)。
对了,有关扩展性是因为,你可以有继承,把你的贪吃蛇的类继承一下,然后重写 intiMap()方法就好呀,其他的不用重写。是不是扩展性很好呐(滑稽)
如果想看源码的童鞋,可以点击下面的连接:
http://blog.csdn.net/angellover2017/article/details/72861992
相关文章推荐
- JAVA游戏编程之二----j2me MIDlet 手机游戏入门开发--贪吃蛇_1
- JAVA游戏编程之二----j2me MIDlet 手机游戏入门开发--贪吃蛇
- Java开发的游戏之二 贪吃蛇 完整源代码
- 人生如梦游戏间,RPG游戏开源开发讲座(JAVA篇)[3]——邯郸学步
- JAVA开发的华容道游戏
- 利用Java Swing 实现游戏开发
- java基础,游戏开发知识
- Java游戏开发案例-方块游戏
- 人生如梦游戏间,RPG游戏开源开发讲座(JAVA篇)[3]——邯郸学步
- Java游戏开发日志(2007-6-1)
- Java游戏开发日志(2007-6-7)
- 人生如梦游戏间,RPG游戏开源开发讲座(JAVA篇)[1]——风云初现
- Java游戏开发起步
- 人生如梦游戏间,RPG游戏开源开发讲座(JAVA篇)[2]——踏破红尘
- 人生如梦游戏间,RPG游戏开源开发讲座(JAVA篇)[2]——踏破红尘
- 人生如梦游戏间,RPG游戏开源开发讲座(JAVA篇)[0]——月晕础润
- [转]Java可以拿來开发游戏吗?
- Java 游戏开发Notes
- JAVA实现贪吃蛇游戏
- 人生如梦游戏间,RPG游戏开源开发讲座(JAVA篇)[1]——风云初现