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

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现