仿2048游戏代码
来源:互联网 发布:怎样进行淘宝网店定位 编辑:程序博客网 时间:2024/05/20 00:51
做android开发的时间也不短了,一直都对游戏开发很感兴趣,利用工作之余的时间,仿照之前非常火热的2048游戏,写了一个差不多的游戏;核心逻辑代码是自己憋着脑袋敲出来的,有些bug难免 .博客也是作为自己的学习的记录
之前看过hongyang大神的拼图游戏的教学视频,有一种茅塞顿开的感觉,不一定所有的游戏都是需要游戏引擎才能写出来,利用android官方控件或者自定义控件也是可以写出好玩的小游戏;大神的拼图游戏:http://blog.csdn.net/lmj623565791/article/details/40595385,(写博客的时候才看到hongyang大神也有关于2048博客:http://blog.csdn.net/lmj623565791/article/details/40020137),看起来比我写的清晰多了@_@.
好吧,开始铺代码了
首先我我自定义了一个类,用于存储游戏中每一个小数字方块的信息
public class NumberItem { private int number;//显示的数字 private int color;//显示的颜色 private int level;//级别从0开始 private int index;//位置角标 private TYPE type;//类型 public TYPE getType() { return type; } public void setType(TYPE type) { this.type = type; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; number = (int) Math.pow(2, level + 1); color = getColorByLevel(level); } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public NumberItem(int level) { number = (int) Math.pow(2, level + 1); color = getColorByLevel(level); type = TYPE.EMPTY; } public enum TYPE { EMPTY,//普通没有数字的状态 NOT_EMPTY//有数字的状态 } public void tagleType() { if (type == TYPE.EMPTY) { type = TYPE.NOT_EMPTY; } else if (type == TYPE.NOT_EMPTY) { type = TYPE.EMPTY; setLevel(0); } } /** * 返回color * * @param level * @return */ private int getColorByLevel(int level) { int[] colors = {R.color.level_0, R.color.level_1, R.color.level_2, R.color.level_3, R.color.level_4, R.color.level_5, R.color.level_6, R.color.level_7, R.color.level_8, R.color.level_9, R.color.level_10, R.color.level_11, R.color.level_12, R.color.level_13}; return colors[level]; } public int getColor() { return color; } public void setColor(int color) { this.color = color; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } @Override public String toString() { return "LEVEL-->" + level + "\nTYPE-->" + type.toString() + "\n"; }}
注释写得差不多的意思,就不赘述了
然后得需要一个自定义的游戏控件,就是用来放游戏小方块的容器,代码如下:
/** * Author:李烽 * Date:2015-09-14 * FIXME * Todo 游戏的容器 */public class GameContainer extends RelativeLayout { private int mColumn = 4;//行数(列数) private float mItemWidth;//每一个块的高度(宽度) private float mWidth;//整个view的高度 private ArrayList<NumberItem> mNumberItems;// private ArrayList<ItemView> mItems;//每一个块块 // private static final int GAME_OVER = 1;//游戏结束// private static final int GAME_SUCCESS = 2;//游戏胜利 private int mMargin = 3;//item之间的间隔 private int mPadding = 3;//主界面的padding private boolean once;//是否是第一次初始化的标记(防止多次初始化) private GestureDetector mGestureDetector;//手势管理者 public GameContainer(Context context) { this(context, null, 0); } public GameContainer(Context context, AttributeSet attrs) { this(context, attrs, 0); } public GameContainer(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { //dp转化为px mMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics()); mPadding = min(getPaddingBottom(), getPaddingLeft(), getPaddingLeft(), getPaddingRight()); mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { final float absX = Math.abs(velocityX); final float absY = Math.abs(velocityY); final float deltaX = e2.getX() - e1.getX(); final float deltaY = e2.getY() - e1.getY(); final int travelX = getWidth() / 20; final int travelY = travelX; if (absY < absX && deltaX < -travelX) { onToLeft(); return true; } else if (absY < absX && deltaX > travelX) { onToRight(); return true; } else if (absX < absY && deltaY > travelY) { onToDown(); return true; } else if (absX < absY && deltaY < -travelY) { onToUp(); return true; } return false; } }); } @Override public boolean onTouchEvent(MotionEvent event) { mGestureDetector.onTouchEvent(event); return true; } /** * 上滑 */ private void onToUp() {// Toast.makeText(getContext(), "top", Toast.LENGTH_SHORT).show(); if (GameHelper.allInTop(mNumberItems, mColumn)) return; for (int i = 0; i < mColumn; i++) { NumberItem[] items = {mNumberItems.get(i), mNumberItems.get(mColumn + i), mNumberItems.get(2 * mColumn + i), mNumberItems.get(3 * mColumn + i)}; GameHelper.judgeRule(items); } GameHelper.createNesItem(mNumberItems); refreshItems(mNumberItems); } /** * 下滑 */ private void onToDown() { if (GameHelper.allInBottom(mNumberItems, mColumn)) return; for (int i = 0; i < mColumn; i++) { NumberItem[] items = {mNumberItems.get(3 * mColumn + i), mNumberItems.get(2 * mColumn + i), mNumberItems.get(mColumn + i), mNumberItems.get(i)}; GameHelper.judgeRule(items); } GameHelper.createNesItem(mNumberItems); refreshItems(mNumberItems); } /** * 右滑 */ private void onToRight() { if (GameHelper.allInRight(mNumberItems, mColumn)) return; for (int i = 0; i < mColumn; i++) { NumberItem[] items = {mNumberItems.get(i * mColumn + 3), mNumberItems.get(i * mColumn + 2), mNumberItems.get(i * mColumn + 1), mNumberItems.get(i * mColumn)}; GameHelper.judgeRule(items); } GameHelper.createNesItem(mNumberItems); refreshItems(mNumberItems); } /** * 左滑 */ private void onToLeft() { if (GameHelper.allInLeft(mNumberItems, mColumn)) return; for (int i = 0; i < mColumn; i++) { NumberItem[] items = {mNumberItems.get(i * mColumn), mNumberItems.get(i * mColumn + 1), mNumberItems.get(i * mColumn + 2), mNumberItems.get(i * mColumn + 3)}; GameHelper.judgeRule(items); } GameHelper.createNesItem(mNumberItems); refreshItems(mNumberItems); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth = min(getMeasuredHeight(), getMeasuredWidth());//确保为正方形 if (!once) { initItem(); once = true; } setMeasuredDimension((int) mWidth, (int) mWidth); } /** * 初始化每一个item */ private void initItem() { mItemWidth = (mWidth - mPadding * 2 - mMargin * (mColumn - 1)) / mColumn; mItems = new ArrayList<>(); mNumberItems = new ArrayList<>(); /*初始的时候有两个是初始的item*/ int num1 = (int) (Math.random() * 16); int num2 = (int) (Math.random() * 16); if (num1 == num2) num2 = num1 - 2; for (int i = 0; i < mColumn * mColumn; i++) { NumberItem item0 = new NumberItem(0); if (i == num1 || i == num2) item0.setType(NumberItem.TYPE.NOT_EMPTY); item0.setIndex(i); mNumberItems.add(item0); } refreshItems(mNumberItems); GameHelper.setGameListener(new GameHelper.GameListener() { @Override public void onGameWin() { if (callBack != null) callBack.onGameWin(); } @Override public void onGameFail() { if (callBack != null) callBack.onGameFail(); } @Override public void onGamePointer(int point) { if (callBack != null) callBack.onGamePointer(point); } }); } private GameCallBack callBack; public void gameRestart() { initItem(); } public void setGameCallBack(GameCallBack callBack) { this.callBack = callBack; } public interface GameCallBack { void onGameWin(); void onGameFail(); void onGamePointer(int point); } /** * 根据数据更新界面 * * @param numberItems */ public void refreshItems(ArrayList<NumberItem> numberItems) { removeAllViews(); for (int i = 0; i < mColumn * mColumn; i++) { ItemView itemView = new ItemView(getContext()); if (numberItems.get(i).getType() == NumberItem.TYPE.NOT_EMPTY) { itemView.setColor(getColorById(numberItems.get(i).getColor())); itemView.setText(numberItems.get(i).getNumber() + ""); } else if (numberItems.get(i).getType() == NumberItem.TYPE.EMPTY) { itemView.setColor(getColorById(R.color.level_13)); itemView.setText(""); } itemView.setId(i + 1); mItems.add(itemView); RelativeLayout.LayoutParams lp = new LayoutParams((int) mItemWidth, (int) mItemWidth); //设置各个item间的margin //不是第一列 if (i % mColumn != 0) { lp.leftMargin = mMargin; lp.addRule(RelativeLayout.RIGHT_OF, mItems.get(i - 1).getId()); } //不是第一行 if ((i + 1) > mColumn) { lp.topMargin = mMargin; lp.addRule(RelativeLayout.BELOW, mItems.get(i - mColumn).getId()); } addView(itemView, lp); } } /** * 获取color值 * * @param id * @return */ private int getColorById(int id) { return getContext().getResources().getColor(id); } /** * 获取多个参数的最小值 * * @param params * @return int */ private int min(int... params) { int min = params[0]; for (int param : params) { if (param < min) min = param; } return min; }}
看起来比较多,里面的initItem()方法是参考hongyang的拼图游戏中的动态的给Relativelayout添加子view;使用了
GestureDetector
捕捉手势,然后
/** * 根据数据更新界面 * * @param numberItems */ public void refreshItems(ArrayList<NumberItem> numberItems) { removeAllViews(); for (int i = 0; i < mColumn * mColumn; i++) { ItemView itemView = new ItemView(getContext()); if (numberItems.get(i).getType() == NumberItem.TYPE.NOT_EMPTY) { itemView.setColor(getColorById(numberItems.get(i).getColor())); itemView.setText(numberItems.get(i).getNumber() + ""); } else if (numberItems.get(i).getType() == NumberItem.TYPE.EMPTY) { itemView.setColor(getColorById(R.color.level_13)); itemView.setText(""); } itemView.setId(i + 1); mItems.add(itemView); RelativeLayout.LayoutParams lp = new LayoutParams((int) mItemWidth, (int) mItemWidth); //设置各个item间的margin //不是第一列 if (i % mColumn != 0) { lp.leftMargin = mMargin; lp.addRule(RelativeLayout.RIGHT_OF, mItems.get(i - 1).getId()); } //不是第一行 if ((i + 1) > mColumn) { lp.topMargin = mMargin; lp.addRule(RelativeLayout.BELOW, mItems.get(i - mColumn).getId()); } addView(itemView, lp); } }
这个方法就是根据自定义的NumberItem的数据集合来更新游戏视图,主要是是否为空
里面用到了ItemView这个自定义控件(其实直接用TextView也可以)
package lee.vioson.game2048.UI.views;import android.content.Context;import android.graphics.Color;import android.util.AttributeSet;import android.util.TypedValue;import android.view.Gravity;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;import lee.vioson.game2048.R;/** * Author:李烽 * Date:2015-09-14 * FIXME * Todo itemiew */public class ItemView extends LinearLayout { private TextView text; private int color; public ItemView(Context context) { this(context, null, 0); } public ItemView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ItemView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); View view = View.inflate(context, R.layout.layout_item_contain, this); text = (TextView) view.findViewById(R.id.text); color = getContext().getResources().getColor(R.color.level_0); text.setBackgroundColor(color); text.setTextColor(Color.parseColor("#FF000000")); text.setGravity(Gravity.CENTER); text.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20 , getResources().getDisplayMetrics())); text.setText(2048 + ""); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); setBackgroundResource(R.drawable.bg_contain); } /** * 设置颜色 * * @param color */ public void setColor(int color) { this.color = color; text.setBackgroundColor(color); } /** * 设置文字 * * @param text */ public void setText(String text) { this.text.setText(text); }}
其实就是一个Linearlayout包裹这一个textview
手势也监听了,游戏视图也显示出来了,现在就是要让视图响应手势,其实也就是通过监听手势对应的改变NumberItem集合中NumberItem的属性,然后根据数据集合刷新GameContainer,方法如下
/** * 处理滑动之后数据的变化,映射到ui上就是界面的变化 * * @param items */ public static void judgeRule(NumberItem[] items) { for (int i = 0; i < items.length; i++) { if (i < items.length - 1) { if (isEmpty(items[i]) && isNotEmpty(items[i + 1])) { //本身为空,后面的一个不为空 if (i == 0 || i == 1) { //第一列和第二列的时候,这时候要考虑三个子项 if (isAllSame(items[i + 1], items[i + 2])) //后面两个相同且都不为空时候融合 fixItem(items[i + 1], items[i + 2]); } exChangeItem(items[i], items[i + 1]); } else if (isNotEmpty(items[i], items[i + 1])) { //本身和后面的一个都不为空时 if (isAllSame(items[i], items[i + 1])) { //如果和身后的那个相同则融合 fixItem(items[i], items[i + 1]); } } else if (isEmpty(items[i], items[i + 1])) { //本身和后面一个都是空时候 if (i == 0) { //第一列 if (isAllSame(items[i + 2], items[i + 3])) { //身后第二个和第三个相同且不为空 融合,并调换至第一列 fixItem(items[i + 2], items[i + 3]); exChangeItem(items[i], items[i + 2]); } else { //身后第二个和第三个不相同时 if (isNotEmpty(items[i + 2])) { //第二个不是空的就将后面两个和自己加后面第一个对换 exChangeItem(items[i], items[i + 2]); exChangeItem(items[i + 1], items[i + 3]); } else if (isEmpty(items[i + 2])) { //第二个是空的,交换自己和最后一个 exChangeItem(items[i], items[i + 3]); } } } else if (i == 1) { //第二列 if (isEmpty(items[i - 1])) //前面一个(也就是第一个)为空的时候,就不管最后一个是否是空,直接和自己对换 exChangeItem(items[i - 1], items[i + 2]); else if (isAllSame(items[i - 1], items[i + 2])) //前面一个不为空,且和最后一个相同,将它们融合就好 fixItem(items[i - 1], items[i + 2]); else //前面一个不为空,不和最后一个相同,直接将最后一个一道自身的位置(交换) exChangeItem(items[i], items[i + 2]); } } else if (isEmpty(items[i + 1]) && isNotEmpty(items[i])) { //前面一个是空的,自身不是空的 if (i == 0 || i == 1) { //第一列和第二列时 if (isAllSame(items[i], items[i + 2])) { //如果自身和后面第二个一样就就直接将自己和第二个融合 fixItem(items[i], items[i + 2]); } } } } } }
写的比较乱,也没有注释,因为我写这个的时候是一边调试一边添加条件,呵呵,脑子不好使,想不出有什么好办法,还好只是4X4的布局
估计里面可能还有一些没有考虑到的情况,但是应该没有影响很大的bug;主要将每一个子项和它后面的一个比较,是否是相同等级的,相同的可以相融合,不同的就不管,如果后面一个是空的就调换位置这样;
然后也没有其它什么了,源码可以下载看,有兴趣可以帮我修改修改.不会录屏,就不上图片了,下载下来运行一下就看到效果
http://download.csdn.net/detail/lifengli123/9108863
- 仿2048游戏代码
- java高仿2048游戏代码
- 仿金山打字通游戏 Java代码
- java仿2048游戏实例源码
- Android仿游戏答题
- 仿DOTA2游戏页面
- 2048游戏C语言代码
- 2048游戏代码逻辑分析
- 2048游戏C语言代码
- Java仿游戏滚屏
- 仿QQ窗口代码
- C#仿记事本代码
- 仿CSDN分页代码
- 仿计算器布局代码
- 仿射变换代码
- 仿QQ音乐代码
- 仿百度搜索代码
- 仿QQ折叠菜单代码
- mysql语句与sql语句的部分对比
- ∇的数学含义
- 网络请求框架---Volley
- oracle v$database 视图
- tcp窗口机制(写的最简单精炼的文章)
- 仿2048游戏代码
- SQLite数据库框架ORMLite与GreenDao的简单比较
- 中国教育还有英国教育
- IT
- C语言中冒泡排序,插入排序,选择排序
- mysql中批量修改所有数据表的格式为utf8
- HDOJ-3037(组合数学)
- 第一支小爬虫
- 列表项中包含按钮的ListView的点击事件的响应