“ 几天前就想写来着,昨天终于动工了。模仿者别人的代码写了大概一个钟的时间,还是很简单的,”
软件的实现逻辑现在是基本清楚了,但是完整的描述出来还是有点难。先上图。我是打算在明白这个游戏原理后为这个游戏重新设计一个ui。可以供自己玩耍。可现实狠狠的给了我一刀,目前分数计算出问题了,解决这个问题只能是自己研究透别人代码的实现逻辑,然后才能随心的使用。
这里简单说一下这个软件的几个核心文件(其实也没有其他文件了)。就这么四个,很简单!事实上你只需要创建一个新的安卓项目,然后写完这几个文件,2048就可以玩耍了,当然,要是想要获得更好的体验,就需要对界面精心设计一番。
activity_main.xml
这个是主界面的布局文件,里面就放了两个用来显示分数的TextView。还有自定义的game布局(就是游戏核心逻辑实现的布局)
MainActivity.java
主界面的逻辑实现文件,里面就写了几个更新分数的方法,供game.java, 调用的。
Crad.java
这个是用来处理卡片的,主要负责设置卡片的颜色,之类的,你也可以在这个文件里面去尝试自定义卡片样式。
Game.java
游戏界面布局和游戏主要实现逻辑都在这里面,但是也仅仅只有300行代码。特别注意上下左右滑动的那几个方法,千万不要写错了。全是for循环,(我已经被绕晕)
你可能会遇到的一些问题。
代码写完如果看到的是灰色屏,看向这里,有解决办法。(如果你是按我仿造的那个博客写的)
我发布的代码是可以正常显示界面的,我学习的那个博客的不可以。
这是添加卡片的方法,原作者是把这个方法放在其他地方的。但是那样做的话游戏界面加载不出来(游戏界面会是灰色的)。查百度说是因为构造方法生命周期的原因。具体你想知道为啥的话你可以私信我。然后那个 Getwidth() 是这样写的。获取的是屏幕的宽高。
上下左右滑动的代码
这是整个游戏的核心部分,不管你界面做的再好,没有这个代码,你的软件都将是一张图片而已。我自己写的是有错误的,这里我把正确的代码截图和查看地址给出来。(如果你的游戏写出来不能玩耍,那一定是这里出了问题)
小小的年纪,大大的悲伤
一层一层的for循环,很是让人头大。
#我参照的博客https://blog.csdn.net/qq_34731703/article/details/73380156?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-5
目前我已经被这个搞得头都大了,游戏在玩耍的时候有些卡片会莫名消失,而且如果直接点击的话分数会一直上涨,停不下来。。我打算去github找一个项目下载下来看看,成功解决问题的话再来更新。
还有参数下面这条线也搞不懂是什么意思,看ide的提示似乎是想表达你这个参数不建议随意改变,不安全。
这个问题很久都没找到解决办法。
讨论区
03
全部代码
game.java
package com.example.mygame;import android.annotation.SuppressLint;import android.app.AlertDialog;import android.content.Context;import android.content.DialogInterface;import android.graphics.Point;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.view.MotionEvent;import android.view.View;import android.widget.GridLayout;import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicBoolean;public class Game extends GridLayout { //记录4*4的卡片数据 private Crad[][] cadrsMap = new Crad[4][4]; //记录空卡片的集合 private List<Point> emptyPoints = new ArrayList<>(); public Game(Context context) { super(context); initGameView(); } public Game(Context context, AttributeSet attrs) { super(context, attrs); initGameView(); } public Game(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initGameView(); } //初始化游戏核心布局(方格子) @SuppressLint("ResourceAsColor")private void initGameView() { //设置列数 setColumnCount(4); addcareds(Getwidth(),Getwidth()); //设置颜色 setBackgroundColor(R.color.colorAccent); //触摸监听事件 setOnTouchListener(new OnTouchListener() { //4个变量记录初始位置和变化值 private float startX, startY, offsetX, offsetY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //记录初始位置 startX = event.getX(); startY = event.getY(); break; case MotionEvent.ACTION_UP: //记录偏移量 offsetX = event.getX() - startX; offsetY = event.getY() - startY; //X的偏移量大于Y的偏移量 if (Math.abs(offsetX) > Math.abs(offsetY)) { if (offsetX < -5) { //向左边滑动的操作 swpeLeft(); } else { //向右边滑动的操作 swipeRight(); } } else { //向上和向下 if (offsetY < -5) { swipeUp(); } else { swipeDown(); } } break; } return true; } });// startgame(); } private int Getwidth() { DisplayMetrics display = getResources().getDisplayMetrics(); int cardswidth; cardswidth = display.widthPixels; return (cardswidth-10)/4; } //当窗口大小改变时执行 @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //记录卡片大小 // int carwidth = (Math.min(w, h) - 10) / 4; //添加卡片 // addcareds(carwidth, carwidth); //开始游戏 startgame(); } //添加卡片 private void addcareds(int carwidth, int carwidth1) { Crad card; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { card = new Crad(getContext()); card.setNum(0); //卡片是正方形的,所以长和宽是等高的 addView(card, carwidth, carwidth1); cadrsMap[i][j] = card; //将卡片与caedsMAp绑定 } } } private void addRandomcards() { //清空记录空卡片的集合 emptyPoints.clear(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (cadrsMap[i][j].getNum() <= 0) { emptyPoints.add(new Point(j, i)); } } } //从空卡片里面随机选择一个卡片 Point point = emptyPoints.remove((int) (Math.random() * emptyPoints.size())); cadrsMap[point.x][point.y].setNum(Math.random() > 0.1 ? 2 : 4); } //开始游戏 private void startgame() { //分数清零 MainActivity.getMainActivity().clearScore(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { cadrsMap[j][i].setNum(0); //初始值为零 } } //随机生成两个卡片 addRandomcards(); addRandomcards(); } //向下移动的方法 private void swipeDown() { AtomicBoolean flag = new AtomicBoolean(false); for (int y = 0; y < 4; y++) { for (int x = 3; x >=0 ; x--) { for (int x1 = x - 1; x1 >=0; x1--) { if (cadrsMap[x1][y].getNum() > 0) { //如果枚举位置有卡片 if (cadrsMap[x][y].getNum() <= 0) { //如果当前位置没有卡片 cadrsMap[x][y].setNum(cadrsMap[x1][y].getNum()); cadrsMap[x1][y].setNum(0); x++; //向下移动 flag.set(true); } else if (cadrsMap[x1][y].equals(cadrsMap[x1][y])) { cadrsMap[x][y].setNum(cadrsMap[x][y].getNum() * 2); cadrsMap[x1][y].setNum(0); MainActivity.getMainActivity().addScore(cadrsMap[x][y].getNum()); flag.set(true); } break; } } } } if (flag.get()) { addRandomcards(); checkcomplet(); } } //向上移动的方法 private void swipeUp() { boolean flag = false; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { for (int x1 = x + 1; x1 < 4; x1++) { if (cadrsMap[x1][y].getNum() > 0) { //如果美剧位置有卡片 if (cadrsMap[x][y].getNum() <= 0) { //如果当前位置没有卡片 cadrsMap[x][y].setNum(cadrsMap[x1][y].getNum()); cadrsMap[x1][y].setNum(0); x--; //向上移动 flag = true; } else if (cadrsMap[x][y].equals(cadrsMap[x1][y])) { cadrsMap[x][y].setNum(cadrsMap[x][y].getNum() * 2); cadrsMap[x1][y].setNum(0); MainActivity.getMainActivity().addScore(cadrsMap[x][y].getNum()); flag = true; } break; } } } } if (flag) { addRandomcards(); checkcomplet(); } } //向右移动的方法 private void swipeRight() { boolean flag = false; for (int x = 0; x < 4; x++) { for (int y = 3; y > 0; y--) { for (int y1 = y - 1; y1 >= 0; y1--) { if (cadrsMap[x][y1].getNum() > 0) { //如果美剧位置有卡片 if (cadrsMap[x][y].getNum() <= 0) { //如果当前位置没有卡片 cadrsMap[x][y].setNum(cadrsMap[x][y].getNum()); cadrsMap[x][y1].setNum(0); y++; //向右移动 flag = true; } else if (cadrsMap[x][y1].equals(cadrsMap[x][y1])) { cadrsMap[x][y].setNum(cadrsMap[x][y].getNum() * 2); cadrsMap[x][y1].setNum(0); MainActivity.getMainActivity().addScore(cadrsMap[x][y].getNum()); flag = true; } break; } } } } if (flag) { addRandomcards(); checkcomplet(); } } //向左移动的方法 private void swpeLeft() { boolean flag = false; for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { for (int y1 = y + 1; y1 < 4; y1++) { if (cadrsMap[x][y1].getNum() > 0) { //如果美剧位置有卡片 if (cadrsMap[x][y].getNum() <= 0) { //如果当前位置没有卡片 cadrsMap[x][y].setNum(cadrsMap[x][y].getNum()); cadrsMap[x][y1].setNum(0); y--; //向前移动 flag = true; } else if (cadrsMap[x][y1].equals(cadrsMap[x][y1])) { cadrsMap[x][y].setNum(cadrsMap[x][y].getNum() * 2); cadrsMap[x][y1].setNum(0); MainActivity.getMainActivity().addScore(cadrsMap[x][y].getNum()); flag = true; } break; } } } } if (flag) { addRandomcards(); checkcomplet(); } } //检查是否结束游戏 private void checkcomplet() { boolean complet = true; All: for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { //判断各个位置是否还有空闲的,来判断游戏是否结束 if (cadrsMap[x][y].getNum() == 0 || (x - 1 >= 0 && cadrsMap[x][y].equals(cadrsMap[x - 1][y])) || (x + 1 > 0 && cadrsMap[x][y].equals(cadrsMap[x + 1][y])) || (y + 1 > 0 && cadrsMap[x][y].equals(cadrsMap[x][y + 1])) || (y - 1 >= 0 && cadrsMap[x][y].equals(cadrsMap[x][y - 1]))) { complet = false; break All; } } } //游戏结束 if (complet) { new AlertDialog.Builder(getContext()) .setTitle("抱歉") .setMessage("游戏结束 \n game over") .setPositiveButton("再来一次", new DialogInterface.OnClickListener() { //重新开始游戏 @Override public void onClick(DialogInterface dialog, int which) { startgame(); } }); } }}
Crad.java
package com.example.mygame;import android.content.Context;import android.graphics.Color;import android.util.Log;import android.view.Gravity;import android.widget.FrameLayout;import android.widget.TextView;import androidx.annotation.NonNull;public class Crad extends FrameLayout { public String CARD = "cradLog"; //记录卡片颜色 private int [] cardcolor = new int[30]; //卡片的lable组件 private TextView lable; //默认数字 private int num = 0;//初始化卡片颜色 public void setColor() { cardcolor[0] = Color.BLUE; cardcolor[1] = Color.MAGENTA; cardcolor[2] = Color.MAGENTA; cardcolor[3] = Color.DKGRAY; cardcolor[4] = Color.BLACK; cardcolor[5] = Color.CYAN; cardcolor[6] = Color.DKGRAY; cardcolor[7] = Color.YELLOW; cardcolor[8] = Color.WHITE; cardcolor[9] = Color.GRAY; cardcolor[10] = Color.TRANSPARENT; } public Crad(@NonNull Context context) { super(context); setColor(); lable = new TextView(getContext()); lable.setTextSize(32); lable.setBackgroundColor(0x33ffffff); lable.setGravity(Gravity.CENTER); //布局剧中 LayoutParams lp = new LayoutParams(-1,-1); lp.setMargins(10,10,0,0); addView(lable,lp); setNum(0); } //获取数字 public int getNum() { return num; }//设置数字 public void setNum(int num) { this.num = num; if (num<= 0){ lable.setBackgroundColor(Color.RED); // 设置为红色 lable.setText(""); }else { //取log得到X的颜色 int x =(int) (Math.log((double)num)); Log.i(CARD,"日志输出x的值为:"+x); x%=11; lable.setBackgroundColor(cardcolor[x]); lable.setText(num +""); } } public boolean equals(Crad o) { return num == o.getNum(); }}
Mainactivity.java
package com.example.mygame;import android.os.Bundle;import android.view.Menu;import android.widget.TextView;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity { private static MainActivity mainActivity = null; private TextView tvsroce; private int score = 0; public MainActivity() { setMainActivity(this); } private static void setMainActivity(MainActivity mainActivity) { MainActivity.mainActivity = mainActivity; } public static MainActivity getMainActivity() { return mainActivity; } public void clearScore() { score = 0; showScore(); } public void showScore() { tvsroce.setText(score + ""); } public void addScore(int s) { score += s; showScore(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvsroce = findViewById(R.id.tvscore); } // 三个小圆点 菜单按键 @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; }}
activity_main.xml
xml version="1.0" encoding="utf-8"?><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:paddingTop="30dp" android:paddingBottom="80dp" android:orientation="vertical" tools:context=".MainActivity"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/score"/> <TextView android:id="@+id/tvscore" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/tvScore"/> LinearLayout> <com.example.mygame.Game android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:id="@+id/gameView" > com.example.mygame.Game>LinearLayout>
The End.
be close to me
和我一起,做一个灵气十足的少年!
都看到这里了点个赞👍(花)再走吧!