俄罗斯方块的arm实现<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
测试知道朋友那块320*240的彩屏液晶是好的以后,想做点什么来玩下。于是用该液晶写了个标准的俄罗斯方块游戏,用arm7(s3c44B0)做的控制。下面做个详细的总结,加深理解,其次有兴趣的同仁可以试着用单片机做做,比较有趣!
先说原理:(原理是在网上查阅先人的资料后,得以总结的,感谢那些把资料发布于互联网的前辈,为我们提供了一个学习的平台)。首先要完成的就是建模工作。
建立模型:本人认为游戏编程中建模是十分重要的,语言的表达以及描述是基本功问题了。标准的俄罗斯方块游戏有一下几种图形:
显然我们可以用一个5*5的数组将其分别表示:可以这样理解在上图中每个图形都是5*5的大小,在黑点处将其在数组中的对应值赋1即可,其余为0。如第一个图“横条子”可以用
a[5][5]={ 0,0,0,0,0
0,0,0,0,0
1,1,1,1,0
0,0,0,0,0
0,0,0,0,0}
表示。其余的类推即可。对于这写图形的变换操作,只需写一个函数void rotateBox(int box1[5][5], int box2[5][5]) 对其进行矩阵的倒转即可。
void rotateBox(int box1[5][5], int box2[5][5])
{
/*旋转box1输出到box2*/
int x, y;
for(x = 0; x < 5; x++) /*这个函数可以须要实际*/
for(y = 4; y >= 0; y--) /*编写一下才能印像深刻*/
box2[y][x] = box1[x][4 - y];
}
我们可以用一个3维数组将7中基本方块全部表示出来:(当然这里MAX_C=7)
int box[MAX_C][5][5] = { /*MAX_C(7)种预定义的盒子*/
{
{0,0,0,0,0},
{0,0,0,0,0},
{1,1,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,0,1,0,0},
{0,1,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,1,1,0,0},
{0,0,1,1,0},
{0,0,0,0,0},
{0,0,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,0,0}
},
{
{0,0,0,0,0},
{0,1,1,0,0},
{0,0,1,0,0},
{0,0,1,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,0,1,1,0},
{0,0,1,0,0},
{0,0,1,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,0,1,1,0},
{0,0,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
}
};
接着要建立的模型就是背景了。我们这里设置的俄罗斯方块游戏的游戏显示区有12行20列。根据前面的思想我们也可以将其抽象为一个12*20的数组,但为了后面判断碰撞方便,我们把其抽象为16*22的数组 int map[MAX_Y][MAX_X]。0,1,14,15列全初始为1,最下面俩行即20,21俩行全初始为1;示意图如下:
我们用下面这个函数来实现上面的数组:
void initMap(void) { /*初始化地图*/
int x, y; /*我们须要一圈卫兵...*/
for(y = 0; y < MAX_Y; y++) {
for(x = 0; x < MAX_X; x++) {
if(x < 2 || x > MAX_X - 3 || y > MAX_Y - 3)
map[y][x] = 1;
else map[y][x] = 0;
}
}
好了有了前面这俩个模型的建立后面的就好理解了。我们可以认为背景就是如上的一个大盒子,而每个基本图形就是上面的小盒子。整个游戏简单来说就是小盒子在大盒子里面的移动(下落,左右移动)。而难点就是如何判断小盒子已经落到了大盒子的边界的问题。
判断碰撞:前面在给大盒子(背景)建模的时候,给大盒子的四周都围了2层1。
那我们可以这样来判断小盒子与大盒子的碰撞。小盒子的数组与其在大盒子数组中的对应位置的值相与。显然若果与后的值为1,证明大盒子与小盒子在此处的值都为1,及表明在此处发生了碰撞。而在最首先背景(大盒子)中都为0,只有边界处有2层1。这时只用来了判断小盒子是否出界。当小盒子降落到大盒子底部后,将小盒子的内容对应写入大盒子数组中,这样大盒子(背景)就得以更新。
int test(int mx, int my, int box[5][5])//测试大小盒子是否碰撞
{
/*测试box在map里mx,my位置上是否能着陆*/
/*这个是最关键的一个函数,它判断是否产生非空冲突*/
/*但算法还是很简单的*/
int x, y;
for(x = 4; x >=0; x--)
for(y = 4; y >=0; y--)
if(map[y +mx][x + my] && box[x][4-y])
return 0;
return 1;
}
void putBox(void) //把碰撞后的小盒子融入到大盒子中,得到新的大盒子
{ /*将curbox填充到地图上*/
int x, y;
for(y = 0; y < 5; y++) /*这个也简单,主要是要根*/
for(x = 0; x < 5; x++) /*据curx,cury指出位置 */
if(curbox[y][x])
map[4-x + curx][y + cury] = curbox[y][x];
}
可以用下面俩副示意图来形象的表示一下:
整体流程:所以我们的主程序的流程可以这样表示:
while(1)//一次游戏的循环
{
if(!newfall())
{
Uart_Printf("game over!. Press any key!\n");
TGlib_disp_hzk16(180,32,"游戏结束再接再厉",8);
break;//如果不能再创建新的图形,则游戏结束
}
dis_next_box();//显示下一个
while(1)//一个图形的降落循环
{
if(!drop())
{
putBox();//把图形加入到背景中
clear_line(); //如果有满行则销层,且更新分数和等级
break;//退出该图形的降落循环,迎接下一个
}
dis_map();//实时显示背景
dis_box(curx,cury,curbox);//实时显示图形
Delay_ms((6-level)*100);//下落的速度控制的延时
}
}
}
这里只是为后面的具体实现打下了理论基础,下一编将来讲解具体的代码实现,每个函数的源代码都具体给出。希望观者给予支持。下面先看几张完成后的游戏图片:
用户1655369 2011-11-26 17:09
用户188194 2010-11-27 19:32
用户248234 2010-3-20 10:25
用户1079511 2008-12-7 10:28