C语言小游戏详解—俄罗斯方块

版权声明:本文为博主原创文章,转载请注明CSDN博客源地址!
https://blog.csdn.net/Mou_Yang/article/details/82424643

一、游戏组成

俄罗斯方块总共有19种方块,每一种方块都是由4个小格子组成。但其实这19种方块是由7种基本方块通过变形得到的。
下面是这7种基本方块:
1 第0种基本方块:有1种形态
2 第1种基本方块:有2种形态
这里写图片描述 第2种基本方块:有2种形态
4第3种基本方块:有4种形态
5第4种基本方块:有4种形态
6第5种基本方块:有2种形态
7第6种基本方块:有4种形态

二、游戏规则

1、在一个行高为20,列高为14的矩形中,随机方块从上方以一定速率下落;
2、其下落过程中可通过按键使它左右移动、变形;
3、方块下落至矩阵底部或者下部与其他方块接触,则固定该方块的位置,并生成新的方块重复步骤2;
4、如果矩阵中的某一行都是方块,则消去该行的方块,并使上面的方块下沉一行;
5、如果方块的堆积高度超过20,则游戏结束。

三、总体设计

主体
我的想法是创建一个25*14的整型数组array,数组元素用0表示空位,1表示堆积的方块,2表示在运动的方块,用另外的数组graph[4][2]记录在运行的方块各个点的坐标。红色位置用作方块运动到底后生成新方块的位置,橙色区域用作玩家可视区域,在该区域方块运动并接收玩家指令变形。

四、定义变量

设置矩阵的行高:

#define H 25

设置列高14:

#define L 14

设置7种方块在红色区域生成时的各点坐标位置:

int block[7][4][2]={{{2,6},{2,7},{3,6},{3,7}},//第0种基本方块
                    {{1,7},{2,7},{2,6},{3,6}},//第1种基本方块
                    {{1,6},{2,6},{2,7},{3,7}},//第2种基本方块
                    {{2,6},{3,6},{3,7},{3,8}},//第3种基本方块
                    {{2,8},{3,8},{3,7},{3,6}},//第4种基本方块
                    {{3,5},{3,8},{3,7},{3,6}},//第5种基本方块
                    {{2,7},{3,6},{3,7},{3,8}}};//第6种基本方块

假设每种方块都有4种变形(如果它只有一种变形,则它的另外三种变形,跟原来一样),每次变形后各点坐标都会变化,创建数组记录坐标变化的差值:

int to_change[7][4][4][2]={{{{0,0},{0,0},{0,0},{0,0}},{{0,0},{0,0},{0,0},{0,0}},{{0,0},{0,0},{0,0},{0,0}},{{0,0},{0,0},{0,0},{0,0}}},
                          //第0种基本方块,每次变化后的坐标都不变,故x,y坐标变化的差值都为0
                          {{{2,1},{1,0},{0,1},{-1,0}},{{-2,-1},{-1,0},{0,-1},{1,0}},{{2,1},{1,0},{0,1},{-1,0}},{{-2,-1},{-1,0},{0,-1},{1,0}}},
                          //第1种基本方块,它有2种变形,故第一次变化的坐标差值跟第三次一样,第二次变化跟第四次一样
                          {{{1,2},{0,1},{1,0},{0,-1}},{{-1,-2},{0,-1},{-1,0},{0,1}},{{1,2},{0,1},{1,0},{0,-1}},{{-1,-2},{0,-1},{-1,0},{0,1}}},
                          //...
                          {{{-1,2},{-2,1},{-1,0},{0,-1}},{{2,0},{1,1},{0,0},{-1,-1}},{{0,-1},{1,0},{0,1},{-1,2}},{{-1,-1},{0,-2},{1,-1},{2,0}}},
                          //...
                          {{{1,0},{0,-1},{-1,0},{-2,1}},{{0,-2},{-1,-1},{0,0},{1,1}},{{-2,1},{-1,2},{0,1},{1,0}},{{1,1},{2,0},{1,-1},{0,-2}}},
                          //...
                          {{{-3,2},{-2,-1},{-1,0},{0,1}},{{3,-2},{2,1},{1,0},{0,-1}},{{-3,2},{-2,-1},{-1,0},{0,1}},{{3,-2},{2,1},{1,0},{0,-1}}},
                          //...
                          {{{0,1},{-2,1},{-1,0},{0,-1}},{{1,-1},{1,1},{0,0},{-1,-1}},{{-1,0},{1,0},{0,1},{-1,2}},{{0,0},{0,-2},{1,-1},{2,0}}}};
                          //...

