从拼图游戏开始(六)_游戏主体的Android实现

2024-06-14 02:08

本文主要是介绍从拼图游戏开始(六)_游戏主体的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实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1059070

相关文章

基于C++的UDP网络通信系统设计与实现详解

《基于C++的UDP网络通信系统设计与实现详解》在网络编程领域,UDP作为一种无连接的传输层协议,以其高效、低延迟的特性在实时性要求高的应用场景中占据重要地位,下面我们就来看看如何从零开始构建一个完整... 目录前言一、UDP服务器UdpServer.hpp1.1 基本框架设计1.2 初始化函数Init详解

Java中Map的五种遍历方式实现与对比

《Java中Map的五种遍历方式实现与对比》其实Map遍历藏着多种玩法,有的优雅简洁,有的性能拉满,今天咱们盘一盘这些进阶偏基础的遍历方式,告别重复又臃肿的代码,感兴趣的小伙伴可以了解下... 目录一、先搞懂:Map遍历的核心目标二、几种遍历方式的对比1. 传统EntrySet遍历(最通用)2. Lambd

springboot+redis实现订单过期(超时取消)功能的方法详解

《springboot+redis实现订单过期(超时取消)功能的方法详解》在SpringBoot中使用Redis实现订单过期(超时取消)功能,有多种成熟方案,本文为大家整理了几个详细方法,文中的示例代... 目录一、Redis键过期回调方案(推荐)1. 配置Redis监听器2. 监听键过期事件3. Redi

SpringBoot全局异常拦截与自定义错误页面实现过程解读

《SpringBoot全局异常拦截与自定义错误页面实现过程解读》本文介绍了SpringBoot中全局异常拦截与自定义错误页面的实现方法,包括异常的分类、SpringBoot默认异常处理机制、全局异常拦... 目录一、引言二、Spring Boot异常处理基础2.1 异常的分类2.2 Spring Boot默

基于SpringBoot实现分布式锁的三种方法

《基于SpringBoot实现分布式锁的三种方法》这篇文章主要为大家详细介绍了基于SpringBoot实现分布式锁的三种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、基于Redis原生命令实现分布式锁1. 基础版Redis分布式锁2. 可重入锁实现二、使用Redisso

SpringBoo WebFlux+MongoDB实现非阻塞API过程

《SpringBooWebFlux+MongoDB实现非阻塞API过程》本文介绍了如何使用SpringBootWebFlux和MongoDB实现非阻塞API,通过响应式编程提高系统的吞吐量和响应性能... 目录一、引言二、响应式编程基础2.1 响应式编程概念2.2 响应式编程的优势2.3 响应式编程相关技术

C#实现将XML数据自动化地写入Excel文件

《C#实现将XML数据自动化地写入Excel文件》在现代企业级应用中,数据处理与报表生成是核心环节,本文将深入探讨如何利用C#和一款优秀的库,将XML数据自动化地写入Excel文件,有需要的小伙伴可以... 目录理解XML数据结构与Excel的对应关系引入高效工具:使用Spire.XLS for .NETC

Nginx更新SSL证书的实现步骤

《Nginx更新SSL证书的实现步骤》本文主要介绍了Nginx更新SSL证书的实现步骤,包括下载新证书、备份旧证书、配置新证书、验证配置及遇到问题时的解决方法,感兴趣的了解一下... 目录1 下载最新的SSL证书文件2 备份旧的SSL证书文件3 配置新证书4 验证配置5 遇到的http://www.cppc

Nginx之https证书配置实现

《Nginx之https证书配置实现》本文主要介绍了Nginx之https证书配置的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起... 目录背景介绍为什么不能部署在 IIS 或 NAT 设备上?具体实现证书获取nginx配置扩展结果验证

SpringBoot整合 Quartz实现定时推送实战指南

《SpringBoot整合Quartz实现定时推送实战指南》文章介绍了SpringBoot中使用Quartz动态定时任务和任务持久化实现多条不确定结束时间并提前N分钟推送的方案,本文结合实例代码给大... 目录前言一、Quartz 是什么?1、核心定位:解决什么问题?2、Quartz 核心组件二、使用步骤1