c + easyx 实现怀旧掌机界面风格俄罗斯方块

文章目录

    • c + easyx 实现怀旧掌机界面风格俄罗斯方块
  • 前言
  • 一、俄罗斯方块的构型
  • 二、数据结构
  • 三、游戏的实现逻辑
    • 1.界面绘制
    • 2.方块的操作
    • 3.行的消除
  • 后记

前言

好久没写东西了,用一个星期的下班休息时间写了一个俄罗斯方块小游戏,算是圆了儿时的一个梦。界面写成了怀旧的掌机风格,之所以写成这样是因为当初在查找资料的时候谷歌出来的第一个条目是github上的一个俄罗斯方块项目(项目地址),一下子就喜欢上这个风格,于是就打算模仿这个写了基本一样效果的。不同于github上的这个项目,我是用C加上easyx库写的,写的时候也并没有参考他的代码(因为没学过,看不懂(︶︿︶)),因为太忙了,写完之后测试暂时没发现什么bug,也就没继续优化代码,可能不少地方写的很啰嗦,没办法,业余爱好,不是专业程序员,每天劳心劳力的上班,精力实在有限,要不是真的喜欢可能早就放弃了。这次很大一部分精力用在了处理图形效果上,真正涉及到俄罗斯方块本身运行逻辑的代码并不算多。而且有点吃瘪的是当初为了省事直接把github上的截了图,以这个截图当做游戏的背景,再在上面进行绘制,本以为这样能省很多事,偷点懒,结果一点懒没偷着,为了精确的绘制,得找准坐标,而截图是人家的,不是自己设计的,坐标什么的自己并不知道,就得测量各个分区的坐标,这样就限制了程序本身的可移植性和适应性,以后再不这么做了,费力不讨好。
最终效果如下:


一、俄罗斯方块的构型

俄罗斯方块一共有七种构型,全部都由四个方块组成:


各种四格方块由左至右,上至下的英文字母代号:I、J、L、O、S、T、Z。为了表示七种构型,创建了枚举变量:

enum TetrisM     //俄罗斯方块的七种类型
{I,J,L,O,S,T,Z,
};

二、数据结构

一个标准的俄罗斯方块操作界面,其大小为:行宽为10,列高为20。我们可以将游戏的操作界面当做一个20 * 10的二维数组,

主要数据结构如下(示例):

/*将整个操作区设为20*10的二维数组,方块的移动可看成在数组中的移动,这样移动方块只
要将相应的数组元素改变颜色即可,方块的堆叠、消除等操作也是对数组的操作*/
typedef struct Block
{                                           int x, y;     //用来记录每一格在屏幕上的坐标,后面绘制的时候用                      COLORREF color;//该格当前颜色,背景色或方块颜色BOOL isoccupy;//方块碰撞到边界或上一轮已停止的方块后状态修改为TRUE,为TRUE的方块在显示的时候用c_used 颜色绘制。
}BLOCK;
//
typedef struct Tetris                       //每个方块的坐标:x,y为方块在
{                                           //二维数组block[20][10]中的索引int x;int y;
}TETRIS;COLORREF c_notused = RGB(138, 152, 116);   //游戏背景没被方块占用时的颜色
COLORREF c_used = BLACK;                   //游戏背景被方块占用时的颜色
BLOCK block[20][10];                        //整个游戏区是10X20的方块大小
TETRIS Tetrix_now[4];                       //当前正在操作的方块

三、游戏的实现逻辑

1.界面绘制

游戏窗口如下:


(一)黄色背景是掌机的外壳,当初为了省事直接用github上的截图当背景

loadimage(&img, _T("rs\\bk.jpg"), 0, 0, false);//加载游戏背景图片putimage(0, 0, W_WIDTH, W_LENGTH, &img, 0, 0, SRCCOPY);

(二)资源加载:
需要的图形资源都在一个文件中,icon.gif。因为小恐龙动画有左右不同方向的效果,而图片只有一个方向,所以需要将图片翻转,这里用的方法是直接操作图形缓冲区,然后逆拷贝,形成方向不同的两组图片。

//加载、初始化各种资源
void init_rs()
{DWORD* dst;DWORD* src;int src_width;int src_height;loadimage(&img_icon, _T("rs\\icon.gif"), 0, 0, false);//加载小恐龙动画图片资源SetWorkingImage(&img_icon);for (int i = 0; i < 4; i++){getimage(&dinosaur[i], i * 100, 98, 100, 88);//加载分割前四个画面getimage(&dinosaur[i + 4], i * 100, 98, 100, 88);//初始化后四个画面,如果不初始化后面翻转操作会报错,因为指针为空}for (int i = 0; i < 4; i++)//小恐龙画面数组后四个画面是前四个的翻转{src = GetImageBuffer(&dinosaur[i]);dst = GetImageBuffer(&dinosaur[i + 4]);src_width = dinosaur[i].getwidth();src_height = dinosaur[i].getheight();for (int iy = 0; iy < src_height; iy++){int dx = src_width - 1;for (int ix = 0; ix < src_width; ix++){dst[dx--] = src[ix];}dst += src_width;src += src_width;}}
}

(三)
1. 中间的液晶屏幕是游戏显示的主要区域,分为左边的游戏区和右边的信息区。 游戏区的背景绘制完成后要保存,后面每次移动方块前都要刷新该区域。
2. 在二维数组区域内绘制20*10个方块,无填充正方形内绘制有填充正方形达到液晶效果。正方形的绘制过程也是二维数组每个元素坐标的计算过程,一并进行了初始化。这样在后面继续绘制背景和方块的时候直接从数组元素中取得坐标即可。

 setlinecolor(c_notused);setfillcolor(c_notused);for (int x = 0; x < 20; x++)//绘制液晶区背景{for (int y = 0; y < 10; y++){rectangle(ix + y * B_WIDTH + y * (Thickness + Interval), iy, ix + y * B_WIDTH + y * (Thickness + Interval) + B_WIDTH, iy + B_WIDTH);solidrectangle(ix + y * B_WIDTH + y * (Thickness + Interval) + 3, iy + 3, ix + y * B_WIDTH + y * (Thickness + Interval) + B_WIDTH - 4, iy + B_WIDTH - 4);block[x][y].x = ix + y * B_WIDTH + y * (Thickness + Interval);block[x][y].y = iy;block[x][y].color = c_notused;block[x][y].isoccupy = FALSE;}iy += B_WIDTH + Thickness + Thickness;}getimage(&imgbk, 0, 0, W_WIDTH, W_LENGTH);//将界面备份以备后面刷新

信息区的显示比较复杂,而且要不停的更新,单独放在一个函数中,根据不同条件进行显示。其中时间的显示因为要每秒钟都闪烁一次时间分隔号“:”,我没有想到别的办法,只能把时间显示放在了单独的线程里,线程调用精确计时函数,每隔一秒钟循环显示一次时间和分隔号。

//线程,用于显示时间
DWORD WINAPI show_Time(LPVOID lpParam)
{SYSTEMTIME t1;int x, i;while (1){HpSleep(1000);//精确计时,每隔一秒显示一次时间,GetLocalTime(&t1);x = t1.wHour;//小时if (x >= 10){i = x / 10;transparentimage(NULL, R_bx + 60, R_by - 31, &img_digit[i], 0xA1B187);x %= 10;transparentimage(NULL, R_bx + 76, R_by - 31, &img_digit[x], 0xA1B187);}else{transparentimage(NULL, R_bx + 60, R_by - 31, &img_digit[0], 0xA1B187);x %= 10;transparentimage(NULL, R_bx + 76, R_by - 31, &img_digit[x], 0xA1B187);}if ((timeinterval++ % 2) == 1)//小时与分钟相隔的冒号,明暗间隔显示transparentimage(NULL, R_bx + 92, R_by - 31, &img_digit[11], 0xA1B187);elsetransparentimage(NULL, R_bx + 92, R_by - 31, &img_digit[12], 0xA1B187);x = t1.wMinute;//分钟if (x >= 10){i = x / 10;transparentimage(NULL, R_bx + 108, R_by - 31, &img_digit[i], 0xA1B187);x %= 10;transparentimage(NULL, R_bx + 124, R_by - 31, &img_digit[x], 0xA1B187);}else{transparentimage(NULL, R_bx + 108, R_by - 31, &img_digit[0], 0xA1B187);x %= 10;transparentimage(NULL, R_bx + 124, R_by - 31, &img_digit[x], 0xA1B187);}}
}

2.方块的操作

主循环:

while (!isend)                       //方块没有触顶上死亡就一直循环{Tetrix_next = rand() % 7;     //随机生成下一个方块类型next_block(Tetrix_next);       //初始化下一个方块show_info(!isend);                //在信息区显示消息while (!istouch)              //方块没有触底或者碰撞其他已存在方块一直循环掉落{if (ln> 20)                    //消除行数大于10行{level = 2;sleeptime = 500;}else if (ln> 50){level = 3;sleeptime = 400;}else if (ln> 80){level = 4;sleeptime = 300;}else if (ln> 100){level = 5;sleeptime = 200;}key = normal;if (_kbhit())                //检测是否有键盘消息KeyMsg();                //处理按键消息while (MouseHit())          //检测是否有鼠标消息,不用while循环的话会错过鼠标消息,因为鼠标消息太多了,容易在鼠标消息缓冲区溢出去MouseMsg();              //处理鼠标消息moveblock();                //根据按键和鼠标点按移动方块showblock();             //显示移动后的方块rowcomplete();                //判断是否需要消除行,如满足条件消除相应的行touchtop();                   //判断方块是否触顶,是游戏死亡,退出循环show_info(!isend);           //在信息区显示消息Sleep(sleeptime);         }

规则:按照从上到下,从左到右的顺序将4个方块进行编号:0、1、2、3,然后以此编号规则对7种类型方块进行初始化和后续的翻转等操作。在初始化的时候只要设定编号0方块的坐标,其他方块的坐标可以由彼此间位置关系得到,在进行翻转操作时也是同样的道理。例如当前正在操作的方块初始函数中:

void Init_Block()
{int x, y;t_type = Tetrix_next;//当前方块类型t_dt = 0;//初始为水平方向switch (t_type){case I:for (int x = 0; x < 4; x++){Tetrix_now[x].x = 0;Tetrix_now[x].y = x + 3;}break;case J:Tetrix_now[0].x = -1;Tetrix_now[0].y = 4;for (int x = 1; x < 4; x++){Tetrix_now[x].x = 0;Tetrix_now[x].y = x + 3;}break;

方块的移动:需要注意的是每次移动方块的时候需要清除移动前的方块,主要是清除颜色。
//清除当前方块,以备下一步移动方块

void cleartetris()
{int x, y;for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x >= 0)block[x][y].color = c_notused;}
}

