资源描述
一、 绪论
1.1. 项目背景
本题目将设计一个俄罗斯方块(Tetris, 俄文:Тетрис)。它是一款风靡全球的电视游戏机和掌上游戏机游戏,由俄罗斯人阿列克谢·帕基特诺夫发明,故得此名。俄罗斯方块的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。由于上手简单、老少皆宜,从而家喻户晓,风靡世界。
1.2. 开发平台
此项目基于android 环境进行开发,使用的编程工具为eclipse,它是以android语言作为其基本语言的一种可视化编程工具。
Android 是Google开发的基于Linux平台的开源手机操作系统. Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器。
应用程序中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。Activity之间通过Intent进行通信。
你的应用可以使用它对外部事件进行过滤只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice 来响应它们收到的信息,或者用NotificationManager 来通知用户。
一个Service 是一段长生命周期的,没有用户界面的程序,可以用来开发如监控类程序。
android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。
注:Activity生命周期
二、 项目规则及设计思路
1.
2.
2.1. 项目规则
玩家通过点触虚拟键盘,左右控制方块左右移动,按上代表旋转,按下代表加速向下移动,每满一行消除,获得相应积分100,积分每增长2000,等级加1,游戏速度加快
2.2. 实现思路
1.
2.
2.1.
2.2.
2.2.1. 界面设计
游戏菜单界面
游戏结束界面
游戏运行界面
排行榜
界面
帮助界面
注:游戏界面中,利用二维数组进行保存,其值为1代表该点有方块占用,值为0代表空白,根据值绘制整张游戏窗口。
2.2.2. 功能设计
从游戏的基本玩法出发,主要算法在于俄罗斯方块的形状和旋转。在游戏设计中,方块采用最基本的7种造型,包括长条型,正方型,正S型,反S型,正7型,反7型,T型,每种造型又可以通过逆时针旋转变化出4种形状,因此利用三维数组保存28种方块形状,并且编号为K~K+3(K=0,1…7)的四个形状为一组。方块采用4*4的二维数组的数据结构,以此在界面中根据其数组对应值进行方块绘制。在旋转过程进行之前,先判断在该位置能否进行旋转,若能,则将其在三维数组中的编号K,编号为K+(K+1)%4的形状即为旋转结果。
游戏过程中,利用随机函数在一个预览窗体中提前展示形状供用户参考,然后将展示的形状复制到游戏窗体中进行摆放,在游戏窗体中用户就可以使用键盘的方向键来控制方块的运动,然后对每一行进行判断,如果有某行的方块是满的,则消除这行的方块,并且使上面的方块自由下落,其中,方块向下的速度通过调用函数进行控制。同时用户也可以使用向下键加快下落速度,定义一个变量,对消除的行数进行记录,最后得出用户的分数,用条件语句对分数进行判断,达到一定的积分就可以升级到下一个等级。
三、 程序流程图
1.
2.
3.
3.1. 总流程
到达底部部
到底游戏结束
到达底部部
结束
销行操作
生成下一个下坠物
将新生的下坠物代替旧的“下一
个下坠物“
将旧的“下一个下坠物”用作当前
下坠物
销行操作
游戏结束处理
下降一个单位
开始
3.2. 底部到达的判断与销行的实现:
是
否
堆积方块,判断接触面状态及是否得分
2.统计分数
判断是否过关
关数增加,游戏速度将变快。开始新的一关,继续游戏
游戏窗口重绘
1.判断行满、处理销行、堆积方块向下移动
将新的下坠物放置到游戏区域中去,这时可能出现马上到达底部的情况,因此需要对它进行判断,如果是到达底部,则进行销行处理,并且修改相应的数据状态。而判断是否已经到达了底部,可以通过当前下坠物件所对应的接触面的方块位置为被占用状态来确定。
统计分数:在消行处理里面有一个专门用来统计消行数的变量,然后根据变量的值决定分数的多少。如果总分数达到过关条件就过关,改变游戏速度,开启新的一关,然后再加载方块。没有达到过关分数或者没有满行,则加载下一个方块继续游戏。
是
否
是
否
否
是
是
否
接上图1点
底行
判断当前行是否为空
判断当前行是否为满
判断是否有满行
判断移动行方块是否为空
当前行向上推动一行
将要移动行所有数据移至当前行
将当前行的所有数据初始化
统计连续几行为满
如果有销行,则刷新游戏区域。接上图2点
4.
5.
5.1.
5.2.
5.3. 随机方块的产生
是
否
7
6
5
4
3
2
1
随机抽取一个数
随机数
一字形
田字形
L 形
J 形
T 形
S 形
Z字形
型
游戏是否结束
保存当前方块坐标
显示方块于屏幕上
游戏结束
保存信息
关数初始化
返回开始界面
四、 部分截图
5.1初始界面 5.2游戏界面
5.3帮助界面 5.4排行界面
1.
2.
3.
4.
5.
五、 关键代码
1.
2.
3.
4.
5.
6.
6.1. ActivityGame.java
package com.HDU.tetris;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.AlertDialog.Builder;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
public class ActivityGame extends Activity { //总游戏窗口
private static final String TAG = "ActivityGame";
TetrisView mTetrisView = null; //处理游戏进程的类
public void onCreate(Bundle saved) //创建活动
{
super.onCreate(saved);
//启动窗体的拓展特性
requestWindowFeature(Window.FEATURE_NO_TITLE);
init();
}
private void init()
{
mTetrisView = new TetrisView(this);
Intent intent = getIntent(); //得到当前的intent
//获取额外信息——等级
int level = intent.getIntExtra(ActivityMain.LEVEL,1);
mTetrisView.setLevel(level); //设置等级
int flag = intent.getFlags(); //得到当前intent的标记符
if(flag == ActivityMain.FLAG_CONTINUE_LAST_GAME)
{//当遇到继续上次游戏事件
mTetrisView.restoreGame(); //恢复上次游戏
}
//得到当前声音设置
boolean isVoice
= intent.getBooleanExtra(ActivityMain.VOICE,true);
mTetrisView.setVoice(isVoice); //设置声音
setContentView(mTetrisView); //设定当前使用的视图
}
public void onPause() //暂停活动
{
mTetrisView.onPause();
super.onPause();
}
public void onResume()//执行活动
{
super.onResume();
mTetrisView.onResume();
}
public void onStop() //停止活动
{
super.onStop();
mTetrisView.saveGame(); //保存游戏
mTetrisView.freeResources(); //释放游戏占用的资源
}
}
6.2. ActivityHelp.java
package com.HDU.tetris;
import android.app.Activity;
import android.os.Bundle;
public class ActivityHelp extends Activity { //帮助窗口
public void onCreate(Bundle saved) //开始
{
super.onCreate(saved);
setContentView(R.layout.help); //初始化帮助窗口的界面
}
}
6.3. ActivityMain.java
package com.HDU.tetris;
// Author: HDU
// 2010.3
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
public class ActivityMain extends Activity {
public static final int FLAG_NEW_GAME = 0; //开始新游戏
public static final int FLAG_CONTINUE_LAST_GAME = 1; //继续上一次的游戏
public static final String FILENAME = "settingInfo"; //文件名
public static final String LEVEL = "level"; //游戏级别
public static final String VOICE = "voice"; //游戏声音
private int mLevel = 1; //当前游戏等级
private Button btNewgame = null; //新游戏按钮
private Button btContinue = null; //继续游戏按钮
private Button btHelp = null; //帮助按钮
private Button btRank = null; //排名按钮
private Button btPre = null; //等级下降按钮
private Button btNext = null; //等级上升按钮
private Button btExit = null; //退出按钮
private TextView tvLevel = null; //等级编辑框
private CheckBox cbVoice = null; //声音选项
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) { //创建,程序执行时的第一步
super.onCreate(savedInstanceState);
setContentView(R.layout.menu); //设置开始界面的菜单
btNewgame = (Button)findViewById(R.id.bt_new); //通过ID查找新游戏按钮在VIEW子控件
btContinue = (Button)findViewById(R.id.bt_continue);//继续游戏按钮控件
btHelp = (Button)findViewById(R.id.bt_help); //帮助按钮控件
btRank = (Button)findViewById(R.id.bt_rank); //排名按钮控件
btPre = (Button)findViewById(R.id.bt_pre); //等级下降按钮控件
btNext = (Button)findViewById(R.id.bt_next); //等级上升 按钮控件
btExit = (Button)findViewById(R.id.bt_exit); //退出按钮控件
tvLevel = (TextView)findViewById(R.id.tv_speed);//等级编辑框控件
cbVoice = (CheckBox)findViewById(R.id.cb_voice);//声音选项框控件
btNewgame.setOnClickListener(buttonListener); // 在新游戏按钮上设置点击监听器
btContinue.setOnClickListener(buttonListener);// 在继续游戏按钮上设置点击监听器
btHelp.setOnClickListener(buttonListener); // 在帮助按钮上设置点击监听器
btRank.setOnClickListener(buttonListener); // 在排名按钮上设置点击监听器
btPre.setOnClickListener(buttonListener); // 在等级下降按钮设置点击监听器
btNext.setOnClickListener(buttonListener); // 在等级上升按钮上设置点击监听器
btExit.setOnClickListener(buttonListener); // 在退出按钮上设置点击监听器
restoreSettings(); //恢复初始设置
}
private Button.OnClickListener buttonListener = new Button.OnClickListener()
{ //按钮上的点击监听器类
@Override
public void onClick(View v) { //按钮点击时的处理程序
if(v == btNewgame) //当被点击的按钮为新游戏按钮时
{
Intent intent = new Intent(ActivityMain.this,ActivityGame.class);//在两个Activity类之间传输数据
intent.setFlags(FLAG_NEW_GAME); //设置标记符
intent.putExtra(VOICE,cbVoice.isChecked()); //添加声音的附加信息
intent.putExtra(LEVEL,mLevel); //添加等级的附加信息
startActivity(intent); //将此intent类传入相应的Activity中
return;
}
if(v == btContinue) //继续游戏按钮处理程序
{
Intent intent = new Intent(ActivityMain.this,ActivityGame.class);//建立intent类
intent.setFlags(FLAG_CONTINUE_LAST_GAME); //设置标记符
intent.putExtra(VOICE,cbVoice.isChecked()); //添加声音选项的附加信息
startActivity(intent); //将此intent类传入相应的Activity中
return;
}
if(v == btHelp) //帮助按钮的处理程序
{
Intent intent = new Intent(ActivityMain.this,ActivityHelp.class);//建立intent类
startActivity(intent); //将此intent类传入相应的Activity中
return;
}
if(v == btRank) //排名按钮的处理程序
{
Intent intent = new Intent(ActivityMain.this,ActivityRank.class);//建立intent类
startActivity(intent); //将此intent类传入相应的Activity中
return;
}
if(v == btPre) //等级下降按钮的才护理程序
{
btPre.setBackgroundColor(0xffc0c0c0); //设置背景颜色
String s = tvLevel.getText().toString(); //从等级编辑框中获取信息,传入字符串中
int level = Integer.parseInt(s); //将获取的字符串转化为数字
--level; //等级减1
level = (level-1+TetrisView.MAX_LEVEL) % TetrisView.MAX_LEVEL;//避免等级益处标准范围,形成循环设置
++level;//等级加1,从0~5 改为标准的1~6
s = String.valueOf(level); //将数字转化为字符串,传回
tvLevel.setText(s); //等级编辑框的内容显示为新的等级数
mLevel = level; //当前等级改为设置的等级
btPre.setBackgroundColor(0x80cfcfcf); //设置背景颜色
return;
}
if(v == btNext) //等级上升按钮的处理程序
{
btNext.setBackgroundColor(0xffc0c0c0); //设置背景颜色
String s = tvLevel.getText().toString(); //从等级编辑框中获取信息,传入字符串中
int level = Integer.parseInt(s); //将获取的字符串转化为数字
--level;//等级减1
level = (level+1) % TetrisView.MAX_LEVEL; //避免等级益处标准范围,形成循环设置
++level;//等级加1,从0~5 改为标准的1~6
s = String.valueOf(level); //将数字转化为字符串,传回
tvLevel.setText(s); //等级编辑框的内容显示为新的等级数
mLevel = level; //当前等级改为设置的等级
btNext.setBackgroundColor(0x80cfcfcf); //设置背景颜色
return;
}
if(v == btExit) //退出按钮的处理程序
{
ActivityMain.this.finish(); //结束该活动
}
}
};
private void saveSettings() //保存设置方法
{
//SharedPreferences是轻量级的存储类,主要是保存一些常用的配置
SharedPreferences settings = getSharedPreferences(FILENAME,0);//得到当前设置信息
settings.edit()
.putInt(LEVEL,mLevel)
.putBoolean(VOICE,cbVoice.isChecked())
.commit(); //将各种信息保存如settings中,完成更新设置
}
private void restoreSettings() //恢复初始设置
{
SharedPreferences settings = getSharedPreferences(FILENAME,0);//得到当前设置信息
mLevel = settings.getInt(LEVEL,1); //等级恢复为1
boolean hasVoice = settings.getBoolean(VOICE,true);
tvLevel.setText(String.valueOf(mLevel)); // 显示初始等级到等级编辑框中
cbVoice.setChecked(hasVoice); // 声音设置为开启
}
public void onStop() //活动停止方法
{
super.onStop();
saveSettings(); //保存当前设置
}
}
6.4. ActivityRank.java
package com.HDU.tetris;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.SimpleCursorAdapter;
public class ActivityRank extends Activity { //排名窗口
private RankDatabase mDatabase = null; //数据库信息
//通过ListView控件,可将项目组成带有或不带有列标头的列,并显示伴随的图标和文本。
private ListView mListView = null;
public void onCreate(Bundle saved) //创建该活动
{
super.onCreate(saved);
setTitle("排行榜"); //显示标题
setContentView(R.layout.rank); //设置窗口初始化信息
}
}
6.5. Court.java
package com.HDU.tetris;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
public class Court { //游戏窗口
public final static int COURT_WIDTH = 11; //窗口的宽度
public final static int COURT_HEIGHT = 23+4; //窗口的高度
public final static int BLOCK_WIDTH = 20; //每个格子的宽度
public final static int BLOCK_HEIGHT = 20; //每个格子的高度
public final static int ABOVE_VISIBLE_TOP = 4; //允许显示的最高点
public final static int BEGIN_DRAW_X = 0; //开始画图的坐标
public final static int BEGIN_DRAW_Y = TetrisView.SCREEN_HEIGHT - Court.BLOCK_WIDTH * Court.COURT_HEIGHT;
private int[][] mCourt = new int[COURT_WIDTH][COURT_HEIGHT]; //保存窗口信息的数组
private Context mContext = null; //context的作用,就是android应用连接service的桥梁。
private ResourceStore mRs = null; //应用环境中的资源信息
public Court(Context context)
{
mContext = context; //保存应用环境全局信息
mRs = new ResourceStore(context); //获取应用环境中的资源信息
clearCourt(); //清空游戏窗口
}
public void clearCourt() //清除窗口信息
{
int i,j;
for(i=0;i<COURT_WIDTH;i++)//遍历窗口所有的点
{
for(j=0;j<COURT_HEIGHT;j++)
{
mCourt[i][j] = 0; //将相应数组信息清空
}
}
}
public boolean isGameOver() //判断游戏是否结束
{
for(int i=0;i<COURT_WIDTH;i++)
{//其中某一行的方块到达可允许显示的最高点,则游戏结束
if(mCourt[i][ABOVE_VISIBLE_TOP] != 0)
return true;
}
return false;
}
public boolean isSpace(int posX,int posY) //判断该点是否为空
{
if (posX < 0 || posX >= COURT_WIDTH) //超出行边界线出错
return false;
if (posY < 0 || posY >= COURT_HEIGHT) //超出列边界线出错
return false;
if(0 == mCourt[posX][posY]) //对应数组的值为0 ,即为空
return true;
return false;
}
public boolean availableForTile(int[][] tile,int x,int y) //判断该格子能否放方块
{
for (int i = 0; i < 4; i++) {//遍历所有点
for (int j = 0; j < 4; j++) {
if (tile[i][j] != 0) {
if (!isSpace(x + i, y + j)) { //该点已经有方块占据
return false;
}
}
}
}
return true;
}
public void placeTile(TileView tile) //放置方块
{
int i,j;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
if (tile.mTile[i][j] != 0) //没有被占据
{//窗口中该点的位置放置相应的方块
mCourt[tile.getOffsetX() + i][tile.getOffsetY() + j] = tile.getColor();
}
}
}
}
public int removeLines() //消除完成了的一整行
{
int high = 0;
int low = COURT_HEIGHT; // 初始最低点
high = highestFullRowIndex(); //得到可消除的最高行数
low = lowestFullRowIndex(); //得到可消除的最低行数
int lineCount = low - high +1; //可消除的总行数
if(lineCount > 0) //存在可消除的行
{
eliminateRows(high,lineCount); //消除找到的可消除的行
return lineCount; //返回消除的行数
}
return 0;
}
private void eliminateRows(int highRow,int rowAmount)//消除已完成的行上的方块
{
int i,j;
for(i = highRow+rowAmount-1;i >= rowAmount;i--)
{
for(j = 0;j < COURT_WIDTH;j++)
{//将待消除行上方的方块整体向下移动,覆盖待消除的行
mCourt[j][i] = mCourt[j][i-rowAmount];
}
}
}
private int highestFullRowIndex() //查找已完成行的最高行数
{
int result = 0;
boolean removeable = true; //标记是否可以消除
int i,j;
for(i = 0;i < COURT_HEIGHT;i++) //从上往下遍历
{
removeable = true;
for(j = 0;j<COURT_WIDTH && removeable;j++)
{
if(isSpace(j,i) ) //该点上没有方块
{
result++;
removeable = false; //标记该行不可消除
}
}
if(removeable) //可消除,则找到结果,退出
break;
}
return result;
}
private int lowestFullRowIndex()//查找已完成行的最低行数
{
int result = COURT_HEIGHT-1; //初始最低点
boolean removeable = true;
int i,j;
for(i = COURT_HEIGHT - 1;i >= 0;i--)//从下往上遍历
{
removeable = true;
for(j = 0;j<COURT_WIDTH && removeable;j++)
{
if(isSpace(j,i) ) //该点上没有方块
{
result--;
removeable = false; //标记该行不可消除
}
}
if(removeable) //可消除,则找到结果,退出
break;
}
return result;
}
private void removeSingleLine(int lineIndex,int time) //消除单行
{
int t,i,j;
for(t = 0;t<time;t++)
{
for(i = lineIndex; i > 0; i--)//将待消除行上方的方块全部向下移动一行
{
for (j = 0; j < COURT_WIDTH; j++)
{
mCourt[j][i] = mCourt[j][i - 1];//上一行覆盖下一行
}
}
}
}
public void paintCourt(Canvas canvas) //根据画布信息画游戏窗口
{
//Paint 中包含了很多方法对其属性进行设置,绘制时所使用的画笔
Paint paint = new Paint();
paint.setAlpha(0x60); // 设置Alpha值
//对图片添加透明效果
canvas.drawBitmap(mRs.getCourtBackground(),0, 0, paint);
paint.setAlpha(0x
展开阅读全文