设置生成的方块的序号:

int picture;//picture=rand()%7;

记录当前运动方块的变形:

int now;  //0<=now<=3

记录运动方块4个点的x和y坐标

int graph[4][2];//Xi=graph[i][0]   Yi=graph[i][1]

设置记录键盘输入的字符变量

char control;//'w'表示变形  'a'表示左移  'd'表示右移

设置计时:

int start;//每500毫秒方块下降一格

五、主要函数

方块变形:调用数组to_change对方块坐标graph[][]进行修改,实现方块变形。注意:判断方块变形后是否会超出屏幕左右边界,如果超出,就要挪一下方块的位置。

void Change();

左右移动:将玩家输入的字符赋值给control,然后作参数传给M,如果为A/a,则左移,为D/d,则右移。注意:当方块触碰左右边界,或者左右有其他已经固定的方块,是无法在移动的。

void Move(char M);

方块到底部:判断方块是否已经到达底部,或者下部跟其他方块接触,是的话返回1,不是返回0。

int See();  

生成方块:重置数组graph[][]的坐标,生成新的方块。

void New();

方块前进:实现方块向下走一格

void Go(); 

游戏开始:开始游戏,初始化picture,nowgraph[][]等数据。

void Begin();

消去一行:遍历数组array,如果某一行全部为1(该行全是方块),则消去该行,上面的方块下沉一格,并在最上面生成空白的一行。同时判断死亡,如果遍历时发现方块堆积高度大于20,返回1代表游戏结束。

int Elimination(); 

可视界面:输出矩阵array可视的部分,即游戏界面。

void Cout(); 

以上是需要自己实现的函数,除此之外还要另外调用4个库函数:
1.实现非显示输入:让用户输入的字符不用在控制台显示出来然后回车赋值给变量,而是直接在输入时就赋值给变量。

#include <conio.h>
char getch();

2.判断是否有键盘信息:当检测到有键盘输入 时,该函数返回值>0,否则返回0。

#include <conio.h>
int kbhit();

3.记录时间:该函数返回设备的启动时间(单位:毫秒),可多次调用,并用后者减去前者,用以计算两者的时间间隔。

#include <ctime>
int GetTickCount();

4.卡住屏幕:在等待到用户的下一步指令之前,卡住程序的运行。

#include <windows.h>
system("cls");

六、完整代码

#include<iostream>
#include<conio.h>
#include<windows.h>
#include<stdlib.h>
#include<ctime>
#define H 25
#define L 14
//这个里要判断它下一步是否会重叠,如果会则表示已经到底
using namespace std;

int array[H][L];    //进行方块组合的地方
//28中变形
int to_change[7][4][4][2]={{{{0,0},{0,0},{0,0},{0,0}},{{0,0},{0,0},{0,0},{0,0}},{{0,0},{0,0},{0,0},{0,0}},{{0,0},{0,0},{0,0},{0,0}}},
              {{{2,1},{1,0},{0,1},{-1,0}},{{-2,-1},{-1,0},{0,-1},{1,0}},{{2,1},{1,0},{0,1},{-1,0}},{{-2,-1},{-1,0},{0,-1},{1,0}}},
              {{{1,2},{0,1},{1,0},{0,-1}},{{-1,-2},{0,-1},{-1,0},{0,1}},{{1,2},{0,1},{1,0},{0,-1}},{{-1,-2},{0,-1},{-1,0},{0,1}}},
              {{{-1,2},{-2,1},{-1,0},{0,-1}},{{2,0},{1,1},{0,0},{-1,-1}},{{0,-1},{1,0},{0,1},{-1,2}},{{-1,-1},{0,-2},{1,-1},{2,0}}},
              {{{1,0},{0,-1},{-1,0},{-2,1}},{{0,-2},{-1,-1},{0,0},{1,1}},{{-2,1},{-1,2},{0,1},{1,0}},{{1,1},{2,0},{1,-1},{0,-2}}},
              {{{-3,2},{-2,-1},{-1,0},{0,1}},{{3,-2},{2,1},{1,0},{0,-1}},{{-3,2},{-2,-1},{-1,0},{0,1}},{{3,-2},{2,1},{1,0},{0,-1}}},
              {{{0,1},{-2,1},{-1,0},{0,-1}},{{1,-1},{1,1},{0,0},{-1,-1}},{{-1,0},{1,0},{0,1},{-1,2}},{{0,0},{0,-2},{1,-1},{2,0}}}};
