Android 之 2048 的游戏逻辑分析
继续学习了极客学院的实战路径课程,讲到了2048游戏的编写过程,我在这里作个总结分享给大家(结果会附源代码和我改写后的代码):
这里主要包括两个方面:1.2048界面的绘制 2.2048算法逻辑的实现 3.添加随机数 4.判断游戏结束
先看效果图(真机上模拟图):

1.界面的绘制
界面的绘制相对还是比较简单的。先新建一个card卡片类,这个类主要是描述在效果图中那16个小方块
/*
* 这个类主要用来初始化2048游戏中的方块
*/
public class Card extends FrameLayout{ private TextView lable;
public Card(Context context) {
super(context);
lable = new TextView(getContext());
lable.setTextSize(32);
lable.setGravity(Gravity.CENTER);
lable.setBackgroundColor(0X30FFFFFF); //30表示透明度,透明度范围是00-ff,后六位是颜色值 //下面设置了Layout_wight和Layout_height分别为match_parent(-1代表match_parent,-2代表wrap_content)
LayoutParams lp = new LayoutParams(-1,-1);
lp.setMargins(10, 10, 10, 10); //设置card的间距
addView(lable,lp);
setNum(0);
} int num = 0;
public int getNum(){
return num;
}
public void setNum(int num) {
this.num = num;
//当cardMap[][]<=0时,设为""
if (num<=0) {
lable.setText("");
}else {
lable.setText(num+"");
}
}
public boolean equals(Card o) {
return getNum()==o.getNum();
}
}
然后新建一个GameView的主类,使它继承GirdLayout,复写其中的方法。再在此之前需要更改一下main_activity布局文件,包括计分的一个textView,和在外面实现的布局。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.game1024.MainActivity" > <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"> <TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:textSize="20sp"
android:text="score:"/>
<TextView
android:id="@+id/cvScore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
/>
</LinearLayout> <com.example.game1024.GameView
android:background="#00ff00"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</com.example.game1024.GameView> </LinearLayout>
public class GameView extends GridLayout{
public GameView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initGame();
}
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
initGame();
}
public GameView(Context context) {
super(context);
initGame();
}
public void initGame(){
setColumnCount(4); //设定规定行数为四行
setBackgroundColor(0XffF4A460);
setOnTouchListener(new View.OnTouchListener() {
private float startX,startY,offsetX,offsetY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//取得点击时的初始坐标
System.out.println("00000");
startX = event.getX();
startY = event.getY();
break;
case MotionEvent.ACTION_UP:
//求得各方向的偏移量
System.out.println("0--0");
offsetX = event.getX() - startX;
offsetY = event.getY() - startY;
/*
* 比较在x轴和y轴的偏移量,可以判断为上下滑动,还是左右滑动
* 接着判断偏移量的正负,来判断是具体向哪个方向的滑动
* 下面我写移动的顺序为:向左,右,上,下
*/
if (Math.abs(offsetX)>Math.abs(offsetY)) {
//当移动的距离大于5dp时才看作移动
if (offsetX<-5) {
swipeLeft();
}else if (offsetX>5) {
swipeRight();
}
}else if (Math.abs(offsetX)<Math.abs(offsetY)) {
if (offsetY<-5) {
swipeUp();
}else if (offsetY>5) {
swipeDown();
}
}
break;
default:
break;
}
return true;
}
});
}
/*
* (non-Javadoc)
* @see android.view.View#onSizeChanged(int, int, int, int)
* 当屏幕的宽高改变时,卡片所占的宽高会随之改变
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int cardWidth =(Math.min(w, h)-10)/4;
addCard(cardWidth, cardWidth);
startGame();
}
//建立一个数组用于存储各个卡片的num
private Card cardMap[][] = new Card[4][4];
/*
* 添加card卡片的方法
*/
private void addCard(int cardwidth, int cardheight){
Card c;
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
c = new Card(getContext());
c.setNum(0);
addView(c,cardwidth,cardheight);
cardMap[x][y]=c;
}
}
}
2.游戏逻辑的算法
这个注释比较清楚,大家看注释吧 ,if(judge)是后面要判断游戏结束时用的
/*
* 移动方面的算法
*/
private void swipeLeft(){
boolean judge = false;
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) { for (int x1 = x+1; x1 < 4; x1++) {
//先判断在当前点的右边是否存在num大于零的点,如果是,进行下一步,否的话继续循环
//判断当前点是否为空(小于等于零相当于空,在card类中已经设置为了不显示),为空:将右面的点赋予当前点,同时自身置零。不为空的话判断当前点是否和
//右面的点相等,相等将当前点的num乘2,并将右面的点置零
if (cardMap[x1][y].getNum()>0) { if (cardMap[x][y].getNum()<=0) {
cardMap[x][y].setNum(cardMap[x1][y].getNum());
cardMap[x1][y].setNum(0);
/*
* 假如在第1个位置有一个数,它将移动到第0个位置,此时继续循环,如果第2个位置也有数,
* 此时因为第0个位置已经存在了数值,所以第2个位置的数不会移动到第一个空白位置,而是保持不变
* 因此需要将x减1,在比较一遍
*/
x--;
judge = true;
}else if (cardMap[x][y].getNum()==cardMap[x1][y].getNum()) {
cardMap[x][y].setNum(cardMap[x][y].getNum()*2);
cardMap[x1][y].setNum(0); MainActivity.getMainActivity().addScore(cardMap[x][y].getNum());
judge = true;
}
break;
}
}
}
}
if (judge) {
checkGame();
addRandom();
}
}
private void swipeRight(){
boolean judge = false;
for (int y = 0; y < 4; y++) {
for (int x = 3; x >= 0; x--) { for (int x1 = x-1; x1 >=0; x1--) {
if (cardMap[x1][y].getNum()>0) { if (cardMap[x][y].getNum()<=0) {
cardMap[x][y].setNum(cardMap[x1][y].getNum());
cardMap[x1][y].setNum(0);
x++;
judge = true;
}else if (cardMap[x][y].getNum()==cardMap[x1][y].getNum()) {
cardMap[x][y].setNum(cardMap[x][y].getNum()*2);
cardMap[x1][y].setNum(0); MainActivity.getMainActivity().addScore(cardMap[x][y].getNum());
judge = true;
}
break;
}
}
}
}
if (judge) {
checkGame();
addRandom();
}
}
private void swipeUp(){
boolean judge = false;
for (int x = 0; x < 4; x++) {
for (int y = 0; y <4; y++) { for (int y1 = y+1 ; y1 <4 ; y1++) {
if (cardMap[x][y1].getNum()>0) { if (cardMap[x][y].getNum()<=0) {
cardMap[x][y].setNum(cardMap[x][y1].getNum());
cardMap[x][y1].setNum(0);
y--;
judge = true;
}else if (cardMap[x][y].getNum()==cardMap[x][y1].getNum()) {
cardMap[x][y].setNum(cardMap[x][y].getNum()*2);
cardMap[x][y1].setNum(0); MainActivity.getMainActivity().addScore(cardMap[x][y].getNum());
judge = true;
}
break;
}
}
}
}
if (judge) {
checkGame();
addRandom();
}
}
private void swipeDown(){
boolean judge = false;
for (int x = 0; x < 4; x++) {
for (int y = 3; y >=0; y--) { for (int y1 = y-1 ; y1 >=0; y1--) {
if (cardMap[x][y1].getNum()>0) { if (cardMap[x][y].getNum()<=0) {
cardMap[x][y].setNum(cardMap[x][y1].getNum());
cardMap[x][y1].setNum(0);
y++;
judge = true;
}else if (cardMap[x][y].getNum()==cardMap[x][y1].getNum()) {
cardMap[x][y].setNum(cardMap[x][y].getNum()*2);
cardMap[x][y1].setNum(0); MainActivity.getMainActivity().addScore(cardMap[x][y].getNum());
judge = true;
}
break;
}
}
}
}
if (judge) {
checkGame();
addRandom();
}
}
3.添加随机出现的点:
同样在GameView中添加方法,startGame()在onSizeChanged()方法中调用
/*
* 游戏开始的初始化方法
*/
private void startGame(){
MainActivity.getMainActivity().clearScore();
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
cardMap[x][y].setNum(0);
}
} addRandom();
addRandom();
} private List<Point> emptyPoint =new ArrayList<Point>();
//添加随机数的方法
private void addRandom(){
emptyPoint.clear();
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
//将cardMap中小于0的点放在emptyPoint中
if (cardMap[x][y].getNum()<=0) {
emptyPoint.add(new Point(x, y));
}
}
}
//随机移除emptyPoint中的一个单元
Point p = emptyPoint.remove((int)(Math.random()*emptyPoint.size()));
cardMap[p.x][p.y].setNum(Math.random()>0.1?2:4);
}
4.判断游戏结束
/*
* 结束游戏方法
* 当存在点和旁边的数相等时,游戏就不会提示已经结束
*/
private void checkGame(){
boolean check = true;
ALL:
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (cardMap[x][y].getNum()==0||
x>0&&cardMap[x][y].equals(cardMap[x-1][y])||
x<3&&cardMap[x][y].equals(cardMap[x+1][y])||
y>0&&cardMap[x][y].equals(cardMap[x][y-1])||
y<3&&cardMap[x][y].equals(cardMap[x][y+1])) {
check = false;
break ALL;
}
}
} // 提示游戏失败
if (check) {
new AlertDialog.Builder(getContext()).setTitle("提示").setMessage("游戏失败").setPositiveButton("再来一次", new DialogInterface.OnClickListener() { @Override
public void onClick(DialogInterface dialog, int which) {
startGame();
}
}).show();
}
}
我改进的2048:
1.增加了最高分显示
2.增加了重新开始游戏按钮
3.增加了颜色变化
Game1024原版:http://pan.baidu.com/s/1sjpCdIl
Game2048改进版地址:http://pan.baidu.com/s/1o6qyfJc
Android 之 2048 的游戏逻辑分析的更多相关文章
- 从零開始开发Android版2048 (二)获取手势信息
今天是尝试開始Android版2048小游戏的第二天.在今天,我主要学习了怎样获取用户在屏幕滑动的手势,以及对布局进行了一些小小的完好. 获取用户操作的手势(比方向左滑.向右滑等)主要用到了Gestu ...
- Swift实战之2048小游戏
上周在图书馆借了一本Swift语言实战入门,入个门玩一玩^_^正好这本书的后面有一个2048小游戏的实例,笔者跟着实战了一把. 差不多一周的时间,到今天,游戏的基本功能已基本实现,细节我已不打算继续完 ...
- 从零開始开发Android版2048 (一)初始化界面
自学Android一个月多了,一直在工作之余零零散散地看一些东西.感觉经常使用的东西都有些了解了,可是一開始写代码总会出各种奇葩的问题.感觉还是代码写得太少.这样继续杂乱地学习下去进度也太慢了,并且学 ...
- 从零開始开发Android版2048 (四) 分数、重置、结束
这一篇的内容主要是在上一篇的基础上,增加分数计算(包含当前分数和最高分数).游戏结束的推断以及游戏界面的重置这三个部分的功能. 一.分数的计算和保存 首先,2048这个游戏的分数包含 ...
- 从零開始开发Android版2048 (三)逻辑推断
近期工作比較忙,所以更新的慢了一点,今天的主要内容是关于Android版2048的逻辑推断,经过本篇的解说,基本上完毕了这个游戏的主体部分. 首先还是看一下,我在实现2048时用到的一些存储的数据结构 ...
- jQuery实践-网页版2048小游戏
▓▓▓▓▓▓ 大致介绍 看了一个实现网页版2048小游戏的视频,觉得能做出自己以前喜欢玩的小游戏很有意思便自己动手试了试,真正的验证了这句话-不要以为你以为的就是你以为的,看视频时觉得看懂了,会写了, ...
- Android快乐贪吃蛇游戏实战项目开发教程-01项目概述与目录
一.项目简介 贪吃蛇是一个很经典的游戏,也很适合用来学习.本教程将和大家一起做一个Android版的贪吃蛇游戏. 我已经将做好的案例上传到了应用宝,无病毒.无广告,大家可以放心下载下来把玩一下.应用宝 ...
- C# 开发2048小游戏
这应该是几个月前,闲的手痒,敲了一上午代码搞出来的,随之就把它丢弃了,当时让别人玩过,提过几条更改建议,但是时至今日,我也没有进行过优化和更改(本人只会作案,不会收场,嘎嘎),下面的建议要给代码爱好的 ...
- Android版的疯狂猜图游戏源码完整版分享
这个游戏源码是在安装教程网那么分享过来的,Android版的疯狂猜图游戏源码完整版分享,也是本人之前很早以前发的一款游戏源码的,大家如果想了解一下,可以看看吧,不说多了,上一个图先吧. > ...
随机推荐
- Tips11:用[Rang]来限制Inspector中的变量
我们在写脚本的过程中可能会用到很多Public变量,如INT型,Float型,这些变量在项目中可能有着一个默认的实际范围,如血量不能为负数,而且int float本来就是有一个范围的,如果对这些变量加 ...
- 使用Html5+C#+微信 开发移动端游戏详细教程 :(五)游戏图像的加载与操作
当我们进入游戏时,是不可能看到所有的图像的,很多图像都是随着游戏功能的打开而出现, 比如只有我打开了"宝石"菜单才会显示宝石的图像,如果是需要显示的时候才加载, 会对用户体验大打折 ...
- [mysql]MySQL忘记密码
1.修改MySQL的登录设置: vi /etc/my.cnf 在[mysqld]的段中加上一句:skip-grant-tables 保存并且退出vi. 2.重新启动mysqld # /etc/init ...
- 3 Servlet监听器
作者:禅楼望月(http://www.cnblogs.com/yaoyinglong) 1. ServletConfig和ServletContext 1.1 ServletConfig和Servle ...
- Laravel 5 多个视图共享数据的方法
我们都知道模板一般会用到继承,导航栏就是一个很好的例子,但是导航栏的数据如何共享,比如有个导航的文件叫在view/navigation.blade.php 为了简单一点,文件里只有设置了一个变量 {{ ...
- 【转】Bloom Filter布隆过滤器的概念和原理
转自:http://blog.csdn.net/jiaomeng/article/details/1495500 之前看数学之美丽,里面有提到布隆过滤器的过滤垃圾邮件,感觉到何其的牛,竟然有这么高效的 ...
- 2013 - Lost connection to MySQL server at 'reading initial communication packet' 错误解决
一.操作与状态 当使用MySQL客户端连接localhost本地数据库时,连接不上,报错.(使用Tomcat连接数据库时可以连接上,但需要很长的请求时间.) 二.原因与解决办法 关于这个问题网上的解决 ...
- 重构第24天 分解复杂的判断(Remove Arrowhead Antipattern)
理解: 当你的代码中有很深的嵌套条件时,花括号就会在代码中形成一个长长的箭头.我们经常在不同的代码中看到这种情况,并且这种情况也会扰乱代码的可读性. 如下代码所示,HasAccess方法里面包含一些嵌 ...
- C#设计模式——迭代器模式(Iterator Pattern)
一.概述在软件开发过程中,我们可能会希望在不暴露一个集合对象内部结构的同时,可以让外部代码透明地访问其中包含的元素.迭代器模式可以解决这一问题.二.迭代器模式迭代器模式提供一种方法顺序访问一个集合对象 ...
- Winform开发框架之字典管理模块的更新,附上最新2013年全国最新县及县以上行政区划代码sql脚本
在很多项目里面,字典管理是必备的项目模块,而这个又是比较通用的功能,因此可以单独做成一个通用字典管理,例如这个模块,可以通过集成的方式,使用在我的<Winform开发框架>.<WCF ...