【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
在上一个项目中,我们已经在窗口上绘制出了一个蓝色的方块,在这个项目中,我们将开始对蛇开始进行编码,所以我们先创建一个类,叫做Snake,放在com.gulang.snake.entity的包下面。在看代码之前,我们先来做一些铺垫。首先我们分析一下蛇的构造,蛇的身体是一节一节的,每一节都是相同的结构,因此,我们需要将蛇的身体抽象出来,成一个类。这里呢我们采用了内部类的方式来实现。即下面的这部分代码: /**
* 蛇的一节身体
* @author jiladeyouxiang@qq.com
*
*/
class Body{
/** 蛇的身体的绘制的横坐标 */
private int x;
/** 蛇的身体的绘制的纵坐标 */
private int y;
/**
* 构造一个蛇的身体节点
* @param x 绘制的横坐标
* @param y 绘制的纵坐标
*/
public Body(int x, int y){
this.x = x;
this.y = y;
}
/**
* 绘制一个蛇身体的一个节点
* @param g
*/
public void drawMe(Graphics g){
//获得画笔原来的颜色
Color c = g.getColor();
//给画笔设置新的颜色
g.setColor(Color.BLUE);
//根据当前蛇的身体节点的起始横纵坐标绘制该节点
g.fill3DRect(x, y, BODY_SIZE, BODY_SIZE, true);
//将画笔的颜色复原
g.setColor(c);
}
}
至于为什么要采用内部类的形式呢,这里孤狼简单的说明一下。Java中,类的引用有3种方式,分别是外部类,内部类和匿名类的方式,对于那种很多文件中都要用到的类,我们一般采用外部类的方式(即新建一个类的方式来实现);对于那种只需要在本类中使用的类来说,就像在本例子中,蛇的身体只有蛇会引用到,其他类不需要建立对其的引用,这时候我们适合使用内部类,并且内部类有一个好处就是可以直接引用自己外面的类的属性和方法;至于匿名类呢就是我们有时候不太关心类的具体信息,而更专注于给类的某些方法或者参数进行设置时,我们会采用匿名类的方式,最典型的例子就是我们给监听器实现我们自己的方法。
好了,我们还是说回我们这个Body类,我们要绘制一个蛇的身体节点,需要3个参数,绘制的起始横坐标,起始纵坐标,和蛇的身体的大小。身体的绘制坐标由于每一个身体节点的位置不同,所以我们需要再使用的时候动态的设置,身体的大小我们就直接引用定义在Snake中的BODY_SIZE参数就可以了。然后,我们在Body类中的drawMe方法中进行身体节点的绘制。
接下来,我们来想一下我们的蛇应该用什么数据结构来设计。这个问题就要想到我们游戏的具体的玩法,要完成贪吃蛇游戏,最关键的问题就是处理蛇移动的问题。那么我们怎么用代码来模拟蛇的移动呢?请看下面的一张图:
上面的呢是我们蛇初始的位置,下面的蛇是移动后的位置。第一种想法呢是我们最容易想到的,就是把我们的蛇的所有节点都向右挪动一个步长,但是这种方法我们觉得不是最好的方法。于是我们有了第二种方法,蛇向前移动一步可以看成是我们先去掉蛇的尾巴,再在新的位置添加上一个新的蛇头(即我们先删除1号节点,然后再在4号位置添加上新的蛇头),这样呢我们就完成了蛇的一次移动。
决定了蛇的运动算法之后,也就决定了我们描述蛇的数据结构,LinkedList就是不二的选择。这都源于LinkedList的特性,LinkedList给我们提供了addFirst(),removeFirst(),addLast(),removeLast()这四个正好符合我们需求的方法。有了这些铺垫,我们就可以来看代码了:
package com.gulang.snake.entity;
import java.awt.Color;
import java.awt.Graphics;
import java.util.LinkedList;
import com.gulang.snake.view.GameView;
/**
* 蛇的对象
* @author jiladeyouxiang@qq.com
*
*/
public class Snake {
/** 蛇的一节身体的大小 */
public static final int BODY_SIZE = 30;
/** 蛇前进的步长,即移动一次前进的距离 */
public static final int STEP = BODY_SIZE;
/** 蛇前进的方向,向上 */
public static final int DIR_UP = 0;
/** 蛇前进的方向,向下 */
public static final int DIR_DOWN = 1;
/** 蛇前进的方向,向左 */
public static final int DIR_LEFT = 2;
/** 蛇前进的方向,想右 */
public static final int DIR_RIGHT = 3;
/** 蛇当前前进的方向 */
private int direction;
/** 蛇的身体 */
private LinkedList<Body> snakeBody = new LinkedList<Body>();
/**
* 初始化一条蛇
*/
public Snake(){
//初始化蛇最开始前进的方向为向右
direction = DIR_RIGHT;
//我们将蛇的第一节身体初始化在游戏窗口的中央
int startX = (GameView.WINDOW_WIDTH - BODY_SIZE) / 2;
int startY = (GameView.WINDOW_HEIGHT - BODY_SIZE) / 2;
//初始化蛇的身体,即向body链表中添加数据,我们初始化蛇的初始节点为3个
for(int i = 0; i < 3; i++){
//逐个的计算出蛇的每一个节点的位置
Body body = new Body(startX - i * BODY_SIZE, startY);
snakeBody.add(body);
}
}
/**
* 将蛇绘制在游戏的窗口上
* @param g
*/
public void drawMe(Graphics g){
for(int i = 0; i < snakeBody.size(); i++){
snakeBody.get(i).drawMe(g);
}
}
/**
* 蛇的一节身体
* @author jiladeyouxiang@qq.com
*
*/
class Body{
/** 蛇的身体的绘制的横坐标 */
private int x;
/** 蛇的身体的绘制的纵坐标 */
private int y;
/**
* 构造一个蛇的身体节点
* @param x 绘制的横坐标
* @param y 绘制的纵坐标
*/
public Body(int x, int y){
this.x = x;
this.y = y;
}
/**
* 绘制一个蛇身体的一个节点
* @param g
*/
public void drawMe(Graphics g){
//获得画笔原来的颜色
Color c = g.getColor();
//给画笔设置新的颜色
g.setColor(Color.BLUE);
//根据当前蛇的身体节点的起始横纵坐标绘制该节点
g.fill3DRect(x, y, BODY_SIZE, BODY_SIZE, true);
//将画笔的颜色复原
g.setColor(c);
}
}
}
理解了Snake类之中的内容后,我们再修改一下GameView中的paint()方法之后,我们就可以看到我们项目的运行效果了,依旧,直接看代码:
/**
* 绘制界面的方法
*/
@Override
public void paint(Graphics g) {
//绘制蛇的对象
snake.drawMe(g);
}
当然,在调用之前,不要忘了在GameView的构造方法中初始化snake对象,我们重新写了一个initGameData()的方法来初始化和游戏数据相关的内容。代码如下:
/**
* 初始化和游戏相关的数据
*/
private void initGameData() {
//构造蛇的对象
snake = new Snake();
}
至此,我们就看到了我们的Snake了,见下图:
是不是感觉有点像样了?不过现在我们的蛇还不会动,在下一篇博文中,我们将让我们的蛇动起来。那么今天就到这吧,下次再见啦。
我把代码都放在迅雷快传上了,大家奔走相告吧,这里给出链接,一篇博文对应一个项目文件,大家直接导入工程就可以运行:
http://kuai.xunlei.com/d/nNdkCUf1HBXTUAQA44e
来源:oschina
链接:https://my.oschina.net/u/554627/blog/97123