C语言——反弹球消砖块

目录

前言

一、小球代码的重构

函数

二、 实现游戏的边框

 三、显示移动挡板

四、反弹小球

五、消除砖块

六、玩家输入

getchar()

kbhit()

七、结尾——最后实现的代码

#define

总结


前言

在之前的弹跳小球的基础上增加反弹小球的挡板、边框和消除砖块。

代码参考了《C语言课程设计与游戏开发实践教程》

一、小球代码的重构

        为了能更好地实现反弹球消除砖块,我先对之前弹跳小球的代码进行重构。这里我不对弹跳小球的代码像上一篇文章一样进行详细的解释,只是在代码中添加注释。

        直接看弹跳小球重构后的代码:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <Windows.h>

// 全局变量
int high, width;      // 游戏画面大小
int ball_x, ball_y;   // 小球的坐标
int ball_vx, ball_vy; // 小球的速度

// 光标移动到(x,y)位置
void gotoxy(int x, int y)
{
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD pos;
    pos.X = x;
    pos.Y = y;
    SetConsoleCursorPosition(handle, pos);
}

// 数据初始化
void startup()
{
    high = 13;
    width = 17;
    ball_x = 0;
    ball_y = width / 2;
    ball_vx = 1;
    ball_vy = 1;
}

// 显示画面
void show()
{
    // 光标移动到原点位置,以下重画清屏
    gotoxy(0, 0);

    int i, j;
    for (i = 0; i <= high + 1; i++)
    {
        for (j = 0; j <= width; j++)
        {
            if ((i == ball_x) && (j == ball_y))
                printf("0"); //   输出小球
            else
                printf(" "); //   输出空格
        }
        printf("\n");
    }
}

// 与用户输入无关的更新
void updateWithoutInput()
{
    ball_x = ball_x + ball_vx;
    ball_y = ball_y + ball_vy;

    if ((ball_x == 0) || (ball_x == high - 1))
        ball_vx = -ball_vx;
    if ((ball_y == 0) || (ball_y == width - 1))
        ball_vy = -ball_vy;

    Sleep(80); // 降低小球的跳动速度
}

