大一实训—-C语言编写俄罗斯方块游戏

一、代码

main.c

#include <stdio.h>
#include <stdlib.h>
#include "game.h"

int main()
{
   gameInit();
    return 0;
}

mywindows.h

#ifndef MYWINDOWS_H_INCLUDED
#define MYWINDOWS_H_INCLUDED

/*系统调用模块*/
#include <windows.h>
//函数声明
//初始化句柄
void initHandle();

//设置颜色
void setColor(int color);

//设置光标位置
void setPos(int x,int y);

//隐藏光标
void hideCursor();

#endif // MYWINDOWS_H_INCLUDED

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};
    SetConsoleCursorPosition(handle,coord);

}

void hideCursor()
{
    CONSOLE_CURSOR_INFO info;
    info.bVisible = FALSE;          //设置光标是否可见
    info.dwSize = 1;                //光标的宽度(1~100)
    SetConsoleCursorInfo(handle,&info);
}

game.h

#ifndef GAME_H_INCLUDED
#define GAME_H_INCLUDED

/*游戏逻辑模块*/

#include <stdio.h>
#include <time.h>

typedef struct{
    int x;
    int y;
    int shape;
    int status;
    int color;
}BLOCK;

//绘制游戏池边框
void windowPrint(int x, int y);

//游戏初始化
void gameInit();

//打印操作说明
void printInfo();

//打印分数等级
void printGradeLevel(int num);

//游戏计时
void gameTime(clock_t star_time);

//打印方块
void printBlock(int x ,int y ,int shape ,int status ,int color);

//删除方块
void deleteBlock(int x, int y, int shape ,int status);

//拷贝方块
void copyBlock();

//产生游戏第一个方块
void startBlock();

//产生下一个方块
void nextBlock();

//方块下移
//返回值:标志方块是否到游戏池底部
int downBlock();

//方块左移
int leftBlock();

//方块右移
int rightBlock();

//方块变形
void changeBlock();

//碰撞检测
int crash(int x, int y, int shape, int status);

//保存方块
void save();

//刷新游戏池
void undateGame();

//暂停
void pause();

//落地
void bottomBlock();

//消行检测
void lineClear();

//消行下移
void lineDown(int line);

//游戏结束动画
void printOver();

//重新开始提示
void printFinish();

//重新开始游戏
void againGame();

//打印开始图案
void printStart(int x, int y);

//清除开始动画
void deleteStart(int x, int y);

//动画效果->定时(边界控制)
void printAnimation();

//读取最高记录
void readFile();

//保存最高记录
void writeFile();


#endif // GAME_H_INCLUDED

game.c