每次移动方块要提前判断是否越界和是否与其他落地的方块碰撞,否才移动,越界则不动,碰撞则停止,并修改占用状态,这里举向左移动为例:

//向左移动方块
void goleft()
{int x, y;int can_move = 1;cleartetris();for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if ((y >= 1) && (block[x][y - 1].isoccupy == TRUE))//左侧有已占用方块不能继续向左移动can_move = 0;if (y == 0)                //已经在边界,不能继续向左移动can_move = 0;}if (can_move){for (int i = 0; i < 4; i++){Tetrix_now[i].y -= 1;//修改坐标,每个方块列减1x = Tetrix_now[i].x;y = Tetrix_now[i].y;block[x][y].color = c_used;//修改颜色}}else                             //不可移动,因为在一开始已经清除了方块,所以这里要恢复{for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x >= 0)block[x][y].color = c_used;}}
}

在加速下降过程中判断是否碰撞的函数,因为涉及到space键的加速下降,所以要监测不同的可能状态并返回优先级最高的检测,检测状态从0到2权值逐级降低。

//加速下降过程中判断是否碰撞
int touchdown()
{int x, y;int r[4];int t;for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x < 18){if (block[x + 1][y].isoccupy == TRUE)r[i] = 0;else if (block[x + 2][y].isoccupy == TRUE)r[i] = 1;elser[i] = 2;}else if (x == 18)r[i] = 1;elser[i] = 0;}t = r[0];for (int i = 1; i < 4; i++)if (r[i] < t)t = r[i];return t;
}

方块的翻转:根据当前方块状态来计算方块翻转后的坐标,设置一个变量

int t_dt;            //当前方块方向,最多有三种方向

不同的方块构型,当前方向不一样翻转后也不一样,但因为彼此之间的位置关系很容易计算翻转后的坐标。struct Tetris里的x,y是当前四个方块中每一个方块在二维数组中的索引,翻转的计算只要找好一个翻转的轴心,修改轴心的索引,其他可由相对位置关系算得。例如:

case J:if (t_dt == horz)//当前为水平方向,修改为垂直方向{t_dt = vert;int x, y;x = Tetrix_now[2].x;                   //保持第3个方块坐标保持y = Tetrix_now[2].y;                  //保持第3个方块坐标保持Tetrix_now[2].x = Tetrix_now[0].x;        //旋转后第3个方块来到第1个方块的位置Tetrix_now[2].y = Tetrix_now[0].y;     //旋转后第3个方块来到第1个方块的位置Tetrix_now[3].x = Tetrix_now[1].x;     //旋转后第4个方块来到第2个方块的位置Tetrix_now[3].y = Tetrix_now[1].y;     //旋转后第4个方块来到第2个方块的位置Tetrix_now[1].x = x - 2;               //旋转后第2个方块,用与原第3个方块的相对位置计算Tetrix_now[1].y = y - 1;              //旋转后第2个方块,用与原第3个方块的相对位置计算Tetrix_now[0].x = x - 2;              //旋转后第1个方块,用与原第3个方块的相对位置计算Tetrix_now[0].y = y;                  //旋转后第1个方块,用与原第3个方块的相对位置计算}

3.行的消除

玩俄罗斯方块最重要的是要能消除填满的行并计算得分,由于最多从1可以到4行进行消除,就需要记录被消除行的起始和终止。判断每一行是否填满非常简单,只有测试每行10个的方块是否被占用就可以,10个都占用了该行消除掉。记录消除行的起止还可以用来实现消除效果和消除后上面的行掉落到下面,具体实现看代码:

//消除行,测试每一行是否全部被方块占满,并记录占满的起始行、终止行行号,并消除相应的行
void rowcomplete()
{int i = 4;                    //动画效果循环计数int rb = -1;             //被占满的起始行行号int re = 0;                 //被占满的终止行行号int rn, l;                   //rn每一行被占据的方块个数for (int x = 0; x < 20; x++)//对全部20行逐行进行从上到下的测试{rn = 0;                   //每次一行前将被占据的方块数计数清零for (int y = 0; y < 10; y++){if (block[x][y].isoccupy)rn++;          //当前行当前方块被占据,计数加1}if (rn == 10)            //计数为10代表当前行被占满{re = x;                //终止行被赋予当前行号if (rb == -1)rb = x;         //第一次计数时,当前行被赋予起始行}          }if (rb >= 0)               //如果起始行号大于等于0,代表有行被方块占满{playsound(s_row);        //播放消除行效果音乐while (i--)              //按计数进行循环实现动画效果{for (int x = rb; x <= re; x++){for (int y = 0; y < 10; y++){setlinecolor(c_notused);setfillcolor(c_notused);rectangle(block[x][y].x, block[x][y].y, block[x][y].x + B_WIDTH, block[x][y].y + B_WIDTH);solidrectangle(block[x][y].x + 3, block[x][y].y + 3, block[x][y].x + B_WIDTH - 4, block[x][y].y + B_WIDTH - 4);}}Sleep(100);           for (int x = rb; x <= re; x++){for (int y = 0; y < 10; y++){setlinecolor(RGB(0, 255, 0));setfillcolor(RGB(0, 255, 0));rectangle(block[x][y].x, block[x][y].y, block[x][y].x + B_WIDTH, block[x][y].y + B_WIDTH);solidrectangle(block[x][y].x + 3, block[x][y].y + 3, block[x][y].x + B_WIDTH - 4, block[x][y].y + B_WIDTH - 4);}}}//清除行的动画效果结束,清空被占据的行for (int x = rb; x <= re; x++){for (int y = 0; y < 10; y++){setlinecolor(c_notused);setfillcolor(c_notused);rectangle(block[x][y].x, block[x][y].y, block[x][y].x + B_WIDTH, block[x][y].y + B_WIDTH);solidrectangle(block[x][y].x + 3, block[x][y].y + 3, block[x][y].x + B_WIDTH - 4, block[x][y].y + B_WIDTH - 4);block[x][y].color = c_notused;block[x][y].isoccupy = FALSE;}}l = re - rb + 1;       //'l'被占据的行数ln += l;             //ln已经被消除的行数,被消除的行数增加计数score += 100 * l;//消除掉被占据的行后,对被占据的其实行以上的行进行重新绘制整理,实现上面的行整体位移到消除后空余空间的效果for (int x = rb - 1; x >= 0; x--)         //从被占据的行上一行开始循环 {for (int y = 0; y < 10; y++)         //对每行的每一个方块进行整理{if (block[x][y].isoccupy)           //如果当前方块是被占据的方块{block[x][y].color = c_notused; //清除当前方块block[x][y].isoccupy = FALSE;block[x + l][y].color = c_used; //整体位移L距离block[x + l][y].isoccupy = TRUE;}                    }}showblock();}
}

后记

游戏音效:主要用MCI函数实现:
在资源加载的时候先把音效文件加载,并重命名为mysong方便后续操作。

mciSendString(L"open rs\\music.wav alias mysong", NULL, 0, NULL);//打开wav音乐文件,别名“mysong”

播放用mciSendString 函数实现,该函数中关键的是播放的起止时间。

void playsound(int type)
{if (!ismusic)//是否关闭音效{switch (type){case s_move:mciSendString(L"play mysong from 2500 to 3000", NULL, 0, NULL);break;case s_rotate:mciSendString(L"play mysong from 500 to 1000", NULL, 0, NULL);break;case s_space:mciSendString(L"play mysong from 1000 to 1500", NULL, 0, NULL);break;case s_end:mciSendString(L"play mysong from 8000 to 9000", NULL, 0, NULL);break;case s_start:mciSendString(L"play mysong from 3000 to 7000", NULL, 0, NULL);break;case s_row:mciSendString(L"play mysong from 0 to 500", NULL, 0, NULL);break;}}
}

全部代码和资源如下:
Tetris.h文件:

#include <conio.h>
#include <easyx.h>
#include <time.h>
#include<mmsystem.h>
#pragma comment( lib, "MSIMG32.LIB")
#pragma comment(lib,"winmm.lib")#define W_WIDTH 664 //窗口宽度 509
#define W_LENGTH 1000 //窗口长度 768
#define G_tx 135     //游戏区左上x坐标 104
#define G_ty 97      //游戏区左上y坐标  75
#define G_bx 530     //游戏区右下x坐标 406
#define G_by 587     //游戏区右下y坐标 375#define R_tx 140  //方块区左上x坐标 110
#define R_ty 100     //方块区左上y坐标 81
#define R_bx 390     //方块区右下x坐标 284
#define R_by 585     //方块区右下y坐标 441#define BX 146       //俄罗斯方块背景区开始坐标 115
#define BY 104      //俄罗斯方块背景区开始坐标 86#define B_WIDTH 20 //每个方块的长宽大小一致 16#define Thickness 2  //线宽
#define Interval 2#define control_button_r 52#define up_button_x 471
#define up_button_y 709#define left_button_x 378
#define left_button_y 805#define right_button_x 566
#define right_button_y 805#define down_button_x 471
#define down_button_y 898#define effective_button_r 27#define pause_button_x 74
#define pause_button_y 684#define sound_button_x 168
#define sound_button_y 684#define replay_button_x 262
#define replay_button_y 684#define space_button_r 83
#define space_button_x 168
#define space_button_y 844typedef struct Block
{int x, y;COLORREF color;BOOL isoccupy;
}BLOCK;typedef struct Tetris
{int x;int y;
}TETRIS;HWND hwnd;
COLORREF c_notused = RGB(138, 152, 116);   //游戏背景没被方块占用时的颜色
COLORREF c_used = BLACK;                   //游戏背景被方块占用时的颜色
BLOCK block[20][10];//整个游戏区是10X20的方块大小
TETRIS Tetrix_now[4];//当前方块
int t_type;         //当前方块类型
int t_dt;           //当前方块方向,最多有三种方向
int Tetrix_next;    //下一个方块类型
BLOCK nextblock[2][4];//在信息区显示的下一个方块
IMAGE imgbk;        //游戏初始化的背景
IMAGE imgbkinf;     //绘制小恐龙动画前的背景
IMAGE img_icon;     //加载的游戏资源
IMAGE dinosaur[8];  //小恐龙图像
IMAGE img_digit[13];//数字图像
IMAGE img_pause[2]; //暂停图像
IMAGE img_music[2]; //音乐图像
int lastsocre = 0; //上一轮分数
int score = 0;     //当前分数
int ln = 0;            //起始行
int level = 1;     //级别
int isend = 0;     //游戏是否结束
int istouch = 0;   //是否碰撞
int ismusic = 0;   //是否静音
int pause = 0;     //是否暂停
int paus_count = 1; //暂停时用以计数来实现间隔显示明暗暂停图案
DWORD timeinterval = 1;//冒号间隔显示
int key;            //按键状态enum TetrisM      //俄罗斯方块的七种类型
{I,J,L,O,S,T,Z,
};
enum KeySt          //按键状态
{up,down,left,right,space,normal,
};enum TetrisDirection
{horz,          //水平vert,           //垂直horz_ag,        //180度水平vert_ag,        //180度垂直
};enum SoundTrack
{s_move,            //移动方块音效s_rotate,       //旋转方块音效s_space,        //掉落方块音效s_end,          //游戏结束音效s_start,        //游戏开始音效s_row,          //消除方块音效
};void Init();                      //初始化开始界面和游戏主界面
void BeforeStart();                 //游戏开始前显示的动画界面
void Start();                       //进入游戏操作循环
void End();                         //方块触顶后的操作
void init_rs();                     //加载、初始化各种资源
void show_dinosaur();               //显示小恐龙动画
void show_info(int);                //在右面信息区显示信息
DWORD WINAPI show_Time(LPVOID);     //线程,用于显示时间
void next_block(int);               //初始化下一个方块
void transparentimage(IMAGE* dstimg, int x, int y, IMAGE* srcimg, UINT transparentcolor);//透明贴图函数
void HpSleep(int);                  //精确定时程序
void Init_Block();                  //初始化当前方块
void MouseMsg();                    //处理鼠标消息
void KeyMsg();                      //处理键盘消息
void moveblock();                   //移动方块
void cleartetris();                 //清除当前方块,以备下一步移动方块
void goup();                        //翻转方块
void goleft();                      //向左移动方块
void goright();                     //向右移动方块
void godown();                      //向下加速移动方块
void gonormal();                    //正常向下方块
int touchdown();                    //方块触底
void changeblock();                 //变换方块
void reconstructblock();            //翻转方块后如果方块越界则重建方块
void showblock();                   //显示方块
void touchtop();                    //判断方块是否触顶
void rowcomplete();                 //消除行,测试每一行是否全部被方块占满,并记录占满的起始行、终止行行号,并消除相应的行
void changebutton(int, int, int);   //鼠标按到按钮后的效果
void playsound(int);                //播放音效

Tetris.cpp文件:

#include "Tetris.h"int main()
{DWORD ThreadID;HANDLE hThread_time;Init();//初始化开始界面和游戏主界面init_rs();//初始化小恐龙动画hThread_time = CreateThread(NULL, 0, show_Time, NULL, 0, &ThreadID);//创建计时线程,用于再信息区显示时间while (!isend){BeforeStart();//绘制小恐龙动画_getch();// 按任意键开始游戏Start();//开始游戏End();//游戏结束}CloseHandle(hThread_time);mciSendString(L"close mysong", NULL, 0, NULL);closegraph();
}//初始化开始界面和游戏主界面
void Init()
{IMAGE img;int ix = BX;int iy = BY;int i = 0;LOGFONT f;// 初始化绘图窗口initgraph(W_WIDTH, W_LENGTH);srand(time(NULL));hwnd = GetHWnd();int WIDTH = GetSystemMetrics(SM_CXSCREEN);MoveWindow(hwnd, (WIDTH - W_WIDTH) / 2, 0, W_WIDTH, W_LENGTH, FALSE);//设置窗口位置和新大小SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_CAPTION);//去标题栏SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) & ~(WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME));//去边框//显示启动画面loadimage(&img, _T("rs\\start.jpg"), W_WIDTH, W_LENGTH, false);putimage(0, 0, &img);_getch();//进入游戏,绘制主界面cleardevice();settextcolor(BLACK);setbkmode(TRANSPARENT);gettextstyle(&f);                        // 获取当前字体设置f.lfHeight = 25;                        // 设置字体高度f.lfWeight = FW_SEMIBOLD;_tcscpy_s(f.lfFaceName, _T("微软雅黑"));   // 设置字体为“黑体”f.lfQuality = ANTIALIASED_QUALITY;     // 设置输出效果为抗锯齿  settextstyle(&f);                        // 设置字体样式loadimage(&img, _T("rs\\bk.jpg"), 0, 0, false);//加载游戏背景图片putimage(0, 0, W_WIDTH, W_LENGTH, &img, 0, 0, SRCCOPY);setfillcolor(RGB(158, 173, 134));solidrectangle(G_tx, G_ty, G_bx, G_by);setlinestyle(PS_SOLID, Thickness);setlinecolor(BLACK);rectangle(R_tx, R_ty, R_bx, R_by);setlinecolor(c_notused);setfillcolor(c_notused);for (int x = 0; x < 20; x++)//绘制液晶区背景{for (int y = 0; y < 10; y++){rectangle(ix + y * B_WIDTH + y * (Thickness + Interval), iy, ix + y * B_WIDTH + y * (Thickness + Interval) + B_WIDTH, iy + B_WIDTH);solidrectangle(ix + y * B_WIDTH + y * (Thickness + Interval) + 3, iy + 3, ix + y * B_WIDTH + y * (Thickness + Interval) + B_WIDTH - 4, iy + B_WIDTH - 4);block[x][y].x = ix + y * B_WIDTH + y * (Thickness + Interval);block[x][y].y = iy;block[x][y].color = c_notused;block[x][y].isoccupy = FALSE;}iy += B_WIDTH + Thickness + Thickness;}getimage(&imgbk, 0, 0, W_WIDTH, W_LENGTH);//将界面备份以备后面刷新//初始化信息区显示“下一个”方块图像的坐标信息ix = R_bx + 40;//信息区“下一个”方块图像背景的x坐标信息iy = R_ty + 320;//信息区“下一个”方块图像背景的y坐标信息for (int x = 0; x < 2; x++){for (int y = 0; y < 4; y++){nextblock[x][y].x = ix + y * B_WIDTH + y * (Thickness + Interval);nextblock[x][y].y = iy;nextblock[x][y].color = c_notused;}iy += B_WIDTH + Thickness + Thickness;}Tetrix_next = rand() % 7;//随机生成下一个方块的类型next_block(Tetrix_next);//初始化下一个方块
}
//游戏开始前显示的动画界面
void BeforeStart()
{int x, y, i = 0;x = (R_bx - R_tx - 100) / 2 + R_tx;y = R_ty + 100;outtextxy(x, y + 100, _T("俄罗斯方块"));outtextxy(x - 3, y + 130, _T("T E T R I S"));show_info(isend);getimage(&imgbkinf, G_tx, G_ty, 250, 460);//将图像备份以备后面刷新,备份的图像是涉及小恐龙动画刷新的部分,其余部分不备份FlushMouseMsgBuffer();                    //清空鼠标消息区FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));//清空键盘消息区playsound(s_start);                       //播放开场音乐while (!_kbhit())                       //没有按键一直显示小恐龙动画,有按键退出进入游戏{show_dinosaur();//显示小恐龙动画Sleep(rand() % 100 + 200);}
}
//进入游戏操作循环
void Start()
{int sleeptime = 600;              //方块每一次移动间隔的时间Init_Block();                     //初始化当前方块putimage(0, 0, &imgbk);                //刷新背景,清除小恐龙动画showblock();                       //显示方块score = 0;                           //因为游戏可以重新开始,死亡后可以重新来一轮,所以每次进入游戏将当前得分,上一轮得分清零lastsocre = 0;show_info(!isend);                 //在信息区显示消息Sleep(sleeptime);while (!isend)                       //方块没有触顶上死亡就一直循环{Tetrix_next = rand() % 7;     //随机生成下一个方块类型next_block(Tetrix_next);       //初始化下一个方块show_info(!isend);                //在信息区显示消息while (!istouch)              //方块没有触底或者碰撞其他已存在方块一直循环掉落{if (score > 5000){level = 2;sleeptime = 500;}else if (score > 10000){level = 3;sleeptime = 400;}else if (score > 30000){level = 4;sleeptime = 300;}else if (score > 10000){level = 5;sleeptime = 200;}key = normal;if (_kbhit())             //检测是否有键盘消息KeyMsg();                //处理按键消息while (MouseHit())          //检测是否有鼠标消息,不用while循环的话会错过鼠标消息,因为鼠标消息太多了,容易在鼠标消息缓冲区溢出去MouseMsg();              //处理鼠标消息moveblock();                //根据按键和鼠标点按移动方块showblock();             //显示移动后的方块rowcomplete();                //判断是否需要消除行,如满足条件消除相应的行touchtop();                   //判断方块是否触顶,是游戏死亡,退出循环show_info(!isend);           //在信息区显示消息Sleep(sleeptime);         }istouch = 0;Init_Block();                 //当前方块触底或者碰触其他已沉底方块,初始化当前新方块}
}
//方块触顶后的操作
void End()
{HWND wnd = GetHWnd();             //取得当前窗口句柄if (MessageBox(wnd, _T("游戏结束。\n重来一局吗?"), _T("询问"), MB_YESNO | MB_ICONQUESTION) == IDYES){putimage(0, 0, &imgbk);         //点击yes重来一局putimage(G_tx, G_ty, &imgbkinf);//刷新背景isend = 0;lastsocre = score;score = 0;ln = 0;}     elseisend = 1;                     //点击no,不玩了
}
//加载、初始化各种资源
void init_rs()
{DWORD* dst;DWORD* src;int src_width;int src_height;loadimage(&img_icon, _T("rs\\icon.gif"), 0, 0, false);//加载小恐龙动画图片资源SetWorkingImage(&img_icon);for (int i = 0; i < 4; i++){getimage(&dinosaur[i], i * 100, 98, 100, 88);//加载分割前四个画面getimage(&dinosaur[i + 4], i * 100, 98, 100, 88);//初始化后四个画面,如果不初始化后面翻转操作会报错,因为指针为空}for (int i = 0; i < 4; i++)//小恐龙画面数组后四个画面是前四个的翻转{src = GetImageBuffer(&dinosaur[i]);dst = GetImageBuffer(&dinosaur[i + 4]);src_width = dinosaur[i].getwidth();src_height = dinosaur[i].getheight();for (int iy = 0; iy < src_height; iy++){int dx = src_width - 1;for (int ix = 0; ix < src_width; ix++){dst[dx--] = src[ix];}dst += src_width;src += src_width;}}for (int i = 0; i < 11; i++){getimage(&img_digit[i], 75 + i * 14, 25, 14, 24);//加载分割10个数字}for (int i = 0; i < 2; i++){getimage(&img_digit[i + 11], 229 + i * 14, 25, 14, 24);//加载分割的两个冒号}getimage(&img_pause[0], 75, 75, 20, 18);//加载分割的两个暂停符号getimage(&img_pause[1], 100, 75, 20, 18);//加载分割的两个暂停符号for (int i = 0; i < 2; i++){getimage(&img_music[i], 150 + i * 25, 75, 25, 21);//加载分割的两个声音符号}SetWorkingImage(NULL);mciSendString(L"open rs\\music.wav alias mysong", NULL, 0, NULL);//打开wav音乐文件,别名“mysong”
}
//显示小恐龙动画
void show_dinosaur()
{int x, y, i = 0;x = (R_bx - R_tx - 100) / 2 + R_tx;y = R_ty + 100;switch (rand() % 2){case 0:while (i++ < 10){putimage(G_tx, G_ty, &imgbkinf);transparentimage(NULL, x, y, &dinosaur[2], 0xffffff);Sleep(60);putimage(G_tx, G_ty, &imgbkinf);transparentimage(NULL, x, y, &dinosaur[3], 0xffffff);Sleep(60);}if ((rand() % 2) == 1){i = 0;while (i++ < 4){putimage(G_tx, G_ty, &imgbkinf);transparentimage(NULL, x, y, &dinosaur[0], 0xffffff);Sleep(60);putimage(G_tx, G_ty, &imgbkinf);transparentimage(NULL, x, y, &dinosaur[1], 0xffffff);Sleep(100);}}putimage(G_tx, G_ty, &imgbkinf);transparentimage(NULL, x, y, &dinosaur[0], 0xffffff);Sleep(1000);break;case 1:while (i++ < 10){putimage(G_tx, G_ty, &imgbkinf);transparentimage(NULL, x, y, &dinosaur[6], 0xffffff);Sleep(60);putimage(G_tx, G_ty, &imgbkinf);transparentimage(NULL, x, y, &dinosaur[7], 0xffffff);Sleep(60);}if ((rand() % 2) == 1){i = 0;while (i++ < 4){putimage(G_tx, G_ty, &imgbkinf);transparentimage(NULL, x, y, &dinosaur[4], 0xffffff);Sleep(60);putimage(G_tx, G_ty, &imgbkinf);transparentimage(NULL, x, y, &dinosaur[5], 0xffffff);Sleep(100);}}putimage(G_tx, G_ty, &imgbkinf);transparentimage(NULL, x, y, &dinosaur[0], 0xffffff);Sleep(1000);break;}
}
//在右面信息区显示信息
void show_info(int type)
{int x, k, l, i = 0;int m, num[6],num2[6];int s = 0;if (type){outtextxy(R_bx + 20, R_ty + 10, _T("当前得分"));}else{//显示上轮得分outtextxy(R_bx + 20, R_ty + 10, _T("上轮得分"));}if (type)s = score;elses = lastsocre;if (s != 0){i = 0;while (s){num2[i] = s % 10;i++;s /= 10;}for (int n = 0; n < (6 - i); n++)//显示液晶数字背景,暗色的8{transparentimage(NULL, R_bx + 40 + n * 14 + n * 2, R_ty + 50, &img_digit[10], 0xA1B187);}m = i;for (i = 0; i < m; i++)transparentimage(NULL, R_bx + 40 + (5 - i) * 14 + (5 - i) * 2, R_ty + 50, &img_digit[num2[i]], 0xA1B187);//逐位显示数字}else{for (int n = 0; n < 5; n++)//显示液晶数字背景,暗色的8{transparentimage(NULL, R_bx + 40 + n * 14 + n * 2, R_ty + 50, &img_digit[10], 0xA1B187);}transparentimage(NULL, R_bx + 40 + 5 * 14 + 5 * 2, R_ty + 50, &img_digit[0], 0xA1B187);}//显示起始行,消除行if (!type){outtextxy(R_bx + 20, R_ty + 100, _T("起始行"));}else{outtextxy(R_bx + 20, R_ty + 100, _T("消除行"));}if (ln == 0){for (int n = 0; n < 5; n++)//显示液晶数字背景,暗色的8{transparentimage(NULL, R_bx + 40 + n * 14 + n * 2, R_ty + 130, &img_digit[10], 0xA1B187);}transparentimage(NULL, R_bx + 40 + 5 * 14 + 5 * 2, R_ty + 130, &img_digit[0], 0xA1B187);}else{l = ln;i = 0;while (l){num[i] = l % 10;i++;l = l / 10;}for (int n = 0; n < (6 - i); n++)//显示液晶数字背景,暗色的8{transparentimage(NULL, R_bx + 40 + n * 14 + n * 2, R_ty + 130, &img_digit[10], 0xA1B187);}m = i;for (i = 0; i < m; i++)transparentimage(NULL, R_bx + 40 + (5 - i) * 14 + (5 - i) * 2, R_ty + 130, &img_digit[num[i]], 0xA1B187);//逐位显示数字}//显示级别outtextxy(R_bx + 20, R_ty + 180, _T("级别"));transparentimage(NULL, R_bx + 40 + 5 * 14 + 5 * 2, R_ty + 220, &img_digit[level], 0xA1B187);//显示下一个outtextxy(R_bx + 20, R_ty + 280, _T("下一个"));for (int y = 0; y < 2; y++)//显示下一个方块图案{for (int x = 0; x < 4; x++){setlinecolor(nextblock[y][x].color);setfillcolor(nextblock[y][x].color);rectangle(nextblock[y][x].x, nextblock[y][x].y, nextblock[y][x].x + B_WIDTH, nextblock[y][x].y + B_WIDTH);solidrectangle(nextblock[y][x].x + 3, nextblock[y][x].y + 3, nextblock[y][x].x + B_WIDTH - 4, nextblock[y][x].y + B_WIDTH - 4);}}//显示静音和暂停符号if (ismusic)putimage(R_bx + 10, R_by - 30, &img_music[0]);elseputimage(R_bx + 10, R_by - 30, &img_music[1]);if (pause){if ((++paus_count) % 2 == 0)putimage(R_bx + 40, R_by - 28, &img_pause[0]);elseputimage(R_bx + 40, R_by - 28, &img_pause[1]);}elseputimage(R_bx + 40, R_by - 28, &img_pause[1]);
}
//线程,用于显示时间
DWORD WINAPI show_Time(LPVOID lpParam)
{SYSTEMTIME t1;int x, i;while (1){HpSleep(1000);//精确计时,每隔一秒显示一次时间,GetLocalTime(&t1);x = t1.wHour;//小时if (x >= 10){i = x / 10;transparentimage(NULL, R_bx + 60, R_by - 31, &img_digit[i], 0xA1B187);x %= 10;transparentimage(NULL, R_bx + 76, R_by - 31, &img_digit[x], 0xA1B187);}else{transparentimage(NULL, R_bx + 60, R_by - 31, &img_digit[0], 0xA1B187);x %= 10;transparentimage(NULL, R_bx + 76, R_by - 31, &img_digit[x], 0xA1B187);}if ((timeinterval++ % 2) == 1)//小时与分钟相隔的冒号,明暗间隔显示transparentimage(NULL, R_bx + 92, R_by - 31, &img_digit[11], 0xA1B187);elsetransparentimage(NULL, R_bx + 92, R_by - 31, &img_digit[12], 0xA1B187);x = t1.wMinute;//分钟if (x >= 10){i = x / 10;transparentimage(NULL, R_bx + 108, R_by - 31, &img_digit[i], 0xA1B187);x %= 10;transparentimage(NULL, R_bx + 124, R_by - 31, &img_digit[x], 0xA1B187);}else{transparentimage(NULL, R_bx + 108, R_by - 31, &img_digit[0], 0xA1B187);x %= 10;transparentimage(NULL, R_bx + 124, R_by - 31, &img_digit[x], 0xA1B187);}}
}
//透明贴图函数
void transparentimage(IMAGE* dstimg, int x, int y, IMAGE* srcimg, UINT transparentcolor)
{// 变量初始化DWORD* dst = GetImageBuffer(dstimg);DWORD* src = GetImageBuffer(srcimg);int src_width = srcimg->getwidth();int src_height = srcimg->getheight();int dst_width = (dstimg == NULL ? getwidth() : dstimg->getwidth());int dst_height = (dstimg == NULL ? getheight() : dstimg->getheight());// 计算贴图的实际长宽int iwidth = (x + src_width > dst_width) ? dst_width - x : src_width;int iheight = (y + src_height > dst_height) ? dst_height - y : src_height;// 修正贴图起始位置dst += dst_width * y + x;// 修正透明色,显示缓冲区中的数据结构为 0xaarrggbbtransparentcolor = 0xff000000 | BGR(transparentcolor);// 实现透明贴图for (int iy = 0; iy < iheight; iy++){for (int ix = 0; ix < iwidth; ix++){if (src[ix] != transparentcolor)dst[ix] = src[ix];}dst += dst_width;src += src_width;}
}
//初始化下一个方块
void next_block(int type)
{for (int y = 0; y < 2; y++)//清空上一次显示内容{for (int x = 0; x < 4; x++){nextblock[y][x].color = c_notused;}}switch (type){case I:for (int x = 0; x < 4; x++){nextblock[1][x].color = c_used;}break;case J:nextblock[0][0].color = c_used;for (int x = 0; x < 3; x++){nextblock[1][x].color = c_used;}break;case L:nextblock[0][3].color = c_used;for (int x = 1; x < 4; x++){nextblock[1][x].color = c_used;}break;case O:for (int y = 0; y < 2; y++){for (int x = 1; x < 3; x++){nextblock[y][x].color = c_used;}}break;case S:for (int x = 1; x < 3; x++){nextblock[0][x].color = c_used;}for (int x = 0; x < 2; x++){nextblock[1][x].color = c_used;}break;case T:nextblock[0][1].color = c_used;for (int x = 0; x < 3; x++){nextblock[1][x].color = c_used;}break;case Z:for (int x = 0; x < 2; x++){nextblock[0][x].color = c_used;}for (int x = 1; x < 3; x++){nextblock[1][x].color = c_used;}break;}
}
//精确定时程序
void HpSleep(int ms)//精确定时函数
{static clock_t oldclock = clock();        // 静态变量,记录上一次 tickoldclock += ms * CLOCKS_PER_SEC / 1000;  // 更新 tickif (clock() > oldclock)                    // 如果已经超时,无需延时oldclock = clock();elsewhile (clock() < oldclock)          // 延时Sleep(1);                      // 释放 CPU 控制权,降低 CPU 占用率
}
//初始化当前方块
void Init_Block()
{int x, y;t_type = Tetrix_next;//当前方块类型t_dt = 0;//初始为水平方向switch (t_type){case I:for (int x = 0; x < 4; x++){Tetrix_now[x].x = 0;Tetrix_now[x].y = x + 3;}break;case J:Tetrix_now[0].x = -1;Tetrix_now[0].y = 4;for (int x = 1; x < 4; x++){Tetrix_now[x].x = 0;Tetrix_now[x].y = x + 3;}break;case L:Tetrix_now[0].x = -1;Tetrix_now[0].y = 6;for (int x = 1; x < 4; x++){Tetrix_now[x].x = 0;Tetrix_now[x].y = x + 3;}break;case O:for (int x = 0; x < 2; x++){Tetrix_now[x].y = x + 4;Tetrix_now[x].x = -1;}for (int x = 2; x < 4; x++){Tetrix_now[x].y = 2 + x;Tetrix_now[x].x = 0;}break;case S:for (int x = 0; x < 2; x++){Tetrix_now[x].y = 5 + x;Tetrix_now[x].x = -1;}for (int x = 2; x < 4; x++){Tetrix_now[x].y = 2 + x;Tetrix_now[x].x = 0;}break;case T:Tetrix_now[0].x = -1;Tetrix_now[0].y = 5;for (int x = 1; x < 4; x++){Tetrix_now[x].y = 3 + x;Tetrix_now[x].x = 0;}break;case Z:for (int x = 0; x < 2; x++){Tetrix_now[x].y = 4 + x;Tetrix_now[x].x = -1;}for (int x = 2; x < 4; x++){Tetrix_now[x].y = 3 + x;Tetrix_now[x].x = 0;}break;}for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x >= 0){block[x][y].color = c_used;}}
}
//处理鼠标消息
void MouseMsg()
{MOUSEMSG m;m = GetMouseMsg();         // 获取一条鼠标消息switch (m.uMsg){case WM_LBUTTONDOWN://根据鼠标消息坐标计算当前按下鼠标左键时是否在按钮范围内,是则显示按钮按下和弹出效果,并通过给按键状态变量赋值来移动方块if (((m.x - up_button_x) * (m.x - up_button_x) + (m.y - up_button_y) * (m.y - up_button_y)) <= (control_button_r * control_button_r)){key = up;changebutton(up_button_x, up_button_y, control_button_r);Sleep(50);changebutton(up_button_x, up_button_y, control_button_r);}else if (((m.x - left_button_x) * (m.x - left_button_x) + (m.y - left_button_y) * (m.y - left_button_y)) <= (control_button_r * control_button_r)){key = left;changebutton(left_button_x, left_button_y, control_button_r);Sleep(50);changebutton(left_button_x, left_button_y, control_button_r);}else if (((m.x - right_button_x) * (m.x - right_button_x) + (m.y - right_button_y) * (m.y - right_button_y)) <= (control_button_r * control_button_r)){key = right;changebutton(right_button_x, right_button_y, control_button_r);Sleep(50);changebutton(right_button_x, right_button_y, control_button_r);}else if (((m.x - down_button_x) * (m.x - down_button_x) + (m.y - down_button_y) * (m.y - down_button_y)) <= (control_button_r * control_button_r)){key = down;changebutton(down_button_x, down_button_y, control_button_r);Sleep(50);changebutton(down_button_x, down_button_y, control_button_r);}else if (((m.x - pause_button_x) * (m.x - pause_button_x) + (m.y - pause_button_y) * (m.y - pause_button_y)) <= (effective_button_r * effective_button_r)){pause = !pause;changebutton(pause_button_x, pause_button_y, effective_button_r);Sleep(50);changebutton(pause_button_x, pause_button_y, effective_button_r);}else if (((m.x - sound_button_x) * (m.x - sound_button_x) + (m.y - sound_button_y) * (m.y - sound_button_y)) <= (effective_button_r * effective_button_r)){ismusic = !ismusic;changebutton(sound_button_x, sound_button_y, effective_button_r);Sleep(50);changebutton(sound_button_x, sound_button_y, effective_button_r);}else if (((m.x - replay_button_x) * (m.x - replay_button_x) + (m.y - replay_button_y) * (m.y - replay_button_y)) <= (effective_button_r * effective_button_r)){istouch = 1;isend = 1;changebutton(replay_button_x, replay_button_y, effective_button_r);Sleep(50);changebutton(replay_button_x, replay_button_y, effective_button_r);}else if (((m.x - space_button_x) * (m.x - space_button_x) + (m.y - space_button_y) * (m.y - space_button_y)) <= (space_button_r * space_button_r)){key = space;changebutton(space_button_x, space_button_y, space_button_r);Sleep(50);changebutton(space_button_x, space_button_y, space_button_r);}break;}
}
//处理键盘消息
void KeyMsg()
{char ikey;ikey = _getch();if (ikey == -32)ikey = -_getch();switch (ikey){case 'w':case 'W':case -72:key = up;break;case 's':case 'S':case -80:key = down;break;case 'a':case 'A':case -75:key = left;break;case 'd':case 'D':case -77:key = right;break;case 32:           //空格键key = space;break;case 27:            //Esc退出键{istouch = 1;isend = 1;}break;default:break;}
}
//移动方块
void moveblock()
{if (!pause){switch (key){case up:goup();playsound(s_rotate);break;case down:godown();playsound(s_move);break;case left:goleft();playsound(s_move);gonormal();break;case right:goright();playsound(s_move);gonormal();break;case space:while (!istouch)gonormal();playsound(s_space);break;case normal:gonormal();break;default:break;}}
}
//清除当前方块,以备下一步移动方块
void cleartetris()
{int x, y;for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x >= 0)block[x][y].color = c_notused;}
}
//翻转方块
void goup()
{int x, y;int can_move = 1;cleartetris();changeblock();reconstructblock();for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x >= 0)block[x][y].color = c_used;}
}
//向左移动方块
void goleft()
{int x, y;int can_move = 1;cleartetris();for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if ((y >= 1) && (block[x][y - 1].isoccupy == TRUE))can_move = 0;if (y == 0)can_move = 0;}if (can_move){for (int i = 0; i < 4; i++){Tetrix_now[i].y -= 1;x = Tetrix_now[i].x;y = Tetrix_now[i].y;block[x][y].color = c_used;}}else{for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x >= 0)block[x][y].color = c_used;}}
}
//向右移动方块
void goright()
{int x, y;int can_move = 1;cleartetris();for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if ((y < 9) && (block[x][y + 1].isoccupy == TRUE))can_move = 0;if (y == 9)can_move = 0;}if (can_move){for (int i = 0; i < 4; i++){Tetrix_now[i].y += 1;x = Tetrix_now[i].x;y = Tetrix_now[i].y;block[x][y].color = c_used;}}else{for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x >= 0)block[x][y].color = c_used;}}
}
//向下加速移动方块
void godown()
{int x, y;int can_move = 1;cleartetris();switch (touchdown()){case 0:for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x >= 0){block[x][y].color = c_used;block[x][y].isoccupy = TRUE;}}istouch = 1;break;case 1:for (int i = 0; i < 4; i++){Tetrix_now[i].x += 1;x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x >= 0){block[x][y].color = c_used;block[x][y].isoccupy = TRUE;}}istouch = 1;break;case 2:for (int i = 0; i < 4; i++){Tetrix_now[i].x += 2;x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x >= 0)block[x][y].color = c_used;}break;}
}
//正常向下方块
void gonormal()
{int x, y;int can_move = 1;cleartetris();for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if ((x < 19) && (block[x + 1][y].isoccupy == TRUE))can_move = 0;else if (x == 19)can_move = 0;}if (can_move){for (int i = 0; i < 4; i++){Tetrix_now[i].x += 1;x = Tetrix_now[i].x;y = Tetrix_now[i].y;block[x][y].color = c_used;}}else{for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x >= 0){block[x][y].color = c_used;block[x][y].isoccupy = TRUE;}}istouch = 1;}
}
//加速下降过程中判断是否碰撞
int touchdown()
{int x, y;int r[4];int t;for (int i = 0; i < 4; i++){x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x < 18){if (block[x + 1][y].isoccupy == TRUE)r[i] = 0;else if (block[x + 2][y].isoccupy == TRUE)r[i] = 1;elser[i] = 2;}else if (x == 18)r[i] = 1;elser[i] = 0;}t = r[0];for (int i = 1; i < 4; i++)if (r[i] < t)t = r[i];return t;
}
//变换方块
void changeblock()
{switch (t_type){case I:if (t_dt == horz)//当前为水平方向,修改为垂直方向,以第2个方块为轴心旋转{t_dt = vert;Tetrix_now[0].x = Tetrix_now[1].x - 1;Tetrix_now[0].y = Tetrix_now[1].y;Tetrix_now[2].x = Tetrix_now[1].x + 1;Tetrix_now[2].y = Tetrix_now[1].y;Tetrix_now[3].x = Tetrix_now[1].x + 2;Tetrix_now[3].y = Tetrix_now[1].y;}else if (t_dt == vert)//当前为垂直方向,修改为水平方向,以第2个方块为轴心旋转{t_dt = horz;Tetrix_now[0].x = Tetrix_now[1].x;Tetrix_now[0].y = Tetrix_now[1].y - 1;Tetrix_now[2].x = Tetrix_now[1].x;Tetrix_now[2].y = Tetrix_now[1].y + 1;Tetrix_now[3].x = Tetrix_now[1].x;Tetrix_now[3].y = Tetrix_now[1].y + 2;}break;case J:if (t_dt == horz)//当前为水平方向,修改为垂直方向{t_dt = vert;int x, y;x = Tetrix_now[2].x;                 //保持第3个方块坐标保持y = Tetrix_now[2].y;                  //保持第3个方块坐标保持Tetrix_now[2].x = Tetrix_now[0].x;        //旋转后第3个方块来到第1个方块的位置Tetrix_now[2].y = Tetrix_now[0].y;     //旋转后第3个方块来到第1个方块的位置Tetrix_now[3].x = Tetrix_now[1].x;     //旋转后第4个方块来到第2个方块的位置Tetrix_now[3].y = Tetrix_now[1].y;     //旋转后第4个方块来到第2个方块的位置Tetrix_now[1].x = x - 2;               //旋转后第2个方块,用与原第3个方块的相对位置计算Tetrix_now[1].y = y - 1;              //旋转后第2个方块,用与原第3个方块的相对位置计算Tetrix_now[0].x = x - 2;              //旋转后第1个方块,用与原第3个方块的相对位置计算Tetrix_now[0].y = y;                  //旋转后第1个方块,用与原第3个方块的相对位置计算}else if (t_dt == vert)//当前为垂直方向,修改为镜像水平方向{t_dt = horz_ag;                           //旋转后第4个方块位置不变Tetrix_now[2].x = Tetrix_now[3].x;       //旋转后第3个方块在原第4个方块右面Tetrix_now[2].y = Tetrix_now[3].y + 1; //旋转后第3个方块在原第4个方块右面Tetrix_now[1].x = Tetrix_now[2].x;      //旋转后第2个方块在旋转后第3个方块右面Tetrix_now[1].y = Tetrix_now[2].y + 1;   //旋转后第2个方块在旋转后第3个方块右面Tetrix_now[0].x = Tetrix_now[1].x + 1;   //旋转后第1个方块在旋转后第2个方块右面Tetrix_now[0].y = Tetrix_now[1].y;        //旋转后第1个方块在旋转后第2个方块右面}else if (t_dt == horz_ag)//当前为镜像水平方向,修改为镜像垂直方向{t_dt = vert_ag;                          //选择后第2个方块位置不变Tetrix_now[0].x = Tetrix_now[1].x;       //旋转后第1个方块在原第2个方块左面Tetrix_now[0].y = Tetrix_now[1].y - 1;  //旋转后第1个方块在原第2个方块左面Tetrix_now[2].x = Tetrix_now[1].x - 1;  //旋转后第3个方块在第2个方块上面Tetrix_now[2].y = Tetrix_now[1].y;       //旋转后第3个方块在第2个方块上面Tetrix_now[3].x = Tetrix_now[2].x - 1;   //旋转后第4个方块在第3个方块上面Tetrix_now[3].y = Tetrix_now[2].y;       //旋转后第4个方块在第3个方块上面}else if (t_dt == vert_ag)//当前为镜像垂直方向,修改为水平{t_dt = horz;                            //旋转后第2个方块位置不变Tetrix_now[0].x = Tetrix_now[1].x - 1;   //旋转后第1个方块来到第2个方块上边Tetrix_now[0].y = Tetrix_now[1].y;      //旋转后第1个方块来到第2个方块上边Tetrix_now[2].x = Tetrix_now[1].x;      //旋转后第3个方块来到第2个方块右面Tetrix_now[2].y = Tetrix_now[1].y + 1; //旋转后第3个方块来到第2个方块右面Tetrix_now[3].x = Tetrix_now[2].x;      //旋转后第4个方块来到第3个方块右面Tetrix_now[3].y = Tetrix_now[2].y + 1; //旋转后第4个方块来到第3个方块右面}break;case L:if (t_dt == horz)//当前为水平方向,修改为垂直方向{t_dt = vert;                          //旋转后第4个方块位置不变Tetrix_now[0].x = Tetrix_now[3].x;       //旋转后第1个方块在旋转后第4个方块右面Tetrix_now[0].y = Tetrix_now[3].y + 1;   //旋转后第1个方块在旋转后第4个方块右面Tetrix_now[2].x = Tetrix_now[3].x - 1;    //旋转后第3个方块在原第4个方块上面Tetrix_now[2].y = Tetrix_now[3].y;      //旋转后第3个方块在原第4个方块上面Tetrix_now[1].x = Tetrix_now[2].x - 1;  //旋转后第2个方块在原第3个方块上面Tetrix_now[1].y = Tetrix_now[2].y;      //旋转后第2个方块在原第3个方块上面         }else if (t_dt == vert)//当前为垂直方向,修改为镜像水平方向{t_dt = horz_ag;                            //旋转后第4个方块位置不变Tetrix_now[2].x = Tetrix_now[3].x;       //旋转后第3个方块在原第4个方块右面Tetrix_now[2].y = Tetrix_now[3].y + 1; //旋转后第3个方块在原第4个方块右面Tetrix_now[1].x = Tetrix_now[2].x;      //旋转后第2个方块在旋转后第3个方块右面Tetrix_now[1].y = Tetrix_now[2].y + 1;   //旋转后第2个方块在旋转后第3个方块右面Tetrix_now[0].x = Tetrix_now[3].x + 1;   //旋转后第1个方块在旋转后第4个方块下面Tetrix_now[0].y = Tetrix_now[3].y;        //旋转后第1个方块在旋转后第4个方块下面}else if (t_dt == horz_ag)//当前为镜像水平方向,修改为垂直镜像方向{t_dt = vert_ag;Tetrix_now[3].x = Tetrix_now[2].x;       //旋转后第4个方块在原第3个方块位置Tetrix_now[3].y = Tetrix_now[2].y;      //旋转后第4个方块在原第3个方块位置Tetrix_now[0].x = Tetrix_now[3].x;      //旋转后第1个方块在第4个方块左面Tetrix_now[0].y = Tetrix_now[3].y - 1;   //旋转后第1个方块在第4个方块左面Tetrix_now[2].x = Tetrix_now[3].x + 1;  //旋转后第3个方块在第4个方块下面Tetrix_now[2].y = Tetrix_now[3].y;       //旋转后第3个方块在第4个方块下面Tetrix_now[1].x = Tetrix_now[2].x + 1;  //旋转后第2个方块在第3个方块下面Tetrix_now[1].y = Tetrix_now[2].y;       //旋转后第2个方块在第3个方块下面}else if (t_dt == vert_ag)//当前为垂直镜像方向方向,修改为水平方向{t_dt = horz;                            //旋转后第4个方块位置不变Tetrix_now[0].x = Tetrix_now[3].x - 1;   //旋转后第1个方块来到第4个方块上边Tetrix_now[0].y = Tetrix_now[3].y;      //旋转后第1个方块来到第4个方块上边Tetrix_now[2].x = Tetrix_now[3].x;      //旋转后第3个方块来到第4个方块左方Tetrix_now[2].y = Tetrix_now[3].y - 1;  //旋转后第3个方块来到第4个方块左方Tetrix_now[1].x = Tetrix_now[2].x;      //旋转后第2个方块来到第3个方块左方Tetrix_now[1].y = Tetrix_now[2].y - 1;  //旋转后第2个方块来到第3个方块左方}break;case O:break;case S:if (t_dt == horz)//当前为水平方向,修改为垂直方向{t_dt = vert;                         //旋转后第4个方块位置不变Tetrix_now[0].x = Tetrix_now[3].x;       //旋转后第1个方块在旋转后第4个方块右面Tetrix_now[0].y = Tetrix_now[3].y + 1;   //旋转后第1个方块在旋转后第4个方块右面Tetrix_now[2].x = Tetrix_now[3].x - 1;    //旋转后第3个方块在原第4个方块上面Tetrix_now[2].y = Tetrix_now[3].y;      //旋转后第3个方块在原第4个方块上面Tetrix_now[1].x = Tetrix_now[0].x + 1; //旋转后第2个方块在原第1个方块下面Tetrix_now[1].y = Tetrix_now[0].y;      //旋转后第2个方块在原第1个方块下面         }else if (t_dt == vert)//当前为垂直方向,修改为镜像水平方向{t_dt = horz;                           //旋转后第4个方块位置不变Tetrix_now[0].x = Tetrix_now[3].x - 1;   //旋转后第1个方块在旋转后第4个方块上面Tetrix_now[0].y = Tetrix_now[3].y;        //旋转后第1个方块在旋转后第4个方块上面Tetrix_now[1].x = Tetrix_now[0].x;        //旋转后第2个方块在旋转后第1个方块右面Tetrix_now[1].y = Tetrix_now[0].y + 1;   //旋转后第2个方块在旋转后第1个方块右面Tetrix_now[2].x = Tetrix_now[3].x;        //旋转后第3个方块在旋转后第4个方块左面Tetrix_now[2].y = Tetrix_now[3].y - 1;    //旋转后第3个方块在旋转后第4个方块左面}break;case T:if (t_dt == horz)//当前为水平方向,修改为垂直方向{t_dt = vert;                            //旋转后第3个方块位置不变Tetrix_now[0].x = Tetrix_now[2].x;       //旋转后第1个方块在旋转后第3个方块右面Tetrix_now[0].y = Tetrix_now[2].y + 1;   //旋转后第1个方块在旋转后第3个方块右面Tetrix_now[1].x = Tetrix_now[2].x - 1;    //旋转后第2个方块在原第3个方块上面Tetrix_now[1].y = Tetrix_now[2].y;      //旋转后第2个方块在原第3个方块上面Tetrix_now[3].x = Tetrix_now[2].x + 1; //旋转后第4个方块在原第3个方块下面Tetrix_now[3].y = Tetrix_now[2].y;      //旋转后第4个方块在原第3个方块下面         }else if (t_dt == vert)//当前为垂直方向,修改为镜像水平方向{t_dt = horz_ag;                            //旋转后第3个方块位置不变Tetrix_now[0].x = Tetrix_now[2].x + 1;  //旋转后第1个方块在旋转后第3个方块下面Tetrix_now[0].y = Tetrix_now[2].y;        //旋转后第1个方块在旋转后第3个方块下面Tetrix_now[1].x = Tetrix_now[2].x;        //旋转后第2个方块在原第3个方块上面Tetrix_now[1].y = Tetrix_now[2].y + 1; //旋转后第2个方块在原第3个方块上面Tetrix_now[3].x = Tetrix_now[2].x;      //旋转后第4个方块在原第3个方块下面Tetrix_now[3].y = Tetrix_now[2].y - 1;  //旋转后第4个方块在原第3个方块下面 }else if (t_dt == horz_ag)//当前为镜像水平方向,修改为镜像垂直方向{t_dt = vert_ag;                       //旋转后第3个方块位置不变Tetrix_now[0].x = Tetrix_now[2].x;       //旋转后第1个方块在旋转后第3个方块左面Tetrix_now[0].y = Tetrix_now[2].y - 1;    //旋转后第1个方块在旋转后第3个方块左面Tetrix_now[1].x = Tetrix_now[2].x + 1;   //旋转后第2个方块在原第3个方块下面Tetrix_now[1].y = Tetrix_now[2].y;      //旋转后第2个方块在原第3个方块下面Tetrix_now[3].x = Tetrix_now[2].x - 1;  //旋转后第4个方块在原第3个方块上面Tetrix_now[3].y = Tetrix_now[2].y;      //旋转后第4个方块在原第3个方块上面 }else if (t_dt == vert_ag)//当前为镜像垂直方向,修改为水平方向{t_dt = horz;                            //旋转后第3个方块位置不变Tetrix_now[0].x = Tetrix_now[2].x - 1;   //旋转后第1个方块在旋转后第3个方块上面Tetrix_now[0].y = Tetrix_now[2].y;        //旋转后第1个方块在旋转后第3个方块上面Tetrix_now[1].x = Tetrix_now[2].x;        //旋转后第2个方块在原第3个方块左面Tetrix_now[1].y = Tetrix_now[2].y - 1;  //旋转后第2个方块在原第3个方块左面Tetrix_now[3].x = Tetrix_now[2].x;      //旋转后第4个方块在原第3个方块右面Tetrix_now[3].y = Tetrix_now[2].y + 1; //旋转后第4个方块在原第3个方块右面 }break;case Z:if (t_dt == horz)//当前为水平方向,修改为垂直方向{t_dt = vert;                         //旋转后第2个方块位置不变Tetrix_now[0].x = Tetrix_now[1].x - 1;   //旋转后第1个方块在旋转后第2个方块上面Tetrix_now[0].y = Tetrix_now[1].y;        //旋转后第1个方块在旋转后第2个方块上面Tetrix_now[2].x = Tetrix_now[1].x;        //旋转后第3个方块在原第2个方块左面Tetrix_now[2].y = Tetrix_now[1].y - 1;  //旋转后第3个方块在原第2个方块左面Tetrix_now[3].x = Tetrix_now[2].x + 1; //旋转后第4个方块在第3个方块下面Tetrix_now[3].y = Tetrix_now[2].y;       //旋转后第4个方块在第3个方块下面          }else if (t_dt == vert)//当前为垂直方向,修改为镜像水平方向{t_dt = horz;                           //旋转后第2个方块位置不变Tetrix_now[0].x = Tetrix_now[1].x;       //旋转后第1个方块在旋转后第2个方块左面Tetrix_now[0].y = Tetrix_now[1].y - 1;    //旋转后第1个方块在旋转后第2个方块左面Tetrix_now[2].x = Tetrix_now[1].x + 1;   //旋转后第3个方块在旋转后第2个方块下面Tetrix_now[2].y = Tetrix_now[1].y;        //旋转后第3个方块在旋转后第2个方块下面Tetrix_now[3].x = Tetrix_now[2].x;        //旋转后第4个方块在旋转后第3个方块右面Tetrix_now[3].y = Tetrix_now[2].y + 1;   //旋转后第4个方块在旋转后第3个方块右面}break;}
}
//翻转方块后如果方块越界则重建方块
void reconstructblock()
{int x, y, d;d = up;                           //越界的方向初始为上方越界,因为上方越界不需要处理while (d != normal)               //越界的坐标可能超过不止一个方块大小,所以要进行不停的测试,直到所有方块都在界内为止{d = normal;                  //每次测试循环初始化为正常,如果本次测试中全部四个方块都在界内则跳出外圈while循环for (int i = 0; i < 4; i++)    //对四个方块进行测试,根据测试结果赋值越界方向{x = Tetrix_now[i].x;y = Tetrix_now[i].y;if (x > 19)d = down;else if (y < 0)d = left;else if (y > 9)d = right;}switch (d)                  //根据越界方向调整方块坐标{case down:for (int i = 0; i < 4; i++)Tetrix_now[i].x -= 1;break;case left:for (int i = 0; i < 4; i++)Tetrix_now[i].y += 1;break;case right:for (int i = 0; i < 4; i++)Tetrix_now[i].y -= 1;break;case normal:break;}}
}
//显示方块
void showblock()
{for (int x = 0; x < 20; x++){for (int y = 0; y < 10; y++){setlinecolor(block[x][y].color);setfillcolor(block[x][y].color);rectangle(block[x][y].x, block[x][y].y, block[x][y].x + B_WIDTH, block[x][y].y + B_WIDTH);solidrectangle(block[x][y].x + 3, block[x][y].y + 3, block[x][y].x + B_WIDTH - 4, block[x][y].y + B_WIDTH - 4);}}
}
//消除行,测试每一行是否全部被方块占满,并记录占满的起始行、终止行行号,并消除相应的行
void rowcomplete()
{int i = 4;                    //动画效果循环计数int rb = -1;             //被占满的起始行行号int re = 0;                 //被占满的终止行行号int rn, l;                   //rn每一行被占据的方块个数for (int x = 0; x < 20; x++)//对全部20行逐行进行从上到下的测试{rn = 0;                   //每次一行前将被占据的方块数计数清零for (int y = 0; y < 10; y++){if (block[x][y].isoccupy)rn++;          //当前行当前方块被占据,计数加1}if (rn == 10)            //计数为10代表当前行被占满{re = x;                //终止行被赋予当前行号if (rb == -1)rb = x;         //第一次计数时,当前行被赋予起始行}          }if (rb >= 0)               //如果起始行号大于等于0,代表有行被方块占满{playsound(s_row);        //播放消除行效果音乐while (i--)              //按计数进行循环实现动画效果{for (int x = rb; x <= re; x++){for (int y = 0; y < 10; y++){setlinecolor(c_notused);setfillcolor(c_notused);rectangle(block[x][y].x, block[x][y].y, block[x][y].x + B_WIDTH, block[x][y].y + B_WIDTH);solidrectangle(block[x][y].x + 3, block[x][y].y + 3, block[x][y].x + B_WIDTH - 4, block[x][y].y + B_WIDTH - 4);}}Sleep(100);           for (int x = rb; x <= re; x++){for (int y = 0; y < 10; y++){setlinecolor(RGB(0, 255, 0));setfillcolor(RGB(0, 255, 0));rectangle(block[x][y].x, block[x][y].y, block[x][y].x + B_WIDTH, block[x][y].y + B_WIDTH);solidrectangle(block[x][y].x + 3, block[x][y].y + 3, block[x][y].x + B_WIDTH - 4, block[x][y].y + B_WIDTH - 4);}}}//清除行的动画效果结束,清空被占据的行for (int x = rb; x <= re; x++){for (int y = 0; y < 10; y++){setlinecolor(c_notused);setfillcolor(c_notused);rectangle(block[x][y].x, block[x][y].y, block[x][y].x + B_WIDTH, block[x][y].y + B_WIDTH);solidrectangle(block[x][y].x + 3, block[x][y].y + 3, block[x][y].x + B_WIDTH - 4, block[x][y].y + B_WIDTH - 4);block[x][y].color = c_notused;block[x][y].isoccupy = FALSE;}}l = re - rb + 1;       //'l'被占据的行数ln += l;             //ln已经被消除的行数,被消除的行数增加计数score += 100 * l;//消除掉被占据的行后,对被占据的其实行以上的行进行重新绘制整理,实现上面的行整体位移到消除后空余空间的效果for (int x = rb - 1; x >= 0; x--)         //从被占据的行上一行开始循环 {for (int y = 0; y < 10; y++)         //对每行的每一个方块进行整理{if (block[x][y].isoccupy)           //如果当前方块是被占据的方块{block[x][y].color = c_notused; //清除当前方块block[x][y].isoccupy = FALSE;block[x + l][y].color = c_used; //整体位移L距离block[x + l][y].isoccupy = TRUE;}                    }}showblock();}
}
//判断方块是否触顶
void touchtop()
{for (int i = 0; i < 10; i++)     //判断方块是否到达顶端,对第0行的每一个方块进行测试,如果有有一个方块被占据就是触顶if (block[0][i].isoccupy){istouch = 1;                //触顶跳出当前游戏循环isend = 1;                 //触顶游戏结束}if (isend)                         //触顶游戏结束{playsound(s_end);              //播放游戏结束音效for (int x = 19; x >= 0; x--)    //实现游戏结束动画效果{for (int y = 0; y < 10; y++){setlinecolor(c_used);setfillcolor(c_used);rectangle(block[x][y].x, block[x][y].y, block[x][y].x + B_WIDTH, block[x][y].y + B_WIDTH);solidrectangle(block[x][y].x + 3, block[x][y].y + 3, block[x][y].x + B_WIDTH - 4, block[x][y].y + B_WIDTH - 4);block[x][y].color = c_notused;block[x][y].isoccupy = FALSE;}Sleep(100);}}
}
//鼠标按到按钮后的效果
void changebutton(int cent_x, int cent_y, int r)
{int x, lim, i, t1, t2;DWORD* dst;DWORD temp;IMAGE img;getimage(&img, 0, 0, W_WIDTH, W_LENGTH);dst = GetImageBuffer(&img);i = 1;for (int y = cent_y - r; y < cent_y; y++, i++){x = cent_x - r;lim = cent_x + r;t1 = (y - 1) * W_WIDTH + x;t2 = (cent_y + r - i) * W_WIDTH + x;for (; x < lim; x++, t1++, t2++){temp = dst[t1];dst[t1] = dst[t2];dst[t2] = temp;}}putimage(0, 0, &img);
}
//播放音效
void playsound(int type)
{if (!ismusic){switch (type){case s_move:mciSendString(L"play mysong from 2500 to 3000", NULL, 0, NULL);break;case s_rotate:mciSendString(L"play mysong from 500 to 1000", NULL, 0, NULL);break;case s_space:mciSendString(L"play mysong from 1000 to 1500", NULL, 0, NULL);break;case s_end:mciSendString(L"play mysong from 8000 to 9000", NULL, 0, NULL);break;case s_start:mciSendString(L"play mysong from 3000 to 7000", NULL, 0, NULL);break;case s_row:mciSendString(L"play mysong from 0 to 500", NULL, 0, NULL);break;}}
}

c + easyx 实现放怀旧掌机界面风格俄罗斯方块相关推荐

  1. 《安富莱嵌入式周报》第287期:下一代Windows12界面,支持各种工业以太网协议参考,百款在线电子开发工具,seL4安全微内核,旋转拨号手机,PSP掌机逆向

    往期周报汇总地址:嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - P ...

  2. pico8 掌机_使用Pico-8构建自己的复古游戏

    pico8 掌机 An example of the kinds of pixel animations people make in Pico-8. 人们在Pico-8中制作的各种像素动画的示例. ...

  3. 高性价比掌机Retroid Pocket 3:搭载展锐芯片T310,采用PowerVR GPU

    Retroid Pocket 3游戏掌机厂商GoRetroid凭借其可靠的产品,极具竞争力的价格,在掌机游戏市场有着较高的影响力. 2022年9月,在Goretroid长达数月的开发和推广后,Retr ...

  4. 树莓派zero制作retropie掌机

    目录 总体设计 调试显示模块 调试游戏手柄模块 调试音频模块 整体组装 背景 游戏掌机对于很多人来说,都是童年时期美好的记忆,但是因为当时条件的限制,或多或少留有一些遗憾,以至于现在网络上还有很多销售 ...

  5. r4卡2020整合内核_R4卡使用方法!游戏介绍及常见问题!任天堂掌机通用

    哈喽大家好!我是掌机百科!今天介绍下R4卡的使用方法及教程,DS,NDS,NDSL,NDSLL,NDSXL,NDSI.NDSILL,NDSIXL,3DS,3DSLL,3DSXL,2DS,NEW3DS, ...

  6. 制作Retropie系统树莓派掌机(三)

    制作Retropie系统树莓派掌机(三) 前面介绍了如何制作外壳和按键.这一节再聊如何组装和接线. 1.组装 安装屏幕和主板 先把屏和主板放上去,原外壳上留好限位卡扣或孔.再把电池(2节18650)和 ...

  7. pico8 掌机_PICOCHAK的制作— PICO-8 Fantasy Console的演示

    pico8 掌机 Last October, I made the first demoscene production after an almost two-decades-long break  ...

  8. 基于labview的姿态测量系统上位机界面编写

    当时学习VB是为了写个上位机去控制LED亮灭,相信大家学习51,stm32都是从流水灯开始的,就像那句"hello world"一样经典.后来学习了LABVIEW,决定用它写个界面 ...

  9. STM32掌机教程6,电子琴

      本节原来是想讲一讲无源蜂鸣器发声的原理,用于添加BGM功能.为了讲原理,就写了一些通俗的代码,没想到越写越多,后来,干脆就形成了一个小小的项目吧--基于STM32与无源蜂鸣器的电子琴. 灯光效果 ...

最新文章

  1. kafka批量启动脚本
  2. c语言试卷浙江理工大学杀人案件追踪,浙江理工大学c语言期末考试模拟试卷6 .pdf...
  3. 梅森增益matlab求解,梅森公式互不接触回路及其增益
  4. 实验五——循环结构学习总结
  5. JavaScript eval() 函数,计算某个字符串,并执行其中的的 JavaScript 代码。
  6. java ssm框架登录代码,求一个SSM框架登录功能的源码,要求能运行成功
  7. 血泪教训!拖垮公司的技术团队常用的 7 个操作
  8. ipvs,ipvsadm的安装及使用
  9. 使用python+机器学习方法进行情感分析(详细步骤)
  10. typedef 与结构体struct
  11. java final关键字
  12. html css 窗口样式,简单的css样式网页
  13. 英雄联盟加载的时候特卡,排查记录:win10那些破玩意
  14. OSCHINA网页旋转-愚人节效果
  15. 计算机毕业设计Java校园疫情信息管理系统(源码+系统+mysql数据库+Lw文档)
  16. .Net使用DES解密发生“数据不正确”的错误
  17. CSS 选择器(超级详细,欢迎补充)
  18. RK3126 人体感应模块驱动
  19. 树莓派——安装OpenCV
  20. Proximal Policy Optimization (PPO) 算法理解:从策略梯度开始

热门文章

  1. 在WPS使用NoteExpress完成论文的思路
  2. 拖延症——想死掉的感觉
  3. 手机室内地磁定位软件_一种基于手机地磁和场景图像的室内定位方法与流程
  4. 什么是吉布斯采样(Gibbs Sampling)
  5. 【一笔画完】通关路径算法的Java代码实现V1.0
  6. Web性能测试用例设计实践
  7. 分享一份完整内容高端企业项目成本管理培训PPT模板
  8. [c++]int、float、char、string、CString、LPCSTR、LPCTSTR的相互转换
  9. 如何在雅特力 AT32F403A 上运行 RT-Thread
  10. Pytorch 中 CAM绘制热度图