//7个图形
int block[7][4][2]={{{2,6},{2,7},{3,6},{3,7}},
                    {{1,7},{2,7},{2,6},{3,6}},
                    {{1,6},{2,6},{2,7},{3,7}},
                    {{2,6},{3,6},{3,7},{3,8}},
                    {{2,8},{3,8},{3,7},{3,6}},
                    {{3,5},{3,8},{3,7},{3,6}},
                    {{2,7},{3,6},{3,7},{3,8}}};
int picture;//图形的序号
int now;  //当前图形的变化号
int graph[4][2];//图形的坐标
char control;
int start;

void Change();
void Move(char M);  //根据输入的AD判断左右移动
int See();         //判断是否到底
void New();         //开新
void Go();          //向下走一格
void Begin();       //一切值初始化
int Elimination(); //把完整行的消去
void Cout();        //输出矩阵
int main()
{

    Begin();
    Cout();
    while(1){

        if(GetTickCount()-start>=500){

            start=GetTickCount();
            if(!See())Go();
            else {
                if(Elimination())break;
                New();
            }
            system("cls");
            Cout();
        }
        if(kbhit()){
            control=getch();
            if(control=='W'||control=='w')Change();
            else Move(control);
            system("cls");
            Cout();
        }
    }
    //虽然背景板的高度为25,但显示时只显示后20格
}

void Change()
{
    for(int i=0;i<4;i++)
        array[graph[i][0]][graph[i][1]]=0;
    now=(now+1)%4;
    for(int i=0;i<4;i++){
        graph[i][0]+=to_change[picture][now][i][0];
        graph[i][1]+=to_change[picture][now][i][1];
    }
    for(int i=0;i<4;i++){
        if(graph[i][1]<0){
            int temp=0-graph[i][1];
            for(int j=0;j<4;j++)graph[j][1]+=temp;
        }
        if(graph[i][1]>L-1){
            int temp=graph[i][1]-(L-1);
            for(int j=0;j<4;j++)graph[j][1]-=temp;
        }
    }
    for(int i=0;i<4;i++)
        array[graph[i][0]][graph[i][1]]=2;
}
void Move(char M)
{
    for(int i=0;i<4;i++)
        array[graph[i][0]][graph[i][1]]=0;

    if((M=='a'||M=='A')&&graph[0][1]&&graph[1][1]&&graph[2][1]&&graph[3][1])
        if(array[graph[0][0]][graph[0][1]-1]!=1&&array[graph[1][0]][graph[1][1]-1]!=1&&array[graph[2][0]][graph[2][1]-1]!=1&&array[graph[3][0]][graph[3][1]-1]!=1){
            graph[0][1]--;
            graph[1][1]--;
            graph[2][1]--;
            graph[3][1]--;
        }
    if((M=='d'||M=='D')&&graph[0][1]<L-1&&graph[1][1]<L-1&&graph[2][1]<L-1&&graph[3][1]<L-1)
        if(array[graph[0][0]][graph[0][1]+1]!=1&&array[graph[1][0]][graph[1][1]+1]!=1&&array[graph[2][0]][graph[2][1]+1]!=1&&array[graph[3][0]][graph[3][1]+1]!=1){
            graph[0][1]++;
            graph[1][1]++;
            graph[2][1]++;
            graph[3][1]++;
        }
    for(int i=0;i<4;i++)
        array[graph[i][0]][graph[i][1]]=2;
}
int See()
{
    for(int i=0;i<4;i++)
        if(graph[i][0]+1==H)return 1;
    for(int i=0;i<4;i++)
        if(array[graph[i][0]+1][graph[i][1]]==1)return 1;
    return 0;
}
void New()
{
    picture=rand()%7;
    now=-1;
    for(int i=0;i<4;i++){
        graph[i][0]=block[picture][i][0];
        graph[i][1]=block[picture][i][1];
        array[graph[i][0]][graph[i][1]]=2;
    }
}
void Go()
{
    for(int i=0;i<4;i++){
        array[graph[i][0]][graph[i][1]]=0;
        graph[i][0]++;
    }
    for(int i=0;i<4;i++)
    array[graph[i][0]][graph[i][1]]=2;
}
void Begin()
{
    srand(time(NULL));
    start=GetTickCount();//开始的时间
    for(int i=0;i<H;i++)
        for(int j=0;j<L;j++)
            array[i][j]=0;
    picture=rand()%7;
    now=-1;
    for(int i=0;i<4;i++){
        graph[i][0]=block[picture][i][0];
        graph[i][1]=block[picture][i][1];
        array[graph[i][0]][graph[i][1]]=2;
    }
}
int Elimination()
{
    for(int i=0;i<4;i++){
        array[graph[i][0]][graph[i][1]]=1;
        if(graph[i][0]<4)return 1;
    }
    for(int i=H-1;i>=0; ){
        int j;
        for(j=0;j<L;j++)
            if(array[i][j]!=1)break;
        if(j==L){
            for(int k=i;k>0;k--)
                for(int l=0;l<L;l++)
                    array[k][l]=array[k-1][l];
        }else i--;
    }
    return 0;
}
void Cout()
{
    for(int i=5;i<H;i++){
        for(int j=0;j<L;j++){
            if(array[i][j]==0)printf(".");
            else if(array[i][j]==1){
                printf("#");

            }else printf("o");
        }
        printf("\n");
    }
}

