Android小游戏之毕业课程设计Word格式文档下载.docx
《Android小游戏之毕业课程设计Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《Android小游戏之毕业课程设计Word格式文档下载.docx(14页珍藏版)》请在冰豆网上搜索。
小3传奇的两位开发者花了近一年半的时间才开发出这个游戏的核心玩法,却在其上架Appstore后仅21天就被1024抄了过去。
最让人意想不到的事,2048则更为迅速,19岁的GabrieleCirulli只用了一个星期改编前两者游戏,却获得最大的成功,Threes却不被人所知。
而到了现在,这类游戏从发布到现在,不光是AppDtore还是Android市场,下载量已经远远超越了千万,晚上各种版本的都有,比如朝代版,后宫版,甲乙丙版,生肖版等。
由于本人十分喜欢2048这款手游,在了解本次课程设计是设计一个Android平台下的软件之后,就选定了2048游戏作为本次课程论文的课题。
在此次课程论文的制作过程中,我将运用我在学校学习到的各种知识以及java编程知识和通过网上现有的资料尝试完成本次课程的开发。
2048游戏可以通过简单的操作,丰富多彩的画面来使人们达到放松的效果,人们只需要几分钟的时间就可以达到放松的效果。
由于2048游戏的开发要求并不高,所以不需要购买特定的设施,只需要安装特定的编程和图像处理软件来实现2048的开发。
内容安排
要开发这一款游戏,要根据以下几步来完成:
首先是要对这款游戏的代码分析。
对整一个游戏,要将各个部位分割成其对应部位的功能代码,并且逐一去分析实现其中不同的功能。
其次是游戏的流程。
在做好每一个部分的代码之后,就需要把所创造的各个关键部分拼接起来,因此,就需要先把该游戏的流程一步一步的弄清楚。
再者是对《2048》这一款游戏的算法分析。
对于整个游戏而言,最主要的心脏就是这一款游戏的算法,游戏好不好,就看这一款游戏的算法是不是符合逻辑。
《2048》这一款游戏的算法分为上下左右自个方向的,具体的下面分析。
《2048》中每个格子的数据和颜色的刷新。
在每一次移动之后,几乎在这4*4的每一个方格上的数据都会变化,因此在每一次移动之后,每一个方格上的数据都要刷新一次。
相对的,每一个数的背景颜色是不同的,也就是说,在每一次数据刷新的时候,每一个方格上的颜色也要同样的刷新一次。
最后是可不可以继续游戏的检测。
当屏幕上没有没有写有数据的方格并且屏幕上的数据没有哪两个相邻的数据是一样的两个数,那么就判定为游戏结束。
功能设计
1、设计界面
在设计整个2048游戏之前先得设计一个2048的游戏界面,只有有了游戏界面,才能够进行2048这一款游戏的下一步设计,这是创建2048小游戏的最基础的步骤。
2048这一款游戏的界面是4*4方格的,于是就需要在界面上设置4*4的方格,有了这些方格之后才能在方格上面装卡片(Card),然后才能继续游戏。
privatevoidonDrawBorder(Canvascanvas){
paint.setShader(null);
paint.setStrokeWidth(16);
paint.setColor(Color.WHITE);
canvas.drawLine(0,0,0,this.getHeight(),paint);
canvas.drawLine(0,0,this.getWidth(),0,paint);
canvas.drawLine(this.getWidth(),0,this.getWidth(),this.getHeight(),paint);
canvas.drawLine(0,this.getHeight(),this.getWidth(),this.getHeight(),paint);
}
方格下面继而显示分数(Score)和最高分数(BestScore)。
2、游戏设计
在2048游戏的界面设计好了之后,要求完成最核心的玩法就是卡片在于界面上4*4的移动,于是下一步的操作就是对卡片的具体设置,以下是一部分关键代码。
(1)创建一个卡片
privateCardgetCard(intnum){
Cardc;
if(cards.size()>
0){
c=cards.remove(0);
}else{
c=newCard(getContext());
addView(c);
c.setVisibility(View.VISIBLE);
c.setNum(num);
returnc;
(2)类Card继承了FrameLayout,目的是作为游戏中的卡片,卡片各个数字和样式的实现:
publicvoidsetNum(intnum){
this.num=num;
if(num<
=0){
label.setText("
"
);
}else{
label.setText(num+"
}
switch(num){
case0:
label.setBackgroundColor(0x);
透明色
break;
case2:
……
default:
label.setBackgroundColor(0xff3c3a32);
}}
(3)卡片设计好了之后根据游戏要求判断在某一操作下同一方向上的卡片是否可以相加,可以相加的部分进行数据合并。
合并相同数据
intaddscode=0;
for(intp=0;
p<
4;
p++){
for(intq=3;
q>
0;
q--){
if(arrays[q][p]==arrays[q-1][p]&
&
arrays[q][p]!
=0){
flag_move=true;
rrays[q][p]+=arrays[q-1][p];
addscode+=arrays[q][p];
arrays[q-1][p]=0;
q--;
}}}
3、事件监听
对于2048这个游戏经过之前对游戏界面的设计、对游戏元素卡片的创建和定义好了之后,最终目的是要使我们能够控制卡片进行移动,目标是使卡片能够根据我们的命令进行向上、向下、向左、向右的四个方向上的移动,这个需要加上4个方位的事件监听。
@Override
publicbooleanonFling(MotionEvente1,MotionEvente2,floatvelocityX,
floatvelocityY){
floatX=e2.getX()-e1.getX();
floatY=e2.getY()-e1.getY();
finalintFLING_Min_DISTANCE=50;
if(X>
FLING_Min_DISTANCE&
Math.abs(velocityX)>
Math.abs(velocityY)){
toRight();
}elseif(X<
-FLING_Min_DISTANCE&
toLeft();
}elseif(Y>
Math.abs(velocityX)<
toDown();
}elseif(Y<
toUp();
}
returnfalse;
以上是事件监听的部分关键代码,事件监听创建好了之后,整个游戏大部分要求已经完成了。
4、游戏最终实现
从游戏的角度来说,这个2048游戏有界面,有元素,可以移动已经基本完成了,但作为有个游戏而言最后还有几步没有完善。
初始化
进入游戏,初始化4*4表格,并随机产生两个数字(2或者4);
二维数组this.tables表格循环存入数据;
random1,random2,random11,random22四个随机数可以确定两个2的xy位置;
方法newNumber里面,根据位置i,j和级别num可以确定一个新的数字;
创建背景cell和cell上面的数字标签cellLabel;
并根据num确定是否显示cellLabel;
最后给cell关联一个data数据;
特别说明这里的number:
num不是精灵上面的数字而是精灵的级别,比如number=11则数字是1024。
游戏结束的判断
每次发生卡片移动,都要检查游戏还能否继续,是否已经结束。
使用函数CheckComplete()完成游戏是否失败的检查。
游戏的计分
在卡片的每一次移动的时候判断是否有两个相同数字进行相加,凡是有相见的两个数字其相加所得的和需要存入计分中,其中如果游戏获得了最高分要对最高分进行保存。
publicclassBestScore{
privateSharedPreferencessp;
publicBestScore(Contextcontext){
sp=context.getSharedPreferences("
bestscore"
context.MODE_PRIVATE);
publicintgetBestScore(){
intbestscore=sp.getInt("
0);
returnbestscore;
publicvoidsetBestScore(intbestScore){
Editoreditor=sp.edit();
editor.putInt("
bestScore);
editormit();
游戏逻辑重新设定
用不同方法制作尝试可能性。
首先看一下,我在实现2048时用到的一些存储的数据结构。
我在实现时,为了省事存储游戏过程中的变量主要用到的是List。
比如说:
List<
lnteger>
spaceList=newArrayList<
();
这个spaceList主要作用于保存,所有空白格的位置,也就是空白格在GridLayout中的位置(从0到15)
对于数字格,以及格子对应的数据,我写了一个类如下:
packagecom.example.t2048;
importjava.util.ArrayList;
importjava.util.List;
importandroid.util.Log;
**
*用于保存数字格,已经数字格对应的数字
*@authorMr.Wang
*
*
publicclassNumberList{
这个list用于保存所有不为空的格子的坐标(在GridLayout中的位置从0到15)
privateList<
Integer>
stuffList=newArrayList<
这个list用于保存所有不为空的格子对应的数字(以2为底数的指数)
numberList=newArrayList<
**
*新加入的数字格
*@paramindex数字格对应的位置
*@paramnumber对应数字的指数(以2为底数)
*
publicvoidadd(intindex,intnumber){
stuffList.add(index);
numberList.add(number);
*用于判断当前位置是否为数字格
*@paramindex当前位置
*@returntrue表示是
publicbooleancontains(intindex){
returnstuffList.contains(index);
*将当前的格子从数字列表中去掉
*@paramindex
publicvoidremove(intindex){
intorder=stuffList.indexOf(index);
numberList.remove(order);
stuffList.remove(order);
*将当前格子对应的数字升级,指数加1
publicvoidlevelup(intindex){
numberList.set(order,numberList.get(order)+1);
*将当前格子对应的位置置换为新的位置
*@paramnewIndex新的位置
publicvoidchangeIndex(intindex,intnewIndex){
stuffList.set(stuffList.indexOf(index),newIndex);
*通过格子对应的位置获取其对应的数字
*@return格子对应数字的指数
publicintgetNumberByIndex(intindex){
returnnumberList.get(order);
publicStringtoString(){
returnstuffList.toString()+numberList.toString();
publicvoidprintLog(){
Log.i("
stuffList"
stuffList.toString());
numberList"
numberList.toString());
这个类主要是我对数字格、数字格对应数字的保存,和增删改等操作。
其实就是两个list,我为了操作起来方便,所以把他们写在一个类里。
然后,游戏的逻辑。
比如,我们在游戏过程中执行了一次向右滑动的操作,这个操作中,我们要对所有可移动和合并的格子进行半段和相应的操作:
数字格的右边如果是空白格,则数字格与空白格交换
数字格右边如果有多个空白格,则数字格与连续的最后一个空白格做交换
数字格的右边如果存在与之相同的数字格,则本格置空,右边的数字格升级(指数加一)
如果滑动方向连续存在多个相同的数字格,右的格子优先升级
在一次滑动中,每个格子最多升级一次
当一个格子存在上述四种中的任意一种时,则完成了对它的操作。
我们试着把上面的判断规则翻译成代码,首先,明确在GridLayout中的坐标位置,我在GridLayout中采用的是水平布局,所以每个格子对应的位置如下
0123
4567
891011
12131415
在这个基础上,我建立如下坐标轴,以左上角为原点,X轴为横轴,Y轴为竖轴。
当向右滑动的时候,从上面逻辑来看,为了方便我们应当从右向左历格子:
for(inty=0;
y<
y++){
for(intx=2;
x>
=0;
x--){
intthisIdx=4*y+x;
Change(thisIdx,direction);
每遍历到一个新的格子,执行一次change()方法,其实应该是没遍历到一个非空的格子,执行一次change()当时我为了省事,把非空判断加到了change的代码里,来看一下change()这个方法的实现,这个方法主要是用来判断,一个格子是需要移动、合并,还是什么都不操作:
*该方法,为每个符合条件的格子执行变动的操作,如置换,升级等
*@paramthisIdx当前格子的坐标
*@paramdirection滑动方向
publicvoidChange(intthisIdx,intdirection){
if(numberList.contains(thisIdx)){
intnextIdx=getLast(thisIdx,direction);
if(nextIdx==thisIdx){
不能移动
return;
}elseif(spaceList.contains(nextIdx)){
存在可以置换的空白格
replace(thisIdx,nextIdx);
}else{
if(numberList.getNumberByIndex(thisIdx)==numberList.getNumberByIndex(nextIdx)){可以合并
levelup(thisIdx,nextIdx);
}else{
intbefore=getBefore(nextIdx,direction);
if(before!
=thisIdx){存在可以置换的空白格
replace(thisIdx,before);
其中getLast()方法,用于获取当前格子在移动方向的可以移动或者合并的最后一个格子,如果返回值还是当前的格子,则表示不能移动。
其中调用的getNext()方法是为了获取当前格子在移动方向的下个格子的位置。
*用于获取移动方向上最后一个空白格之后的位置
*@paramindex当前格子的坐标
*@paramdirection移动方向
*@return
publicintgetLast(intthisIdx,intdirection){
intnextIdx=getNext(thisIdx,direction);
if(nextIdx<
0)
returnthisIdx;
else{
if(spaceList.contains(nextIdx))
returngetLast(nextIdx,direction);
else
returnnextIdx;
}
然后是replace(intthisdx,intnextldx),这个方法是执行两个格子互换位置,内容主要是对格子中的view更换背景图片,然后操作空白格的list和数字格的list:
*该方法用来交换当前格与目标空白格的位置
*@paramnextIdx目标空白格的坐标
publicvoidreplace(intthisIdx,intnextIdx){
moved=true;
获取当前格子的view,并将其置成空白格
ViewthisView=gridLayout.getChildAt(thisIdx);
ImageViewimage=(ImageView)thisView.findViewById(R.id.image);
image.setBackgroundResource(icons[0]);
获取空白格的view,并将其背景置成当前格的背景
ViewnextView=gridLayout.getChildAt(nextIdx);
ImageViewnextImage=(ImageView)nextView.findViewById(R.id.image);
nextImage.setBackgroundResource(icons[numberList.getNumberByIndex(thisIdx)]);
在空白格列表中,去掉目标格,加上当前格
spaceList.remove(spaceList.indexOf(nextIdx));
spaceList.add(thisIdx);
在数字格列表中,当前格的坐标置换成目标格的坐标
numberList.changeIndex(thisIdx,nextIdx);
Levelup(intthisldx,intnextldx)这个方法是为了实现相同数字格的合并操作,其实就是将当前的格子置成空白格,将移动方向上下一个格子对应的背景置成下一个背景:
*刚方法用于合并在移动方向上两个相同的格子
*@paramnextIdx目标格子的坐标
publicvoidlevelup(intthisIdx,intnextIdx){
一次移动中,每个格子最多只能升级一次
if(!
changeList.contains(nextIdx)){
moved=true;
获取当前格子的view,并将其置成空白格
ViewthisView=gridLayout.getChildAt(thisIdx);
ImageViewimage=(ImageView)thisView.findViewById(R.id.image);
image.setBackgroundResource(icon