2048游戏拆解_附上我最真诚的心得

经过对2048游戏的拆解,发现了里面越来越多的好点子,拆游戏,拆得是游戏的思路,为了拆一个游戏,我把每一行代码基本上都写了注释,所以就算是新手也能轻易的了解一个游戏的来龙去脉。

           此游戏的代码需要的点子有很多:

1.View代码布局

2.SharedPreferences

3.requestWindowFeature

4.2048游戏代码逻辑分析

还有很多知识点我就不一一罗列了,比如Point的使用,移动动画 TranslateAnimation,缩放动画ScaleAnimation,对View.OnTouchListener()的重写等等。

 

现在开始上代码加注解:

主类,此类主要用来初始化

MainActivity:

 

public class MainActivity extends Activity {

	/*
	 * 
	 * QQ:845145080
	 */

	public MainActivity() {
		mainActivity = this;
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		requestWindowFeature(Window.FEATURE_NO_TITLE); // 去掉标题
		setContentView(R.layout.activity_main);

		root = (LinearLayout) findViewById(R.id.container); // 通过id找到Linearlayout
		root.setBackgroundColor(0xfffaf8ef); // 给背景加颜色

		tvScore = (TextView) findViewById(R.id.tvScore); // 分数的显示
		tvBestScore = (TextView) findViewById(R.id.tvBestScore); // 最高分数的显示

		gameView = (GameView) findViewById(R.id.gameView); // 将GameView这个类做成了一个控件,在主文件中找到这个控件

		btnNewGame = (Button) findViewById(R.id.btnNewGame); // 重新开始按钮
		btnNewGame.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				gameView.startGame(); // GameView类中定义的方法,清除分数,将所有的格子里的显示为0
			}
		});

		animLayer = (AnimLayer) findViewById(R.id.animLayer); // 找到布局文件中的控件
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {

		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	public void clearScore() { // 点击重新开始时,会把分数一栏归零
		score = 0;
		showScore();
	}

	public void showScore() {
		tvScore.setText(score + ""); // 显示分数
	}

	public void addScore(int s) { // 如果Card移动的时候遇到一个有值的,就将自己的值和遇到的值相加
		score += s;
		showScore();

		int maxScore = Math.max(score, getBestScore()); // 将现在的值和存储的最大的值进行比较,取出两个中较大的一个
		saveBestScore(maxScore); // 将最大的那个值存到sd卡中
		showBestScore(maxScore); // 在显示最大的那个TextView中显示出来
	}

	public void saveBestScore(int s) { // 保存数据,将最大的值存到sd卡中
		Editor e = getPreferences(MODE_PRIVATE).edit();
		e.putInt(SP_KEY_BEST_SCORE, s);
		e.commit();
	}

	public int getBestScore() { // MODE_PRIVATE为能读能写
		return getPreferences(MODE_PRIVATE).getInt(SP_KEY_BEST_SCORE, 0); // SP_KEY_BEST_SCORE="bestScore"
																			// getInt用键值对
	}

	public void showBestScore(int s) { // 最高纪录的那个地方会显示出保存的最好的成绩
		tvBestScore.setText(s + "");
	}

	public AnimLayer getAnimLayer() {
		return animLayer;
	}

	private int score = 0;
	private TextView tvScore, tvBestScore;
	private LinearLayout root = null;
	private Button btnNewGame;
	private GameView gameView;
	private AnimLayer animLayer = null;

	private static MainActivity mainActivity = null;

	public static MainActivity getMainActivity() {
		return mainActivity;
	}

	public static final String SP_KEY_BEST_SCORE = "bestScore";

}

 

 

此类主要用来描述动画的效果
AnimLayout:

 

public class AnimLayer extends FrameLayout {

	public AnimLayer(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initLayer();
	}

	public AnimLayer(Context context, AttributeSet attrs) {
		super(context, attrs);
		initLayer();
	}

	public AnimLayer(Context context) {
		super(context);
		initLayer();
	}

	private void initLayer() {
		
		
	}