七、效果界面

超简易版的俄罗斯方块,没有积分,没有开始菜单。(但其实这些自己去加就可以了,开个变量记一下)
这里写图片描述 这里写图片描述

八、总结

作为一个已经学了两年编程的菜鸟来说,写这个东西还花了一天,确实有点丢人。不过因为刚开的博客想写点东西上来,就水了个俄罗斯方块。整个过程我已经很详细地介绍了,但如果还有什么疏漏或者错误的地方,欢迎在下方评论指出,一起探讨。

  • 58
    点赞
  • 221
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
好的,我可以帮您简单介绍一下俄罗斯方块的实现思路。 俄罗斯方块是一个经典的小游戏,主要由七种不同形状的方块组成,玩家需要控制方块在一个矩形区域内下落并堆叠,以填满整行并得分。 以下是一个简单的 C 语言实现思路: 1. 定义方块形状 用二维数组表示每个方块的形状,例如: ``` int block[7][4][4] = { { {0,0,0,0}, {0,1,1,0}, {0,1,1,0}, {0,0,0,0} }, { {0,0,0,0}, {0,0,2,0}, {0,2,2,2}, {0,0,0,0} }, // ... }; ``` 2. 定义游戏区域 用二维数组表示游戏区域,其中 0 表示空格,非零数字表示已堆叠的方块。 ``` int board[20][10] = {0}; ``` 3. 实现方块下落 每个方块初始位置为游戏区域的顶部中央位置,用两个变量 `x` 和 `y` 表示方块的位置。每次循环时,先检查方块是否已经落到最下方或者已经堆叠在其他方块上,如果没有,则将方块下移一格。 ``` int x = 4, y = 0; // 初始位置 while (1) { // 检查是否可以下落 if (can_move_down(x, y)) { y++; } else { // 将方块堆叠到游戏区域中 merge_block(x, y); // 检查是否有完整的行 int lines = check_lines(); if (lines > 0) { // 更新分数 score += lines * 100; // 消除行 remove_lines(); } // 生成新的方块 x = 4; y = 0; current_block = random_block(); if (!can_move_down(x, y)) { // 游戏结束 break; } } } ``` 4. 实现方块移动和旋转 玩家可以通过键盘控制方块的左右移动、旋转。用变量 `dx` 和 `dy` 表示方块的横向和纵向偏移量,用变量 `r` 表示方块的旋转次数。 ``` int dx = 0, dy = 0, r = 0; while (1) { // 处理键盘输入 int ch = getch(); if (ch == KEY_LEFT) { // 左移 if (can_move_left(x + dx, y + dy)) { dx--; } } else if (ch == KEY_RIGHT) { // 右移 if (can_move_right(x + dx, y + dy)) { dx++; } } else if (ch == KEY_DOWN) { // 加速下落 delay = 50; } else if (ch == KEY_UP) { // 旋转 int new_r = (r + 1) % 4; if (can_rotate(x + dx, y + dy, new_r)) { r = new_r; } } // 检查是否可以移动或旋转方块 if (!can_move(x + dx, y + dy, r)) { dx = 0; dy = 0; r = 0; } // 更新方块位置和形状 draw_block(x + dx, y + dy, r); } ``` 5. 实现游戏界面和计分系统 使用 ncurses 库实现游戏界面,包括游戏区域、分数、下一个方块等。 ``` // 初始化 ncurses initscr(); noecho(); curs_set(0); keypad(stdscr, TRUE); // 绘制游戏界面 draw_board(); draw_score(); // 进入游戏循环 while (1) { // ... } ``` 以上是一个简单的俄罗斯方块 C 语言实现思路,当然具体实现细节可能会有所不同,您可以根据自己的需求和能力进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值