说完了游戏介绍,我们开始进入实战的开发。第一步,我们要先设计一下游戏的主界面和游戏包含的一些功能。经过初步的设计,定了如下几个功能:1、开始游戏(开始新的游戏),2、继续游戏(保存游戏进度有后,可以继续游戏),3、排行榜功能(对于成功围住神经猫后的玩家,可以保存记录),4、设置(设置障碍物个数、游戏背景色等)。
上篇文章中已经附图了,看到了游戏的主界面。这篇文章我们就开始探讨一下开始游戏这一部分。
关于游戏开发,尤其这种小游戏,不需要用到一些游戏引擎,只需用SurfaceView即可。关于surfaceView的一些介绍,大家可以查很多文章。好了,我们现在开始正题。
现在先看一下游戏开始后的图:
对游戏界面分析,我们可以知道,上部分为一个简单的计步器,下部分为游戏真正发生的地方(不知道用什么形容好了T_T)。我们主要分析的也是这一部分。
好了,分析游戏,首先我们可以看到的是游戏的布局,一个10x10的布局,里面有15个黄色障碍物和一个红色猫,剩下的是一些可以移动和点击的灰色圆。对于游戏场景,我们可以自定义一个Playground.class,然后在这个类里面绘制游戏场景,包括对圆的绘制。然后我们定义一个Dot.class来存放圆的一些属性。
分析圆,它所包含的属性应该有:圆的大小、状态和XY坐标。根据这个,我们可以简单的写出Dot.class的代码
public class Dot {
int x,y;// 位置
int status;// 状态
public static final int STATUS_ON = 1;// 可点击状态
public static final int STATUS_OFF = 0;// 不可点击状态(障碍物)
public static final int STATUS_IN = 9;// 不可点击状态(猫)
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public void setXY(int x,int y) {
this.x = x;
this.y = y;
}
public Dot(int x, int y) {
super();
this.x = x;
this.y = y;
status = STATUS_OFF;
}
}
下一步,分析Playground.class这个类。这个类比较复杂,先贴出代码再分析把
public class Playground extends SurfaceView {
// Playground 类主要是一些游戏界面的绘制,和一些方法的集合,触控操作放在主类中
public static int WIDTH = 40;
public static final int ROW = 10;// 行
public static final int COL = 10;// 列
private Dot matrix[][];
public int blocks = 15;// 默认添加路障的个数
public int blockscolor,catcolor,background;
private SharedPreferences sp;
public Playground(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
System.out.println("Playground(Context context, AttributeSet attrs, int defStyle)");
getHolder().addCallback(callback);
matrix = new Dot[ROW][COL];
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
matrix[i][j] = new Dot(j, i);
}
}
sp = context.getSharedPreferences("setting", Context.MODE_PRIVATE);
blockscolor = sp.getInt("blockscolor", 0xFFFFFF00);
catcolor = sp.getInt("catcolor", 0xFFFF0000);
background = sp.getInt("background", 0xFFCCCCCC);
blocks = sp.getInt("blocks", 15);
}
// 绘制游戏场景
public void redraw() {
Canvas c = getHolder().lockCanvas();// 画布,锁定画布,绘制游戏的界面
c.drawColor(background);
Paint paint = new Paint();// 画笔
paint.setFlags(Paint.ANTI_ALIAS_FLAG);// 抗锯齿
for (int i = 0; i < ROW; i++) {
int offset = 0;// 缩进量
if (i%2 == 0) {// 偶数行缩进
offset = WIDTH/2;
}
for (int j = 0; j < COL; j++) {
Dot one = getDot(j, i);
switch (one.getStatus()) {
case Dot.STATUS_OFF:
paint.setColor(0xFFEEEEEE);
break;
case Dot.STATUS_ON:
paint.setColor(blockscolor);
break;
case Dot.STATUS_IN:
paint.setColor(catcolor);
break;
default:
break;
}
// 需要注意的地方,画圆
c.drawOval(new RectF(
one.getX()*WIDTH+offset,
one.getY()*WIDTH,
(one.getX()+1)*WIDTH+offset,
(one.getY()+1)*WIDTH), paint);
}
}
getHolder().unlockCanvasAndPost(c);
}
public boolean isAtEdge(Dot d) {
if (d.getX()*d.getY() == 0 || d.getX()+1 == COL || d.getY()+1 == ROW) {
return true;
}
return false;
}
public Dot getNeighbor(Dot one, int dir)
{
switch (dir) {
case 1:
return getDot(one.getX()-1, one.getY());
case 2:
if (one.getY() % 2 == 0) {
return getDot(one.getX(), one.getY()-1);
}else {
return getDot(one.getX()-1, one.getY()-1);
}
case 3:
if (one.getY() % 2 == 0) {
return getDot(one.getX()+1, one.getY()-1);
}else {
return getDot(one.getX(), one.getY()-1);
}
case 4:
return getDot(one.getX()+1, one.getY());
case 5:
if (one.getY() % 2 == 0) {
return getDot(one.getX()+1, one.getY()+1);
}else {
return getDot(one.getX(), one.getY()+1);
}
case 6:
if (one.getY() % 2 == 0) {
return getDot(one.getX(), one.getY()+1);
}else {
return getDot(one.getX()-1, one.getY()+1);
}
default:
break;
}
return null;
}
// 判断距离,在移动的时候用
public int getDistance(Dot one, int dir)
{
int distance = 0;
Dot ori, next;// 定义下一个点和当前点
ori = one;
while (true) {
next = getNeighbor(ori, dir);
// 如果碰到边缘,或者是碰到障碍物,即可返回距离
if (next.getStatus() == Dot.STATUS_ON) {
return distance*-1;
}
if (isAtEdge(next)) {
distance ++;
return distance;
}
distance++;
ori = next;// 迭代
}
}
// 坐标转换!!!因为触控的XY坐标和数组的XY坐标相反,所以需要getDot转换坐标
public Dot getDot(int x, int y) {
return matrix[y][x];
}
// surfaceView回调函数
Callback callback = new Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
}
// view创建时调用
@Override
public void surfaceCreated(SurfaceHolder arg0) {
WIDTH = getWidth()/(COL+1);
redraw();
}
// 当view发生变换调用
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
WIDTH = arg2/(COL+1);
redraw();
}
};
// 初始化游戏
public void initGame() {
WIDTH = getWidth() / (COL+1);
for (int i = 0; i |
因为涉及到了保存游戏和设置游戏相关的一些数据,所以一下子看可能看不懂。 因为涉及到了保存,大家可以把那些参数自己定义,不需要用SharedPreferences。
游戏中涉及到猫的移动,所以关于是否可以移动以及移动的最优方向进行了判断。先看下是否可移动,这个包含了两种可能,第一种是到达了游戏的边界,即isatEdge()
public boolean isAtEdge(Dot d) {
if (d.getX()*d.getY() == 0 || d.getX()+1 == COL || d.getY()+1 == ROW) {
return true;
}
return false;
}
只需传入猫的位置, d.getX()*d.getY() == 0表示在游戏的上边界和左边界。d.getX()+1 == COL 和d.getY()+1 == ROW表示到达游戏的右边界和下边界。
public Dot getNeighbor(Dot one, int dir)
{
switch (dir) {
case 1:
return getDot(one.getX()-1, one.getY());
case 2:
if (one.getY() % 2 == 0) {
return getDot(one.getX(), one.getY()-1);
}else {
return getDot(one.getX()-1, one.getY()-1);
}
case 3:
if (one.getY() % 2 == 0) {
return getDot(one.getX()+1, one.getY()-1);
}else {
return getDot(one.getX(), one.getY()-1);
}
case 4:
return getDot(one.getX()+1, one.getY());
case 5:
if (one.getY() % 2 == 0) {
return getDot(one.getX()+1, one.getY()+1);
}else {
return getDot(one.getX(), one.getY()+1);
}
case 6:
if (one.getY() % 2 == 0) {
return getDot(one.getX(), one.getY()+1);
}else {
return getDot(one.getX()-1, one.getY()+1);
}
default:
break;
}
return null;
}
getNeighbor()函数,是对猫附近的六个方向进行遍历。1-6分别为从左边开始的方向。
// 判断距离,在移动的时候用
public int getDistance(Dot one, int dir)
{
int distance = 0;
Dot ori, next;// 定义下一个点和当前点
ori = one;
while (true) {
next = getNeighbor(ori, dir);
// 如果碰到边缘,或者是碰到障碍物,即可返回距离
if (next.getStatus() == Dot.STATUS_ON) {
return distance*-1;
}
if (isAtEdge(next)) {
distance ++;
return distance;
}
distance++;
ori = next;// 迭代
}
}
getDistance()函数,某一方向的距离判断。