	public void createMoveAnim(final Card from, final Card to, int fromX,
			int toX, int fromY, int toY) {

		final Card c = getCard(from.getNum());      //将要移动的Card从原来的地方消失
		LayoutParams lp = new LayoutParams(Config.CARD_WIDTH, Config.CARD_WIDTH);   //CARD_WIDTH=0
		lp.leftMargin = fromX * Config.CARD_WIDTH;  //将card里面的布局的宽和高变成0;
		lp.topMargin = fromY * Config.CARD_WIDTH;
		c.setLayoutParams(lp);  //c在执行这个方法之后,view的大小将发生改变

		if (to.getNum() <= 0) {    //如果移动到的位置的Card为0,则将此Card的TextView隐藏。
			to.getLabel().setVisibility(View.INVISIBLE);
		}
		
/* TranslateAnimation为位移动画       
 * TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
 * 
 * 在上面的时候,因为自定义的布局的大小发生了改变,所以会自动调用onSizeChanged()方法,Config.CARD_WIDTH = (Math.min(w, h) - 10) / Config.LINES;
 * */
		TranslateAnimation ta = new TranslateAnimation(0, Config.CARD_WIDTH * (toX - fromX), 0, Config.CARD_WIDTH * (toY - fromY));
		ta.setDuration(100);  //动画持续的时间
		ta.setAnimationListener(new Animation.AnimationListener() {

			@Override
			public void onAnimationStart(Animation animation) {
			}

			@Override
			public void onAnimationRepeat(Animation animation) {
			}

			@Override
			public void onAnimationEnd(Animation animation) {
				to.getLabel().setVisibility(View.VISIBLE);  //将移过去的布局再打开
				recycleCard(c);
			}
		});
		c.startAnimation(ta);   
	}

	private Card getCard(int num) {
		Card c;
		if (cards.size() > 0) {
			c = cards.remove(0);
		} else {
			c = new Card(getContext());
			addView(c);
		}
		c.setVisibility(View.VISIBLE);
		c.setNum(num);   
		return c;
	}

	private void recycleCard(Card c) {   //因为原来要移动的Card已经移走了,剩下的地方就是空了,将剩下的地方添加到list中,list是专门存储空的card的
		c.setVisibility(View.INVISIBLE);
		c.setAnimation(null);
		cards.add(c);
	}

	private List cards = new ArrayList();
	
	/*
	 * ScaleAnimation(float fromX, float toX, float fromY, float toY,int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) 
     *
     *float fromX 动画起始时 X坐标上的伸缩尺寸 
      float toX 动画结束时 X坐标上的伸缩尺寸 
      float fromY 动画起始时Y坐标上的伸缩尺寸 
      float toY 动画结束时Y坐标上的伸缩尺寸 
      int pivotXType 动画在X轴相对于物件位置类型 
      float pivotXValue 动画相对于物件的X坐标的开始位置 
      int pivotYType 动画在Y轴相对于物件位置类型 
      float pivotYValue 动画相对于物件的Y坐标的开始位置 
      
      
      Animation.RELATIVE_TO_SELF为相对于自身
      Animation.RELATIVE_TO_PARENT为相对于父控件
     *
    */

	public void createScaleTo1(Card target) {
		ScaleAnimation sa = new ScaleAnimation(0.1f, 1, 0.1f, 1,
				Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,    //相对于Card自身的0.5f就是Card的中心
				0.5f);
		sa.setDuration(100);  //持续时间为0.5s
		target.setAnimation(null);
		target.getLabel().startAnimation(sa);   //给Card对应的TextView控件添加动画
	}

}


此类主要用来实现关键性功能,如判断手移动的方向

 

GameView:

 

public class GameView extends LinearLayout {

	public GameView(Context context) {
		super(context);
		initGameView();
	}

