华容道游戏开发--android小组
这个是我们小组的同学做的一个游戏,游戏界面做的比较简单一点,但是还是麻雀虽小五脏俱全滴。。。
1,游戏背景
华容道是古老的中国游戏,以其变化多端、百玩不厌的特点与七巧板、九连环合称“中国的难题”。
华容道游戏取自著名的三国故事,曹操在赤壁大战中被刘备和孙权的“苦肉计”、“火烧连营”打败,被迫退逃到华容道,又遇上诸葛亮的伏兵,关羽为了报答曹操对他的恩情,明逼实让,终于帮助曹操逃出了华容道。
游戏就是依照“曹瞒兵败走华容,正与关公狭路逢。只为当初恩义重,放开金锁走蛟龙”这一故事情节进行设计的。
2,功能简介
游戏的操作非常简单,其具体方法如下:
(1)运行游戏,首先进入的是主菜单界面,如图1所示。
(2)在菜单界面,可以通过“打开声音”/“关闭声音”来控制游戏声音的开关,单击帮助菜单可进入帮助界面,如图2所示。
(3)在菜单界面单击“开始游戏”可进入游戏的主界面,如图3所示
(4)在游戏界面,玩家可以通过单击人物图片,对人物进行移动;可以单击声音按钮控制游戏声音;单击“上一关”或“下一关”按钮选择关卡;单击“重玩”可以从头开始玩本关。如图4所示。
(5)当“曹操”被移动到棋盘最下方的绿线处时,游戏胜利。游戏会记录当前已走的步数,以及每一关的历史最好成绩。
(6)在游戏过程中,玩家可以单击X处,结束游戏,回到主菜单。图2 游戏简介
1,游戏背景
华容道是古老的中国游戏,以其变化多端、百玩不厌的特点与七巧板、九连环合称“中国的难题”。
华容道游戏取自著名的三国故事,曹操在赤壁大战中被刘备和孙权的“苦肉计”、“火烧连营”打败,被迫退逃到华容道,又遇上诸葛亮的伏兵,关羽为了报答曹操对他的恩情,明逼实让,终于帮助曹操逃出了华容道。
游戏就是依照“曹瞒兵败走华容,正与关公狭路逢。只为当初恩义重,放开金锁走蛟龙”这一故事情节进行设计的。
2,功能简介
游戏的操作非常简单,其具体方法如下:
(1)运行游戏,首先进入的是主菜单界面,如图1所示。
(2)在菜单界面,可以通过“打开声音”/“关闭声音”来控制游戏声音的开关,单击帮助菜单可进入帮助界面,如图2所示。
(3)在菜单界面单击“开始游戏”可进入游戏的主界面,如图3所示
(4)在游戏界面,玩家可以通过单击人物图片,对人物进行移动;可以单击声音按钮控制游戏声音;单击“上一关”或“下一关”按钮选择关卡;单击“重玩”可以从头开始玩本关。如图4所示。
(5)当“曹操”被移动到棋盘最下方的绿线处时,游戏胜利。游戏会记录当前已走的步数,以及每一关的历史最好成绩。
(6)在游戏过程中,玩家可以单击X处,结束游戏,回到主菜单。
图1 游戏主菜单
图2 游戏简介
图3 游戏主界面
3,游戏的策划及准备工作
l 游戏类型
该游戏属于中国传统益智游戏,其操作方式类似拼图,游戏变化多端,百玩不厌。
l 运行平台
目标平台为Android2.2,但开发过程中采用的技术都是Android的基础技术,所以在低版本的Android平台上也可以正常运行。
l 操作方式
本游戏采用屏幕事件进行操作,玩家可使用触控笔单击屏幕来完成对游戏的控制。
l 音效设计
考虑到玩家的体验,并结合游戏的背景,我们选择了“三国杀”的音乐作为背景音乐,游戏过程中,移动曹操时,还会有特别的音效。
l 游戏开发的准备工作
游戏开发前的准备工作是必不可少的,主要是要搜集需要用到的图片、声音等资源。本游戏的开发过程中用到的资源主要有以下几大类:
图片资源:
(1)人物头像(2)菜单及按钮(3)关卡名称(4)游戏背景界面
图4 头像图片
图5 按钮
图6 关卡名称
声音资源:
背景音乐:gamesound.wav
移动曹操时的音乐:caocaosound.wav
游戏胜利有的音乐:caocao_succed.wav
4,游戏的架构
我们将游戏中的类分成3部分,下面分别对其进行介绍
4.1,共有类
Activity的实现类Huarongdao.java。该类是通过扩展基类Activity得到的,是整个游戏的控制器,也是整个程序的入口。
Huarongdao类实现代码如下:
public class Huarongdao extends Activity {
boolean isSound = true;//是否播放声音
MediaPlayer gamesound;//游戏声音
MediaPlayer caocaoSound;
MediaPlayer caocaoSucceed;
static int CURRENT_LEVEL=0;
static int[] bestSteps=new int[Maps.LEVEL_NUM];
String[] level=new String[]{"level0","level1","level2","level3","level4","level5","level6","level7","level8","level9"};
Handler myHandler = new Handler(){//用来更新UI线程中的控件
public void handleMessage(Message msg) {
if(msg.what == 1){//MenuView传来的消息,切换到GameView
initGameView();//初始化并切换到游戏界面
}
else if(msg.what ==2){//MenuView传来的消息,切换到HelpView
initHelpView();//初始化并切换到帮助界面
}
else if(msg.what==3){
initMenuView();//切换到menuview
}
}
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//全屏
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN ,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
gamesound = MediaPlayer.create(this, R.raw.gamesound);//游戏过程的背景声音
caocaoSound=MediaPlayer.create(this, R.raw.caocaosound);//游戏过程中曹操移动时的声音
caocaoSucceed=MediaPlayer.create(this, R.raw.caocao_succeed);//游戏过程的背景声音
gamesound.setLooping(true);//设置游戏声音循环播放
caocaoSound.setLooping(false);
caocaoSucceed.setLooping(false);
//从preferences中读取出每一关的最好成绩
SharedPreferences beststeps=getPreferences(Activity.MODE_PRIVATE);
for(int i=0;i<Maps.LEVEL_NUM;i++)
bestSteps[i]=beststeps.getInt(level[i], 0);
this.initMenuView();//初始化菜单界面
}
private void initMenuView() {
// TODO Auto-generated method stub
if(this.isSound){//是否播放声音
gamesound.start();//播放声音
}
this.setContentView(new MenuView(this,this));
}
protected void initHelpView() {
// TODO Auto-generated method stub
this.setContentView(new HelpView(this,this));
}
protected void initGameView() {
// TODO Auto-generated method stub
this.setContentView(new GameView(this,this));
}
public void updateBestSteps() {
// TODO Auto-generated method stub
SharedPreferences newsteps=getPreferences(0);
SharedPreferences.Editor editor=newsteps.edit();
editor.putInt(level[CURRENT_LEVEL], bestSteps[CURRENT_LEVEL]);
editor.commit();
}
4.2,辅助界面相关类
有3个:菜单界面类MenuView,为菜单界面的实现类,负责绘制菜单界面以及对菜单界面的屏幕进行监听。
MenuView类的实现如下:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MenuView extends SurfaceView implements SurfaceHolder.Callback{
Huarongdao activity;
Bitmap startGame;
Bitmap openSound;
Bitmap closeSound;
Bitmap help;
Bitmap exit;
private TutorialThread thread;//刷帧的线程
public MenuView(Context context,Huarongdao huarongdao) {
super(context);
// TODO Auto-generated constructor stub
this.activity=huarongdao;//得到huarongdao引用
getHolder().addCallback(this);
this.thread = new TutorialThread(getHolder(), this);//启动刷帧线程
initBitmap();
}
private void initBitmap() {
// TODO Auto-generated method stub
startGame = BitmapFactory.decodeResource(getResources(), R.drawable.startgame);//开始游戏按钮
openSound = BitmapFactory.decodeResource(getResources(), R.drawable.opensound);//开始声音按钮
closeSound = BitmapFactory.decodeResource(getResources(), R.drawable.closesound);//关闭声音按钮
help = BitmapFactory.decodeResource(getResources(), R.drawable.help);//帮助按钮
exit = BitmapFactory.decodeResource(getResources(), R.drawable.exit);//退出按钮
}
public void onDraw(Canvas canvas){
canvas.drawColor(Color.BLACK);//清屏
canvas.drawBitmap(startGame, 50, 50, null);//绘制图片
if(activity.isSound){//放声音时,绘制关闭声音图片
canvas.drawBitmap(closeSound, 50, 150, null);//绘制关闭声音
}else{//没有放声音时绘制打开声音图片
canvas.drawBitmap(openSound, 50, 150, null);//绘制开始声音
}
canvas.drawBitmap(help, 50, 250, null);//绘制帮助按钮
canvas.drawBitmap(exit, 50, 350, null);//绘制退出按钮
}
public boolean onTouchEvent(MotionEvent event) {//屏幕监听
if(event.getAction() == MotionEvent.ACTION_DOWN){
if(event.getX()>105 && event.getX()<220
&&event.getY()>60 && event.getY()<95){//点击的是开始游戏
activity.myHandler.sendEmptyMessage(1);
}
else if(event.getX()>105 && event.getX()<220
&&event.getY()>160 && event.getY()<195){//点击的是声音按钮
activity.isSound = !activity.isSound;//将声音开关取反
if(!activity.isSound){//当没有放声音时
if(activity.gamesound != null){//检查当前是否已经有声音正在播放
if(activity.gamesound.isPlaying()){//当游戏声音正在播放时,
activity.gamesound.pause();//停止声音的播放
}
}
}else{//当需要播放声音时
if(activity.gamesound != null){//当gamesound不为空时
if(!activity.gamesound.isPlaying()){//且当前声音没有在播放
activity.gamesound.start();//则播放声音
}
}
}
}
else if(event.getX()>105 && event.getX()<220
&&event.getY()>260 && event.getY()<295){//点击的是帮助按钮
activity.myHandler.sendEmptyMessage(2);//向activity发送Hander消息通知切换View
}else if(event.getX()>105 && event.getX()<220
&&event.getY()>360 && event.getY()<395){//点击的是退出游戏
System.exit(0);//直接退出游戏
}
}
return super.onTouchEvent(event);
}
class TutorialThread extends Thread{//刷帧线程
private int span = 500;//睡眠的毫秒数
private SurfaceHolder surfaceHolder;//SurfaceHolder的引用
private MenuView menuView;//MenuView的引用
private boolean flag = false;//循环标记位
public TutorialThread(SurfaceHolder surfaceHolder, MenuView menuView) {//构造器
this.surfaceHolder = surfaceHolder;//得到surfaceHolder引用
this.menuView = menuView;//得到menuView引用
}
public void setFlag(boolean flag) {//设置循环标记位
this.flag = flag;
}
public void run() {//重写的run方法
Canvas c;//画布
while (this.flag) {//循环
c = null;
try {
// 锁定整个画布,在内存要求比较高的情况下,建议参数不要为null
c = this.surfaceHolder.lockCanvas(null);
synchronized (this.surfaceHolder) {//同步锁
menuView.onDraw(c);//调用绘制方法
}
} finally {//使用finally保证下面代码一定被执行
if (c != null) {
//更新屏幕显示内容
this.surfaceHolder.unlockCanvasAndPost(c);
}
}
try{
Thread.sleep(span);//睡眠指定毫秒数
}catch(Exception e){//捕获异常
e.printStackTrace();//有异常时打印异常堆栈信息
}
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
this.thread.setFlag(true);//设置循环标志位
this.thread.start();//启动线程
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;//循环标志位
thread.setFlag(false);//设置循环标志位
while (retry) {//循环
try {
thread.join();//等待线程结束
retry = false;//停止循环
}catch (InterruptedException e){}//不断地循环,直到刷帧线程结束
}
}
}
HelpView,实现帮助界面。Maps用于存放关卡布局信息,如果要新增关卡,只需修改这个类即可。
HelpView实现如下:
public class HelpView extends SurfaceView implements SurfaceHolder.Callback{
Bitmap bg;
Bitmap introduction;
Bitmap exit2;
Huarongdao activity;
int intro_y=480;//文字的y坐标
private TutorialThread thread;//刷帧的线程
public HelpView(Context context,Huarongdao huarongdao) {
super(context);
// TODO Auto-generated constructor stub
this.activity=huarongdao;//得到huarongdao引用
getHolder().addCallback(this);
this.thread = new TutorialThread(getHolder(), this);//启动刷帧线程
initBitmap();
}
public void onDraw(Canvas canvas){
canvas.drawColor(Color.BLACK);//清屏
canvas.drawBitmap(bg, 0, 0, null);//绘制背景图片
canvas.drawBitmap(introduction, 0, intro_y, null);//绘制文字介绍
if(intro_y==0)
canvas.drawBitmap(exit2, 240, 440, null);// 绘制退出按钮
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {// 只取鼠标按下的事件
if (event.getX() > 240 && event.getX() < 280
&& event.getY() > 440
&& event.getY() < 480) {// 按下了退出按钮
activity.myHandler.sendEmptyMessage(3);// 发送消息,切换到MenuView
}
}
return super.onTouchEvent(event);
}
private void initBitmap() {
// TODO Auto-generated method stub
bg=BitmapFactory.decodeResource(getResources(), R.drawable.bg);
introduction=BitmapFactory.decodeResource(getResources(), R.drawable.introduction);
exit2=BitmapFactory.decodeResource(getResources(), R.drawable.exit2);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
this.thread.setFlag(true);//设置循环标志位
this.thread.start();//启动线程
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;//循环标志位
thread.setFlag(false);//设置循环标志位
while (retry) {//循环
try {
thread.join();//等待线程结束
retry = false;//停止循环
}catch (InterruptedException e){}//不断地循环,直到刷帧线程结束
}
}
class TutorialThread extends Thread{//刷帧线程
private int span = 100;//睡眠的毫秒数
private SurfaceHolder surfaceHolder;//SurfaceHolder的引用
private HelpView helpView;
private boolean flag = false;//循环标记位
public TutorialThread(SurfaceHolder surfaceHolder, HelpView helpView) {//构造器
this.surfaceHolder = surfaceHolder;//得到surfaceHolder引用
this.helpView = helpView;
}
public void setFlag(boolean flag) {//设置循环标记位
this.flag = flag;
}
public void run() {//重写的run方法
Canvas c;//画布
while (this.flag) {//循环
c = null;
try {
// 锁定整个画布,在内存要求比较高的情况下,建议参数不要为null
c = this.surfaceHolder.lockCanvas(null);
synchronized (this.surfaceHolder) {//同步锁
helpView.onDraw(c);//调用绘制方法
}
} finally {//使用finally保证下面代码一定被执行
if (c != null) {
//更新屏幕显示内容
this.surfaceHolder.unlockCanvasAndPost(c);
}
}
try{
Thread.sleep(span);//睡眠指定毫秒数
if(intro_y>0)
intro_y-=3;
}catch(Exception e){//捕获异常
e.printStackTrace();//有异常时打印异常堆栈信息
}
}
}
}
}
4.3,游戏界面相关类
GameView.java是本游戏中最主要的类,负责绘制游戏过程中所有的信息,如布局、最好成绩、关卡名、各个按钮等。该类中还包含了一个非常重要的线程TutorialThread,它负责游戏界面的定时刷新。
GameView是本游戏中最重要的类,它继承了SurfaceView,同时实现了SurfaceHolder.Callback,重写了屏幕监听方法onTouchEvent(MotionEvent event),该类中还包含了一个刷帧的线程TutorialThread,每隔一段时间调用一次GameView的onDraw方法进行屏幕的重绘。
在数据存储方面,考虑到本游戏需要存储的数据只有“最好成绩”这一项,所以我们没有采用数据库,而是使用了轻量级的Preference存储方案。Preference主要用于存储和查询简单数据类型的数据,这些简单数据类型包括boolean,int,float,long以及string等,存储方式以键值对的形式存放在应用程序的私有文件夹下。
5,游戏的优化与改进
经过我们的努力,游戏的基本功能已经完成,但是有很多地方可以提升。例如,现在的版本中,我们需要先选中人物,再点击空格位置,才能移动人物。更好的方法是,点击人物后,就自动移动到空位置。此外,游戏的界面还可以做的更漂亮一些。
通过这个小游戏的开发,我们加深了对Android操作系统的认识,对Android的基础知识有了更深入的了解,也为以后大型游戏和应用的开发打好了基础。