--------写在前面--------
第一次做标题党,大家轻喷哈。这个游戏是博主在大一c语言实训时独立完成的,所有内容均为原创。小游戏耗时5天完成,除了常见的单人模式外,增加了作弊模式,双人模式,计时赛等玩法,真滴很好玩哦。虽然现在看起来很简陋,但对于当时的我来说实属不易,从页面设计到游戏背景音乐的选取再到关键算法的编写,每一步都凝汇了自己的努力,通宵鏖战的画面依然历历在目。现在分享出来,一方面是希望可以帮助到大家,另一方面也想纪念美好的大一时光。源码地址放在文末了,大家自取。
如果对您有帮助,可以点赞收藏支持一下,如果能点个关注那就更好了✅,谢谢大家啦!
-----------------------正文-----------------------
主页
模式选择
按1进入单人模式
按2进入双人模式
标准模式:除了左右下移动,增加了直接下落和暂停,增加了等级判定
地狱模式:在标准模式的前提下加快了下落速度,更刺激!
作弊模式:增加炫彩模式,变形,加速以及减速功能
双人标准赛:设计了一个判定胜负平局的算法
双人地狱存活赛:刺激到不行!!
双人计时赛:设置了计时器,重新设定了判定胜负平局的算法,在规定时间内分数高者胜!
结束动画
main.c
#include
#include
#include "game.h"
#include "mywindows.h"
#include
#include
#pragma comment (lib, "winmm.lib")
void chooseMode(){
if(kbhit()){
mciSendString("close g",NULL,0,NULL);
switch(getch()){
case 49:
case 97: gameInit(1);break;
case 50:
case 98: gameInit(2);break;
case 51:
case 99: gameInit(3);break;
}
}
}
void chooseMode2(){
if(kbhit()){
mciSendString("close g",NULL,0,NULL);
switch(getch()){
case 49:
case 97: gameInit1(1);break;
case 50:
case 98: gameInit1(2);break;
case 51:
case 99: gameInit1(3);break;
}
}
}
int main()
{
///初始化句柄,必须放在最开始
initHandle();
///开始动画
mciSendString("open 俄罗斯方块进入音乐.mp3 alias g",NULL,0,NULL);
mciSendString("play g repeat",NULL,0,NULL);
printAnimation();
if(kbhit()){
getch();
chooseWindow();
}
if(kbhit()){
switch(getch()){
case 49:
case 97: chooseWindow2();chooseMode();break;
case 50:
case 98: chooseWindow3();chooseMode2();break;
}
}
return 0;
}
mywindows.c
#include "mywindows.h" //尖括号常用于引入系统头文件,双引号常用于引入自己定义的头文件 默认检索顺序不同
HANDLE handle;
//函数定义
void initHandle(){
handle = GetStdHandle(STD_OUTPUT_HANDLE);
hideCursor();//游戏启动后隐藏光标位置
//
}
void setColor(int color){
SetConsoleTextAttribute(handle,color);
}
void setPos(int x,int y){
COORD coord = {x*2,y}; //字母abcd:一个字符,汉字:两个字符
SetConsoleCursorPosition(handle,coord); //设置句柄位置
}
void hideCursor(){
CONSOLE_CURSOR_INFO info; //系统的结构体变量名字都是大写
info.bVisible = FALSE; //设置光标是否可见
info.dwSize = 1; //设置光标宽度(1-100)
SetConsoleCursorInfo(handle,&info);//指针取地址符
}
game.c 关键代码
单人模式窗体打印
void printGradeLevel1(int num){
switch(num){
case 1:
grade1+=10; break;
case 2:
grade1+=30; break;
case 3:
grade1+=50; break;
case 4:
grade1+=80; break;
}
if(grade1 < 100){
level1 = 1;
}
else if(grade1 >= 100 && grade1 < 300){
level1 =2;
}
setColor(0x0c);
setPos(4,8);
printf("分数:%d",grade1);
setPos(4,9);
printf("等级:%d",level1);
}
void printGradeLevel2(int num){
switch(num){
case 1:
grade2+=10; break;
case 2:
grade2+=30; break;
case 3:
grade2+=50; break;
case 4:
grade2+=80; break;
}
if(grade2 < 100){
level2 = 1;
}
else if(grade2 >= 100 && grade2 < 300){
level2 =2;
}
setColor(0x09);
setPos(51,8);
printf("分数:%d",grade2);
setPos(51,9);
printf("等级:%d",level2);
}
双人模式窗体打印
void windowPrint2(int x,int y){
int i,j; //用来遍历二维数组
for(i=0;i<25;i++){
for(j=0;j<58;j++){
if(windowShape2[i][j] == 1){
setColor(0xc0);
setPos(x+j,y+i); //x是列,y是行
printf("%2s",""); // <--> printf(" ");
}
}
}
for(i=0;i<25;i++){
for(j=29;j<58;j++){
if(windowShape2[i][j] == 1){
setColor(0x90);
setPos(x+j,y+i); //x是列,y是行
printf("%2s",""); // <--> printf(" ");
}
}
}
}
//操作规则框架
void printInfo(){
setColor(0x0c);
setPos(2,2);
printf("N");
setPos(2,3);
printf("E");
setPos(2,4);
printf("X");
setPos(3,3);
printf("T");
setPos(3,14);
printf("红方操作规则");
setPos(3,15);
printf("------------");
setPos(2,16);
printf("按 a 或 A 左移");
setPos(2,17);
printf("按 d 或 D 右移");
setPos(2,18);
printf("按 s 或 S 下移");
setPos(2,19);
printf("按 w 或 W 变方向");
setPos(2,20);
printf("按 q 直接下落");
setColor(0x03);
setPos(49,2);
printf("N");
setPos(49,3);
printf("E");
setPos(49,4);
printf("X");
setPos(50,3);
printf("T");
setPos(50,14);
printf("蓝方操作规则");
setPos(50,15);
printf("------------");
setPos(49,16);
printf("按 ←键 左移");
setPos(49,17);
printf("按 →键 右移");
setPos(49,18);
printf("按 ↑键 下移");
setPos(49,19);
printf("按 ↓键 变方向");
setPos(49,20);
printf("按 回车 直接下落");
setPos(27,1);
printf("--战况--");
setPos(27,3);
printf("--------");
}
方块打印
void printBlock1(int x,int y,int shape,int status,int color){
int i,j;
for(i = 0;i<4;i++){
for(j = 0;j<4;j++){
if(block[shape][status][i][j] == 1){
setColor(color);
setPos(x+j,y+i);
printf("■");
}
}
}
}
void printBlock2(int x,int y,int shape,int status,int color){
int i,j;
for(i = 0;i<4;i++){
for(j = 0;j<4;j++){
if(block[shape][status][i][j] == 1){
setColor(color);
setPos(x+j,y+i);
printf("■");
}
}
}
}
碰撞检测
//碰撞检测基于下一个位置的检测,数组与界面坐标的对应
int crash1(int x,int y,int shape,int status){
int i,j;
for(i = 0;i<4;i++){
for(j = 0;j<4;j++){
if(block[shape][status][i][j] == 1){
if(windowShape2[y+i][x+j] == 1){
///发生碰撞
if(cur_block1.x == 17 && cur_block1.y == 1){
///游戏结束
return -2;
}
///方块落到游戏池底部,发生碰撞
return -1;
}
}
}
}
return 0;
}
胜负判断
void bottomBlock1(){
while(crash1(cur_block1.x,cur_block1.y+1,cur_block1.shape,cur_block1.status) != -1&&crash1(cur_block1.x,cur_block1.y+1,cur_block1.shape,cur_block1.status) != -2){
cur_block1.y += 1;
}
if(crash1(cur_block1.x,cur_block1.y+1,cur_block1.shape,cur_block1.status) == -1){
///发生碰撞:方块落到游戏池底部
///产生新的方块:下一个方块值 -> 当前正在下落的方块,重新产生下一个方块
save1();
removeLine1();
// lineClear();
updateGame1();
copyBlock1();
}
else if(crash1(cur_block1.x,cur_block1.y+1,cur_block1.shape,cur_block1.status) == -2){
///游戏结束
}
}
按键检测以及双人胜负判断算法
int gameInit1(int mode){
if(mode == 1){
mciSendString("open 双人标准.mp3 alias d",NULL,0,NULL);
mciSendString("play d repeat",NULL,0,NULL);
}
if(mode == 2){
mciSendString("open 双人地狱.mp3 alias e",NULL,0,NULL);
mciSendString("play e repeat",NULL,0,NULL);
}
if(mode == 3){
mciSendString("open 双人限时.mp3 alias f",NULL,0,NULL);
mciSendString("play f repeat",NULL,0,NULL);
}
int counter = 180;
float speed ;
if(mode == 1){
speed = 0.45;
}
else if(mode == 2){
speed = 0.25;
}
else if(mode == 3){
speed = 1;
}
///初始化句柄,必须放在最开始
initHandle();
///打开音乐文件
windowPrint2(0,0);
printInfo();
printGradeLevel1(0);
printGradeLevel2(0);
///游戏开始时间
clock_t startTime = clock();
///定时器
clock_t time1,time2;
time1 = clock();
startBlock1();
startBlock2();
nextBlock1();
nextBlock2();
while(1){
//按键驱动
///检测是否有按键按下
if(kbhit()){
switch(getch()){
case 'w':
case 'W':
changeStatusBlock1();break;
case 'a':
case 'A':
leftBlock1();break;
case 'd':
case 'D':
rightBlock1();break;
case 's':
case 'S':
downBlock1();break;
case 72:
changeStatusBlock2();break;
case 75:
leftBlock2();break;
case 77:
rightBlock2();break;
case 80:
downBlock2();break;
case 32:
bottomBlock1();break;
case 13:
bottomBlock2();break;
}
}
time2 = clock();
if(mode == 1|| mode == 2){
///每0.45秒下落一次
if((float)(time2-time1)/CLOCKS_PER_SEC > speed){
setPos(27,2);
if(downBlock1() == -2) {
if(grade2>grade1){
printf("蓝 方 胜!");break;
}
else if(downBlock2() == -2){
if(grade1==grade2){
printf("平 局");break;
}
else{
printf("红 方 胜!");break;
}
}
}
setPos(27,3);
if(downBlock2() == -2) {
if(grade1>grade2){
printf("红 方 胜!");break;
}
else if(downBlock1() == -2){
if(grade1==grade2){
printf("平 局");break;
}
else{
printf("蓝 方 胜!");break;
}
}
}
time1 = time2;
}
}
else if(mode == 3){
setPos(2,10);
printf("剩余时间:%3dS",counter);
setPos(50,10);
printf("剩余时间:%3dS",counter);
if((float)(time2-time1)/CLOCKS_PER_SEC > speed){
counter--;
setPos(27,2);
if(counter == 0){
if(grade2>grade1){
printf("蓝 方 胜!");break;
}
if(grade1==grade2){
printf("平 局");break;
}
else{
printf("红 方 胜!");break;
}
}
if(downBlock1() == -2) {
if(grade2>grade1){
printf("蓝 方 胜!");break;
}
else if(downBlock2() == -2){
if(grade1==grade2){
printf("平 局");break;
}
else{
printf("红 方 胜!");break;
}
}
}
setPos(27,3);
if(downBlock2() == -2) {
if(grade1>grade2){
printf("红 方 胜!");break;
}
else if(downBlock1() == -2){
if(grade1==grade2){
printf("平 局");break;
}
else{
printf("蓝 方 胜!");break;
}
}
}
time1 = time2;
}
}
}
if(mode == 1){
mciSendString("close d",NULL,0,NULL);
}
if(mode == 2){
mciSendString("close e",NULL,0,NULL);
}
if(mode == 3){
mciSendString("close f",NULL,0,NULL);
}
Sleep(1150);
printOver();
printFinish(mode);
}
初始界面动画打印
void printStart(int x,int y){
//随机产生颜色
int color = rand()%0x10;
//处理黑色的情况
if(color == 0x00)
{
color = 0x0f;
}
setColor(color);
setPos(x,y);
printf("■■■■■ ■■■■■ ■■■■■ ■■■■ ■■■ ■■■■");
setPos(x,y+1);
printf(" ■ ■ ■ ■ ■ ■ ■");
setPos(x,y+2);
printf(" ■ ■■■■ ■ ■■■ ■ ■■■");
setPos(x,y+3);
printf(" ■ ■ ■ ■ ■ ■ ■");
setPos(x,y+4);
printf(" ■ ■■■■■ ■ ■ ■ ■■■ ■■■");
setPos(25,14);
printf("按任意键开始游戏!");
setPos(50,30);
printf("author 赵敬轩");
}
game.c 完整代码
gitee:https://gitee.com/i-dream-code/Tetris/
github:https://github.com/zhaojingxuan123/StrongestTetris
百度云盘:链接:https://pan.baidu.com/s/1dJeANXcNqChrXf7zuRQRWw 提取码:geee
博主使用的是codeblocks,一款很好用的编译器
网盘链接:链接:https://pan.baidu.com/s/1JkgUL8852lwx87j1NK2nsw 提取码:qqfm
安装好codeblocks后,将 20200705.cbp 直接拖到codeblocks的左侧面板即可
上方build=>run即可运行
现在回过头来开,这个项目存在着很多问题,比如代码臃肿,复用性差,没有利用到设计模式,重复造了很多轮子。除此之外,有一些功能设定也不够合理,比如没有返回上一级菜单,切换模式只能重新编译游戏才可以进入。游戏是一次性游戏,如果增加计分板来存储玩家的最高分也可以增加游戏体验。日后有机会再完善一下。
“在繁华中自律,在落魄中自愈”, 恍惚间,回首岁月静好。