(100分)策划俄罗斯方块的完整游戏流程(有可能加50分,谢谢)
分享...........
我用TC调试成功。
因为有
# include & ltstdio.h & gt
# include & ltstdlib.h & gt
# include & ltbios.h & gt/*在这里,你需要读取系统运行时间作为计时器*/
# include & ltgraphics.h & gt/*可惜TC2简单的图形让我放弃使用*/
# include & ltconio.h & gt/*win32+openGL来解释。*/
#define MAX_X 14 /*最大可见X*/
#define MAX_Y 21 /*可见最大Y*/
/*我们已经定义了最大可见X和Y,那么还有没有?
可见的部分,其实就是地图中的左右(大方框)
两边和最下面两行都填1,大大简化了。
界外判断,其实本案不是这样的。
码,因为1旁边有个圈,防止小盒子变大。
箱子的冲压范围
*/
#定义MAX_C 7 /*最大类别,这个不用解释*/
#define KEY_UP 'w' /*定义上下左右按钮*/
#定义向下键
#define KEY_LEFT 'a '
#定义KEY_RIGHT 'd '
#define KEY_ESC 27 /* Exit */
typedef int BOOL
#定义假0
#定义TRUE 1 /*没有这些TC...自己定义吧:)*/
/*时钟结构*/
Typedef结构{/*时钟结构*/
BOOL已启用;/*时钟是否开启*/
无符号整数区间;/*时间间隔*/
无符号整数lasttime/*这是内部变量*/
}定时器;
/*
*现在已经进入编程初级阶段。
*一开始,我会写所有的函数原型和它们的功能。
*主功能在程序的最后,你可以在这里看到整个游戏的组织结构。
*很好,只有几十行,而且很好理解。当然,我们先来看看函数的原型。
*和解释
*/
/******************************************************\
*功能原型和描述*
\******************************************************/
/*下面三个函数可以参考定时器结构。在函数声明*/之后
int GetTickCount();/*返回计算机或操作系统运行所用的时间*/
/*已包含在win32环境下的windows.h中,4字节*/
/*在DOS(代码)环境下,要自己写,用BIOS.h内容*/
int setTimer(Timer *t,unsigned int intv,BOOL en);
/*设置时钟t,参数为时钟指针、时间间隔和活动*/
/*时间间隔,win32下毫秒,DOS下1/18秒(有点低)*/
BOOL test Timer(Timer * t);/*测试时钟t是否到达预定时间*/
/*比如下面的代码:*/
/*
setTimer(& amp;t,1,1);设置1单位的间隔
while(1) {
if(testTimer(& amp;t)) printf("Active!\ n ");
}
活跃会定期印在屏幕上(1台)!
一般来说,testTimer必须循环重复执行,激活时返回1。
*/
void渲染(void);/*独特的绘图功能*/
/*注意,这个函数根据贴图中的点阵和
在地图中间坐标的右边位置画一个小方框*/
/*DOS图形当然很低,但是这里全屏画图还是可以的。
是的,我使用双缓冲来交换图纸,这让我感觉更好*/
void init map(void);/*初始化地图(大框)*/
/*之前提到过这个二维数组中有一个1的圆来阻止它。
小方框越界了,这是函数*/
void new game();/*创建新游戏*/
/*此函数初始化一个或多个时钟,并构建第一个下降框*/
/*当然,在构造之后,预览应该是一个一个生成的*/
void rotateBox(int box 1[5][5],int box 2[5][5]);
/*核心函数成员,逆时针旋转box1 90度,保存在box2 */
void rebidnext();
/*核心函数成员,生成下一个框*/
int drop();
/*核心函数成员,向下移动下降框(实际上是增加下降框)
子的y值,当然要判断是否与贴图格子重叠*/
/*与地图重叠,无法完成下落操作。返回0*/
void putBox();
/*在此之上,落盒和地图是两个独立的维度*/
/*当下落失败时,小盒子必须返回顶部重新进行下落,这*/
/*当原框内容当然会变成map、putBox的内容。
就是根据XY */
void clear();
/*此功能在坠落失败和putBox后执行,扫描整个地图*/
/*清除格子的全行,细节在函数里*/
int move(int dir);
/*左右移动下拉框,dir表示向左或向右,和drop一样。
是一样的*/
int test(int mx,int my,int box[5][5]);
/*这个更重要。判断盒子在MX和My的坐标上,和地图上是一样的。
非空格子是否重叠。一个很常见的函数*/
int rotate();
/*旋转下降盒。当然,如果旋转后与地图冲突,就会
取消旋转,返回0,但是返回值好像没用~ */
int new fall();
/*创建一个拖放元素,并将“下一个”预览的内容复制到下拉框*/
/*并将下拉框移动到地图顶部。当然,这个过程,如果顶
如果部门有冲突,则返回0,描述完整...gameOver*/
/******************************************************\
*可变面积*
\******************************************************/
/*在上面的解释中,可能会有一些无知,因为可能对实际使用的变量没有了解。
*是的。
*/
int map[MAX _ Y+4][MAX _ X+4];/*地图\大框...MAX_X,Y是可视面积*/
/*我说过我们需要在外面安排两个“警卫*/
int curbox[5][5];/*当前下落的盒子*/
int curx,cury/*保存当前活动框在地图上的位置*/
int next box[5][5];/*容纳下一个形状的盒子*/
/*这只是几个方框和坐标*/
/*这里有七个标准的俄罗斯方块图形格子,会复制到Phase */
/*应该是的框...:) */
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}
}
};
/******************************************************\
*时钟*
\******************************************************/
/*时钟部分也很好理解,一个用来设置时钟,一个用来测试时钟激活状态*/
定时器tDown/*正常的下降定时时钟间隔将会更大*/
定时器tFast/*按下KEY_DOWN时使用快速下降*/
int speed = 13;/*控制下降时间间隔*/
#定义快速_INTV 1 /*快速时钟的间隔*/
/******************************************************\
*时钟*
\******************************************************/
/*时钟部分也很好理解,一个用来设置时钟,一个用来测试时钟激活状态*/
定时器tDown/*正常的下降定时时钟间隔将会更大*/
定时器tFast/*按下KEY_DOWN时使用快速下降*/
#定义快速_INTV 1 /*快速时钟的间隔*/
Int GetTickCount() {/*读取BIOS时钟*/
int ret
ret = peek(0x0,0x46e);/*实际在0:046e读取内存内容*/
ret & lt& lt= 8;/*这个地方是$ % # $ $ % &;^*/
ret += peek(0x0,0x 46c);/*新东西那么多,找几本书看看*/
返回(ret);
}
int setTimer(Timer *t,unsigned int intv,BOOL en) {
t->;enabled = en/*设置时钟*/
t->;intervel = intv
t->;last time = GetTickCount();/*lasttime记录最后的*/
/* What /*tickcount返回*/
/*以便再次测试时间时,生成新的tickcount。
它减去最后一次tickcount得到一个时间。
间隔,这个可以和intervel比较一下,看是不是
激活的
*/
返回0;
}
Boolstesttimer (timer * t) {/*以上六行解释:)*/
无符号int tmp,dt;
如果(!(t->;enabled))返回FALSE
tmp = GetTickCount();
dt = tmp-(t-& gt;last time);
if(dt & gt;= t->;intervel) {
t->;lasttime = tmp
返回TRUE
}
返回FALSE
}
/******************************************************\
*渲染部分*
\******************************************************/
/*提供渲染以更新整个屏幕*/
/*这个功能还有很多话要说,为了追求美观和编译*/
灵活性当/*时,这个函数写得相当长...*/
/*现在写一些关于这个游戏的图形...我也不用TC2图形。
我很乐意。毕竟已经过时了,但是鉴于它的简单性和实用性,还是用来教学的。
太完美了,这也是教学中一直用TC的原因。老师不喜欢让学生学习。
学生问一些他们难以掌握的问题...
/*这里我用VGAMED模式代替VGAHI,因为有两个VGAMED。
Page(可以想象成一个缓冲区),可以用来画图,不用闪烁。也就是在后台。
在页面上画一个图形,完成后显示。
这里使用了两个函数:
Setactivepage(1 | 0)参数只能是1或0。选择一个绘图页,例如,选择。
选择1后,以后所有的绘图动作都将在1页上绘制。
Setvisualpage(1 | 0)这叫选择可见页面,也就是在屏幕上选择。
显示页面1或0
*/
void渲染(void) {
int x,y;
静态int c page = 0;/*当前页面,使用*/
#define STARTX 50 /*定义几个常量*/
#定义STARTY 0
#定义镜头18
setactivepage(cPage=(cPage == 0?1:0));/*选择页面*/
clear device();/*清空屏幕*/
set color(15);
矩形(STARTX + LEN * 2 - 2,
STARTY + LEN * 3 - 2
STARTX + LEN * (MAX_X - 2) + 2,
STARTY+LEN *(MAX _ Y-2)+2);
/*用白色画出轮廓*/
setfillstyle(SOLID_FILL,5);
for(y = 3;y & ltMAX _ Y-2;Y++) {/*画地图*/
for(x = 2;x & ltMAX _ X-2;x++) {
if(map[y][x]) {
矩形(x * LEN + STARTX,
y * LEN + STARTY,
x * LEN + STARTX + LEN,
y * LEN+STARTY+LEN);
bar( x * LEN + STARTX + 1,
y * LEN + STARTY + 1,
x * LEN + STARTX + LEN - 2,
y * LEN+STARTY+LEN-2);
}
}
}
/*不要把画图操作介绍的太复杂,只是为了写作*/
/*在上面的段落中,根据局部地图上的点阵将地图反映到屏幕上*/
for(y = 0;y & lt5;Y++) {/*画落体*/
for(x = 0;x & lt5;x++) {
if(curbox[y][x]) {
if(y+cury & gt;2) {
矩形((x + curx) * LEN + STARTX,
(y + cury) * LEN + STARTY,
(x + curx) * LEN + STARTX + LEN,
(y+cury)* LEN+STARTY+LEN);
bar((x+curx)* LEN+STARTX+1,
(y + cury) * LEN + STARTY + 1,
(x + curx) * LEN + STARTX + LEN - 2,
(y+cury)* LEN+STARTY+LEN-2);
}
}
}
}
/*根据下落框在地图上的坐标,将下落框绘制到相应的区域*/
for(y = 0;y & lt5;Y++) {/*画下一个*/
for(x = 0;x & lt5;x++) {
if(nextbox[y][x]) {
矩形(x * LEN + 320,
y * LEN + 10,
x * LEN + 338,
y * LEN+28);
bar( x * LEN + 321,
y * LEN + 11,
x * LEN + 336,
y * LEN+26);
}
}
}
/*这将绘制下一个框的预览*/
setvisual page(c page);/*确保在页面中绘制*/
/*展示它*/
}
/******************************************************\
*初始化部分*
\******************************************************/
/*提供newGame()来初始化新游戏*/
Void initMap(void) {/*初始化映射*/
int x,y;/*我们需要一圈警卫...*/
for(y = 0;y & ltMAX _ Y;y++) {
for(x = 0;x & ltMAX _ X;x++) {
if(x & lt;2 | | x & gtMAX _ X-3 | | y & gt;MAX_Y - 3)
map[y][x]= 1;
else map[y][x]= 0;
}
}/*此形状在此处初始化*/
}/*当然没有封面...*/
Void newGame() {/*创建新游戏*/
int x,y;
init map();/*初始化映射*/
srand(GetTickCount());/*初始化随机生成器*/
rebidnext();/*创建下一个*/
setTimer(& amp;tDown,速度,1);/*开始时钟(快速和慢速)*/
setTimer(& amp;tFast,FAST_INTV,1);
new fall();/*操作跌落箱*/
/*这是第一个下落的方块。
它位于地图的顶部*/
}
/******************************************************\
*核心功能*
\******************************************************/
void rotate box(int box 1[5][5],int box2[5][5]) {
/*旋转box1并输出到box2*/
int x,y;
for(x = 0;x & lt5;X++) /*这个函数可能需要实际的*/
for(y = 4;y & gt= 0;Y-)/*写出来打动*/
box 2[y][x]= box 1[x][4-y];
}
Void rebuild next () {/*创建下一个形状并将其放入nextbox */
int i,x,y;
I = random(MAX _ C);/*选择几个方块中的一个*/
for(y = 0;y & lt5;Y++) /*并复制它*/
for(x = 0;x & lt5;x++)
next box[y][x]= box[I][y][x];/*复制*/
}
Int drop() {/*行踪,返回成功或失败*/
int newy/*盒子将放置的新位置*/
newy = cury+1;/*是当前的Y位置+1*/
if(test(curx,newy,curbox)) {
cury = newy/*测试箱在这个位置*/
返回1;/*有冲突吗?如果没有,*/
}/*直接设置cury */*/
返回0;
}
Void putBox() {/*在地图上填充边界框*/
int x,y;
for(y = 0;y & lt5;Y++) /*这个也简单,主要是对root */
for(x = 0;x & lt5;X++) /*根据curx,cury表示位置*/
if(curbox[y][x])
map[y+cury][x+curx]= curbox[y][x];
}
Void clear() {/*清除整行*/
/*这个函数其实效率很低,简单来说。
从头到尾都经过测试*/
/*具体算法是:
从第0行到最后一行,测试地图格子是否满,如果满。
从当前行开始,上面的地图将下降一行*/
int x,y;
int dx,dy;
int完整标志;
for(y = 0;y & ltMAX _ Y-2;Y++) {/*最后两行是保留的*/
完整标志= 1;/*假设已满*/
for(x = 2;x & ltMAX _ X-2;X++) {/*保留列~ */
如果(!map[y][x]) {
完整标志= 0;
打破;
}
}
If(fullflag) {/*下移一行*/
for(dy = y;dy & gt0;dy -)
for(dx = 2;dx & ltMAX _ X-2;dx++)
map[dy][dx]= map[dy-1][dx];
for(dx = 2;dx & ltMAX _ X-2;dx++)
map[0][dx]= 0;
/*并清除第一行*/
}
}
}
Int move(int dir) {/*返回成功或失败*/
int newx
if(dir)newx = curx+1;
/*像drop一样,准备好移动的坐标*/
else newx = curx-1;
If(test(newx,cury,curbox)) {/*测试是否有冲突*/
curx = newx/*开关curx*/
返回1;
}
返回0;
}
int test(int mx,int my,int box[5][5]) {
/*测试箱子是否能降落在mx,我在地图中的位置*/
/*这是最关键的函数,它决定了是否存在非空冲突*/
/*但是算法很简单*/
int x,y;
for(y = 0;y & lt5;y++)
for(x = 0;x & lt5;x++)
if(map[y+my][x+MX]& amp;& amp方框[y][x])
返回0;
返回1;
}
int rotate() {
int x,y;
int newbox[5][5];/*我们必须将当前框旋转到新框*/
/*再次测试这个新盒子的冲突*/
rotateBox(curbox,new box);/*转到新框*/
if(test(curx,cury,newbox)) {
/*并且新框可以放在地图上不冲突*/
for(y = 0;y & lt5;y++)
for(x = 0;x & lt5;x++)
curbox[y][x]= new box[y][x];/*复制到*/
返回1;
}
否则返回0;
}
Int newfall() {/*无法创建下降元素。返回0*/
int x,y;
curx = MAX _ X/2-2;/*重新分配小方框的位置*/
cury = 0;
for(y = 0;y & lt5;y++)
for(x = 0;x & lt5;x++)
curbox[y][x]= next box[y][x];/*复制下一个框*/
rebidnext();/*重建nextBox*/
返回测试(curx、cury、curbox);
}
/************************************************************\
*主要功能-整个游戏架构*
\************************************************************/
int main() {
char键;/*记录当前按键*/
int I;
int gd = VGA,gm = VGAMED/*初始化图形模式*/
Timer * ptDown/*时钟下降(快或慢)*/
定时器趋势器;/*以免渲染给程序造成太大负担*/
/*使用时钟控制渲染速度*/
/*将其设置为interval = 1,*/
/*那就是18 FPS,当然达不到标准*/
/* 60帧/秒...毕竟这是DOS...*/
setTimer(& amp;趋势者,1,1);
init graph(& amp;gd,& ampgm,“”;/*初始化图形*/
new game();/*新游戏...*/
While(1) {/*主游戏循环*/
If(kbhit()) {/*如果按下键盘*/
key = getch();/*将键值读取到key*/
}
else key = 0;
开关(键){/*判断读取键*/
案例索引_UP:
rotate();/*,旋转下拉框*/
打破;
案例关键点_向下:
ptDown = & amptFast/*使用tFast时钟*/
打破;
案例关键字_左:
移动(0);/*向左移动*/
打破;
案例关键字_右:
移动(1);/*向右移动*/
打破;
案例键_ESC:
closegraph();/*结束游戏*/
退出(0);
默认值:
ptDown = & amptDown/*使用原始速度*/
}
If(testTimer(ptDown)) {/*已经在上面设置了行踪。
使用的时钟在ptDown */中
如果(!Drop()) {/* falls,0*/
putBox();/*写在地图上*/
clear();/*清除整行*/
如果(!Newfall()) {/*创建新的掉落,如果失败,游戏结束*/
closegraph();
退出(0);
}
}
}
if(testTimer(& amp;Trender)) /*最后...提供;给予...*/
render();
}
}