随着经济的发展,生活节奏的不断加快,人们的生活压力也不断加重,而轻松益智游戏对缓解生活压力,调节情绪具有重要意义。
《2048》益智小游戏在2014年发布出来后,迅速在网上走红,现如今已延伸出了后宫版、朝代版、六边形版多种模式,而我将创建出属于我自己的版本。
本文采用JAVA语言开发了《2048》小游戏,并在Android平台与真机上运行,游戏经过图形界面的处理,以不同颜色显示不同的数字或文字,游戏不仅有最传统的数字玩法,也融合了朝代模式,让对数字叠加痴迷的玩家,过足瘾,也让那些喜欢历史的朋友可以通过游戏来巩固历史的发展。该游戏还有查看当前得分,查看最高分,重玩,退出等功能,App界面简洁明了,操作直观且简单。对于游戏的各部分功能都进行了测试,测试结果正常,目前没有发现问题。
关键字:Android;Java;游戏;益智
目录
第一章 项目背景
1.1课题概述
移动互联网时代的来临,直接促进了搭载智能操作系统、具有强大扩展性的智能手机、平板电脑等移动终端产业的蓬勃发展。而从便携性和随身性这两方面来考虑,电脑所带来的体验已经不能和手机相提并论了。
现如今Android、苹果等各智能手机已经基本占领整个手机市场,从而使得更多应用的出现,其中手机游戏应用在其中占领主要的位置。
2048小游戏,便曾一度风靡,并且从最初的4*4风格的数字模式,一直向前延伸演化,现如今已出现多种模式的2048,其中有由具有进化或包含关系的对象来替代基本的2048的加法运算从而增加趣味性,又有扩大4*4的模式向5*5,6*6的模式演化从而降低难度,增加游戏的可玩性,也有缩小4*4的模式向3*3的模式演化从而增加游戏的难度,增加游戏的挑战性。
此次毕业设计便是从2048的演化得到的启示,从而设计的一款具有个人特色的2048益智小游戏,该游戏通过简单和易操作的Android游戏来帮助手机用户打发时间。并且该游戏集数字模式和朝代模式于一身的2048使游戏即可以通过数字叠加来开动大脑,又可以学习朝代历史发展,玩家开动大脑,制定战略来取得更好的成绩,更是让用户能够在任何环境任何时间都能够放松心情和娱乐身心充分展示了在娱乐中学习的理念。
1.2涉及技术和理论基础
1.2.1 Java语言
Java是由Sun Microsystems公司推出的Java面向对象程序设计语言(以下简称Java语言)和Java平台的总称。Java由James Gosling和同事们共同研发,并在1995年正式推出。Java最初被称为Oak,是1991年为消费类电子产品的嵌入式芯片而设计的。1995年更名为Java,并重新设计用于开发Internet应用程序。用Java实现的HotJava浏览器(支持Java applet)显示了Java的魅力:跨平台、动态Web、Internet计算。从此,Java被广泛接受并推动了Web的迅速发展,常用的浏览器均支持Javaapplet。另一方面,Java技术也不断更新。Java自面世后就非常流行,发展迅速,对C++语言形成有力冲击。在全球云计算和移动互联网的产业环境下,Java更具备了显著优势和广阔前景。2010年Oracle公司收购Sun Microsystems。
图1-1 Java 图标
1.2.2 Android操作系统
1)简介
Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。
图1-2 Android 图标
Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。随后Google以Apache开源许可证的授权方式,发布了Android的源代码。第一部Android智能手机发布于2008年10月。Android逐渐扩展到平板电脑及其他领域上,如电视、数码相机、游戏机等。2011年第一季度,Android在全球的市场份额首次超过塞班系统,跃居全球第一。 2013年的第四季度,Android平台手机的全球市场份额已经达到78.1%。[1] 2013年09月24日谷歌开发的操作系统Android在迎来了5岁生日,全世界采用这款系统的设备数量已经达到10亿台。
系统架构
android的系统架构和其操作系统一样,采用了分层的架构。从架构图看,android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和linux核心层。
图1-1 Android系统架构图
1、应用程序层
Android平台不仅仅是操作系统,也包含了许多应用程序,诸如SMS短信客户端程序、电话拨号程序、图片浏览器、Web浏览器等应用程序。这些应用程序都是用Java语言编写的,并且这些应用程序都是可以被开发人员开发的其他应用程序所替换,这点不同于其他手机操作系统固化在系统内部的系统软件,更加灵活和个性化。
2、应用程序框架层
应用程序框架层是我们从事Android开发的基础,很多核心应用程序也是通过这一层来实现其核心功能的,该层简化了组件的重用,包括Activity Manager(活动管理器)、Window Manager(窗口管理器)、Content Provider(内容提供器)、View System(视图系统)、Notification Manager(通告管理器)、Package Manager(包管理器)、Telephony Manager(电话管理器)、Resource Manager(资源管理器)、Location Manager(位置管理器)、XMPP Service(XMPP服务)十个部分。开发人员可以直接使用其提供的组件来进行快速的应用程序开发,也可以通过继承而实现个性化的拓展。
3)系统运行库层
从图中可以看出,系统运行库层可以分成两部分,分别是系统库和Android运行时,分别介绍如下:
a)系统库
统库是应用程序框架的支撑,是连接应用程序框架层与Linux内核层的重要纽带。其主要分为如下几个:Surface Manager、Media Framework(多媒体库)SQLite、OpenGL|ES、FreeType、WebKit、SGL、SSL、Libc。
b)Android运行时
Android应用程序时采用Java语言编写,程序在Android运行时中执行,其运行时分为核心库和Dalvik虚拟机两部分。
4)Linux内核层
Android是基于Linux2.6内核,其核心系统服务如安全性、内存管理、进程管理、网路协议以及驱动模型都依赖于Linux内核。
1.2.3 开发平台介绍
2048 Android游戏开发平台为Eclpipse:4.2.0+ADT:22.3.0+JDK:1.8.0,模拟器测试平台为Android SDK模拟器,真机测试平台为SM-A5000。
Eclipse
Eclipse是著名的跨平台的自由集成开发环境(IDE)。最初主要用来Java语言开发,通过安装不同的插件Eclipse可以支持不同的计算机语言,比如C++和Python等开发工具。Eclipse的本身只是一个框架平台,但是众多插件的支持使得Eclipse拥有其他功能相对固定的IDE软件很难具有的灵活性。许多软件开发商以Eclipse为框架开发自己的IDE。
ADT
ADT(Android Development Tools)即安卓开发工具,为Android开发提供开发工具的升级或者变更,简单理解为在Eclipse下开发工具的升级下载工具。
JDK
JDK(Java Development Kit)是Sun Microsystems针对Java开发员的产品。自从Java推出以来,JDK已经成为使用最广泛的Java SDK。JDK 是整个Java的核心,包括了Java运行环境,Java工具和Java基础的类库。
第二章 用户需求分析
设计游戏的游戏应该满足基本的游戏需求,操作界面首先简洁明了,用户一目了然,并且规则说明简单易懂。
游戏开始:游戏界面任意位置出现2个值,数字模式时出现的数字分别为2或者4,随机数2和4的比例为9:1;朝代模式时,出现的2个随机值均为“夏”。
游戏结束:当棋盘上所有位置被填满,并且相邻位置的值不相同,无法进行位移或合并。
游戏模式:第一种是数字模式,数字模式也是无尽模式,只有符合游戏结束规则时才会结束;另一种是朝代模式,当游戏重新“新中国”时,游戏胜利。
随机数:玩家可以通过上下左右四个方向进行滑动,若棋盘上数字出现位移或合并,即视为有效移动,并且在随机位置出现新的随机数。
分数显示:每当有合并时,便将合并所得数字累计到当前分数上,并且比较当前数字和最高分,如果当前得分大于最高分,则更新最高分。
2.1用户功能描述
2.1.1 2048游戏设计功能模块说明如下
A.界面布局
B.游戏实现(逻辑、代码)
C.模式切换:普通数字模式、朝代模式
D.当前分、最高分
E.重新开始
F.菜单设置
2.1.2 游戏对象
适用于所有拥有安卓机,并且安卓版本在4.4以上的手机用户,尤其是对数字叠加或对中国朝代历史有兴趣的人,以及想要了解如何开发2048游戏的初学者或者工程师。
2.2 开发环境
开发平台:Windows7
2.3 开发工具
程序的编写软件是:Eclpipse:4.2.0。
第三章 概要设计
3.1游戏主要流程
A.界面布局,B.游戏实现,C.模式切换:普通数字模式、朝代模式,D.当前分、最高分,E.重新开始,F.菜单设置
游戏的主要流程图,如图3-1 2048游戏流程图
图3-1 2048游戏流程图
3.2 流程结构图
3.2.1界面布局
如图3-2界面设计流程图
图3-2界面设计流程图
3.2.2游戏实现(逻辑、代码)
如图3-3游戏实现设计流程图
图3-3游戏实现设计流程图
3.2.3模式切换
如图3-4模式切换设计流程图
图3-4模式切换设计流程图
3.2.4当前分、最高分
如图3-5当前分、最高分设计流程图
图3-5当前分、最高分设计流程图
3.2.5重新开始
如图3-6重新开始设计流程图
图3-6重新开始设计流程图
3.2.6菜单设置
如图3-7菜单设置流程图
图3-7菜单设置流程图
第四章 详细设计
4.1游戏逻辑结构
根据游戏功能设计的要求以及功能模块的划分,对于游戏逻辑结构进行分析,得出如图3-2 2048游戏逻辑流程图。
图3-8 2048游戏逻辑流程图
4.2 主要用户设计界面与技术讲解
4.2.1界面布局
图4-1数字模式 图4-2朝代模式
游戏主要采用LinearLayout(线性布局)、RelativeLayout(相对布局)、GridLayout(网格布局)。
首先游戏界面主要分为上下2个部分用一个线性布局包裹起来,上面部分用相对布局来存放模式切换的ImageView、显示当前得分、最高分和重来按钮。下面部分使用了网格布局,并将其绑定GameView类,用来显示游戏的主模块。
当前得分、最高分的文字和分数显示用线性布局包裹起来,其中圆角样式是通过设置线性布局的背景android:background="@drawable/circular"调用一个circular.xml文件,来设置背景颜色和圆角半径。
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#bcafa1" />
<corners android:radius="10dip" />
</shape>
4.2.2游戏实现(逻辑、代码)
卡片类:用来显示卡片的文本以及背景颜色,其中分为2种情况,数字和文字,数字不同显示的文本以及背景颜色就不同,获取模式type,
int type = MainActivity.getMainActivity().getType();当type为0时,卡片上显示数字,type为1时,显示文字。
public void setNum(int num) {
this.num = num;
String text = "";
switch (num) {
case 0:
text="";
label.setBackgroundColor(Color.rgb(204, 192, 179));
break;
case 2:
text="夏";
label.setBackgroundColor(Color.rgb(238, 228, 218));
break;
case 4:
text="商";
label.setBackgroundColor(Color.rgb(237, 224, 200));
break;
case 8:
text="周";
label.setBackgroundColor(Color.rgb(242, 177, 121));
break;
......
default:
text ="新中国";
label.setBackgroundColor(Color.rgb(156, 76, 14));
break;
}
if (num <= 0) {
label.setText("");
} else {
label.setText(num + "");
int type = MainActivity.getMainActivity().getType();
if(type == 0){
label.setText(num + "");
}else{
label.setText(text);
}
}
}
添加随机数:首先定义一个动态数组emptyPoints来存放棋盘格上空框的坐标,遍历棋盘格上所有位置,当每个棋盘格上的数字小于或等于0时,将该坐标写入emptyPoints数组中。并且每次在进行这些操作前,必须先清空emptyPoints的数据,重新遍历添加,当type等于1时,卡片设置的数字都为2(代表文字“夏”),否则根据1:9的比例设置数字4和2。
private void addRandomNum() {
emptyPoints.clear();
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (cardsMap[x][y].getNum() <= 0) {
emptyPoints.add(new Point(x, y));
}
}
}
Point p = emptyPoints.remove((int) (Math.random() * emptyPoints.size()));
int type = MainActivity.getMainActivity().getType();
if (type == 1) {
cardsMap[p.x][p.y].setNum(2);
} else {
cardsMap[p.x][p.y].setNum(Math.random() > 0.1 ? 2 : 4);
}
}
触控交互:该游戏主要通过用户用手指滑动屏幕,判断用户的意图。
首先,定义起始位置横坐标:startX,起始位置纵坐标:startY,手指滑动横坐标方向上的位移:offsetX,手指滑动纵坐标方向上的位移:offsetY。通过onTouch方法判断滑动方向。
特殊情况:1、由于人的手指在滑动方向会有偏差,一般不会精准的向四个方向滑动,肯定会有偏差,所以要判断手指滑过方向的是水平方向还是垂直方向的意图,当水平方向的偏移量大于垂直方向的偏移量时,表明用户是水平滑动意图,当垂直方向的偏移量大于水平方向的偏移量,表明用户是垂直方向滑动的意图。
2、由于人的手指只有滑动一段距离才能表明用户的意图,不可能点一下屏幕或者稍微动一下就表示滑动意图,这显然不太符合现实情况,所以要定义偏移一定的距离才能判断用户的真正意图。规定当水平方向的偏移距离大于或者等于水平方向的偏移距离,且当偏移小于5时,用户向上滑动,大于5使,规定向下滑动;当水平方向的偏移距离大于或者等于水平方向的偏移距离,且当偏移小于5时,规定用户向左滑动,如果大于5,用户向右滑动。
setOnTouchListener(new OnTouchListener() {
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;
if (Math.abs(offsetX) > Math.abs(offsetY)) {
if (offsetX < -5) {
swipeLeft();
} else if (offsetX > 5) {
swipeRight();
}
} else {
if (offsetY < -5) {
swipeUp();
} else if (offsetY > 5) {
swipeDown();
}
}
break;
}
return true;
}
});
数字合并:不同的滑动方向,执行不同的遍历顺序,以左上角第一个格子为起始点,自左向右,自上而下每个格子的坐标分布如下图。
以向右滑动为例:每一行每一行地遍历,首先从第一行开始遍历,如果第一行数据如下,
2 2 2 2 → 2 2 0 4 → 2 0 2 4 → 0 0 4 4:则从右向左开始遍历,首先用下标为3的数字和前面3个数字进行比较,以此类推。
中间操作:
- 生成一个新的数字,如果发生移动或者合并,则生成一个数字
- 加分,如果发生合并,则加分,分值为合并得到的数字,比如 4,4 -> 8 ,即加8分
private void swipeRight() {
boolean marge = false;
for (int y = 0; y < 4; y++) {
for (int x = 3; x >= 0; x--) {
for (int x1 = x - 1; x1 >= 0; x1--) {
if (cardsMap[x1][y].getNum() > 0) {
if (cardsMap[x][y].getNum() <= 0) {
cardsMap[x][y].setNum(cardsMap[x1][y].getNum());
cardsMap[x1][y].setNum(0);
x++;
marge = true;
} else if (cardsMap[x][y].equals(cardsMap[x1][y])) {
cardsMap[x][y].setNum(cardsMap[x][y].getNum() * 2);
cardsMap[x1][y].setNum(0);
MainActivity.getMainActivity().addScore(
cardsMap[x][y].getNum());
marge = true;
}
break;
}
}
}
}
if (marge) {
addRandomNum();
checkComplate();
}
}
4.2.3模式切换
图4-3模式切换
设置一个整数类型参数type,默认为0,将数字模式记为0,朝代模式记为1,当点击模式切换按钮时,便弹出提示框如果点击否,则不做任何操作,如果点击是,则切换“模式切换”背景图,重新设置type,并重绘界面。
private int type = 0;
if (bType) {
bType = false;
text = "朝代模式";
iv.setBackgroundResource(R.drawable.game);
setType(0);
mGameView.startGame();
} else {
bType = true;
text = "数字模式";
iv.setBackgroundResource(R.drawable.bg);
setType(1);
mGameView.startGame();
}
4.2.4当前得分、最高分
图4-4当前分、最高分
当前得分:每次有合并,便将当前合并所得分数累计起来,并显示在界面上。而且每当游戏重新开始,就将当前分数清空
最高分:首先新建一个BestScore类来存放数据信息
1、打开Preferences,名称为setting,如果存在则打开它,否则创建新的Preferences
public BestScode(Context context){
sp = context.getSharedPreferences("bestscode", context.MODE_PRIVATE);
}
2、让setting处于编辑状态
Editor editor = sp.edit();
3、存放数据
editor.putInt("bestscode", bestScode);
4、完成提交
editor.commit();
再在MainActivity主类中读取数据信息,取出数据
currentBestScore = bs.getBestScode();
最后让当前分数与最高分比较,如果最高分大于当前分数,则更新最高分,并显示出来
if (score > currentBestScore) {
currentBestScore = score;
BestScode bs = new BestScode(this);
bs.setBestScode(currentBestScore);
tv_bestScore.setText("" + currentBestScore);
}
4.2.5重新开始
图4-5重新开始
当点击重新开始按钮时,弹出提示框,如果点击否,则不做任何操作,如果点击是,则调用开始游戏的方法,重新开始游戏,重绘游戏界面。
public void startGame() {
MainActivity.getMainActivity().clearScore();
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
cardsMap[x][y].setNum(0);
}
}
addRandomNum();
addRandomNum();
}
4.2.6菜单设置
图4-6菜单设置
新建一个新的界面布局文件menu.xml,存放3个item
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/help"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/help"/>
<item
android:id="@+id/quit"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/quit"/>
<item
android:id="@+id/about"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/about"/>
</menu>
调用Activity中的onCreateOptionsMenu方法,引用menu.xml布局文件,添加菜单
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
菜单项监听:只要菜单中的菜单项被点击,便触发onOptionsItemSelected(MenuItem item)点击事件,item参数即为被点击的菜单项。通过id判断点击了哪个菜单项,三个菜单分别使用了三种显示方法。
当点击帮助按钮时,调用AlertDialog.Builder创建一个对话框,setTitle :为对话框设置标题;setMessage:为对话框设置内容;setPositiveButton:给对话框添加"我明白了"按钮,点击后返回原页面。其中通过“\u3000\u3000”来显示空格,“\n”来换行。
点击关于按钮时,调用简易的消息提示框Toast,提示相关信息。
点击退出按钮时,调用MainActivity.this.finish();来关闭程序。
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.help:
new AlertDialog.Builder(mainActivity).setTitle("帮助")
.setMessage("\u3000\u3000"+"左上角图标可切换游戏模式,数字模式:无尽模式;朝代模式:当玩出“新中国”,则为胜利。\n"+"\u3000\u3000"+"右上角图标为重来按钮,点击即可重新开始新游戏。")
setPositiveButton("我明白了",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0,int arg1) {
return;
}
}).show();
break;
case R.id.about:
Toast.makeText(MainActivity.this, "版权:刘舜琼", Toast.LENGTH_LONG).show();
break;
case R.id.quit:
MainActivity.this.finish();
break;
}
return false;
}