Android小项目--2048小游戏

文章详细描述了一个基于滑动操作的游戏玩法,如上滑合并数字卡片,判断滑动方向并执行相应操作。算法涉及遍历棋盘、比较数字和执行合并规则。同时介绍了游戏模式切换和分数记录功能。
摘要由CSDN通过智能技术生成

case MotionEvent.ACTION_UP:

offsetX = event.getX() - setX;

offsetY = event.getY() - setY;

// 判断滑动方向

int orientation = getOrientation(offsetX, offsetY);

switch (orientation) {

case 0:

// 向右滑动

swipeRight();

break;

case 1:

// 向左滑动

swipeLeft();

break;

case 2:

// 向下滑动

swipeDown();

break;

case 3:

// 向上滑动

swipeUp();

break;

default:

break;

}

default:

break;

}

}

return true;

});

}

  • 添加滑动事件(这里举例上滑):

  • 简要说一下此处实现的游戏玩法算法:首先用for循环一行一行地去遍历每一个cell,然后从当前的位置往右去遍历,判断如果获取到了某一个值不是空的,此时有两种情况,一是当前位置上的值是空的,此时把获取到的值放到当前位置上,同时把获取到的位置上的数字清掉;二是当前位置上的值不是空的,并且获取到的值和当前位置上的值相同,则把合并这两个卡片,把当前位置上的值乘以二,同时把获取到的位置上的数字清掉。

  • 还有一种情况是,如果我们当前位置上是空的,然后把右边的值放到当前的位置上去了,此时继续往后边(右边)去遍历,后边的位置还是空的,然后右边又有一个数字和之前放过去的数字是一样的情况的话,也是把它放到这个空位置上去了,这时会发生一个状况:这两张数字实际是一样的,但是它们并不合并。为了避免这种情况的发生,我们再让它去遍历一次,即让x-- ,这样这个问题就解决了。

//上滑事件

private void swipeUp() {

// 判断是否需要添加数字

boolean needAddDigital = false;

for (int i = 0; i < gridColumnCount; i++) {

for (int j = 0; j < gridColumnCount; j++) {

// 获取当前位置数字

int currentDigital = cells[j][i].getDigital();

someData.add(currentDigital);

if (currentDigital != 0) {

// 记录数字

if (recordPreviousDigital == -1) {

recordPreviousDigital = currentDigital;

} else {

// 记录的之前的数字和当前数字不同

if (recordPreviousDigital != currentDigital) {

// 加入记录的数字

dataAfterSwipe.add(recordPreviousDigital);

recordPreviousDigital = currentDigital;

} else {// 记录的之前的数字和当前的数字相同

// 加入*2

dataAfterSwipe.add(recordPreviousDigital * 2);

// 记录得分

recordScore(recordPreviousDigital * 2);

// 重置记录数字

recordPreviousDigital = -1;

}

}

}

}

if (recordPreviousDigital != -1) {

dataAfterSwipe.add(recordPreviousDigital);

}

// 补0

for (int p = dataAfterSwipe.size(); p < gridColumnCount; p++) {

dataAfterSwipe.add(0);

}

// 若原始数据和移动后的数据不同,视为界面发生改变

if (!someData.equals(dataAfterSwipe)) {

needAddDigital = true;

}

someData.clear();

// 重新设置格子数据

for (int k = 0; k < dataAfterSwipe.size(); k++) {

cells[k][i].setDigital(dataAfterSwipe.get(k));

}

// 重置数据

recordPreviousDigital = -1;

dataAfterSwipe.clear();

}

if (needAddDigital) {

// 添加一个随机数字(2或4)

addDigital(false);

playSound();

}

judgeOverOrAccomplish();

}

  • 判断滑动方向:

/**

  • 注:先依据在轴上滑动距离的大小,判断在哪个轴上滑动

  • @param offsetX 在X轴上的移动距离

  • @param offsetY 在Y轴上的移动距离

  • @return 滑动方向

  • 注:0右滑、1左滑、2下滑、3上滑、-1未构成滑动

*/

