这几天一直想写点自己的小游戏,刚巧手头有本《J2MEMIDP手机游戏程序设计》,其中第六章有讲俄罗斯方块的写法,但是一看之后却发现根本不是,于是在网上四处查找简单有效的俄罗斯方块源码,并且研究了一下,发现,基本思想大同小异,但是我找到的代码虽然简单,却有疏漏,他没有考虑方块在边界的时候能否变形,没有考虑方块夹在别的方块中间的时候能不能变形等等这样的问题,所以,我将此代码大幅修改,感觉可以写点心得体会出来:
关键点:
1:关于方块的定义和地图的定义
地图其实就是二维数组,而方块也是一个大的二维数组,我见过别的写法,是把每个方块各自定义,这里的代码比较简单,就是一个二维数组,定义了所有的7种方块体。
/**
* 这个数组存放了所有类型的方块
*/
private static int blocklibary[][] = {
{ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 },// I
{ 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0 },// 7
{ 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },// 反7
{ 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },// Z
{ 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },// S
{ 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },// O
{ 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 } // T
};
每个都是4*4
地图:
if (getWidth() == 240) {
CHESSW = 10;
} else if (getWidth() == 128) {
CHESSW = 10;
}
int tempH = getHeight();
if (tempH >= 266) {
CHESSH = 30;
} else {
CHESSH = 20;
}
chess = new int[CHESSW][CHESSH];
根据手机屏幕的宽高进行定义,也是个二维数组。
2:旋转
俄罗斯方块最关键的逻辑就是旋转,以及之后的碰撞检测,包括变形之后是否出界(上下左右),变形之后是否会与其他方块体重合。
这里的锚点用的是左上角的第一个点:
/**
* 贴墙,贴别的方块,都可以转动了,如果能转的话
*
* @return
*/
public boolean canTurn() {
int i, j, next;
int tempX = currentX;
boolean isAlianSizeLeft = false;
boolean isAlianSizeRight = false;
int[][] blockNext = new int[4][4];
next = (currentBlockDirection + 1) % blockDirection;// 准备要变化的方向
for (int ii = 0; ii < 4; ii++) {
for (int jj = 0; jj < 4; jj++) {
blockNext[ii][jj] = blocklibary[currentBlockType][next * 16
+ ii * 4 + jj];
}
}
// 判断是否出界
// 左右下
if (currentX < 0)// 左检测
{
isAlianSizeLeft = true;
for (i = 0; i < -tempX; i++) {
// 之所以有可能是负数,是因为锚点坐标是左上角,而左上角有可能在屏幕外,比如说“竖”
// {0,1,0,0
// 0,1,0,0
// 0,1,0,0
// 0,1,0,0}
for (j = 0; j < 4; j++) {
if (blockNext[j][i] == 1) {// 就是说,变化之后的方块在屏幕外,那就返回false,这个写法不太智能,就是说,竖靠边就不能变化了,所以改成了下面这样
tempX++;// 比如说T,他变成卜并且靠左的时候那就不能变了,可以先把他向右移一格,下面再去判断是不是和原有重合了
break;
}
}
}
}
if (currentX + 3 > (CHESSW - 1))// 右检测
{
isAlianSizeRight = true;
for (i = CHESSW - currentX; i < 4; i++) {// 这个意思是有几个空的竖行,就循环几次,行
for (j = 0; j < 4; j++) {// 列
if (blockNext[j][i] == 1) {// 看屏幕外竖着的那些
// 行有没有小方块,如果有,那一定是说变形之后有点在屏幕外
tempX--;// 同样道理,先左移一格
break;
}
}
}
}
if (currentY + 3 > CHESSH)// 下检测
{
for (i = 0; i < 4; i++) {
for (j = CHESSH - currentY; j < 4; j++) {
if (blockNext[j][i] == 1)// 变形之后出下边界的部分有没有小方块,如果有,就不能变形了
return false;
}
}
}
// 判断是否重合
if (isAlianSizeLeft || isAlianSizeRight) {
for (j = 0; j < 4; j++) {// 竖
for (i = 0; i < 4; i++)// 横
{
if (blockNext[j][i] == 1
&& chess[tempX + i][currentY + j] == 1) {
return false;
}
// if (i + tempX < 0 || i + tempX > (CHESSW - 1)) {
// // 左右贴边
// continue;
// }
//
// if (j + currentY < 0 || j + currentY > CHESSH) {
// // 上下?
// continue;
// }
}
}
} else {
int miniMap[][] = testShowAreaMap(tempX, currentY);
for (j = 0; j < 4; j++) {// 竖
for (i = 0; i < 4; i++)// 横
{
if (blockNext[j][i] == 1 && miniMap[j][i] == 1) {
// System.out.println("方块重合检测");
if (i % 4 == 0 || i % 4 == 1) {// 说明是数组的最左边那一列,也就是方块左边靠着别的方块
tempX++;
miniMap = testShowAreaMap(tempX, currentY);
for (j = 0; j < 4; j++) {
// 竖
for (i = 0; i < 4; i++)// 横
{
if (blockNext[i][j] == 1// 仅仅是试验坐标平移,方块形状没变,还是可以用这个blockNext
&& miniMap[i][j] == 1) {// 如果贴边变形后还是和别的地方重合了,就返回失败,不能变形
return false;
} // 要考虑变形后是不是已经出了右边界
if (tempX + 3 > (CHESSW - 1))// 右检测
{
for (i = CHESSW - tempX; i < 4; i++) {
for (j = 0; j < 4; j++) {
if (blockNext[j][i] == 1) {
return false;
}
}
}
}
}
}
} else if (i % 4 == 3 || i % 4 == 2) {// 说明是数组的最右边那一列,也就是方块右边靠着别的方块
// System.out.println("右边有方块");
// int temp1 = tempX - 1;
if (i % 4 == 3) {
tempX -= 1;
} else {
tempX -= 2;
}
for (j = 0; j < 4; j++) {
// 竖
for (i = 0; i < 4; i++)// 横
{
// System.out.println("右边有方块,检测左边");
if (tempX < 0) {// 左检测
for (i = 0; i < -tempX; i++) {
for (j = 0; j < 4; j++) {
if (blockNext[j][i] == 1) {// 移位变形后,出界了,那就不能变
return false;
}
}
}
} else {
miniMap = testShowAreaMap(tempX,
currentY);
if (blockNext[j][i] == 1
&& miniMap[j][i] == 1) {// 如果贴边变形后还是和别的地方重合了,就返回失败,不能变形
// System.out.println("不能变形!");
return false;
}
}
}
}
}
}
}
}
}
// System.out.println("重合检测完毕");
currentX = tempX;
return true;
}
这样就可以了。
3:左右下移动
通过上面的代码其实能看出来如何判断左右边界,就不多说了。
4:界面美化和debug
由于写法简单,直接可以绘制方块,所以要立刻看到结果并不是太难,但是由此带来的问题就是如何美化界面,以及如何让debug的过程更加便利
以下是我的debug界面:
<img src="https://img-blog.csdn.net/20150625190152120?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYnl3dXU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
这样可以很好地理解锚点,地图,和其他方块键的虚实位置关系。
下图是实际运行画面:
最后放上代码,下次讲讲把这个项目移植到android的方法。
也可能研究一下AI。。。
http://download.csdn.net/detail/bywuu/8839377