C语言结构数组实现贪吃蛇小游戏

C语言结构数组实现贪吃蛇小游戏攻略

前言

贪吃蛇是一种经典的小游戏,类似于蛇果子等。在游戏中,玩家需要控制蛇吃掉食物,并不断地变长,直到撞墙或撞到自己的身体为止。这个游戏对于程序员来说是一个很好的练手项目,也是锻炼从事编程工作所需的基本能力必不可少的一步。在本文中,我们将详细讲解如何使用C语言结构数组实现贪吃蛇小游戏。

开发环境准备

在实现这个小游戏前,我们需要一些开发环境的准备。首先需要安装Code::Blocks和Dev-C++两个IDE,以及游戏需要的Windows库文件。

  • Code::Blocks下载地址:https://www.codeblocks.org/downloads/binaries/
  • Dev-C++下载地址:https://sourceforge.net/projects/orwelldevcpp/
  • Windows库文件下载地址:https://www.mediafire.com/file/zh4adh6l1msthz5/windows_lib_files.zip/file

下载完之后解压缩到C盘根目录下。

完整代码

以下是实现贪吃蛇小游戏的完整代码。代码注释翻译如下:

#include<graphics.h>
#include<conio.h>
#include<time.h>

#define DELAY 100
#define HEIGHT 30
#define WIDTH 30
#define SNAKE_LEN_MAX (HEIGHT-2)*(WIDTH-2)


/*结构体:坐标位置*/
struct point {
    int x;
    int y;
};

/*枚举类型:方向*/
enum direction {
    UP,
    DOWN,
    LEFT,
    RIGHT,
    STOP
};

/*全局变量*/
int HEIGHT_PIXEL;
int WIDTH_PIXEL;
int CELL_SIZE;
int DELAY_TIME;
int MOVE_SPEED;
int SCORE;
int SNAKE_LEN;
struct point snake[SNAKE_LEN_MAX+1];
struct point food;
enum direction dir;

/*函数原型*/
void init();                /*初始化*/
void add_a_food();          /*添加一个食物*/
void move();                /*蛇移动*/
void change_dir();          /*改变蛇的移动方向*/
int is_snake_hit_wall();    /*判断蛇是否撞墙*/
int is_snake_hit_food();    /*判断蛇是否撞到食物*/
int is_snake_hit_self();    /*判断蛇是否撞到自己*/
void game_over();           /*游戏结束*/
void draw_score();          /*画分数*/
void draw_food();           /*画食物*/
void draw_snake_head();     /*画蛇头*/
void draw_snake_tail();     /*画蛇尾*/
void draw_snake_body();     /*画蛇身*/
void draw_snake();          /*画整条蛇*/
void draw_string();         /*画字符串*/

int main()
{
    init();
    while(1) {
        add_a_food();
        while(!kbhit()) {
            move();
            delay(DELAY_TIME);
        }
        change_dir();
        if(is_snake_hit_wall() || is_snake_hit_self()) {
            game_over();
            break;
        }
        if(is_snake_hit_food()) {
            SCORE++;
            SNAKE_LEN++;
            if(SNAKE_LEN > SNAKE_LEN_MAX) {
                game_over();
                break;
            }
            add_a_food();
        }
        cleardevice();
        draw_snake();
        draw_food();
        draw_score();
    }
    getch();
    return 0;
}

void init()
{
    int mousex, mousey;

    /*初始化图形库*/
    initgraph(&mousex, &mousey, "C:\\WINDOWS\\system32\\msimg32.dll");  /*此处是Windows基础库文件,需要判断文件位置*/

    /*设置窗口大小*/
    HEIGHT_PIXEL = 500;
    WIDTH_PIXEL = 500;
    setaspectratio(1,1);
    setbkcolor(BLACK);
    cleardevice();

    /*设置游戏速度*/
    MOVE_SPEED = 6;
    DELAY_TIME = 1000/MOVE_SPEED;

    /*初始化蛇身*/
    SNAKE_LEN = 1;
    snake[1].x = WIDTH/2;
    snake[1].y = HEIGHT/2;

    /*初始化方向*/
    dir = LEFT;
}

void add_a_food()
{
    struct point temp;

    /*随机生成一个食物坐标(不能在蛇身上)*/
    while(1) {
        temp.x = 1 + rand() % (WIDTH-2);
        temp.y = 1 + rand() % (HEIGHT-2);
        for(int i =1; i <= SNAKE_LEN; i++) {
            if(snake[i].x == temp.x && snake[i].y == temp.y) {
                temp.x = 0;
                temp.y = 0;
                break;
            }
        }
        if(temp.x != 0) {
            break;
        }
    }

    /*将随机生成的食物坐标赋值给全局变量*/
    food.x = temp.x;
    food.y = temp.y;
}