// 隐藏光标函数
void HideCursor()
{
    CONSOLE_CURSOR_INFO cursor_info = {1, 0};
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

int main()
{
    // 数据初始化
    startup();
    // 隐藏光标
    HideCursor();
    //  游戏循环执行
    while (1)
    {
        show();               // 显示画面
        updateWithoutInput(); // 与用户输入无关的更新
    }

    return 0;
}

当我们运行这段代码时会发现小球与之前一样跳动了,同时代码也更加清晰了。这是因为用函数实现了功能的模块化,让每一个函数实现一个功能。

函数

1、定义

        在维基百科中,函数的定义叫做子程序

2.分类

      (1)库函数:C语言内部提供的函数。

      (2)自定义函数:自我发挥写出的函数。

3、具体请看:这位大佬的

二、 实现游戏的边框

        当我们成功对代码进行重构,便于后续添加功能后,我们就要开始添加功能了。首先我们来完成游戏的边界的显示,这里我用符号 ' | ' 来显示右边界,符号 ' - ' 来显示下边界(当然你也可以根据自己的喜好来设置边界的样式)。先来看代码:

    int i, j;
    for (i = 0; i <= high + 1; i++)
    {
        for (j = 0; j <= width; j++)
        {
            if ((i == ball_x) && (j == ball_y))
                printf("0"); //   输出小球
            else if (j == width)
                printf("|"); //   输出右边框
            else if (i == high + 1)
                printf("-"); //   输出下边框
            else if ((i == high) && (j > left) && (j < right))
                printf("*"); //   输出挡板
            else if ((i == block_x) && (j == block_y))
                printf("B"); //   输出方块
            else
                printf(" "); //   输出空格
        }
        printf("\n");
    }

同之前对小球的打印原理一样,将光标移动到指定的位置后使用先前设置好的符号来填充,使得游戏界面有可以看见的边界。

这里我让小球与边界的实现在同一个函数里面,所以你会发现在打印边界的同时也打印了小球。

 三、显示移动挡板

        在反弹球消除砖块中,当然有一块可以移动的挡板啦。刚刚我们实现了边界和小球的打印,接下来我们接着来实现可以移动的挡板。老规矩先来看代码:

int position_x, position_y; // 挡板中心坐标
int ridus;                  // 挡板半径大小
int left, right;            // 挡板左右位置


    ridus = 6;
    position_x = high;
    position_y = width / 2;
    left = position_y - ridus;
    right = position_y + ridus;

 这些是挡板设置的参数,为了减少代码量的我把挡板的实现与打印边界放在一起了,就在上一段代码里面。这个挡板的实现与小球和边界的实现原理是一样的,可以看看我之前发的C语言——弹跳小球,这里面有我实现小球打印的方法。

四、反弹小球

        我们结束小球、边界和挡板的打印后就开始小球的弹跳了。先来看代码:

    if (ball_x == high - 1)
    {
        if ((ball_y >= left) && (ball_y <= right)) // 被挡板挡住
        {
            ball_number++;
            printf("\a"); // 响铃
                          // ball_y = ball_y + rand()%4-2;
        }
        else // 没有被挡板挡住
        {
            printf("游戏失败\n");
            system("pause");
            exit(0);
        }
    }

    ball_x = ball_x + ball_vx;
    ball_y = ball_y + ball_vy;

    if ((ball_x == 0) || (ball_x == high - 1))
        ball_vx = -ball_vx;
    if ((ball_y == 0) || (ball_y == width - 1))
        ball_vy = -ball_vy;

实现原理:

        通过判断小球的坐标是否在规定的范围内,是就可以让小球进行反弹(也就是小球相应的速度更改为负数,通过数的相加来实现小球的反弹),不是就结束游戏。

五、消除砖块

        消除砖块的原理与反弹小球的原理是一样的,首先设置相应的变量来表示砖块的位置,以及使用符号来表示砖块。接下来就是判断小球的坐标是否与砖块的坐标重合,是就将原来的砖块消除,同时小球反弹。也可以定义一个变量来记录砖块的消除数,小球的反弹数(这个最好在设置小球的与挡板的碰撞时就一并设置) 。

    if ((ball_x == block_x) && (ball_y == block_y)) // 小球击中方块
    {
        score++;                  // 分数加1
        block_y = rand() % width; // 产生新的方块
    }

六、玩家输入

        当我们实现这些后,我们会发现其中最重要的玩家输入数据没有实现。这里我们用getchar()函数来实现数据的输入,kbhit()函数来时实现游戏进行的流畅性。

void updateWithInput() // 与用户输入有关的更新
{
    char input;
    if (kbhit()) // 判断是否有输入
    {
        input = getch(); // 根据用户的不同输入来移动,不必输入回车
        if (input == 'a')
        {
            position_y--; // 位置左移
            left = position_y - ridus;
            right = position_y + ridus;
        }
        if (input == 'd')
        {
            position_y++; // 位置右移
            left = position_y - ridus;
            right = position_y + ridus;
        }
    }
}

getchar()

        我们用这个函数来实现输入字符后不用回车来确定,增加游戏体验度。(具体用法请看这里

kbhit()

        用这个函数来实现,当玩家没有输入字符时游戏依旧可以进行。(具体用法请看这里) 

七、结尾——最后实现的代码

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <time.h>

// 宏定义数据,便于更改数值
#define RIDUS 5 // 挡板的半径

// 全局变量
int high, width;            // 游戏画面大小
int ball_x, ball_y;         // 小球的坐标
int ball_vx, ball_vy;       // 小球的速度
int position_x, position_y; // 挡板中心坐标
int left, right;            // 挡板左右位置
int ball_number;            // 反弹小球的次数
int block_x, block_y;       // 方块的位置
int score;                  // 消掉方块的个数

void gotoxy(int x, int y) // 光标移动到(x,y)位置
{
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD pos;
    pos.X = x;
    pos.Y = y;
    SetConsoleCursorPosition(handle, pos);
}

void startup() // 数据初始化
{
    high = 13;
    width = 17;
    ball_x = 0;
    ball_y = width / 2;
    ball_vx = 1;
    ball_vy = 1;
    position_x = high;
    position_y = width / 2;
    left = position_y - RIDUS;
    right = position_y + RIDUS;
    ball_number = 0;
    block_x = 0;
    block_y = width / 2 + 1;
    score = 0;
}

void show() // 显示画面
{
    gotoxy(0, 0); // 光标移动到原点位置,以下重画清屏
    int i, j;
    for (i = 0; i <= high + 1; i++)
    {
        for (j = 0; j <= width; j++)
        {
            if ((i == ball_x) && (j == ball_y))
                printf("0"); //   输出小球
            else if (j == width)
                printf("|"); //   输出右边框
            else if (i == high + 1)
                printf("-"); //   输出下边框
            else if ((i == high) && (j > left) && (j < right))
                printf("*"); //   输出挡板
            else if ((i == block_x) && (j == block_y))
                printf("B"); //   输出方块
            else
                printf(" "); //   输出空格
        }
        printf("\n");
    }
    printf("反弹小球数:%d\n", ball_number);
    printf("消掉的方块数:%d\n", score);
}

void updateWithoutInput() // 与用户输入无关的更新
{
    if (ball_x == high - 1)
    {
        if ((ball_y >= left) && (ball_y <= right)) // 被挡板挡住
        {
            ball_number++;
            printf("\a"); // 响铃
                          // ball_y = ball_y + rand()%4-2;
        }
        else // 没有被挡板挡住
        {
            printf("游戏失败\n");
            system("pause");
            exit(0);
        }
    }

    if ((ball_x == block_x) && (ball_y == block_y)) // 小球击中方块
    {
        score++;                  // 分数加1
        block_y = rand() % width; // 产生新的方块
    }

    ball_x = ball_x + ball_vx;
    ball_y = ball_y + ball_vy;

    if ((ball_x == 0) || (ball_x == high - 1))
        ball_vx = -ball_vx;
    if ((ball_y == 0) || (ball_y == width - 1))
        ball_vy = -ball_vy;

    Sleep(80);
}

void updateWithInput() // 与用户输入有关的更新
{
    char input;
    if (kbhit()) // 判断是否有输入
    {
        input = getch(); // 根据用户的不同输入来移动,不必输入回车
        if (input == 'a')
        {
            position_y--; // 位置左移
            left = position_y - RIDUS;
            right = position_y + RIDUS;
        }
        if (input == 'd')
        {
            position_y++; // 位置右移
            left = position_y - RIDUS;
            right = position_y + RIDUS;
        }
    }
}

void HideCursor() // 隐藏光标函数
{
    CONSOLE_CURSOR_INFO cursor_info = {1, 0};
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

int main()
{
    srand((unsigned)time(NULL)); // 创建随机数种子
    startup();                   // 初始化
    HideCursor();
    while (1)
    {
        show();               // 显示画面
        updateWithoutInput(); // 与用户输入无关的更新
        updateWithInput();    // 与用户输入有关的更新
    }
    return 0;
}

最后我们可以使用 #define 来让变量便于修改

#define

        在C语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。在C语言中,“宏”分为有参数和无参数两种。具体请看这里

总结

 学习C语言的过程中我门更多的应该是去使用我们学到知识,而不是去看、去记。

下次再见。 

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开朗碳基生物

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值