初学者怎样看懂代码怎么制作俄罗斯方块(如何用代码制作俄罗斯方
如何用C语言编一个俄罗斯方块
游戏界面预览:
菜单预览:
自定义每个小方块颜色功能界面:
游戏主要有四部分组成:Square类,Block类,gameField类,游戏引擎
Square类:
这个类描述的对象是组成大方块中的每个小正方形实体。
类设计:
class Square
{
public Point location; //小方块的坐标
public Size size; //小方块大小
public Color foreColor; //小方块前景色
public Color backColor; //小方块背景色
public Square(Size initSize,Color initForeColor,Color initBackColor) //构造函数
{ ……}
public void Draw(System.IntPtr winHandle) //在指定设备上画方块
{ …… }
public void Erase(System.IntPtr winHandle)//擦除方块
{ …… }
}
Block类:
这个类描述的对象是某一个大方块的实体。每个大方块由四个小正方形组成,一共有7种组合方式。这个类需要实现一个大方块实体所有的属性和动作。包括:方块的形状,位置,方块左移,右移,下移,旋转等。
类设计:
class Block
{
public Square square1; //组成block的四个小方块
public Square square2;
public Square square3;
public Square square4; private const int squareSize = GameField.SquareSize; //小方块的边长
public enum BlockTypes
{
undefined = 0,
square = 1,
line = 2,
J = 3,
L = 4,
T = 5,
Z = 6,
S = 7
};//一共有7种形状
public BlockTypes blockType; //方块的形状
//七个小方块的颜色数组
private Color foreColor;
private Color backColor;
//方块的方向
public enum RotateDirections
{
North = 1,
East = 2,
South = 3,
West = 4
};
public RotateDirections myRotation = RotateDirections.North;
public Block(Point thisLocation,BlockTypes bType)
{ ……}
//含有自定义颜色的重载
public Block(Point thisLocation, BlockTypes bType,Color fc,Color bc)
{ ……} /*画方块*/
public void Draw(System.IntPtr winHandle)
{…… }
/*擦方块*/
public void Erase(System.IntPtr winHandle)
{…… } /*移动*/
public bool down()
{……}
public bool left()
{……}
public bool right()
{……}
/*旋转block*/
public void Rotate()
{……}
/*检测是否到顶*/
public int Top()
{……}
}
GameField类:
这个类描述的对象是游戏场景实体,包括场景的背景色,大小,方块是否还可以移动,以及场景中填满一行的检测等。
类设计:
class GameField
{
public const int width = 20; //场景的宽,以方块个数为单位
public const int height = 30;
public const int SquareSize = 15; //每个四分之一小方块的边长
public static Color BackColor; //场景的背景色
public static System.IntPtr winHandle; //场景的handle
public static Color[] BlockForeColor ={ Color.Blue, Color.Beige, Color.DarkKhaki, Color.DarkMagenta, Color.DarkOliveGreen, Color.DarkOrange, Color.DarkRed };
public static Color[] BlockBackColor ={ Color.LightCyan, Color.DarkSeaGreen, Color.Beige, Color.Beige, Color.Beige, Color.Beige, Color.Beige };
public static bool isChanged=false; //设置是否被更改的标志位
public static SoundPlayer sound = new SoundPlayer(); //播放声音 public static Square[,] arriveBlock = new Square[width, height]; //保存已经不能再下落了的方块
public static int[] arrBitBlock=new int[height]; //位数组:当某个位置有方块时,该行的该位为1
private const int bitEmpty = 0x0; //0000 0000 0000 0000 0000
private const int bitFull = 0xFFFFF; //1111 1111 1111 1111 1111 /*检测某个位置是否为空*/
public static bool isEmpty(int x, int y)
{……}
/*将方块停住*/
public static void stopSquare(Square sq, int x, int y)
{……}
/*检测行是否满
* 返回:成功消除的行数和 (方便统计分数)
*/
public static int CheckLines()
{ ……}
/*播放声音*/
public static void PlaySound(string soundstr)
{……}
/*重画*/
public static void Redraw()
{ …… }
//结束
}
游戏引擎:
游戏引擎正如其名,就像一个发动机一样让游戏不间断运行。本游戏中就是让方块以一定的速度下落。并响应键盘事件,实行左右移动,和向下加速功能。(代码见源码)
声音播放:
音效是游戏不可缺少的一部分。在.Net2.0中已经提供了一个类来播放声音。在using System.Media;命名空间。
本游戏中播放声音的代码如下:(在 GameField类中)
using System.Media;
public static SoundPlayer sound = new SoundPlayer();
/*播放声音*/
public static void PlaySound(string soundstr)
{
switch (soundstr)
{
case "FinishOneLine": //消除一行的声音
if (!File.Exists("FinishOneLine.wav")) return;
sound.SoundLocation = "FinishOneLine.wav";
break;
case "CanNotDo": //当无法操作时
if (!File.Exists("CanNotDo.wav")) return;
sound.SoundLocation = "CanNotDo.wav";
break;
}
sound.Play();
}
要播放的时候调用PlaySound()方法即可。
其实步骤很简单,先引用System.Media空间,然后创建一个SoundPlayer 对象,用SoundLocation 属性设置声音文件的地址,然后调用Play()方法即可播放。不过注意,这个类可以播放的声音格式只有Wav文件。
保存游戏设置:
在游戏中经常要保存用户自定义的设置。本游戏通过写进ini文件来保存。
主要代码如:
/*加载窗体时从配置文件Setting.ini中读取游戏设置*/
private void getSettings()
{
if (!File.Exists("Setting.ini"))
return;
FileStream fs = new FileStream("Setting.ini", FileMode.OpenOrCreate, FileAccess.ReadWrite);
StreamReader sr = new StreamReader(fs);
string line1=sr.ReadLine();
string line2=sr.ReadLine();
string line3=sr.ReadLine();
if (line1 != null line1.Split('=').Length 1)
{
GameField.BackColor = Color.FromArgb(int.Parse(line1.Split('=')[1]));
picBackGround.BackColor = GameField.BackColor;
}
if (line2 != null line2.Split('=').Length 1)
GameField.BlockForeColor = strToColor(line2.Split('=')[1]);
if (line3 != null line3.Split('=').Length 1)
GameField.BlockBackColor = strToColor(line3.Split('=')[1]);
sr.Close();
fs.Close();
}
/*如果游戏设置被更改,将新的设置保存到Setting.ini*/
private void saveSettings()
{
FileStream fs = new FileStream("Setting.ini", FileMode.Create, FileAccess.ReadWrite);
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine("GameFieldColor="+GameField.BackColor.ToArgb());
sw.WriteLine("BlockFroeColor=" + colorToStr(GameField.BlockForeColor));
sw.WriteLine("BlockBackColor=" + colorToStr(GameField.BlockBackColor));
sw.Flush();
sw.Close();
fs.Close();
}
要源码+QQ348199903
俄罗斯方块C语言代码
俄罗斯方块C源代码
#include?stdio.h
#include?windows.h
#include?conio.h
#include?time.h
#define??ZL??4 ? ? //坐标增量,?不使游戏窗口靠边
#define?WID??36 ?? //游戏窗口的宽度
#define?HEI??20 ?? //游戏窗口的高度
int?i,j,Ta,Tb,Tc; ? ?? //?Ta,Tb,Tc用于记住和转换方块变量的值
int?a[60][60]={0}; ?? //标记游戏屏幕各坐标点:0,1,2分别为空、方块、边框
int?b[4]; ? ? ?? //标记4个"口"方块:1有,0无,类似开关
int?x,y,?level,score,speed; ?? //方块中心位置的x,y坐标,游戏等级、得分和游戏速度
int?flag,next; ? //当前要操作的方块类型序号,下一个方块类型序号
void?gtxy(int?m,?int?n); ? //以下声明要用到的自编函数
void?gflag(?);? //获得下一方块序号
void?csh(?);? //初始化界面
void?start(?);? //开始部分
void?prfk?(?);? //打印方块
void?clfk(?);? //清除方块
void?mkfk(?);? //制作方块
void?keyD(?);? //按键操作
int??ifmov(?);? //判断方块能否移动或变体
void clHA(?);? //清除满行的方块
void?clNEXT(?);? //清除边框外的NEXT方块
int?main(?)
{?csh(?); ??
?? while(1)
? ?? {start(?);??//开始部分
? ? ?? while(1)
? ? ?? {?prfk(?);??
? ? ? ?? Sleep(speed);? //延时
? ? ? ? ? clfk(?);
? ? ? ? ? Tb=x;Tc=flag;??//临存当前x坐标和序号,以备撤销操作
? ? ? ? ? keyD(?); ?
? ? ? ? ? y++;?????//方块向下移动
? ? ? ?? if?(ifmov(?)==0)?{?y--;?prfk(?);?dlHA(?);?break;}?//不可动放下,删行,跨出循环
? ? ?? }
? ? ? for(i=y-2;iy+2;i++){?if?(i==ZL)?{?j=0;?}?}? //方块触到框顶
? ?? if?(j==0)?{?system("cls");gtxy(10,10);printf("游戏结束!");?getch();?break;?}?
? ?? clNEXT(?); ? //清除框外的NEXT方块
? ? }
? return?0;
}
void?gtxy(int?m,?int?n)??//控制光标移动
{COORD?pos;??//定义变量
pos.X?=?m;??//横坐标
pos.Y?=?n;???//纵坐标
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),?pos);
}
void?csh(?)????//初始化界面
{gtxy(ZL+WID/2-5,ZL-2);?printf("俄罗斯方块");??????//打印游戏名称
gtxy(ZL+WID+3,ZL+7);?printf("*******?NEXT:");??//打印菜单信息
gtxy(ZL+WID+3,ZL+13);?printf("**********");
gtxy(ZL+WID+3,ZL+15);?printf("Esc?:退出游戏");
gtxy(ZL+WID+3,ZL+17);?printf("↑键:变体");
gtxy(ZL+WID+3,ZL+19);?printf("空格:暂停游戏");
gtxy(ZL,ZL);??printf("╔");??gtxy(ZL+WID-2,ZL);??printf("╗");??//打印框角
gtxy(ZL,ZL+HEI);??printf("╚");??gtxy(ZL+WID-2,ZL+HEI);??printf("╝");
a[ZL][ZL+HEI]=2;??a[ZL+WID-2][ZL+HEI]=2;??//记住有图案
for(i=2;iWID-2;i+=2)?{gtxy(ZL+i,ZL);??printf("═");?}??//打印上横框
for(i=2;iWID-2;i+=2)?{gtxy(ZL+i,ZL+HEI);?printf("═");?a[ZL+i][ZL+HEI]=2;?}?//下框
for(i=1;iHEI;i++)?{?gtxy(ZL,ZL+i);??printf("║");?a[ZL][ZL+i]=2;?}??//左竖框记住有图案
for(i=1;iHEI;i++)?{gtxy(ZL+WID-2,ZL+i);?printf("║");?a[ZL+WID-2][ZL+i]=2;?}?//右框
CONSOLE_CURSOR_INFO?cursor_info={1,0};???//以下是隐藏光标的设置
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),cursor_info);
level=1;?score=0;?speed=400;
gflag(?);??flag=next;??//获得一个当前方块序号
}
void?gflag(?) ? //获得下一个方块的序号
{?srand((unsigned)time(NULL));?next?=?rand()%19+1; }
void?start(?)??//开始部分
{?gflag(?);?Ta=flag;?flag=next;??//保存当前方块序号,将下一方块序号临时操作
x=ZL+WID+6;?y=ZL+10;?prfk(?);??//给x,y赋值,在框外打印出下一方块
flag=Ta;?x=ZL+WID/2;?y=ZL-1;??//取回当前方块序号,并给x,y赋值
}
void?prfk?(?)??//打印俄罗斯方块
{?for(i=0;i4;i++)?{b[i]=1;?}??//数组b[4]每个元素的值都为1
mkfk?(?);??//制作俄罗斯方块
for(?i=?x-2;?i=x+4;?i+=2?)??//打印方块
{?for(j=y-2;j=?y+1;j++)?{?if(?a[i][j]==1??jZL?){?gtxy(i,j);?printf("□");?}?}?}
gtxy(ZL+WID+3,ZL+1); ? printf("level?:?%d",level); ?//以下打印菜单信息
gtxy(ZL+WID+3,ZL+3);? printf("score?:?%d",score);
gtxy(ZL+WID+3,ZL+5);? printf("speed?:?%d",speed);
}
void?clfk(?)??//清除俄罗斯方块
{?for(i=0;i4;i++)?{?b[i]=0;?}??//数组b[4]每个元素的值都为0
mkfk?(?);??//制作俄罗斯方块
for(?i=x-2;?i=x+4;?i+=2?)??//清除方块
{?for(j=y-2;j=y+1;j++){?if(?a[i][j]==0??jZL?){?gtxy(i,j);?printf("??");?}?}?}
}
void?mkfk(?)??//制作俄罗斯方块
{?a[x][?y]=b[0];??//方块中心位置状态:?1-有,0-无
switch(flag)???//共6大类,19种小类型
{?case?1:?{?a[x][y-1]=b[1];?a[x+2][y-1]=b[2];?a[x+2][y]=b[3];?break;?}??//田字方块
case?2:?{?a[x-2][y]=b[1];?a[x+2][y]=b[2];?a[x+4][y]=b[3];?break;?}??//直线方块:----
case?3:?{?a[x][y-1]=b[1];?a[x][y-2]=b[2];?a[x][y+1]=b[3];?break;?}??//直线方块:?|
case?4:?{?a[x-2][y]=b[1];?a[x+2][y]=b[2];?a[x][y+1]=b[3];?break;?}??//T字方块
case?5:?{?a[x][y-1]=b[1];?a[x][y+1]=b[2];?a[x-2][y]=b[3];?break;?}??//T字顺时针转90度
case?6:?{?a[x][y-1]=b[1];?a[x-2][y]=b[2];?a[x+2][y]=b[3];?break;?}??//T字顺转180度
case?7:?{?a[x][y-1]=b[1];?a[x][y+1]=b[2];?a[x+2][y]=b[3];?break;?}??//T字顺转270度
case?8:?{?a[x][y+1]=b[1];?a[x-2][y]=b[2];?a[x+2][y+1]=b[3];?break;?}?//Z字方块
case?9:?{?a[x][y-1]=b[1];?a[x-2][y]=b[2];?a[x-2][y+1]=b[3];?break;?}??//Z字顺转90度
case?10:?{?a[x][y-1]=b[1];?a[x-2][y-1]=b[2];?a[x+2][y]=b[3];?break;?}??//Z字顺转180度
case?11:?{?a[x][y+1]=b[1];?a[x+2][y-1]=b[2];?a[x+2][?y]=b[3];?break;?}?//Z字顺转270度
case?12:?{?a[x][y-1]=b[1];?a[x][y+1]=b[2];?a[x-2][y-1]=b[3];?break;?}??//7字方块
case?13:?{a[x-2][y]=b[1];?a[x+2][y-1]=b[2];?a[x+2][y]=b[3];?break;?}??//7字顺转90度
case?14:?{?a[x][y-1]=b[1];?a[x][y+1]=b[2];?a[x+2][y+1]=b[3];?break;?}??//7字顺转180度
case?15:?{?a[x-2][y]=b[1];?a[x-2][y+1]=b[2];?a[x+2][y]=b[3];?break;?}??//7字顺转270度
case?16:?{?a[x][y+1]=b[1];?a[x][y-1]=b[2];?a[x+2][y-1]=b[3];?break;?}??//倒7字方块
case?17:?{?a[x-2][y]=b[1];?a[x+2][y+1]=b[2];?a[x+2][y]=b[3];?break;?}??//倒7字顺转90度
case?18:?{?a[x][y-1]=b[1];?a[x][y+1]=b[2];?a[x-2][y+1]=b[3];?break;?}??//倒7字顺转180度
case?19:?{?a[x-2][y]=b[1];?a[x-2][y-1]=b[2];?a[x+2][y]=b[3];?break;?}??//倒7字顺转270度
}
}
void?keyD(?)??//按键操作
{?if?(kbhit(?))
{?int?key;
?? key=getch();
if?(key==224)
{?key=getch();
? ? ?? if?(key==75)?{?x-=2;?}??//按下左方向键,中心横坐标减2
if?(key==77)?{?x+=2;?}??//按下右方向键,中心横坐标加2
? ? ? if?(key==72)?????//按下向上方向键,方块变体
{?if?(flag=2??flag=3?)?{?flag++;?flag%=2;?flag+=2;?}
if?(?flag=4??flag=7?)?{?flag++;?flag%=4;?flag+=4;?}
if?(flag=8??flag=11?)?{?flag++;?flag%=4;?flag+=8;?}
if?(flag=12??flag=15?)?{?flag++;?flag%=4;?flag+=12;?}
if?(?flag=16??flag=19?)?{?flag++;?flag%=4;?flag+=16;?}?}
? ? ?? }
? ? if?(key==32)?????//按空格键,暂停
{?prfk(?);?while(1)?{?if?(getch(?)==32)?{?clfk(?);break;}?}?}? //再按空格键,继续游戏
? ? if?(ifmov(?)==0)?{?x=Tb;?flag=Tc;?}? //如果不可动,撤销上面操作
? ? else?{?prfk(?);?Sleep(speed);?clfk(?);?Tb=x;Tc=flag;} ? //如果可动,执行操作
}
}
int?ifmov(?)???//判断能否移动
{?if?(a[x][y]!=0)?{?return?0;?}??//方块中心处有图案返回0,不可移动
else{?if?(?(flag==1??(?a[x][?y-1]==0??a[x+2][y-1]==0??a[x+2][y]==0?)?)?||
? ? ?? (flag==2??(?a[x-2][y]==0??a[x+2][y]==0??a[x+4][y]==0?)?)?||
? ? ?? (flag==3??(?a[x][y-1]==0??a[x][y-2]==0??a[x][y+1]==0?)?)?||
? ? ?? (flag==4??(?a[x-2][y]==0??a[x+2][y]==0??a[x][y+1]==0?)?)?||
? ? ?? (flag==5??(?a[x][y-1]==0??a[x][y+1]==0??a[x-2][y]==0?)?)?||
? ? ?? (flag==6??(?a[x][?y-1]==0??a[x-2][y]==0??a[x+2][y]==0?)?)?||
? ? ?? (flag==7??(?a[x][y-1]==0??a[x][y+1]==0??a[x+2][y]==0?)?)?||
? ? ?? (flag==8??(?a[x][y+1]==0??a[x-2][y]==0??a[x+2][y+1]==0?)?)?||
? ? ?? (flag==9??(?a[x][y-1]==0??a[x-2][y]==0??a[x-2][y+1]==0?)?)?||
? ? ?? (flag==10??(?a[x][y-1]==0??a[x-2][y-1]==0??a[x+2][y]==0?)?)?||
? ? ?? (flag==11??(?a[x][y+1]==0??a[x+2][y-1]==0??a[x+2][y]==0?)?)?||
? ? ?? (flag==12??(?a[x][y-1]==0??a[x][y+1]==0??a[x-2][y-1]==0?)?)?||
? ? ? ( flag==13 ( a[x-2][y]==0 a[x+2][y-1]==0 a[x+2][y]==0 ) ) ||
????( flag==14 ( a[x][y-1]==0 a[x][y+1]==0 a[x+2][y+1]==0 ) ) ||
? ?? (flag==15 ( a[x-2][y]==0 a[x-2][y+1]==0 a[x+2][y]==0 ) ) ||
? ?? (flag==16 ( a[x][y+1]==0 a[x][y-1]==0 a[x+2][y-1]==0 ) ) ||
? ?? ( flag==17 ( a[x-2][y]==0 a[x+2][y+1]==0 a[x+2][y]==0 ) ) ||
????(flag==18 ( a[x][y-1]==0 a[x][y+1]==0 a[x-2][y+1]==0 ) ) ||
? ?? (flag==19 ( a[x-2][y]==0 a[x-2][y-1]==0
? ? ? ? ? ? ?a[x+2][y]==0?)?)?)?{?return?1;?}
}
return?0; ? //其它情况返回0
}
void?clNEXT(?) ? //清除框外的NEXT方块
{?flag?=?next;??x=ZL+WID+6;??y=ZL+10;??clfk(?);?}
void clHA(?) ? //清除满行的方块
{?int?k,?Hang=0; ?? //k是某行方块个数,?Hang是删除的方块行数
for(j=ZL+HEI-1;j=ZL+1;j--)??//当某行有WID/2-2个方块时,则为满行
{?k=0;?for(i=ZL+2;iZL+WID-2;i+=2)
{?if?(a[i][j]==1)???//竖坐标从下往上,横坐标由左至右依次判断是否满行
{?k++; ? //下面将操作删除行
? ?? if?(k==WID/2-2)? { ? for(k=ZL+2;kZL+WID-2;k+=2)
? ? ? ?? {?a[k][j]=0;?gtxy(k,j);?printf("??");?Sleep(1);?}
? ? ? ? for(k=j-1;kZL;k--)
? ? ? ? {?for(i=ZL+2;iZL+WID-2;i+=2)??//已删行数上面有方块,先清除再全部下移一行
? ? ? ? ? {?if(a[i][k]==1)?{?a[i][k]=0;?gtxy(i,k);?printf("??");a[i][k+1]=1;
? ? ? ? ? ? gtxy(i,k+1);?printf("□");?}?}
? ? ? ? ? }
? ? ? ? j++;?????//方块下移后,重新判断删除行是否满行
? ? ? ? Hang++;??//记录删除方块的行数
? ? ?? }
? ? }
?? }
}
score+=100*Hang; ? //每删除一行,得100分
if?(?Hang0??(score%500==0?||?score/500?level-1?)?) ?//得分满500速度加快升一级
? {?speed-=20;?level++;?if(speed200)speed+=20; }
}
一个简单的c语言写的俄罗斯方块程序?
1、考虑怎么存储俄罗斯方块
俄罗斯方块的形状一共有19种类型,如果拿数组来表示的话,可能会比较会浪费空间(网上有很多实现代码)
考虑到每种方块形状的范围是4 *4的小方块,用 字模点阵的方式来存储,即设置一个4行4列的数组,元素置1即代表这个位置有小?
方块,元素置0即代表这个位置无小方块,这个整个的4*4的数组组成俄罗斯方块的形状。?
1000?
1000?
1100?
0000?
上述4*4来表示L形状的方块。?
4*4 =16 bit 正好为short类型,所以每一个方块可以用一个short类型的数据来表示。
我们把俄罗斯方块点阵的数位存在rockArray中,我们可以事先把这19种方块的字模点阵自己转化成十六进制,然后在rockArray数组的初始化时赋值进去。?
但是这种方式扩展性不好,每当有一种新方块时需要改动,?
所以可以写一个配置文件来表示19种方块。(RockShape.ini)
@###@###@@######1234
从配置文件中读取方块的类型的代码在(Init.h的ReadRock函数中)在下面3中解释下代码如何实现
2如何画出方块
可以使用EasyX库来画出简单的图形,?
EasyX库是在VC下实现TC的简单绘图功能的一个库,这个库很容易学会(直接 百度EasyX库,里面有详细的教程)
那么如何画出方块,方块已经存储到一个short类型中了?
从short中读取出,可以用一个掩码mask = 1来与short的每个bit位相与,结果为1,则画出一个小方块;?
函数声明:
void DisplayRock(int rockIdx, ?RockLocation_t* ?LocatePtr, bool displayed)1
参数1:表示在数组中的下标,取出short类型的方块表示数据?
参数2:表示当前坐标,即画出方块的左上角的坐标x,y?
参数3:true表示画出该方块,false 表示擦除该方块。
//方块在图形窗口中的位置(即定位4*4大块的左上角坐标) ?typedef struct LOCATE
{ ? ?int left; ? ?int top;
} RockLocation_t;123456
3如何实现同一种类型方块的翻转,
在按‘↑’时应该翻转同一种类型的方块,?
比如下面的横杆和竖杆
@###@###@###@###@@@@############****1234567891011
可以假想成静态循环链表来实现这种方式?
使同一种类型的方块循环起来,
用一个struct结构来表示一种方块
typedef struct ROCK
{ ? ?//用来表示方块的形状(每一个字节是8位,用每4位表示方块中的一行)
unsigned short rockShapeBits; ? ?int ? ? ? ? ?nextRockIndex; ?//下一个方块,在数组中的下标 ?} RockType;123456
定义一个RockType类型的数组来存储19种方块?
RockType RockArray[19] = { (0, 0) };
当我们按“↑”时,把传入画方块函数DrawRock中的rockIndex变为当前方块结构体中的nextRockIndex即可。
简单解释下ReadRock函数的实现:当读取到空行的时候表示 一种方块已经读取完毕,当读取到****?行时 表示同一种类型的方块读取完毕,具体看代码实现,代码中具体的注释
4、主要游戏实现的逻辑
贴一个预览图吧?
注:上述预览图的游戏控制区和游戏显示区在Draw.h的DrawGameWindow()函数实现的
(1)在初始位置画出方块,在预览区画出下一次的方块?
(2)方块有两种行为:响应键盘命令UserHitKeyBoard(),自由下落?
如果敲击键盘了(w ,a ,s ,d, )空格表示暂停,如果在规定时间内没有敲击键盘的话,方块自由下落一个单位
if (kbhit()) //如果敲击键盘了 就处理按键
{
userHit = getch();
UserHitKeyBoard(userHit, curRockIndex, curRockLocation);
} ? ? ? ?//没有 就自动下移一个单位 :不能用else,因为可能按键不是上下左右
DWORD newtime = GetTickCount(); ? ? ? ?if (newtime - oldtime = (unsigned int)(300) moveAbled == TRUE)
{
oldtime = newtime;
DisplayRock(curRockIndex, curRockLocation, false);
curRockLocation.top += ROCK_SQUARE_WIDTH; //下落一格
}1234567891011121314
(3)当方块落地(即不能下移了)时,判断是否满行,如果满行则消除,然后再判断游戏是否结束,游戏结束的话,直接退出游戏
判断满行:FullLine()函数,从最底下的一行开始判断,直到遇到一行空行,
while (count != xROCK_SQUARE_NUM ) //遇到空行 14
{
linefull = true; ? ? ? ?count = 0; ? ? ? ?for (int i = 1; i = xROCK_SQUARE_NUM; ++i)
{ ? ? ? ? ? ?if (game_board[idx][i] == 0)
{
linefull = false; ? ? ? ? ? ? ? ?count++;
}
} ? ? ? ?if (linefull) //满行,消除当前行,更新分数
{
DelCurLine(idx);//消除满行
game_socres += 3;
UpdateSocres(game_socres);
idx++;//因为下面要减1
}
idx--;
}123456789101112131415161718192021
(4)消除满行?
将要删除的满行擦除:即将方块化成与背景色相同的,该代码为黑色?
然后将上面的一行向下移,移一行删除一行,直到遇到空行?
具体看代码的具体实现 game.h?
void DelCurLine(int rowIdx)
(4)判断方块是否能移动?
在game.h中实现
bool MoveAble(int rockIndex, RockLocation_t* currentLocatePtr, int f_direction)1
**比较当前位置的坐标(左上角)开始,能否放下rockIndex的方块。?
注:f_direction为”↑”的话,则传入的rockIndex为下一个方块**
如果不能移动的话,给游戏game_board设置标记表示该位置被占有
//全局变量-游戏板的状态描述(即表示当前界面哪些位置有方块) ?//0表示没有,1表示有(多加了两行和两列,形成一个围墙,便于判断方块是否能够移动) ?int game_board[yROCK_SQUARE_NUM + 2][xROCK_SQUARE_NUM + 2] = { 0 };123
实现过程遇到的一些问题
(1)在快速下落的时候,可能方块会掉出围墙的范围内,?
快速下落是使方块每次下落2个单位距离。?
在判断不能下落时,使当前坐标的top即y减去一个单位的距离
(2)遇到多行满行时消除不了,?
在判断满行时,循环找出满行,找出一个满行,就消除一行,然后继续判断是否满行,直到遇到空行
怎样制作俄罗斯方块
以下代码粘贴在主场经第一祯,测试影片就看到了:
N = 20;//行数
WIDTH = 20;//方块边长
level = 0;//开始等级(下落速度)
ret = new Array();//当前出现的方块
nextret = new Array();//下一个出现的方块
bg = new Array();//背景数组
createEmptyMovieClip("panel", 1048575);//所有方块都在此mc里
for (i = 0; i 5; i++) {
//初始化方块数组,2*5格式,前四行代表每个方块的4个小块的位置坐标,最后一行第一列是方块形状,第二列是方块旋转方向
ret.push(new Array(2));
nextret.push(new Array(2));
}
for (i = 0; i 20; i++) {//初始化背景数组,10*20格式
bg.push(new Array(10));
}
X = Y = panel._x = panel._y = 0;//换为X、Y表示
function reach(x:Number, y:Number, ret:Object) {
//x、y为方块位置,ret为方块形状,若方块ret下落一格碰到边界或者方块返回1
var i:Number, j:Number, k:Number;
for (i = 0; i N; i++) {
for (j = 0; j 10; j++) {
if (bg[i][j] == 219) {
for (k = 0; k 4; k++) {
if (x + ret[k][0] == j y + ret[k][1] + 1 == i) {
return 1;
}
}
}
}
}
return 0;
}
function lrnotout(lorr:Number, a:Object) {
//lorr==-1代表a往左边一格可行性的判断,lorr==1代表右边一格可行性的判断,lorr==0代表a的位置合理性的判断,出现不合理则返回0
var i:Number;
if (lorr == -1) {
for (i = 0; i 4; i++) {
if (x + a[i][0] - 1 0 || reach(x - 1, y - 1, a)) {
return 0;
}
}
}
if (lorr == 1) {
for (i = 0; i 4; i++) {
if (x + a[i][0] + 1 9 || reach(x - 1, y + 1, a)) {
return 0;
}
}
}
if (lorr == 0) {
for (i = 0; i 4; i++) {
if (x + a[i][0] 0 || x + a[i][0] 9) {
return 0;
}
}
}
return 1;
}
function rv(a:Object, ret:Object) {
//方块赋值,将a方块赋值到ret方块
var i:Number;
for (i = 0; i 5; i++) {
ret[i][0] = a[i][0], ret[i][1] = a[i][1];
}
}
function rotate(ret:Object) {
//根据方块ret最后一行(分别是形状指示变量和旋转方向变量)为ret的前四行赋以具体形状值
switch (ret[4][0]) {
case 0 ://方形
a = [[1, 0], [2, 0], [1, 1], [2, 1], [0, 0]];
rv(a, ret);
return;
case 1 ://长形
switch (ret[4][1]) {
case 1 :
a = [[0, 0], [1, 0], [2, 0], [3, 0], [1, 0]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 0 :
a = [[1, 0], [1, 1], [1, 2], [1, 3], [1, 1]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
}
case 2 ://Z形
switch (ret[4][1]) {
case 1 :
a = [[0, 1], [1, 1], [1, 2], [2, 2], [2, 0]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 0 :
a = [[2, 0], [1, 1], [2, 1], [1, 2], [2, 1]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
}
case 3 ://反Z形
switch (ret[4][1]) {
case 1 :
a = [[1, 1], [2, 1], [0, 2], [1, 2], [3, 0]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 0 :
a = [[1, 0], [1, 1], [2, 1], [2, 2], [3, 1]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
}
case 4 ://T形
switch (ret[4][1]) {
case 3 :
a = [[1, 0], [0, 1], [1, 1], [2, 1], [4, 0]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 0 :
a = [[1, 0], [0, 1], [1, 1], [1, 2], [4, 1]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 1 :
a = [[0, 1], [1, 1], [2, 1], [1, 2], [4, 2]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 2 :
a = [[1, 0], [1, 1], [2, 1], [1, 2], [4, 3]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
}
case 5 ://倒L形
switch (ret[4][1]) {
case 3 :
a = [[1, 0], [2, 0], [1, 1], [1, 2], [5, 0]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 0 :
a = [[0, 1], [0, 2], [1, 2], [2, 2], [5, 1]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 1 :
a = [[2, 0], [2, 1], [1, 2], [2, 2], [5, 2]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 2 :
a = [[0, 1], [1, 1], [2, 1], [2, 2], [5, 3]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
}
case 6 ://L形
switch (ret[4][1]) {
case 3 :
a = [[1, 0], [2, 0], [2, 1], [2, 2], [5, 0]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 0 :
a = [[0, 1], [1, 1], [2, 1], [0, 2], [5, 1]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 1 :
a = [[1, 0], [1, 1], [1, 2], [2, 2], [5, 2]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
case 2 :
a = [[2, 1], [0, 2], [1, 2], [2, 2], [5, 3]];
if (lrnotout(0, a) !reach(x, y - 1, a)) {
rv(a, ret);
}
return;
}
}
}
function generate(ret:Object) {//随机产生方块函数(可进一步修正)
ret[4][0] = Math.floor(Math.random() * 7);
ret[4][1] = Math.floor(Math.random() * 4);
rotate(ret);//完成方块ret的具体形状的赋值
}
function init() {//初始化背景、方块、运动函数
var i:Number, j:Number;
for (i = 0; i N; i++) {//初始化背景,边界为219,其余为' '
for (j = 0; j 10; j++) {
if (i == N - 1) {
bg[i][j] = 219;
} else {
bg[i][j] = ' ';
}
}
}
for (i = 0; i 5; i++) {//为当前方块赋初值0
ret[i][0] = ret[i][1] = 0;
}
generate(ret);//产生当前方块
generate(nextret);//产生下一个方块
y = 0, x = 3, score = lines = 0, level=0;//当前位置坐标和计分系统初始化
_tetris.removeTextField();//如果从结束过的游戏恢复,删除结束标志
display();//显示画面
frameflag = 0;//标示下落时间间隔
onEnterFrame = function () {
frameflag++;
if (10 - frameflag level) {//根据等级level确定下落时间间隔
frameflag = 0;
go();//下落及判断
}
};
}
function drawblock(a, b, c, d) {//绘制方块的小块
with (panel) {
beginFill(0x000FFF, 100);
lineStyle(1, 0xFF00FF);
moveTo(panel._x + a, panel._y + b);
lineTo(panel._x + c, panel._y + b);
lineTo(panel._x + c, panel._y + d);
lineTo(panel._x + a, panel._y + d);
lineTo(panel._x + a, panel._y + b);
endFill();
}
}
function erase() {//删除一行方块
var n:Number = 0, i:Number, j:Number, k:Number, l:Number;
for (i = 0; i N - 1; i++) {
for (j = 0; j 10; j++) {
if (bg[i][j] == ' ') {//如果该行有空,则开始判断下一行
i++, j = -1;
if (i == N - 1) {//行N-1为底线,不判断
break;
}
} else if (j == 9) {//判断到该行最后一列都没有空
for (k = i; k = 1; k--) {//上方方块下落
for (l = 0; l 10; l++) {
bg[k][l] = bg[k - 1][l];
}
}
for (l = 0; l 10; l++) {//删除该行
bg[0][l] = ' ';
}
n++;//此次删除行数变量增一
if ((lines + n) % 30 == 0) {//删除行数总数到30的倍数则等级上升
level = (level + 1) % 10;
}
}
}
}
lines += n, score += (n * n + n) * 50;//总行数增n,计算得分
}
function display() {
//显示函数,采用全部清除再重绘制的方法(因为这个程序本来是在Turbo C 2.0的文本环境下完成的)
var i:Number, j:Number;
panel.clear();
with (panel) {//画边界
lineStyle(1, 0x0000FF);
moveTo(panel._x, panel._y);
lineTo(panel._x + WIDTH * 10, panel._y);
lineTo(panel._x + WIDTH * 10, panel._y + WIDTH * (N - 1));
lineTo(panel._x, panel._y + WIDTH * (N - 1));
lineTo(panel._x, panel._y);
}
for (i = 0; i 4; i++) {//当前方块占据的地方赋值为边界类型219
bg[y + ret[i][1]][x + ret[i][0]] = 219;
}
for (i = 0; i N - 1; i++) {//绘制背景方块
for (j = 0; j 10; j++) {
if (bg[i][j] == 219) {
drawblock(j * WIDTH + X, i * WIDTH + Y, j * WIDTH + WIDTH + X, i * WIDTH + WIDTH + Y);
}
}
}
for (i = 0; i 4; i++) {//绘制当前方块
drawblock(nextret[i][0] * WIDTH + 14 * WIDTH + X, nextret[i][1] * WIDTH + 12 * WIDTH + Y, nextret[i][0] * WIDTH + WIDTH + 14 * WIDTH + X, nextret[i][1] * WIDTH + WIDTH + 12 * WIDTH + Y);
}
for (i = 0; i 4; i++) {//当前方块绘制完毕,重新将当前位置改为' '
bg[y + ret[i][1]][x + ret[i][0]] = ' ';
}
createTextField("_lvltxt", 1, 270, 100, 100, 20);//绘制计分系统
createTextField("_scrtxt", 2, 270, 130, 100, 20);
createTextField("_lnstxt", 3, 270, 160, 100, 20);
_lvltxt.text = "Level: " + level;
_scrtxt.text = "Score: " + score;
_lnstxt.text = "Lines: " + lines;
}
function go() {//下落函数
var sss:Number = reach(x, y, ret);//当前方块下落一格是否碰到边界或方块
var ii:Number;
if (!sss) {
y++;//如果当前方块下落一格没有碰到边界或方块则下落一格
}
display();//重新绘制
if (sss) {//碰到边界或方块
score += 10;//得10分
display();//重新绘制
for (ii = 0; ii 4; ii++) {//修改背景数组,将当前方块的位置改为边界类型
bg[y + ret[ii][1]][x + ret[ii][0]] = 219;
}
erase();//删除行判断及执行
rv(nextret, ret);//将下一个方块赋值为当前方块
y = 0, x = 3;//重置方块位置
generate(nextret);//生成下一个方块
display();//重新绘制
if (reach(x, y, ret)) {//如果下一格碰到方块则游戏结束
createTextField("_tetris", 100000, WIDTH * 3.3, WIDTH * N / 3, 70, 20);
_tetris._x += 200;
_tetris._y += 50;
_tetris._xscale = 300;
_tetris._yscale = 300;
_tetris.background = true;
_tetris.text = "Game Over!";
onEnterFrame = function () {//停止下落
};
}
}
}
function key() {
if (Key.isDown(Key.UP)) {
rotate(ret);
display();
}
if (Key.isDown(Key.LEFT)) {
if (lrnotout(-1, ret)) {//左移可行性判断
x--;
display();
}
}
if (Key.isDown(Key.RIGHT)) {
if (lrnotout(1, ret)) {//右移可行性判断
x++;
display();
}
}
if (Key.isDown(Key.DOWN)) {//键盘控制下落
go();
}
if (Key.isDown(Key.SPACE)) {//一键下落到底
while (!reach(x, y, ret)) {
y++;
}
go();
}
if (Key.isDown(82)) { //重新开始游戏
init();
}
}
init();//初始化
setInterval(key, 80);//每个80毫秒执行一次键盘事件函数
createTextField("hinttxt",33324,200,20,300,50);
hinttxt.text="键盘键:上,下,左,右,R(reset),空格";