继续学习了极客学院的实战路径课程,讲到了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