从拼图游戏开始(六)_游戏主体的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

相关文章

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获

Python中pywin32 常用窗口操作的实现

《Python中pywin32常用窗口操作的实现》本文主要介绍了Python中pywin32常用窗口操作的实现,pywin32主要的作用是供Python开发者快速调用WindowsAPI的一个... 目录获取窗口句柄获取最前端窗口句柄获取指定坐标处的窗口根据窗口的完整标题匹配获取句柄根据窗口的类别匹配获取句

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

利用python实现对excel文件进行加密

《利用python实现对excel文件进行加密》由于文件内容的私密性,需要对Excel文件进行加密,保护文件以免给第三方看到,本文将以Python语言为例,和大家讲讲如何对Excel文件进行加密,感兴... 目录前言方法一:使用pywin32库(仅限Windows)方法二:使用msoffcrypto-too