--------写在前面--------
第一次做标题党,大家轻喷哈。这个游戏是博主在大一c语言实训时独立完成的,所有内容均为原创。小游戏耗时5天完成,除了常见的单人模式外,增加了作弊模式,双人模式,计时赛等玩法,真滴很好玩哦。虽然现在看起来很简陋,但对于当时的我来说实属不易,从页面设计到游戏背景音乐的选取再到关键算法的编写,每一步都凝汇了自己的努力,通宵鏖战的画面依然历历在目。现在分享出来,一方面是希望可以帮助到大家,另一方面也想纪念美好的大一时光。源码地址放在文末了,大家自取。
【项目攻坚|俄罗斯方块【六种模式】【c语言】【史上最强】】如果对您有帮助,可以点赞收藏支持一下,如果能点个关注那就更好了?,谢谢大家啦!
-----------------------正文-----------------------
最终效果 主页
文章图片
模式选择
文章图片
按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即可运行
文章图片
总结
现在回过头来开,这个项目存在着很多问题,比如代码臃肿,复用性差,没有利用到设计模式,重复造了很多轮子。除此之外,有一些功能设定也不够合理,比如没有返回上一级菜单,切换模式只能重新编译游戏才可以进入。游戏是一次性游戏,如果增加计分板来存储玩家的最高分也可以增加游戏体验。日后有机会再完善一下。
“在繁华中自律,在落魄中自愈”, 恍惚间,回首岁月静好。
推荐阅读
- 笔记|数据结构->二叉树的介绍
- 笔记|堆排序详解+TOP-K问题
- 笔记|堆的实现+堆排序
- 笔记|动态内存管理分析理解
- XingleiGao的日常|Atlas 200 DK开发者套件基于CANN的垃圾分类实验踩坑指南
- 学习交流|C语言关键字auto,case,default,switch应该如何使用
- C编程语言|《c陷阱与缺陷》1-3章笔记
- 学习交流|C语言sizeof与strlen的区别与使用
- C语言|C语言打印字符,转义字符,注释(小白版)