安卓大作业—围住神经猫小游戏
《移动终端应用开发》
大
作
业
报
告
书
题目:围住神经猫小游戏
专业:计算机科学与技术
学号:
学生姓名:××
指导教师:叉叉叉
完成日期:2016-5-31
目录
一、需求分析--------------------------- 错误!未定义书签。
二、系统功能描述----------------------- 错误!未定义书签。
三、系统概要设计---------------------------------------- 2
3.1功能模块设计 -------------------------------------- 2
3.1.1程序流程图 ----------------------------------- 2
3.1.2程序模块设计 --------------------------------- 2
3.1.3界面功能详细设计------------------------------ 3
四、系统主要代码---------------------------------------- 4
五、总结----------------------------------------------- 17
5.1开发中遇到的问题 --------------------------------- 17
5.2系统有待实现的功能 ------------------------------- 17
5.3收获总结 ----------------------------------------- 18
六、参考文献------------------------------------------- 18
一需求分析
安卓游戏已经是大势所趋,开发安卓游戏也将是游戏开发者的重中之重,所以我选择试做一个安卓小游戏来作为我的android课程大作业。围住神经猫是一款益智类的小游戏,它可以在人们生活烦躁工作无聊之时提高一种新的打发时间、舒缓压力的休闲方式,也可以锻炼人的逻辑思维能力、判断力和观察力。
首先我们从游戏界面开始分析,一开始是游戏欢迎界面,点击“开始游戏”,就可以切换到游戏界面,开始玩游戏了。在游戏界面中,有游戏背景,还有神经猫在移动时的样式,并把神经猫可移动的范围以坐标的方式分为若干个点。游戏结束时会对玩家进行提示,若玩家成功围住神经猫则显示玩家所用的步数。
然后是对神经猫这个游戏控制的分析,神经猫可移动的范围是9×9,我设置神经猫的初始位置是固定的,但是系统在游戏一开始生成的路障是随机的,一共是16个路障,之后玩家将根据游戏的情况对神经猫进行堵截,当玩家每选择一个点作为自己想设置的路障时,那个点将改变为橘色,而神经猫将根据可选择的路径自动移动到下一个地点,当神经猫移动到任意一个边缘时,游戏会提示玩家失败,当玩家成功围住神经猫时,游戏会提示玩家成功且会显示出玩家所用的步数。
这个游戏要用到二维数组,把神经猫可以移动的范围进行记录,还必须要对神经猫的移动方式进行设置,还有每个点不同状态的转换,路障的自动生成,还有对玩家所用步数的统计,这个游戏所要用到的主要软件是Eclipse、ADT,它的主要语言是java,操作系统是windows7.
二系统功能设计
项目具体功能描述如下:
1)游戏开始界面:有比较搞笑的背景图来吸引玩家,然后点击开始游戏,
切换到游戏界面
2)游戏界面:神经猫一开始在游戏的正中间,平且还有系统随机生成的16
个路障,范围是9×9的空间里,并且把它分成了点,玩家在
点上设置好路障后,神经猫会在看选取的路径中随机选取一
条,并移动到下一个点,直至游戏结束。
3) 游戏结束界面:当神经猫移动到任意边缘时,会显示通关失败的提示;
但玩家成功围住神经猫时,则显示成功通关页面,并且统计了玩家所用的步数。
三 项目概要设计
3.1功能模块设计
(1)程序流程图
根据用户需求,本软件程序流程图如下:
成功 通关失败
通关 再来一次
图1.程序流程图
(2)程序模块设计
启动程序
游戏开始界面
游戏界面
通关失败界面
通关界面
为了提高代码的重复利用率,程序设计时自定义了以下几个类:
①自定义坐标类(Dot):每一个点都是一个抽象的对象,需要把每一个点抽象为一个类,然后让每一个圆圈继承于这个类。
②自定义绘制布局类(playground):继承surfaceview,将制定的绘图呈现在surfaceview上,界面的响应和绘制都在surfaceview上完成的。
③自定义实现接口(OnTouchListener):为了界面的点击做出响应。(3)界面功能详细设计
各界面功能详尽介绍如下:
1) 游戏开始界面(图1:homepage):打开程序首先显示游戏开始界面,通过点击开始游戏,马上自动跳转到游戏界面图(homepage2)
图1:homepage
2)游戏界面(图2:homepage2):进入到游戏界面以后,就可以正式开始游戏啦玩家开始对神经猫进行围追截堵
图2:homepage2
3)游戏结束界面(图3:homepage3、图4:homepage4):若玩家成功围
住神经猫则显示图3,若失败则显示图4
图3:homepage3 图4:homepage4
四项目主要代码
系统实现相应功能的主要代码如下:
记录每个场景中的元素它的X,Y坐标点的状态:
package com.example.crazycat;
public class Dot {//记录每个场景中的元素它的X,Y坐标点的状态。并不会直接参与界面的响应和界面的绘制
private int x, y;
private int status;//记录这个点的状态
public static final int STATUS_OFF = -1;//代表可走的路径
public static final int STATUS_IN = 0;//猫的当前位置
public static final int STATUS_ON = 1;//已经设置的路障
//指定x,y的坐标
public Dot(int x, int y) {
this.x = x;
this.y = y;
this.status = STATUS_OFF;
}
//指定geter和sette方法
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;
}
//同时设置x。y的方法
public void setXY(int x, int y) {
this.x = x;
this.y = y;
}
}
Playground类的实现:
package com.example.crazycat;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import android.annotation.SuppressLint; import android.app.AlertDialog;
import android.app.AlertDialog.Builder; import android.content.Context;
import android.content.DialogInterface; import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable; import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback; import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Toast;
public class PlayGround extends SurfaceView implements OnTouchListener {
//界面的响应和界面的绘制在SurfaceView完成,触摸事件的响应通过OnTouchListener接口实现
// 行数
private static final int ROW = 9;
// 列数
private static final int COL = 9;
// 障碍的数量
private static final int BOCKS = COL * ROW / 5;
// 屏幕宽度去
private int SCREEN_WIDTH;
// 每个通道的宽度
private int WIDTH;
// 奇数行和偶数行通道间的位置偏差量
private int DISTANCE;
// 屏幕顶端和通道最顶端间的距离
private int OFFSET;
// 整个通道与屏幕两端间的距离
private int length;
// 做成神经猫动态图效果的单张图片
private Drawable cat_drawable;
// 背景图
private Drawable background;
// 神经猫动态图的索引
private int index = 0;
private Dot[][] matrix;
private Dot cat;
private Timer timer = null;
private TimerTask timerttask = null;
private Context context;
private int steps;
private int[] images = { R.drawable.cat1, R.drawable.cat2,
R.drawable.cat3,
R.drawable.cat4, R.drawable.cat5, R.drawable.cat6,
R.drawable.cat7,
R.drawable.cat8, R.drawable.cat9, R.drawable.cat10,
R.drawable.cat11, R.drawable.cat12, R.drawable.cat13,
R.drawable.cat14, R.drawable.cat15, R.drawable.cat16 };
@SuppressLint("ClickableViewAccessibility")
public PlayGround(Context context) {
super(context);//使用Context创建当前类构造函数
matrix= new Dot[ROW][COL];//将行高,列宽传递进去,指定数组大小
cat_drawable = getResources().getDrawable(images[index]);
background = getResources().getDrawable(R.drawable.bg);
this.context = context;
initGame();//调用游戏初始化
getHolder().addCallback(callback);//将Callback对象指定给getholder
setOnTouchListener(this);//设定为自己的触摸监听器
this.setFocusable(true);
this.setFocusableInTouchMode(true);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) {
stopTimer();
}
return super.onKeyDown(keyCode, event);
}
// 初始化游戏:分别对可走路径位置,猫的位置和路障位置进行初始化private void initGame() {
steps=0;
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
matrix[i][j] = new Dot(j, i);/*X,Y坐标值和行列值是相反的。
即通过查找列值获得X坐标,查找行值获得Y坐标*/
}
}
for (int i = 0; i < ROW; i++) {//用for循环将所有点设置为STATUS_OFF,即可用状态
for (int j = 0; j < COL; j++) {
matrix[i][j].setStatus(Dot.STATUS_OFF);
}
}
cat = new Dot(COL / 2 - 1, ROW / 2 - 1);//设置猫的起始点
getDot(cat.getX(), cat.getY()).setStatus(Dot.STATUS_IN);//把猫的起始点的状态设置为STATUS_IN,才能记录猫的位置
for (int i = 0; i < BOCKS;) {//用for循环随机的指定16个点的坐标作为路障
int x = (int) ((Math.random() * 1000) % COL);
int y = (int) ((Math.random() * 1000) % ROW);//随机获取1对坐标点
if (getDot(x, y).getStatus() == Dot.STATUS_OFF) {//对当前可用路径点进行选择
getDot(x, y).setStatus(Dot.STATUS_ON);//把这个点设置成路障
i++;
}
}
}
//实现界面绘制,在redraw方法中将所有元素以图形化显示出来,也就是将它绘制在Canvas对象上
private void redraw() {
Canvas canvas = getHolder().lockCanvas();//锁定画布
canvas.drawColor(Color.GRAY);//设置颜色为浅灰色
Paint paint = new Paint();//创建Paint对象
paint.setFlags(Paint.ANTI_ALIAS_FLAG);//开启抗锯齿,优化视频质量
//用两个For循环嵌套将所有的点显示到界面中来
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
DISTANCE = 0;//引入偏移量
if (i % 2 != 0) {
DISTANCE = (int) WIDTH / 2;//对偶数行进行缩进
}
Dot dot = getDot(j, i);//将坐标赋值给内部变量one
switch(dot.getStatus()) {//由于每个点对应的三种状态颜色不一样,要用一个switch语句
case Dot.STATUS_IN:
paint.setColor(0XFFEEEEEE);//STATUS_IN状态时设置颜色为浅灰
break;
case Dot.STATUS_ON:
paint.setColor(0XFFFFAA00);//STATUS_ON状态时设置颜
色为橙色
break;
case Dot.STATUS_OFF:
paint.setColor(0X74000000);//STATUS_OFF状态时设置颜色为黑色
break;
default:
break;
}
canvas.drawOval(new RectF(dot.getX() * WIDTH + DISTANCE
+ length, dot.getY() * WIDTH+ OFFSET, (dot.getX() + 1)
* WIDTH+ DISTANCE+ length, (dot.getY() + 1) * WIDTH
+ OFFSET), paint);
/*在Canvas画布上画椭圆并界定它的上下左右边界宽度且有错位*/
}
}
int left = 0;
int top = 0;
if (cat.getY() % 2 == 0) {
left = cat.getX() * WIDTH;
top = cat.getY() * WIDTH;
} else {
left = (int) (WIDTH / 2) + cat.getX() * WIDTH;
top = cat.getY() * WIDTH;
}
// 此处神经猫图片的位置是根据效果图来调整的
cat_drawable.setBounds(left - WIDTH / 6 + length, top - WIDTH / 2
+ OFFSET, left + WIDTH + length, top + WIDTH + OFFSET);
cat_drawable.draw(canvas);
background.setBounds(0, 0, SCREEN_WIDTH, OFFSET);
background.draw(canvas);
getHolder().unlockCanvasAndPost(canvas);;//取消Canvas的锁定,把绘图内容更新到界面上
}
//为Surfaceview添加Callback
Callback callback = new Callback() {//声明并实例化一个Callback接口public void surfaceCreated(SurfaceHolder holder) {
redraw();//执行redraw函数,在界面第一次显示时将指定的内容显示到界面上
startTimer();
}
//使用surfaceChanged方法来适配不同的屏幕尺寸
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
WIDTH = width / (COL + 1);
OFFSET = height - WIDTH * ROW - 2 * WIDTH;
length = WIDTH / 3;
SCREEN_WIDTH = width;
}
public void surfaceDestroyed(SurfaceHolder holder) {
stopTimer();
}
};
// 开启定时任务
private void startTimer() {
timer = new Timer();
timerttask = new TimerTask() {
public void run() {
gifImage();
}
};
timer.schedule(timerttask, 50, 65);
}
// 停止定时任务
public void stopTimer() {
timer.cancel();
timer.purge();
}
// 动态图
private void gifImage() {
index++;
if (index > images.length - 1) {
index = 0;
}
cat_drawable = getResources().getDrawable(images[index]);
redraw();
}
// 获取通道对象
private Dot getDot(int x, int y) {
return matrix[y][x];
}
// 判断神经猫是否处于边界
private boolean inEdge(Dot dot) {
if (dot.getX() * dot.getY() == 0 || dot.getX() + 1 == COL
|| dot.getY() + 1 == ROW) {
return true;//当前点的坐标无论是x或y为0,或x或y值是定义的游戏场景最大值时都处于边界
}
return false;
}
// 移动cat至指定点
private void moveTo(Dot dot) {
dot.setStatus(Dot.STATUS_IN);//dot的状态设置为猫所处的点
getDot(cat.getX(), cat.getY()).setStatus(Dot.STATUS_OFF);//将猫当前点的状态复位
cat.setXY(dot.getX(), dot.getY());//将猫移动到新的点}
// 获取one在方向dir上的可移动距离,中心点6个方向距离的判断与获取。路径结束点为路障用负数表示,为场景边缘用正数表示
private int getDistance(Dot one, int dir) {
int distance = 0;
if (inEdge(one)) {
return 1;//如果下一个点已在屏幕边缘,无需再判断
}
Dot ori = one;//定义ori和next,ori指定为当前函数传入的one
Dot next;
while (true) {
next = getNeighbour(ori, dir);
if (next.getStatus() == Dot.STATUS_ON) {
return distance* -1;//下一个点为路障的话,直接返回距离0 }
if (inEdge(next)) {
distance++;
return distance;//下一个点为场景边缘时,距离+1并返回}
distance++;
ori = next;
}
}
// 获取dot的相邻点,返回其对象
private Dot getNeighbour(Dot dot, int dir) {//每个点都有6个相邻点switch (dir) {
case 1:
return getDot(dot.getX() - 1, dot.getY());//获得中心点左边相邻点
case 2:
if (dot.getY() % 2 == 0) {
return getDot(dot.getX() - 1, dot.getY() - 1);//奇数行获得中心点左上相邻点:x-1,y-1
} else {
return getDot(dot.getX(), dot.getY() - 1);//偶数行获得中心点左上相邻点:y-1
}
case 3:
if (dot.getY() % 2 == 0) {
return getDot(dot.getX(), dot.getY() - 1);//奇数行获得中心点右上相邻点:y-1
} else {
return getDot(dot.getX() + 1, dot.getY() - 1);//偶数行获得中心点右上相邻点:x+1,y-1
}
case 4:
return getDot(dot.getX() + 1, dot.getY());//获得中心点右边相邻点
case 5:
if (dot.getY() % 2 == 0) {
return getDot(dot.getX(), dot.getY() + 1);//奇数行获得中心点右下相邻点:y+1
} else {
return getDot(dot.getX() + 1, dot.getY() + 1);//偶数行获得中心点右下相邻点:x+1,y+1
}
case 6:
if (dot.getY() % 2 == 0) {
return getDot(dot.getX() - 1, dot.getY() + 1);//奇数行获得中心点左下相邻点:x-1,y+1
} else {
return getDot(dot.getX(), dot.getY() + 1);//偶数行获得中心点左下相邻点:y+1
}
}
return null;
}
// cat的移动算法
private void move() {
if (inEdge(cat)) {
failure();
return;//猫处于游戏边缘,失败
}
Vector
Vector
HashMap
for (int i = 1; i < 7; i++) {//如果当前猫被6个邻点围住
Dot n = getNeighbour(cat, i);
if (n.getStatus() == Dot.STATUS_OFF) {
available.add(n);//如果相邻点可用,把它添加到avaliable 记录器中
hash.put(n, i);//为pl传入方向i
if (getDistance(n, i) > 0) {
direct.add(n);//当它有一个路径可以直接到达屏幕边缘,把n传递进positive中
}
}
}
//移动算法的优化
if (available.size() == 0) {
win();//周围的6个点都不可走,没有可用点,成功围住猫} else if (available.size() == 1) {
moveTo(available.get(0));//只有一个方向可走,可用点有一个,移动到这个可用点上
} else {//有多个方向可走
Dot best = null;
if (direct.size() != 0) {//存在可以直接到达屏幕边缘的走向int min = 20;
for (int i = 0; i < direct.size(); i++) {
if (inEdge(direct.get(i))) {
best = direct.get(i);
break;
} else {
int t = getDistance(direct.get(i),
hash.get(direct.get(i)));
if (t < min) {
min = t;//把最短路径长度传给min
best = direct.get(i);//选出拥有最短路径的点
}
}
}
} else {//所有方向都存在路障
int max = 1;
for (int i = 0; i < available.size(); i++) {
int k = getDistance(available.get(i),
hash.get(available.get(i)));
if (k < max) {//所有方向都存在路障,距离k为负数
max = k;
best = available.get(i);//选出拥有最短路径的点
}
}
}
moveTo(best);//移动到最短路径的下一点
}
// redraw();
if (inEdge(cat)) {
failure();
}
}
// 通关失败
private void failure() {
AlertDialog.Builder dialog = new Builder(context);
dialog.setTitle("通关失败");
dialog.setMessage("你让神经猫逃出精神院啦(ˉ▽ˉ;)...");
dialog.setCancelable(false);
dialog.setNegativeButton("再玩一次", new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { initGame();
}
});
dialog.setPositiveButton("取消", null);
dialog.show();
}
// 通关成功
private void win() {
AlertDialog.Builder dialog = new Builder(context);
dialog.setTitle("通关成功");
dialog.setMessage("你用" + (steps + 1) + "步捕捉到了神经猫耶( ??ω ??)y");
dialog.setCancelable(false);
dialog.setNegativeButton("再玩一次", new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { initGame();
}
});
dialog.setPositiveButton("取消", null);
dialog.show();
}
// 触屏事件
@SuppressLint("ClickableViewAccessibility")
public boolean onTouch(View v, MotionEvent event) {
//将屏幕的坐标转换为游戏的坐标
int x, y;
if (event.getAction() == MotionEvent.ACTION_UP) {//当用户触摸之后手离开屏幕释放的瞬间才对事件进行响应
if (event.getY() <= OFFSET) {
return true;
}
y = (int) ((event.getY() - OFFSET) / WIDTH);//横向状态下,奇、偶数行有坐标偏移,而纵向的Y值是不变的,将y进行转换
if (y % 2 == 0) {
if (event.getX() <= length
|| event.getX() >= length + WIDTH * COL) {
return true;
}
x = (int) ((event.getX() - length) / WIDTH);
} else {
if (event.getX() <= (length + WIDTH / 2)
|| event.getX() > (length+ WIDTH/ 2 + WIDTH* COL)) {
return true;
}
x = (int) ((event.getX() - WIDTH / 2 - length) / WIDTH);
}