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

相关文章

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

Java中List转Map的几种具体实现方式和特点

《Java中List转Map的几种具体实现方式和特点》:本文主要介绍几种常用的List转Map的方式,包括使用for循环遍历、Java8StreamAPI、ApacheCommonsCollect... 目录前言1、使用for循环遍历:2、Java8 Stream API:3、Apache Commons

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

使用Python实现高效的端口扫描器

《使用Python实现高效的端口扫描器》在网络安全领域,端口扫描是一项基本而重要的技能,通过端口扫描,可以发现目标主机上开放的服务和端口,这对于安全评估、渗透测试等有着不可忽视的作用,本文将介绍如何使... 目录1. 端口扫描的基本原理2. 使用python实现端口扫描2.1 安装必要的库2.2 编写端口扫

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

MySQL分表自动化创建的实现方案

《MySQL分表自动化创建的实现方案》在数据库应用场景中,随着数据量的不断增长,单表存储数据可能会面临性能瓶颈,例如查询、插入、更新等操作的效率会逐渐降低,分表是一种有效的优化策略,它将数据分散存储在... 目录一、项目目的二、实现过程(一)mysql 事件调度器结合存储过程方式1. 开启事件调度器2. 创

使用Python实现操作mongodb详解

《使用Python实现操作mongodb详解》这篇文章主要为大家详细介绍了使用Python实现操作mongodb的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、示例二、常用指令三、遇到的问题一、示例from pymongo import MongoClientf

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

基于Go语言实现一个压测工具

《基于Go语言实现一个压测工具》这篇文章主要为大家详细介绍了基于Go语言实现一个简单的压测工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录整体架构通用数据处理模块Http请求响应数据处理Curl参数解析处理客户端模块Http客户端处理Grpc客户端处理Websocket客户端

Java CompletableFuture如何实现超时功能

《JavaCompletableFuture如何实现超时功能》:本文主要介绍实现超时功能的基本思路以及CompletableFuture(之后简称CF)是如何通过代码实现超时功能的,需要的... 目录基本思路CompletableFuture 的实现1. 基本实现流程2. 静态条件分析3. 内存泄露 bug