	public GameView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initGameView();
	}

	private void initGameView() {
		setOrientation(LinearLayout.VERTICAL);
		setBackgroundColor(0xff9B30FF);

		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:   //如果是点击下来,获取点击地点的x和y的坐标
					startX = event.getX();   
					startY = event.getY();
					break;
				case MotionEvent.ACTION_UP:     //离开屏幕时的位置,获取离开屏幕时的位置,并获得位移量
					offsetX = event.getX() - startX;  //手指离开时的X坐标减去按下去时X的坐标
					offsetY = event.getY() - startY; // 手指离开时 的Y坐标减去按下去时的Y的坐标

					if (Math.abs(offsetX) > Math.abs(offsetY)) {  //取offsetxX和offsetY的绝对值
						if (offsetX < -5) {                 
							swipeLeft();  //滑动向左
						} else if (offsetX > 5) {
							swipeRight();   //向右划
						}
					} else {
						if (offsetY < -5) {
							swipeUp();    //向上划
						} else if (offsetY > 5) {
							swipeDown();   //向下划
						}
					}

					break;
				}
				return true;   //这个地方如果是false的话,手指抬起时是不会得到坐标的
			}
		});
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {   //在定义的view的大小改变时,系统会自动调用这个方法
		super.onSizeChanged(w, h, oldw, oldh);

		Config.CARD_WIDTH = (Math.min(w, h) - 10) / Config.LINES;

		addCards(Config.CARD_WIDTH, Config.CARD_WIDTH);

		startGame();
	}

	private void addCards(int cardWidth, int cardHeight) {

		Card c;

		LinearLayout line;
		LinearLayout.LayoutParams lineLp;

		for (int y = 0; y < Config.LINES; y++) {
			line = new LinearLayout(getContext());
			lineLp = new LinearLayout.LayoutParams(-1, cardHeight);
			addView(line, lineLp);

			for (int x = 0; x < Config.LINES; x++) {
				c = new Card(getContext());
				line.addView(c, cardWidth, cardHeight);

				cardsMap[x][y] = c;
			}
		}
	}

	public void startGame() {   //重新开始按钮,点击之后分数会被清零

		MainActivity aty = MainActivity.getMainActivity();
		aty.clearScore();
		aty.showBestScore(aty.getBestScore());
		for (int y = 0; y < Config.LINES; y++) {
			for (int x = 0; x < Config.LINES; x++) {  //LINES=4
				cardsMap[x][y].setNum(0);   //最开始的时候,将所有的卡片的值都设为0,
			}
		}

		addRandomNum();   //一开始的时候会产生两个card
		addRandomNum();
	}

	private void addRandomNum() {

		emptyPoints.clear();

		for (int y = 0; y < Config.LINES; y++) {
			for (int x = 0; x < Config.LINES; x++) {
				if (cardsMap[x][y].getNum() <= 0) {   //如果Card为空的话,则将这个card记录下来
					emptyPoints.add(new Point(x, y));
				}
			}
		}

		if (emptyPoints.size() > 0) {
			/*
			 * Math.random()会随机产生一个0-1之间的小数,假如emptyPoints.size()等于16
			 * (int) (Math.random() * emptyPoints.size())会产生一个0到16之间的数。
			 * 
			 * */
			Point p = emptyPoints.remove((int) (Math.random() * emptyPoints.size()));  //从Card为0的卡片中随机移除一个
			cardsMap[p.x][p.y].setNum(Math.random() > 0.1 ? 2 : 4);    //被移除的card被2或者4替换,且出现2的几率更大一些
			MainActivity.getMainActivity().getAnimLayer()
					.createScaleTo1(cardsMap[p.x][p.y]);    //给新生成的Card添加动画效果
		}
	}

	//滑动向左移动运算
	private void swipeLeft() {

		boolean merge = false;

		for (int y = 0; y < Config.LINES; y++) {  //LINES=4
			for (int x = 0; x < Config.LINES; x++) {

				for (int x1 = x + 1; x1 < Config.LINES; x1++) {//向左滑动时,将全部的数组遍历一遍,如果找到不为0的,且其左边为0时
					if (cardsMap[x1][y].getNum() > 0) {  

						if (cardsMap[x][y].getNum() <= 0) {   //cardsMap[x][y].getNum()获取card上面的数字

							MainActivity
									.getMainActivity()
									.getAnimLayer()
									.createMoveAnim(cardsMap[x1][y],
											cardsMap[x][y], x1, x, y, y);

							cardsMap[x][y].setNum(cardsMap[x1][y].getNum());   //
							cardsMap[x1][y].setNum(0);

							x--;
							merge = true;

						} else if (cardsMap[x][y].equals(cardsMap[x1][y])) {
							MainActivity
									.getMainActivity()
									.getAnimLayer()
									.createMoveAnim(cardsMap[x1][y],
											cardsMap[x][y], x1, x, y, y);   //使用这个方法之后,在效果上可以表示成移动了
							cardsMap[x][y].setNum(cardsMap[x][y].getNum() * 2);//这个地方可以修改数字的增加原本是2
							cardsMap[x1][y].setNum(0);

							MainActivity.getMainActivity().addScore(   
									cardsMap[x][y].getNum());
							merge = true;
						}

						break;
					}
				}
			}
		}

		if (merge) {
			addRandomNum();
			checkComplete();
		}
	}
	//滑动向右移动运算
	private void swipeRight() {

		boolean merge = false;

		for (int y = 0; y < Config.LINES; y++) {
			for (int x = Config.LINES - 1; x >= 0; x--) {

				for (int x1 = x - 1; x1 >= 0; x1--) {
					if (cardsMap[x1][y].getNum() > 0) {

						if (cardsMap[x][y].getNum() <= 0) {
							MainActivity
									.getMainActivity()
									.getAnimLayer()
									.createMoveAnim(cardsMap[x1][y],
											cardsMap[x][y], x1, x, y, y);
							cardsMap[x][y].setNum(cardsMap[x1][y].getNum());
							cardsMap[x1][y].setNum(0);

							x++;
							merge = true;
						} else if (cardsMap[x][y].equals(cardsMap[x1][y])) {
							MainActivity
									.getMainActivity()
									.getAnimLayer()
									.createMoveAnim(cardsMap[x1][y],
											cardsMap[x][y], x1, x, y, y);
							cardsMap[x][y].setNum(cardsMap[x][y].getNum() * 2);
							cardsMap[x1][y].setNum(0);
							MainActivity.getMainActivity().addScore(
									cardsMap[x][y].getNum());
							merge = true;
						}

						break;
					}
				}
			}
		}

		if (merge) {
			addRandomNum();
			checkComplete();
		}
	}
	//滑动向上移动运算
	private void swipeUp() {

		boolean merge = false;

		for (int x = 0; x < Config.LINES; x++) {
			for (int y = 0; y < Config.LINES; y++) {

				for (int y1 = y + 1; y1 < Config.LINES; y1++) {
					if (cardsMap[x][y1].getNum() > 0) {

						if (cardsMap[x][y].getNum() <= 0) {
							MainActivity
									.getMainActivity()
									.getAnimLayer()
									.createMoveAnim(cardsMap[x][y1],
											cardsMap[x][y], x, x, y1, y);
							cardsMap[x][y].setNum(cardsMap[x][y1].getNum());
							cardsMap[x][y1].setNum(0);

							y--;

							merge = true;
						} else if (cardsMap[x][y].equals(cardsMap[x][y1])) {
							MainActivity
									.getMainActivity()
									.getAnimLayer()
									.createMoveAnim(cardsMap[x][y1],
											cardsMap[x][y], x, x, y1, y);
							cardsMap[x][y].setNum(cardsMap[x][y].getNum() * 2);
							cardsMap[x][y1].setNum(0);
							MainActivity.getMainActivity().addScore(
									cardsMap[x][y].getNum());
							merge = true;
						}

						break;

					}
				}
			}
		}

		if (merge) {
			addRandomNum();
			checkComplete();
		}
	}
	//滑动向下移动运算
	private void swipeDown() {

		boolean merge = false;

		for (int x = 0; x < Config.LINES; x++) {
			for (int y = Config.LINES - 1; y >= 0; y--) {

				for (int y1 = y - 1; y1 >= 0; y1--) {
					if (cardsMap[x][y1].getNum() > 0) {

						if (cardsMap[x][y].getNum() <= 0) {
							MainActivity
									.getMainActivity()
									.getAnimLayer()
									.createMoveAnim(cardsMap[x][y1],
											cardsMap[x][y], x, x, y1, y);
							cardsMap[x][y].setNum(cardsMap[x][y1].getNum());
							cardsMap[x][y1].setNum(0);

							y++;
							merge = true;
						} else if (cardsMap[x][y].equals(cardsMap[x][y1])) {
							MainActivity
									.getMainActivity()
									.getAnimLayer()
									.createMoveAnim(cardsMap[x][y1],
											cardsMap[x][y], x, x, y1, y);
							cardsMap[x][y].setNum(cardsMap[x][y].getNum() * 2);
							cardsMap[x][y1].setNum(0);
							MainActivity.getMainActivity().addScore(
									cardsMap[x][y].getNum());
							merge = true;
						}

						break;
					}
				}
			}
		}

		if (merge) {
			addRandomNum();
			checkComplete();
		}
	}

	private void checkComplete() {

		boolean complete = true;

		ALL: for (int y = 0; y < Config.LINES; y++) {
			for (int x = 0; x < Config.LINES; x++) {
				/*
                   1.卡片的数字为0时
                   3.卡片的数字不为0时,x在0-3之间,且x卡片的数字和左右相邻的卡片的数字相同时
                   4.卡片的数字不为0时,  0 0 && cardsMap[x][y].equals(cardsMap[x - 1][y]))
						|| (x < Config.LINES - 1 && cardsMap[x][y]
								.equals(cardsMap[x + 1][y]))
						|| (y > 0 && cardsMap[x][y].equals(cardsMap[x][y - 1]))
						|| (y < Config.LINES - 1 && cardsMap[x][y]
								.equals(cardsMap[x][y + 1]))) {
					complete = false;
					break ALL;   //跳出循环
				}
			}
		}

		if (complete) {  //给出错误提示信息
			new AlertDialog.Builder(getContext())
					.setTitle(R.string.tishi)
					.setMessage(R.string.falses)
					.setPositiveButton(R.string.queding,
							new DialogInterface.OnClickListener() {

								@Override
								public void onClick(DialogInterface dialog,
										int which) {
									startGame();
								}
							}).show();
		}

	}

	private Card[][] cardsMap = new Card[Config.LINES][Config.LINES];
	//point是一个二维的,数组中的x和y可以确定出来一个card,emptyPoints可以将这些card存起来
	private List emptyPoints = new ArrayList();  
}


代码的源码下载地址:    http://download.csdn.net/detail/cuicanxingchen123456/9212117

 

 

 

你可能感兴趣的:(游戏开发)