#include "game.h"
#include "data.h"
#include "mywindows.h"
#include <conio.h>
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib")
int grade = 0;         //分数
int level = 1;         //等级
int max=0;             //最高记录
BLOCK cur_block;       //当前方块
BLOCK next_block;      //下一个方块
void windowPrint(int x, int y)
{
int i,j;
for(i = 0; i < 25; i++){
for( j = 0; j < 37; j++){
if(windowShape[i][j] == 1){
setPos(x+j,y+i);//打印位置,坐标位置和数组内容相反
setColor(0x0f);
printf("■");
}else if(windowShape[i][j] == 2){
setPos(x+j,y+i);
setColor(0x04);
printf("■");
}else if(windowShape[i][j] == 3){
setPos(x+j,y+i);
setColor(0x0e);
printf("■");
}else if(windowShape[i][j] == 4){
setPos(x+j,y+i);
setColor(0x06);
printf("■");
}
}
}
}
void printInfo()
{
setColor(0x0f);
setPos(31,8);
printf("操作规则:");
setPos(33,9);
printf("变形:↑");
setPos(33,11);
printf("下移:↓");
setPos(33,13);
printf("左移:←");
setPos(33,15);
printf("右移:→");
setPos(33,17);
printf("直接下落:Enter");
setPos(33,19);
printf("暂停:space");
setPos(33,21);
printf("退出:ESC");
setPos(33,23);
printf("音乐:开O/关C");
}
void printGradeLevel(int num)
{
switch(num){
case 0:break;
case 1:grade += 10;break;
case 2:grade += 20;break;
case 3:grade += 30;break;
case 4:grade += 40;break;
}
setColor(0x0f);
if(grade < 20){
level = 1;
setPos(10,9);
printf("速度:0.45s");
}
else if(grade <= 40 && grade >= 20){
level = 2;
setPos(10,9);
printf("速度:0.30s");
}
else if(grade <= 60 && grade > 40){
level = 3;
setPos(10,9);
printf("速度:0.20s");
}
else if(grade <= 100 && grade > 60){
level = 4;
setPos(10,9);
printf("速度:0.15s");
}
setColor(0x0f);
setPos(10,6);
printf("分数:%d", grade);
setPos(10,7);
printf("等级:%d", level);
setPos(10,8);
printf("最高分:%d", max);
setPos(11,16);
setColor(0x04);
printf("■");
setPos(10,10);
setColor(0x0e);
printf("■■■");
setPos(9,11);
setColor(0x0e);
printf("■■■■■");
setPos(9,12);
setColor(0x0e);
printf("■■■■■");
setColor(0x0e);
setPos(9,13);
printf("■■■■■");
setPos(8,14);
setColor(0x0e);
printf("■■■■■■■");
}
void gameTime(clock_t star_time)
{
setColor(0x06);
setPos(42,12);
printf("本次游戏已运行 %ld s",(clock()-star_time)/CLOCKS_PER_SEC);
}
void printBlock(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 deleteBlock(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){
setPos(x+j, y+i);
printf(" ");
}
}
}
}
void startBlock()
{
//第一个方块:形状/形态/颜色->随机 位置(x,y)固定
//初始化cur_block
//设置随机数种子(时间永远不一样)
srand((unsigned)time(NULL));
cur_block.x = 22;
cur_block.y = 1;
cur_block.shape = rand()%7;
cur_block.status = rand()%4;
cur_block.color = rand()%0x10;   //0x00~0x0f
if(cur_block.color == 0x00){
cur_block.color = 0x01;
}
else if(cur_block.color == 0x0f){
cur_block.color = 0x06;
}
else if(cur_block.color == 0x07){
cur_block.color = 0x0d;
}
else if(cur_block.color == 0x08){
cur_block.color = 0x09;
}
printBlock(cur_block.x, cur_block.y, cur_block.shape, cur_block.status, cur_block.color);
}
void nextBlock()
{
//形状、形态、颜色->随机,位置->固定
//初始化next_block值
deleteBlock(next_block.x, next_block.y, next_block.shape, next_block.status);
next_block.x = 34;
next_block.y = 2;
next_block.shape = rand()%7;
next_block.status = rand()%4;
next_block.color = rand()%0x10;   //0x00~0x0f
if(next_block.color == 0x00){
next_block.color = 0x01;
}
else if(next_block.color == 0x0f){
next_block.color = 0x06;
}
else if(next_block.color == 0x07){
next_block.color = 0x0d;
}
else if(next_block.color == 0x08){
next_block.color = 0x09;
}
printBlock(next_block.x, next_block.y, next_block.shape, next_block.status, next_block.color);
}
void copyBlock()
{
//当前方块=下一个方块,并且产生新的下一个方块
cur_block = next_block;
cur_block.x= 22;
cur_block.y = 1;
printBlock(cur_block.x, cur_block.y, cur_block.shape, cur_block.status, cur_block.color);
nextBlock();
}
int downBlock()
{
//原理:删除正在显示的方块,纵坐标加1,重新打印
if(crash(cur_block.x, cur_block.y+1, cur_block.shape,cur_block.status) == -1){
//发生碰撞,方块落地游戏池底部
//产生新的方块:下一个方块值->当前正在下落的方块,重新产生下一个方块
//保存方块->检测消行—>打印游戏池->产生新方块
save();
lineClear();
updateGame();
copyBlock();
return -1;
}
else if(crash(cur_block.x, cur_block.y+1, cur_block.shape, cur_block.status))
{
//游戏结束
return -2;
}
else{
deleteBlock(cur_block.x, cur_block.y, cur_block.shape, cur_block.status);
cur_block.y += 1;
printBlock(cur_block.x, cur_block.y, cur_block.shape, cur_block.status, cur_block.color);
return 0;
}
}
int leftBlock(){
if(crash(cur_block.x-1, cur_block.y, cur_block.shape,cur_block.status) == -1){
//如果发生碰撞,什么都不做,直接返回
return;
}
deleteBlock(cur_block.x, cur_block.y, cur_block.shape, cur_block.status);
cur_block.x -= 1;
printBlock(cur_block.x, cur_block.y, cur_block.shape, cur_block.status, cur_block.color);
}
int rightBlock()
{
if(crash(cur_block.x+1, cur_block.y, cur_block.shape,cur_block.status) == -1){
return;
}
deleteBlock(cur_block.x, cur_block.y, cur_block.shape, cur_block.status);
cur_block.x += 1;
printBlock(cur_block.x, cur_block.y, cur_block.shape, cur_block.status, cur_block.color);
}
void changeBlock()
{
if(crash(cur_block.x, cur_block.y, cur_block.shape,(cur_block.status+1)%4) == -1){
return;
}
//通过数组下标加一,获得下一个形态,下标[0~3]
deleteBlock(cur_block.x, cur_block.y, cur_block.shape, cur_block.status);
cur_block.status = (cur_block.status+1) % 4;
printBlock(cur_block.x, cur_block.y, cur_block.shape, cur_block.status, cur_block.color);
}
int crash(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(windowShape[i+y][j+x-15] == 1){
//发生碰撞
if(cur_block.x == 22 && cur_block.y == 1)
{
//方块产生就发生碰撞,游戏结束
return -2;
}
//方块落到游戏池底部发生碰撞
return -1;
}
}
}
}
}
void save()
{
//根据方块在界面上的坐标,把坐标对应到游戏池下标的对应位置
//方块为1保存到windowShape数组中
int i,j;
for(i = 0; i < 4; i++){
for(j = 0; j < 4; j++){
if(block[cur_block.shape][cur_block.status][i][j] == 1){
windowShape[i+cur_block.y][j+cur_block.x-15] = 1;
}
}
}
}
void updateGame()
{
int i,j;
for(i = 23; i > 0; i--){
for(j = 1; j < 15; j++){
if(windowShape[i][j] == 1){
setColor(0x0e);
setPos(15+j,i);
printf("■");
}else{
setColor(0x00);
setPos(15+j,i);
printf(" ");
}
}
}
}
void pause()
{
while(1){
if(getch() == 32){
break;
}
}
}
void bottomBlock()
{
while(1){
if(crash(cur_block.x, cur_block.y+1, cur_block.shape, cur_block.status) == -1){
save();
lineClear();
updateGame();
copyBlock();
return;
}else if(crash(cur_block.x, cur_block.y+1, cur_block.shape, cur_block.status) == -2){
//游戏结束
return;
}else{
++ cur_block.y;
}
}
}
void lineClear()
{
//遍历windowShape,判断是否有满行,这行所有值加起来为14
int i,j;
int number = 0;
for(i = 23; i > 0; i--){
int total = 0;
for(j = 1; j < 15; j++){
total += windowShape[i][j];
}
if(total == 14){
lineDown(i);
i += 1;
//统计一次消几行
number += 1;
}
}
printGradeLevel(number);
}
void lineDown(int line)
{
//从第line行开始,i = i - 1
int i,j;
for(i = line; i > 1; i --){    //i>1,保证方块不被下移
for(j = 1; j < 15; j ++){
windowShape[i][j] = windowShape[i-1][j];
}
}
}
void printOver()
{
int i,j;
for(i = 23; i > 0; i --){
for(j = 1; j < 15; j++){
setColor(0x00);
setPos(j+15,i);
printf("□");
Sleep(5);
}
}
}
void printFinish()
{
mciSendString("open E:\\project\\tetris\\bin\\Debug\\res\\gameover.wav alias o",NULL, 0, NULL);
mciSendString(TEXT("play o"), NULL, 0, NULL);
setColor(0x0f);
setPos(21,21);
printf("游戏结束!");
setPos(17,23);
printf("按Y重新开始");
setPos(23,23);
printf("按N结束游戏");
setPos(17,10);
setColor(0x0e);
if(grade > max)
{
printf("纪录打破,目前最高纪录:%d",grade);
writeFile();
}else if(grade == max){
printf("纪录持平,请突破你的极限!");
}else{
printf("继续努力,与最高记录差:%d",max-grade);
}
switch(getch())
{
case 'y':
case 'Y':
againGame();break;
case 'n':
case 'N':
system("cls");
exit(0);
break;
default:
printFinish();break;
}
}
void againGame()
{
//重置分数、等级、游戏池数据->开始游戏
int level = 1;
int grade = 0;
//重置游戏池数据
int i,j;
for(i = 1; i < 24; i ++){
for(j = 1; j < 15; j ++){
windowShape[i][j] = 0;
}
}
setColor(0x00);
system("cls");        //清屏
gameInit();
}
void printStart(int x, int y)
{
int color = rand()%0x10;
if(color == 0x00){
color = 0x01;
}
else if(color == 0x07){
color = 0x0d;
}
else if(color == 0x08){
color = 0x09;
}
//绘制图案
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(x,y+8);
printf("~~~~");
setPos(x+2,y+6);
printf("┌═════════┐...┌════════┐...┌═════════┐ ╭═════╮\n");
setPos(x+2,y+7);
printf("║ ║ ═ ║ ║ ═ ║ ║═ ║   ║\n");
setPos(x+2,y+8);
printf("└⊙═⊙═⊙═⊙ ⊙═⊙═⊙═⊙ ~ └⊙═⊙═⊙═⊙~~╰⊙═⊙╯\n");
setPos(28,15);
setColor(0x0f);
printf("按R键开始游戏!");
}
void deleteStart(int x, int y)
{
int i,j;
for(i = y- 5; i <=  y+8; i++){
for(j = x-5; j <= x+36; j++){
setPos(j,i);
printf(" ");
}
}
}
void printAnimation()
{
clock_t time1, time2;
time1 = clock();
int x = 5;
printStart(x,5);
while(1)
{
time2 = clock();
if(time2 - time1 > 300){    //300ms
time1 = time2;
deleteStart(x,5);
printStart(++x,5);
if(25 == x){
deleteStart(x,5);
x = 5;
}
}
//按任意键退出:检测是否有按键输入,若有则返回一个非0值,否则返回0
if(kbhit()){
break;
}
}
system("cls");
}
void readFile()    
{
FILE *fp;
fp = fopen("俄罗斯方块记录.txt","r+");
if(fp == NULL)
{
fp = fopen("俄罗斯方块记录.txt","w+");
fwrite(&max,sizeof(int),1,fp);
}
fseek(fp,0,0);
fread(&max,sizeof(int),1,fp);
fclose(fp);
}
void writeFile()   
{
FILE *fp;
fp = fopen("俄罗斯方块记录.txt","r+");
fwrite(&grade,sizeof(int),1,fp);
fclose(fp);
}
void gameInit()
{
//初始化句柄必须放在最前面
initHandle();
//打开音乐文件
mciSendString("open E:\\project\\tetris\\bin\\Debug\\res\\俄罗斯方块游戏背景音乐.mp3 alias a",NULL, 0, NULL);
//播放音乐
mciSendString(TEXT("play a"), NULL, 0, NULL);
//开始动画
printAnimation();
//读写最高值
readFile();
windowPrint(15,0);
printInfo();
printGradeLevel(0);
//游戏开始时间
clock_t startTime = clock();
//定时器
clock_t time1,time2;
time1 = clock();
startBlock();
nextBlock();
while(1){
//检测是否有按键按下
if(kbhit()){
switch(getch()){
//变形
case 72:
PlaySound("E:\\project\\tetris\\bin\\Debug\\res\\change.wav", NULL, SND_FILENAME | SND_ASYNC);
changeBlock();break;
//左移
case 75:
PlaySound("E:\\project\\tetris\\bin\\Debug\\res\\move.wav", NULL, SND_FILENAME | SND_ASYNC);
leftBlock();break;
//右移
case 77:
PlaySound("E:\\project\\tetris\\bin\\Debug\\res\\move.wav", NULL, SND_FILENAME | SND_ASYNC);
rightBlock();break;
//下移
case 80:
PlaySound("E:\\project\\tetris\\bin\\Debug\\res\\move.wav", NULL, SND_FILENAME | SND_ASYNC);
downBlock();break;
//暂停
case 32:
PlaySound("E:\\project\\tetris\\bin\\Debug\\res\\matchend.wav", NULL, SND_FILENAME | SND_ASYNC);
pause();break;
//落地
case 13:
PlaySound("E:\\project\\tetris\\bin\\Debug\\res\\garbage.wav", NULL, SND_FILENAME | SND_ASYNC);
bottomBlock();break;
case 27:
system("cls");
setColor(0x0f);
setPos(10,10);
printf("---游戏结束!---");
setPos(9,11);
printf("---按任意键退出!---");
getch();
exit(0);
break;
case 'c':
case 'C':
//关闭音乐
mciSendString(TEXT("stop a"), NULL, 0, NULL);
break;
case 'o':
case 'O':
//播放音乐
mciSendString(TEXT("play a"), NULL, 0, NULL);
}
}
time2 = clock();
//每0.45s下落一次
if(level == 1){
if(time2 - time1 > 0.45 * CLOCKS_PER_SEC){
//游戏计时
gameTime(startTime);
if(downBlock() == -2){
break;
}
time1 = time2;
}
}else if(level == 2){
if(time2 - time1 > 0.30 * CLOCKS_PER_SEC){
//游戏计时
gameTime(startTime);
if(downBlock() == -2){
break;
}
time1 = time2;
}
}else if(level == 3){
if(time2 - time1 > 0.20 * CLOCKS_PER_SEC){
//游戏计时
gameTime(startTime);
if(downBlock() == -2){
break;
}
time1 = time2;
}
}else if(level == 4){
if(time2 - time1 > 0.15 * CLOCKS_PER_SEC){
//游戏计时
gameTime(startTime);
if(downBlock() == -2){
break;
}
time1 = time2;
}
}
}
printOver();
printFinish();
}

