YuHang’s Blog

CrazyCat项目总结

因为要开始学习游戏开发了,先做一个小项目练练手,项目地址

项目描述

抓住神经猫:在一个10*10的地图上,随机散落这一些障碍物,地图中心是一直神经猫。每次点击一个空白的格子会放置一个障碍物,同时,神经猫也会向自己认为合理的地方走一个单位,当神经猫抵达网格边缘时游戏失败(视为神经猫成功逃离),神经猫无路可走时游戏胜利
胜利图片

项目总结

布局加载

整个游戏面板是在一个自定义surfaceView中实现的,在MainActivity中,通过setContentView(View)将自定义的surfaceView传入,实现页面的加载功能。
setContentView(new PlayGround(this));
这种方式与之前写Android的方式很不相同,使用surfaceView的好处在于不会阻塞主线程的操作,所以在触摸事件频繁的游戏开发中常常使用这种方式。

界面绘制

一开始不在Activity中所以感觉有点迷茫,在surfaceView中提供了SurfaceHolder与CallBack机制。
SurfaceHolder是一个接口,可以通过他操作SurfaceView。而CallBack机制可以实现事件提供了surfaceCreate,changed,destroyed时候的回调。项目中每次点击一个点的时候就需要对于Canvas进行重新绘制,来实现更新的效果。
界面绘制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private void reDraw() {
Canvas canvas = getHolder().lockCanvas();
canvas.drawColor(Color.LTGRAY);
Paint paint = new Paint();
for (int i = 0; i < ROW; i++) {
if (i % 2 != 0) { //奇数行
OFFSET_X = 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(0xFFFFAA00);
break;
case Dot.STATUS_IN:
paint.setColor(0xFFFF0000);
break;
default:
break;
}
// 画圆
canvas.drawOval(new RectF(one.getX()*WIDTH + OFFSET_X,one.getY()*WIDTH+OFFSET_Y,
(one.getX()+1)*WIDTH + OFFSET_X,(one.getY()+1)*WIDTH+OFFSET_Y), paint);
}
OFFSET_X = 0;
}
getHolder().unlockCanvasAndPost(canvas);
}

回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
getHolder().addCallback(callback);//注册CallBack
....
....
....
SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
WIDTH = width/(COL+1);
OFFSET_Y = (height - (WIDTH * ROW))/2;
reDraw();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {}
};

事件响应

由于继承自View类,SurfaceView可以使用setOnTouchListener()等方式来事件事件的响应各种事件。项目中通过event的getX(),getY()获取坐标进行判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
int x, y;
y = (int) ((event.getY()-OFFSET_Y) / WIDTH);
if (y % 2 == 0) {
x = (int) (event.getX() / WIDTH);
} else {
x = (int) ((event.getX() - WIDTH / 2) / WIDTH);
}
if (x + 1 > COL || y + 1 > ROW || x < 0 || y < 0) {
initGame();
} else if (getDot(x,y).getStatus()==Dot.STATUS_OFF){
getDot(x, y).setStatus(Dot.STATUS_ON);
move();
}
reDraw();
}
return true;
}

编程思考

一开始准备做这个项目的时候,一直在考虑怎么通过获取XY坐标来找点的位置,打算在逻辑中加入大量的坐标运算。感觉很是麻烦。但是这个项目中学到的将XY的实际坐标值转化为行列值的做法让我眼前一亮。做法如下:
1.定义一个变量WIDTH,这个变量表示每个圆圈的直径。
2.计算(X/WIDTH)与(Y/WIDTH)。这是我们就可以得到这个圆所处的行列值。运算只在触摸方法中进行
3.在逻辑编写中,直接将行列值拿来处理,这样我们就完成一层由实际坐标到逻辑坐标(行列)的抽象,这种抽象让我在编程中方便很多~