void move()
{
    /*将蛇尾设为蛇头*/
    for(int i = SNAKE_LEN; i > 0; i--) {
        snake[i] = snake[i-1];
    }

    /*根据方向改变蛇头位置*/
    switch(dir) {
        case UP:
            snake[1].y--;
            break;
        case DOWN:
            snake[1].y++;
            break;
        case LEFT:
            snake[1].x--;
            break;
        case RIGHT:
            snake[1].x++;
            break;
        case STOP:
            break;
        default:
            break;
    }
}

void change_dir()
{
    /*改变移动方向*/
    if(kbhit()) {
        int input = getch();
        switch(input) {
            case 'w':
            case 'W':
            case 72:    /*方向键上*/
                if(dir != DOWN)
                    dir = UP;
                break;
            case 's':
            case 'S':
            case 80:    /*方向键下*/
                if(dir != UP)
                    dir = DOWN;
                break;
            case 'a':
            case 'A':
            case 75:    /*方向键左*/
                if(dir != RIGHT)
                    dir = LEFT;
                break;
            case 'd':
            case 'D':
            case 77:    /*方向键右*/
                if(dir != LEFT)
                    dir = RIGHT;
                break;
            case 27:
                dir = STOP;
                break;
            default:
                break;
        }
    }
}

int is_snake_hit_wall()
{
    /*判断是否撞墙*/
    if(snake[1].x == 0 || snake[1].x == (WIDTH-1) || snake[1].y == 0 || snake[1].y == (HEIGHT-1)) {
        return 1;
    }
    return 0;
}

int is_snake_hit_food()
{
    /*判断是否吃到食物*/
    if(snake[1].x == food.x && snake[1].y == food.y) {
        return 1;
    }
    return 0;
}

int is_snake_hit_self()
{
    /*判断是否撞到自己*/
    for(int i=2; i <= SNAKE_LEN; i++) {
        if(snake[1].x == snake[i].x && snake[1].y == snake[i].y) {
            return 1;
        }
    }
    return 0;
}

void game_over()
{
    draw_string("Game Over", WIDTH/2, HEIGHT/2);
    getch();
}

void draw_score()
{
    char score_str[10];
    sprintf(score_str, "Score:%d", SCORE);
    draw_string(score_str, 1, 1);
}

void draw_food()
{
    setfillstyle(SOLID_FILL, YELLOW);
    setcolor(YELLOW);
    fillellipse(food.x * CELL_SIZE, food.y * CELL_SIZE, CELL_SIZE/2, CELL_SIZE/2);
}

void draw_snake_head()
{
    setfillstyle(SOLID_FILL, GREEN);
    setcolor(GREEN);
    fillellipse(snake[1].x * CELL_SIZE, snake[1].y * CELL_SIZE, CELL_SIZE/2, CELL_SIZE/2);
}

void draw_snake_tail()
{
    setfillstyle(SOLID_FILL, BLACK);
    setcolor(BLACK);
    bar(snake[SNAKE_LEN].x * CELL_SIZE, snake[SNAKE_LEN].y * CELL_SIZE, (snake[SNAKE_LEN].x+1) * CELL_SIZE, (snake[SNAKE_LEN].y+1) * CELL_SIZE);
}

void draw_snake_body(int i)
{
    setfillstyle(SOLID_FILL, GREEN);
    setcolor(GREEN);
    bar(snake[i].x * CELL_SIZE, snake[i].y * CELL_SIZE, (snake[i].x+1) * CELL_SIZE, (snake[i].y+1) * CELL_SIZE);
}

void draw_snake()
{
    draw_snake_head();
    for(int i = 2; i < SNAKE_LEN; i++) {
        draw_snake_body(i);
    }
    draw_snake_tail();
}

void draw_string(char *str, int x, int y)
{
    outtextxy(x*CELL_SIZE, y*CELL_SIZE, str);
}

整体思路

本篇文章使用结构体数组来实现贪吃蛇小游戏。在游戏中,使用全局变量来存储对游戏状态的控制和存储蛇的位置、食物的位置、本局游戏得分等信息。在每一个游戏循环内,我们需要判断以下几个事项:

  1. 是否添加了食物
  2. 是否需要移动蛇的位置
  3. 是否撞到了墙或自己,如果是,则游戏结束
  4. 是否撞到了食物

对于以上每一个判断条件,我们都可以为之定义一个函数。同时,使用图形处理库来绘制游戏界面。当蛇移动时,会先把蛇尾的位置删除,再新生成一条蛇头。

示例说明

示例1

下面是一个简单的实例,当蛇移动时,它的头部始终向右:

void move()
{
    /*将蛇尾设为蛇头*/
    for(int i = SNAKE_LEN; i > 0; i--) {
        snake[i] = snake[i-1];
    }

    /*改变蛇头位置*/
    snake[1].x++;
}

示例2

下面是一个更复杂的示例,当蛇头受到用户控制改变方向时,蛇会按照改变后的方向移动:

void change_dir()
{
    /*改变移动方向*/
    if(kbhit()) {
        int input = getch();
        switch(input) {
            case 'w':
            case 'W':
            case 72:    /*方向键上*/
                if(dir != DOWN)
                    dir = UP;
                break;
            case 's':
            case 'S':
            case 80:    /*方向键下*/
                if(dir != UP)
                    dir = DOWN;
                break;
            case 'a':
            case 'A':
            case 75:    /*方向键左*/
                if(dir != RIGHT)
                    dir = LEFT;
                break;
            case 'd':
            case 'D':
            case 77:    /*方向键右*/
                if(dir != LEFT)
                    dir = RIGHT;
                break;
            case 27:
                dir = STOP;
                break;
            default:
                break;
        }
    }
}

void move()
{
    /*将蛇尾设为蛇头*/
    for(int i = SNAKE_LEN; i > 0; i--) {
        snake[i] = snake[i-1];
    }

    /*根据方向改变蛇头位置*/
    switch(dir) {
        case UP:
            snake[1].y--;
            break;
        case DOWN:
            snake[1].y++;
            break;
        case LEFT:
            snake[1].x--;
            break;
        case RIGHT:
            snake[1].x++;
            break;
        case STOP:
            break;
        default:
            break;
    }
}

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言结构数组实现贪吃蛇小游戏 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • 关于java:使用streams累加bigdecimals

    以下是关于“关于java:使用streams累加bigdecimals”的完整攻略,包括基本知识和两个示例。 基本知识 在Java中,可以使用Streams API对集合进行操作Streams API提供了一种简单、活、可读性强的方式来处理集合中的元素。在Streams API中,可以使用reduce()方法集合中的元素进行累加操作。 BigDecimal是…

    other 2023年5月7日
    00
  • 网站设计经验 建设网站常犯错误汇总

    网站设计经验 建设网站常犯错误汇总 简介 在设计和建设一个网站时,即使是有经验的设计师和开发者也可能会犯一些错误。这些常见错误可能会导致用户体验、安全性和网站性能方面的问题。本文将汇总一些常见的网站设计和建设错误,并提供解决方案来避免这些错误。 常见错误与解决方案 1. 使用低质量的图片 使用低分辨率或模糊的图像会破坏页面的整体外观和用户体验。网站应该使用高…

    other 2023年6月26日
    00
  • 注解处理器(APT)是什么

    注解处理器(APT)是什么 注解处理器(Annotation Processing Tool,简称APT)是Java编译器提供的一个工具,它可以在编译时扫描和处理源代码中的注解,并生成新的Java代码。APT通过在源代码中添加注解来实现编译时的自动化处理。 APT的优势 APT的主要优势在于它可以在编译时根据注解生成新的代码,这种特性可以帮助开发人员减少重复…

    other 2023年6月28日
    00
  • linux-make命令和运行build.sh脚本之间的主要区别是什么

    以下是关于linux-make命令和运行build.sh脚本之间的主要区别的完整攻略: 1. make命令 make是一个常用的Linux命令,于自动化构建和编译程序。它使用Makefile文件来定义构建过程中的依赖关系和编译规则。make命令自动检测文件的修改时间,只编译需要重新编译的文件,从而提高了编译效率。 以下是一个简单的Makefile的示例: C…

    other 2023年5月8日
    00
  • CAD怎么创建块和分解块?

    以下是在CAD软件中创建块和分解块的完整攻略: 创建块 打开CAD软件,并打开您要创建块的绘图文件。 选择要创建块的对象,可以是单个对象或多个对象。 在CAD软件的菜单栏中,找到“编辑”或“修改”等选项,点击打开下拉菜单。 在下拉菜单中,找到“创建块”或类似的选项,点击进入块创建界面。 在块创建界面中,输入块的名称,并根据需要设置其他属性,如插入点、旋转角度…

    other 2023年10月16日
    00
  • 关于对python中self的深入理解

    关于对Python中self的深入理解 1. 什么是self? 在Python中,self是一个约定俗成的参数名,用于表示当前对象实例。它在类的方法中作为第一个参数传递,用于访问和操作对象的属性和方法。 2. self的作用 使用self可以在类的方法内部访问和操作对象的属性和方法。通过self,我们可以实现以下功能: 访问对象的属性:利用self可以在类的…

    other 2023年6月28日
    00
  • modelsim安装步骤

    ModelSim安装步骤 ModelSim是一款数字电路仿真和验证工具,广泛应用于FPGA设计和数字电路相关领域。以下是ModelSim安装的详细步骤: 硬件和软件环境要求 操作系统:Windows或Linux 处理器:Intel Core i5或更高版本 内存:4GB或更高版本 硬盘空间:10GB或更高版本 可以在官方网站下载到的ModelSim安装包 M…

    其他 2023年3月28日
    00
  • python内建类型与标准类型

    Python内建类型与标准类型 Python提供了许多内建类型和标准类型,用于处理不同的数据和操作。以下是对Python内建类型和标准类型的详细讲解: 内建类型 数字类型 整数(int):表示整数值,例如:x = 10 浮点数(float):表示带有小数点的数值,例如:x = 3.14 复数(complex):表示实部和虚部的复数,例如:x = 2 + 3j…

    other 2023年10月15日
    00
合作推广
合作推广
分享本页
返回顶部