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

一、代码

main.c

#include 
#include 
#include "game.h"

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

mywindows.h

#ifndef MYWINDOWS_H_INCLUDED
#define MYWINDOWS_H_INCLUDED

/*系统调用模块*/
#include 
//函数声明
//初始化句柄
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 
#include 

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 
#include 
#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键进入游戏
大一实训----C语言编写俄罗斯方块游戏_第1张图片
游戏过程中,按ESC退出游戏的效果图
大一实训----C语言编写俄罗斯方块游戏_第2张图片
游戏版面
游戏池和右边的小恐龙是由一个二维数组完成,详细代码位于data.h中。左边的铃铛是直接打印的。
大一实训----C语言编写俄罗斯方块游戏_第3张图片
这是游戏结束的效果图,游戏结束自带动画效果,当方块触碰到游戏池顶部时,方块自上而下消失并打印游戏结束和分数比较等字样。
大一实训----C语言编写俄罗斯方块游戏_第4张图片

三、实训笔记

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
#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);

你可能感兴趣的:(笔记)