本文主要是介绍从拼图游戏开始(六)_游戏主体的Android实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
好的,要开始正式编码了。说实话目前为止笔者也不知道这个东西该怎么写,只是觉得能够完成它,于是就写了。以至于这种边想边写的程序,必然会存在一些需要优化的地方,但是这里笔者也仅是抛砖引玉,不敢自称高手。
今天写的是游戏主体的实现,因为不想一上来就摆一大堆实体类、字段名,看的人头大。所以本文论述的仅仅是游戏主题的实现,暂时不考虑数据持久化时的字段等细节,而诸如字段这样的细节,会在后面的文章中统一论述。
先上效果图,代码可在本文最后下载:
看上去有点卡,这是因为模拟器和PC屏幕录制软件的问题,笔者可以保证在真机上效果很流畅。这里有以下几点需要论述一下:
随机问题数据的生成方法,通常有两种方法:
一、随机的交换矩阵中的任意两个元素,这种方式生成的问题,求解过程通常十分耗时。
二、从目标状态开始随机移动,造成一种随机的分布,在随机移动步数不大时,这种生成方法比较容易求解。
本程序采用第一种方法生成随机数据,但是目前演示时为了避免过程时间的求解等待时间,这里使用了一个固定问题。读者也可以手动移动问题后,再单击“自动”按钮进行求解。
随机问题数据的可解性讨论:
可以参见本人的另外一篇文章:从拼图游戏开始(三)_可行解的讨论
问题的求解方法:
可以参考本人的另外一篇文章:从拼图游戏开始(四)_IDA*算法求解Java实现
实现中需要注意的细节:
一、单元格的移动使用Animation和View.setLayoutParams()实现,注意需要在AnimationListener中的onAnimationEnd()方法中清除组件上的Animation,否则会出现抖动现象。
二、自定义组件GamePanel类,继承自RelativeLayout,向其中添加自定义"方格"类TileView,注意如果需要自定义GamePanel的尺寸,则需要重写onMeasure()方法。GamePanel类的实现代码在本文最后。
以下是部分代码:
游戏主体GamePanel
package com.wly.puzzle15;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.RelativeLayout;
/**
* 游戏单元,一个自定义的组件,封装了组件和事件
* @author wly
*
*/
public class GamePanel extends RelativeLayout implements OnClickListener {
//当前游戏数据
private int[][] mData;
//游戏中大图片被切割后的小图片数组
private Bitmap[] bitmaps;
//当前"空格"的位置
private int blank_row;
private int blank_column;
TileView[][] tiles;
private int tileWidth;
private int tileHeight;
private Context mContext;
private Handler mHandler;
//自动机
private AutoRunner autoRunner;
// //当前游戏状态
// private int mState;
// //当前步骤
// private int mCost;
// //游戏中用到的大图片
// private Bitmap originalBitmap;
// //最后一次玩的时间
// private long lastPlay;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
move(msg.getData().getInt("direction"),
msg.getData().getInt("row"),
msg.getData().getInt("column"));
}
};
public GamePanel(Context context,Handler handler) {
super(context);
this.mContext = context;
this.mHandler = handler;
init(context);
}
public Context getMContext() {
return mContext;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
}
public void autoRun() {
if(autoRunner != null) {
autoRunner.cancel();
}
autoRunner = new AutoRunner(this,handler, new SolvePuzzleListener() {
@Override
public void start() {
//发送消息给MainActivity,显示加载进度条
Message msg = new Message();
msg.what = 0;
mHandler.sendMessage(msg);
}
@Override
public void finished() {
//发送消息给MainActivity,隐藏加载进度条
Message msg = new Message();
msg.what = 1;
mHandler.sendMessage(msg);
}
});
autoRunner.start();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(tileWidth * Conf.SIZE, tileHeight * Conf.SIZE);
}
/**
* 重新随机生成问题数据
*/
public void init(Context context) {
this.removeAllViews();
this.mData = new PuzzleGenerator().getPuzzleData();
for(int i=0;i<mData.length;i++) {
for(int j=0;j<mData.length;j++) {
if(mData[i][j] == 0) {
blank_row = i;
blank_column = j;
}
}
}
this.setBackgroundColor(Conf.panelBG);
bitmaps = new Bitmap[Conf.SIZE * Conf.SIZE];
tiles = new TileView[Conf.SIZE][Conf.SIZE];
bitmaps[0]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no0);
bitmaps[1]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no1);
bitmaps[2]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no2);
bitmaps[3]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no3);
bitmaps[4]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no4);
bitmaps[5]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no5);
bitmaps[6]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no6);
bitmaps[7]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no7);
bitmaps[8]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no8);
bitmaps[9]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no9);
bitmaps[10]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no10);
bitmaps[11]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no11);
bitmaps[12]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no12);
bitmaps[13]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no13);
bitmaps[14]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no14);
bitmaps[15]=BitmapFactory.decodeResource(context.getResources(), R.drawable.no15);
tileWidth = bitmaps[0].getWidth();
tileHeight = bitmaps[0].getHeight();
RelativeLayout.LayoutParams containerParams =
new RelativeLayout.LayoutParams(Conf.SIZE * tileWidth,Conf.SIZE * tileHeight);
this.setLayoutParams(containerParams);
for(int i=0;i<Conf.SIZE;i++) {
for(int j=0;j<Conf.SIZE;j++) {
tiles[i][j] = new TileView(context,mData[i][j],i,j);
tiles[i][j].setImageBitmap(bitmaps[mData[i][j]]);
// tiles[i][j].setId(i*Conf.SIZE + j + 2000);
// tiles[i][j].setOnTouchListener(this);
tiles[i][j].setOnClickListener(this);
RelativeLayout.LayoutParams layoutParams =
new RelativeLayout.LayoutParams(tileWidth, tileHeight);
layoutParams.leftMargin = tileWidth * j;
layoutParams.topMargin = tileHeight * i;
tiles[i][j].setLayoutParams(layoutParams);
if(mData[i][j] != 0) { //不添加"空格"
this.addView(tiles[i][j]);
}
}
}
}
public int getBlankRow() {
return blank_row;
}
public int getBlankColumn() {
return blank_column;
}
/**
* 得到当前单击单元的可以移动的方向
* @return
*/
public int getMoveDirection(int row,int column) {
if(row > 0 && tiles[row-1][column].getData() == 0) {
return Conf.UP;
} else if(row < (mData.length-1)
&& tiles[row+1][column].getData() == 0) {
return Conf.DOWN;
} else if(column > 0 && tiles[row][column-1].getData() == 0) {
return Conf.LEFT;
} else if(column < (mData[0].length-1)
&& tiles[row][column+1].getData() == 0) {
return Conf.RIGHT;
} else {
return Conf.UNMOVABLE;
}
}
/**
* 移动数据,交换数据元素
* @param array
* @param row
* @param column
* @param direction
*/
private void moveData(int[][] array,TileView[][] tiles,int row,int column,int direction) {
int temp = 0;
TileView tempView;
switch(direction) {
case Conf.UP:
temp = array[row][column];
array[row][column] = array[row-1][column];
array[row-1][column] = temp;
//设置TileView的位置标记
tempView = tiles[row][column];
tiles[row][column] = tiles[row-1][column];
tiles[row][column].setRow(row+1);
tiles[row-1][column] = tempView;
tiles[row-1][column].setRow(row-1);
blank_row ++;
break;
case Conf.DOWN:
temp = array[row][column];
array[row][column] = array[row+1][column];
array[row+1][column] = temp;
//设置TileView的位置标记
tempView = tiles[row][column];
tiles[row][column] = tiles[row+1][column];
tiles[row][column].setRow(row-1);
tiles[row+1][column] = tempView;
tiles[row+1][column].setRow(row+1);
blank_row --;
break;
case Conf.LEFT:
temp = array[row][column];
array[row][column] = array[row][column-1];
array[row][column-1] = temp;
tempView = tiles[row][column];
tiles[row][column] = tiles[row][column-1];
tiles[row][column].setColumn(column+1);
tiles[row][column-1] = tempView;
tiles[row][column-1].setColumn(column-1);
blank_column ++;
break;
case Conf.RIGHT:
temp = array[row][column];
array[row][column] = array[row][column+1];
array[row][column+1] = temp;
//设置TileView的位置标记
tempView = tiles[row][column];
tiles[row][column] = tiles[row][column+1];
tiles[row][column].setColumn(column-1);
tiles[row][column+1] = tempView;
tiles[row][column+1].setColumn(column+1);
blank_column --;
break;
case Conf.UNMOVABLE:
break;
}
}
public void printMatirx(int[][] array) {
for(int i=0;i<array.length;i++) {
for(int j=0;j<array[0].length;j++) {
System.out.print(array[i][j] + " ");
}
System.out.println();
}
}
@Override
public void onClick(final View v) {
if(v instanceof TileView) {
int row = ((TileView) v).getRow();
int column = ((TileView) v).getColumn();
int direction = getMoveDirection(row,column);
move(direction, row, column);
}
}
public int[][] getData() {
return mData;
}
public void setData(int[][] mData) {
this.mData = mData;
}
/**
* 自动机解题调用方法
* @param direction
*/
public void move(int direction,int row,int column) {
final TileView v = tiles[row][column];
TranslateAnimation tAnimation = null;
final RelativeLayout.LayoutParams layoutParams =
new RelativeLayout.LayoutParams(tileWidth,tileHeight);
direction = getMoveDirection(row,column);
moveData(mData,tiles,row, column, direction);
printMatirx(mData);
switch(direction) {
case Conf.UP:
tAnimation = new TranslateAnimation(0,
0, 0, -tileHeight);
layoutParams.leftMargin = v.getLeft();
layoutParams.topMargin = v.getTop() - tileHeight;
break;
case Conf.DOWN:
tAnimation = new TranslateAnimation(0,
0, 0, tileHeight);
layoutParams.leftMargin = v.getLeft();
layoutParams.topMargin = v.getTop() + tileHeight;
break;
case Conf.LEFT:
tAnimation = new TranslateAnimation(0,
-tileWidth, 0, 0);
layoutParams.leftMargin = v.getLeft() - tileWidth;
layoutParams.topMargin = v.getTop();
break;
case Conf.RIGHT:
tAnimation = new TranslateAnimation(0,
tileWidth, 0, 0);
layoutParams.leftMargin = v.getLeft() + tileWidth;
layoutParams.topMargin = v.getTop();
break;
case Conf.UNMOVABLE:
break;
}
if(tAnimation != null) { //可能单击了不可移动的位置
tAnimation.setDuration(500);
v.startAnimation(tAnimation);
tAnimation.setFillAfter(false);
tAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
//取消组件上的动画,来避免发生闪烁
v.clearAnimation();
//设置Layout,因为使用Animation移动的图片,并没有移动焦点
v.setLayoutParams(layoutParams);
}
});
}
GamePanel.this.invalidate();
}
}
自动求解机AutoRunner
package com.wly.puzzle15;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
/**
* 一个线程做的自动机
* @author wly
*
*/
public class AutoRunner extends Thread{
//每次移动单元的间隔时间
private long MOVE_TIME = 1000;
private GamePanel gamePanel;
//用于存放解的步骤:0,1,2,3
private int[] moves = new int[1000];
private boolean isAlive = false;
//因为求解问题是一个比较耗时的动作,所以这里做了一个回调接口,来显示加载进度条
private SolvePuzzleListener mListener;
private Handler mHandler;
public AutoRunner(GamePanel gamePanel,Handler handler,SolvePuzzleListener listener) {
this.gamePanel = gamePanel;
this.mListener = listener;
this.mHandler = handler;
}
@Override
public void run() {
super.run();
mListener.start();
IDAStarAlgorithm idaStarAlgorithm = new IDAStarAlgorithm(gamePanel.getData());
moves = idaStarAlgorithm.getSolvePath(gamePanel.getData());
mListener.finished();
int i = 0;
isAlive = true;
while(isAlive && moves[i] != -1) {
Message msg = new Message();
Intent intent = new Intent();
Bundle bundle = new Bundle();
switch(moves[i]) {
case Conf.UP:
bundle.putInt("row", gamePanel.getBlankRow()-1);
bundle.putInt("column", gamePanel.getBlankColumn());
bundle.putInt("direction", Conf.DOWN);
msg.setData(bundle);
mHandler.sendMessage(msg);
break;
case Conf.DOWN:
bundle.putInt("row", gamePanel.getBlankRow()+1);
bundle.putInt("column", gamePanel.getBlankColumn());
bundle.putInt("direction", Conf.UP);
msg.setData(bundle);
mHandler.sendMessage(msg);
break;
case Conf.LEFT:
bundle.putInt("row",gamePanel.getBlankRow());
bundle.putInt("column", gamePanel.getBlankColumn()-1);
bundle.putInt("direction", Conf.RIGHT);
msg.setData(bundle);
mHandler.sendMessage(msg);
break;
case Conf.RIGHT:
bundle.putInt("row",gamePanel.getBlankRow());
bundle.putInt("column", gamePanel.getBlankColumn()+1);
bundle.putInt("direction", Conf.LEFT);
msg.setData(bundle);
mHandler.sendMessage(msg);
break;
}
i++;
try {
this.sleep(MOVE_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void cancel() {
isAlive = false;
}
}
/**
* 表示当前求解问题的回调接口
* @author wly
*
*/
interface SolvePuzzleListener {
public void start();
public void finished();
}
问题生成器PuzzleGenerator
package com.wly.puzzle15;
/**
* 游戏数据生成器
* 其中关于问题的可解性讨论,可参考:
* http://blog.csdn.net/u011638883/article/details/17139739
* @author wly
*
*/
public class PuzzleGenerator {
/**
* 得到一个可解的问题数据
* @return
*/
public int[][] getPuzzleData() {
//-------------------------
//随机生成问题数据正确方法
//-------------------------
// int[][] data = new int[Conf.SIZE][Conf.SIZE];
// for(int i=0;i<data.length;i++) {
// for(int j=0;j<data.length;j++) {
// data[i][j] = i*data.length + j;
// }
// }
// for(int i=0;i<data.length;i++) {
// for(int j=0;j<data.length;j++) {
// int index1 = (int)(Math.random() * Conf.SIZE);
// int index2 = (int)(Math.random() * Conf.SIZE);
// int temp = data[index1][index2];
// data[index1][index2] = data[i][j];
// data[i][j] = temp;
// }
// }
//-------------------------
//简单测试数据,仅作演示之用
//-------------------------
int[][] data = {
{5,1,3,4},
{9,2,6,8},
{13,10,7,11},
{0,14,15,12}
};
//检查问题是否可解
if(canSolve(data)) {
return data;
} else {
return getPuzzleData();
}
}
/**
* 讨论问题的可解性
* @param state 状态
*/
private boolean canSolve(int[][] state) {
int blank_row = 0; //"空格"所在的行数
for(int i=0;i<state.length;i++) {
for(int j=0;j<state.length;j++) {
if(state[i][j] == 0) {
blank_row = i;
}
}
}
if(state.length % 2 == 1) { //问题宽度为奇数
return (getInversions(state) % 2 == 0);
} else { //问题宽度为偶数
if((state.length - blank_row) % 2 == 1) { //从底往上数,空格位于奇数行
return (getInversions(state) % 2 == 0);
} else { //从底往上数,空位位于偶数行
return (getInversions(state) % 2 == 1);
}
}
}
/**
* 计算问题的"倒置变量和"
* @param state
*/
private int getInversions(int[][] state) {
int inversion = 0;
int temp = 0;
for(int i=0;i<state.length;i++) {
for(int j=0;j<state[i].length;j++) {
int index = i* state.length + j + 1;
while(index < (state.length * state.length)) {
if(state[index/state.length][index%state.length] != 0
&& state[index/state.length]
[index%state.length] < state[i][j]) {
temp ++;
}
index ++;
}
inversion = temp + inversion;
temp = 0;
}
}
return inversion;
}
}
完整工程下载:http://download.csdn.net/detail/u011638883/6761927
O啦~~~
转载请保留出处:http://blog.csdn.net/u011638883/article/details/17401179
谢谢!!
这篇关于从拼图游戏开始(六)_游戏主体的Android实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!