private int getOrientation(float offsetX, float offsetY) {

// X轴移动

if (Math.abs(offsetX) > Math.abs(offsetY)) {

if (offsetX > MIN_DIS) {

return 0;

} else if (offsetX < -MIN_DIS) {

return 1;

} else {

return -1;

}

} else {// Y轴移动

if (offsetY > MIN_DIS) {

return 2;

} else if (offsetY < -MIN_DIS) {

return 3;

} else {

return -1;

}

}

}

2. Cell类的设计:Cell用来表示游戏中的小格子

  • Cell表示游戏中移动的小格子,格子的颜色、显示数字等属性都在对象中进行设置,Cell类如下:

public class Cell extends FrameLayout {

//显示数字的TextView

private TextView cellShowText;

//显示的数字

private int digital;

public Cell(Context context) {

super(context);

}

public Cell(@NonNull Context context, int leftMargin, int topMargin, int bottomMargin) {

super(context);

init(context, leftMargin, topMargin, bottomMargin);

}

//初始化

private void init(@NonNull Context context, int leftMargin, int topMargin, int bottomMargin) {

cellShowText = new TextView(context);

// 不同难度设置不同字体大小

switch (Config.GRIDColumnCount) {

case 4:

cellShowText.setTextSize(36);

break;

case 5:

cellShowText.setTextSize(28);

break;

case 6:

cellShowText.setTextSize(20);

break;

default:

cellShowText.setTextSize(36);

break;

}

cellShowText.setGravity(Gravity.CENTER);

// 抗锯齿

cellShowText.getPaint().setAntiAlias(true);

// 粗体

cellShowText.getPaint().setFakeBoldText(true);

// 颜色

cellShowText.setTextColor(ContextCompat.getColor(context, R.color.colorWhite));

// 填充整个父容器

LayoutParams params = new LayoutParams(-1, -1);

params.setMargins(leftMargin, topMargin, 0, bottomMargin);

addView(cellShowText, params);

setDigital(0);

}

//获取卡片

public TextView getItemCell() {

return cellShowText;

}

//获取数字

public int getDigital() {

return digital;

}

//设置数字

public void setDigital(int digital) {

this.digital = digital;

cellShowText.setBackgroundResource(getBackgroundResource(digital));

if (digital <= 0) {

cellShowText.setText(“”);

} else {

cellShowText.setText(String.valueOf(digital));

}

}

//根据数字获取相应的背景

private int getBackgroundResource(int number) {

switch (number) {

case 0:

return R.drawable.bg_cell_0;

case 2:

return R.drawable.bg_cell_2;

case 4:

return R.drawable.bg_cell_4;

case 8:

return R.drawable.bg_cell_8;

case 16:

return R.drawable.bg_cell_16;

case 32:

return R.drawable.bg_cell_32;

case 64:

return R.drawable.bg_cell_64;

case 128:

return R.drawable.bg_cell_128;

case 256:

return R.drawable.bg_cell_256;

case 512:

return R.drawable.bg_cell_512;

case 1024:

return R.drawable.bg_cell_1024;

case 2048:

return R.drawable.bg_cell_2048;

default:

return R.drawable.bg_cell_default;

}

}

}

3. 将cell添加到GameView中:

  • 游戏初始化需要根据难度向GameView添加所有的Cell

/**

  • 初始化向布局中添加空格子

  • @param cellWidth 格子宽

  • @param cellHeight 格子高

*/

private void addCell(int cellWidth, int cellHeight) {

Cell cell;

for (int i = 0; i < gridColumnCount; i++) {

for (int j = 0; j < gridColumnCount; j++) {

if (i == gridColumnCount - 1) {

// 为最底下的格子加上bottomMargin

cell = new Cell(getContext(), 16, 16, 16);

} else {

cell = new Cell(getContext(), 16, 16, 0);

}

cell.setDigital(0);

addView(cell, cellWidth, cellHeight);

cells[i][j] = cell;

}

}

}

  • 所有格子需要获取数字,最初全部设为0,即所有格子为空

//获取空格子