data.h

#ifndef DATA_H_INCLUDED
#define DATA_H_INCLUDED
/*数据模块*/
//界面数组
int windowShape[25][37] =
{
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,2,2,2,2,2,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,2,2,1,0,2,2,0,0,0,4,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,2,2,1,1,2,2,0,0,4,3,4},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,0,0,4,3,4},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,2,2,2,0,0,0,2,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,2,2,2,0,0,2,2,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,2,2,2,2,2,2,2,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,2,2,2,2,2,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,2,2,2,2,0,0,0,0},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,2,0,2,0,0,0,0,0,0}
};//边框为1,游戏池长度为14
//第一维:七种类型的方块
//第二维:每种方块有4种形态
//第三维,第四维:保存方块数据{}
int block[7][4][4][4] =
{
{//Z
{{1,1,0,0},{0,1,1,0},{0,0,0,0},{0,0,0,0}},
{{0,1,0,0},{1,1,0,0},{1,0,0,0},{0,0,0,0}},
{{1,1,0,0},{0,1,1,0},{0,0,0,0},{0,0,0,0}},
{{0,1,0,0},{1,1,0,0},{1,0,0,0},{0,0,0,0}}
},
{//S
{{0,1,1,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,1,0,0},{0,1,0,0},{0,0,0,0}},
{{0,1,1,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,1,0,0},{0,1,0,0},{0,0,0,0}}
},
{//L
{{1,0,0,0},{1,0,0,0},{1,1,0,0},{0,0,0,0}},
{{1,1,1,0},{1,0,0,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{0,1,0,0},{0,1,0,0},{0,0,0,0}},
{{0,0,1,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}}
},
{//J
{{0,1,0,0},{0,1,0,0},{1,1,0,0},{0,0,0,0}},
{{1,0,0,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{1,0,0,0},{1,0,0,0},{0,0,0,0}},
{{1,1,1,0},{0,0,1,0},{0,0,0,0},{0,0,0,0}}
},
{//I
{{1,1,1,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,0,0,0},{1,0,0,0},{1,0,0,0}},
{{1,1,1,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,0,0,0},{1,0,0,0},{1,0,0,0}}
},
{//T
{{1,1,1,0},{0,1,0,0},{0,0,0,0},{0,0,0,0}},
{{0,1,0,0},{1,1,0,0},{0,1,0,0},{0,0,0,0}},
{{0,1,0,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,1,0,0},{1,0,0,0},{0,0,0,0}}
},
{//田
{{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}}
}
};
#endif // DATA_H_INCLUDED

二、运行效果图:

游戏开始界面动画,以左右移动的火车和“TETRIS”为主,颜色为随机变化。“按R键开始游戏”–>其实设置的是任意键,但在后面游戏中有一些按键被利用,为了保证游戏体验的正常进行,则写的是R键进入游戏

游戏过程中,按ESC退出游戏的效果图

游戏版面
游戏池和右边的小恐龙是由一个二维数组完成,详细代码位于data.h中。左边的铃铛是直接打印的。

这是游戏结束的效果图,游戏结束自带动画效果,当方块触碰到游戏池顶部时,方块自上而下消失并打印游戏结束和分数比较等字样。

三、实训笔记

1.开发环境的安装:

安装文档,安装流程,新建工程以及无法编译解决方法。

2.程序开发流程:

可行性研究:项目是否可行
需求分析:目标系统必须完整提现用户需求
总体设计:系统的体系结构,是定义程序由哪些模块组成以及模块之间的关系
详细设计:详细设计每个模块确定每个实现模块功能所需要的算法和数据结构
编码/单元测试:代码编写和测试.
综合测试:通过各种测试手段使软件达到用户需求.
运行维护:使软件持款的满足用户需求.

3.windows.h

句柄:标识windows资源(菜单图标,窗口)或设备对象
STD_INPUT_HANDLE(标准输入设备句柄):键盘\鼠标
STD_OUTPUT_HANDLE(标准输出设备句柄):屏幕
STD_ERROR_HANDLE(标准错误句柄):屏幕
设置控制台窗口标题 : SetConsoleTitle
获取句柄:GetStdHandle(handle,nstdhandle)
设文字颜色:SetConsoleTextAttribute(handle,color)
FOREGROUND_BLUE 蓝色 FOREGROUND_GREEN 绿色
FOREGROUND_RED 红色 BACKGROUND_INTENSITY 背景色加强
设置输出字体高亮并添加红色背景:
SetConsoleTextAttribute(handle,\BACKGROUND_INTENSITY|BACKGROUND_RED);
设光标位置:COORD pos->SetConsoleCursorPosition(handle,pos)
定时器:clock()
CLOCKS_PER_SEC:1000

4.需求分析

必要需求:
游戏开始:开始动画,游戏说明,开始按钮,游戏设置(分数/难度),游戏模式
游戏界面:游戏池边界,计时,图案,得分情况,方块数目,提示下一个方块,正在下落的方块,最高分记录
方块:颜色,形状,形态,位置信息
方块运动:定时下落,用户控制(方块下移,左移,右移,变形,落地,暂停)
消行:消行得分
重新开始
退出游戏
次级需求:
登录,分辨率,音乐,声音,存档,排行榜,双人对战,成绩分享,成就系统,充值

5.总体设计

系统调用模块:封装windows.h里常用函数
数据模块:程序里共用数据
游戏逻辑模块

6.函数:

返回值:int,void,double
函数名:字母\数字\下划线,只能以字母和下划线开头
命名:小驼峰:第一个单词首字母小写,后面单词首字母大写(函数/变量)
大驼峰:每个单词首字母都大写(类)
形参:(),(int)

7.结构体

是一种集合,可以包含多个变量或者数组(成员)
struct 结构体名{
结构体包含的变量或者数组;
};
(1).先定义结构体类型,在定义结构体变量
struct stu{
char *name;
int age;
float score;
};
struct stu stu1, stu2; //stu1,stu2是结构体变量
(2).定义结构体类型的同时定义结构体变量
struct stu{
char *name;
int age;
float score;
}stu1,stu2;
struct stu stu3,stu4;
(3).直接定义结构体变量
struct{
char *name;
int age;
float score;
}stu1,stu2;
(没有结构体名称,后面无法定义结构体变量)
(4).取别名
typedef struct stu{
char *name;
int age;
float score;
}STU;
STU stu1,stu2;

8.getch()

是编程中所用的函数,这个函数是一个不回显函数,当用户按下某个字符时,函数自动读取,无需按回车,是一个计算机函数,在windows平台下从控制台无回显地取一个字符。有的C语言命令行程序会用到此函数做游戏。
getch():
所在头文件:conio.h
函数用途:从控制台读取一个字符,但不显示在屏幕上
函数原型:int getch(void)
返回值:读取的字符
例如:
char ch;或int ch;
getch();或ch=getch();
用getch();会等待你按下任意键,再继续执行下面的语句;
用ch=getch();会等待你按下任意键之后,把该键字符所对应的ASCII码赋给ch,再执行下面的语句。

CodeBlocks播放音乐

1.playSound

PlaySound函数功能比较灵活,可以播放系统声音,也可以播放指定音频文件。音频可以是作为资源插在程序中、可以存放在内存中、还可以是本地的音频文件。但是,有一一个缺点就是,使用PlaySound不能同时播放两个或者多个声音,因为PlaySound是独占播放音频文件的。如果使用PlaySound播放新的声音,那么之前使用它播放的声音会立即停止,从而播放新声音。要实现同时播放多个声音,需要使用mciSendString来实现了。
PlaySound函数的声明为:
BOOL PlaySound(LPCSTR pszSound, HMODULE hmod,DWORD fdwSound);

(1).参数pszSound是指定了要播放声音的字符串,该参数可以是WAVE文件的名字,或是WAV资源的名字,或是内存中声音数据的指针,或是在系统注册表WIN.INI中定义的系统事件声音。如果该参数为NULL则停止正在播放的声音。

(2).参数hmod是应用程序的实例句柄,当播放WAV资源时要用到该参数,否则它必须为NULL。参数fdwSound是标志的组合,如下表所示。若成功则函数返回TRUE,否则返回FALSE。

(3).播放标志以及含义:
SND_APPLICATION
用应用程序指定的关联来播放声音。
SND_ALIAS
pszSound参数指定了注册表或WIN.INI中的系统事件的别名。
SND_ALIAS_ID
pszSound参数指定了预定义的声音标识符。
SND_ASYNC
用异步方式播放声音,PlaySound函数在开始播放后立即返回。
SND_FILENAME
pszSound参数指定了WAVE文件名。
SND_LOOP
重复播放声音,必须与SND_ASYNC标志一块使用。
SND_MEMORY
播放载入到内存中的声音,此时pszSound是指向声音数据的指针。
SND_NODEFAULT
不播放缺省声音,若无此标志,则PlaySound在没找到声音时会播放缺省声音。
SND_NOSTOP
PlaySound不打断原来的声音播出并立即返回FALSE。
SND_NOWAIT
如果驱动程序正忙则函数就不播放声音并立即返回。
SND_PURGE
停止所有与调用任务有关的声音。若参数pszSound为NULL,就停止所有的声音,否则,停止pszSound指定的声音。
SND_RESOURCE
pszSound参数是WAVE资源的标识符,这时要用到hmod参数。
SND_SYNC
同步播放声音,在播放完后PlaySound函数才返回。

(4).直接播出声音文件,相应的代码为:

PlaySound("c:\\win95\\media\\The Microsoft Sound.wav", NULL, SND_FILENAME | SND_ASYNC);

(5).用PlaySound播放系统声音,Windows启动的声音是由SystemStart定义的系统声音,因此可以用下面的方法播放启动声音:

PlaySound("SystemStart",NULL,SND_ALIAS|SND_ASYNC);

2.mciSendString

(1).添加头文件
#include <mmsystem. h>
#pragma comment (lib , “winmm.lib”)

头文件顺序错误
windows. h必须在mmsystem. h之前

文件名中有空格
不会报错,但是播放没有声音,应该是因为命令之间是用空格隔开的,所以不符合解析格式,所以没声音。

编译mciSendString报错
undefined reference to mcisendstri ngA@16
原因是#pragma comment(lib, “Winmm.lib)微软自家vC编译器cl.exe专有宏指令,别家的不认。解决方法:自己添加链接
settings->complier->linker settings->add->winmm

(2).打开音乐文件

mciSendString("open D:\\prolltetris\bin\\Debug\\res\\俄罗斯方块游戏背景音乐. mp3 alias a", NULL, 0, NULL);

这里使用的绝对路径,音乐文件路径改变后无法播放,替换成相对路径即可

(3).播放文件

mciSendString(TEXT("play a"), NULL, 0, NULL);

(4).程序结束关闭文件

mciSendString("stop a", NULL, 0, NULL);

本文地址:https://blog.csdn.net/qq_45799098/article/details/107310895

(0)
上一篇 2022年3月22日
下一篇 2022年3月22日

相关推荐