小游戏2048
#前言
之前上嵌入式的推广课时,老师说在开发板上完成2048可以加分。不过由于我连基础项目都没完成,所以就没时间弄了。有点灰心,所以现在在安卓上开发了这个游戏了。
#从算法入手
为了便于开发,首先我在vsc完成了我的2048算法代码,这部分代码写在了GameBoardUtils类中,我可以通过输入1234模拟上下左右滑动,并在终端中以4*4的形式输出游戏表盘。
算法中使用了列表用于记录空的值,这样可以便于在随机生成数字时决定生成位置。使用了列表来不断记录表盘数据,这样可以撤销数据。每一次用户执行操作都会检查用户操作是否能够改变表盘,以及是否游戏结束。这个类中还有初始化游戏数据的方法initGameData(),以及恢复游戏数据的方法restoreGameData(),后者可以在执行撤销操作时或从bundle中获得数据时,传入表盘数据后完成恢复
#ui与数据
展示数据使用了GridView,为改变游戏界面写了两个方法:一个是用于初始化游戏界面的,这个方法会调用setAdapter();另一个则是刷新数据。
为GridView调用setOnTouchListener()来识别并执行滑动操作:在event==MotionEvent.ACTION_DOWN时记录数据,在event==MotionEvent.ACTION_UP时,也就是手指离开屏幕时识别这是什么操作,在event==MotionEvent.ACTION_MOVE时必须返回true,否则没有机会看到event==MotionEvent.ACTION_UP时。
测试了以下功能,发现确实能够实现想要的效果了,但是却觉得怪怪的,好像反应太过灵敏把我愣住了。所以在返回结果数据更新的过程中,我使用了Handler延时300ms。又为了能够向用户说明2048每次随机生成的数字在哪里,使得数据刷新后不会看起来太突兀,为对应的itemView设置了透明度动画:setGeneratePosition只是为了传入生成位置,会在getView中找到这个位置并设置动画
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
adapter.setGeneratePosition(locationKey);
gameViewUpdate();
}
},DELAY_MILLIS);
游戏结束后,我希望能够生成一则通知以告诉用户刚刚的游戏中,所有方块中的最大值是什么,同时会发出AlertDialog询问用户要不要继续游戏,如果用户点击确认,则直接初始化表盘数据,如果点击取消,则生成初始按钮。发送通知前,还需要为安卓8.0以上的版本设置NotificationChannel
private void gameOver() {
if(undoButton!=null)undoButton.setEnabled(false);
createNotificationChannel();
NotificationCompat.Builder builder = new NotificationCompat.Builder(this,CHANNEL_ID)
.setSmallIcon(R.drawable.abc_vector_test)
.setContentTitle("Game Over!")
.setContentText("max cube:"+getMaxCube())
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(), 0));
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(this);
notificationManagerCompat.notify(NOTIFICATION_ID,builder.build());
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle("Game Over...")
.setMessage("restart the game?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
restartGame();
Log.d(TAG, "onClick: "+getIsGameOver());
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
initGameData();
startButtonInit();
}
}).show();
}
最后,我在按钮上设置了两个按钮:撤销和从头开始。当没有数据可撤销时,应该让撤销按钮不可点击
undoButton = menu.findItem(R.id.action_game_undo);
undoButton.setEnabled(false);
为了让这个小游戏在手表上也能够运行,所以使用限定符small创建了手表专用布局,并为了能够让2048的游戏表盘最大化,为这个布局配备了没有actionBar的布局。这样,当我在gridview范围内滑动时会识别操作,但是在手表上滑动时,由于表盘太小了,而且左划总是触发返回桌面,返回后应用就被杀死了,看上去终究还是挺糟糕的 (/▽\)
#小结
开发过程遇到最大的问题是到了后期时,随着对2048表盘的操作选项增多,逻辑有些混乱,然后重新捋捋思路,觉得所有的操作其实分为两类:修改数据,更新数据到gridview中,并且简化了一下代码
代码:https://github.com/rabbitAiry/my2048