private void getEmptyCell() {

// 清空

emptyCellPoint.clear();

// 遍历所有格子,记录所有空格子的坐标位置

for (int i = 0; i < gridColumnCount; i++) {

for (int j = 0; j < gridColumnCount; j++) {

// 空格子

if (cells[i][j].getDigital() <= 0) {

emptyCellPoint.add(new Point(i, j));

}

}

}

}

  • 以4:6的概率随机获取一个数字2或4

public void addDigital(boolean isCheat) {

getEmptyCell();

if (emptyCellPoint.size() > 0) {

// 随机取出一个空格子的坐标位置

Point point = emptyCellPoint.get((int) (Math.random() * emptyCellPoint.size()));

cells[point.x][point.y].setDigital(Math.random() > 0.4 ? 2 : 4);

// 设置动画

setAppearAnim(cells[point.x][point.y]);

}

}

4. 游戏模式的改变:

  • 扩展功能:无限模式和经典模式的切换:

//打开切换模式对话框

private void showChangeModeDialog() {

String subject = “”;

if (Config.CurrentGameMode == Constant.MODE_CLASSIC) {

subject = “无限模式”;

} else if (Config.CurrentGameMode == Constant.MODE_INFINITE) {

subject = “经典模式”;

}

CommonDialog dialog = new CommonDialog(this, R.style.CustomDialog);

dialog.setCancelable(true);

dialog.setTitle(getResources().getString(R.string.tip))

.setMessage(“是否要切换到” + subject)

.setOnPositiveClickedListener(“”, v -> {

if (Config.CurrentGameMode == Constant.MODE_CLASSIC) {

Toast.makeText(GameActivity.this, “已进入无限模式”, Toast.LENGTH_SHORT).show();

enterInfiniteMode();

} else {

Toast.makeText(GameActivity.this, “已进入经典模式”, Toast.LENGTH_SHORT).show();

enterClassicsMode();

}

dialog.cancel();

})

.setOnNegativeClickListener(“”, v -> dialog.cancel())

.show();

}

// 进入无限模式

private void enterInfiniteMode() {

Config.haveCheat = false;

Config.CurrentGameMode = Constant.MODE_INFINITE;

// 保存游戏模式

ConfigManager.putCurrentGameMode(this, Constant.MODE_INFINITE);

titleDescribe.setText(getResources().getString(R.string.game_mode_infinite));

bestScores.setText(String.valueOf(ConfigManager.getBestScoreWithinInfinite(this)));

bestScoresRank.setText(getResources().getText(R.string.tv_best_score_infinite));

currentScores.setText(String.valueOf(ConfigManager.getCurrentScoreWithinInfinite(this)));

modeDescribe.setText(getResources().getString(R.string.tv_describe_infinite));

setTextStyle(titleDescribe);

gameView.initView(Constant.MODE_INFINITE);

}

//进入经典模式

private void enterClassicsMode() {

Config.haveCheat = false;

Config.CurrentGameMode = Constant.MODE_CLASSIC;

// 保存游戏模式

ConfigManager.putCurrentGameMode(this, Constant.MODE_CLASSIC);

titleDescribe.setText(getResources().getString(R.string.game_mode_classics));

// 读取到历史最高分

bestScores.setText(String.valueOf(Config.BestScore));

bestScoresRank.setText(getString(R.string.best_score_rank, Config.GRIDColumnCount));

currentScores.setText(String.valueOf(ConfigManager.getCurrentScore(this)));

modeDescribe.setText(getResources().getString(R.string.tv_describe));

setTextStyle(titleDescribe);

gameView.initView(Constant.MODE_CLASSIC);

}

5. 记录当前分数及历史最高分数

//记录得分

private void recordScore(int score) {

currentScores.setText(String.valueOf(score));

// 当前分数大于最高分

if (Config.CurrentGameMode == Constant.MODE_CLASSIC) {

if (score > ConfigManager.getBestScore(this)) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

跳槽季整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

g-CfhhMgc5-1712707915891)]

[外链图片转存中…(img-jLQpUFOM-1712707915891)]

[外链图片转存中…(img-O0cInPkQ-1712707915891)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

跳槽季整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-3C1CKc7b-1712707915891)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值