安卓大作业—围住神经猫小游戏

安卓大作业—围住神经猫小游戏
安卓大作业—围住神经猫小游戏

《移动终端应用开发》

题目:围住神经猫小游戏

专业:计算机科学与技术

学号:

学生姓名:××

指导教师:叉叉叉

完成日期: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 available = new Vector();//avaliable容器记录可用点

Vector direct = new Vector();;//positive容器记录这个方向上可以直接到达屏幕边缘的路径

HashMap hash = new HashMap();//pl 容器记录方向

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);

}

相关主题
相关文档
最新文档