C语言实现贪吃蛇[内附详细步骤]

C语言贪吃蛇项目

今天给分享一下使用C语言写个贪吃蛇小游戏,主要包含以下10部分内容:

1、windows光标定位与隐藏

2、欢迎界面

3、打印围墙

4、产生食物

5、初始化蛇身

6、响应按键

7、蛇身运动

8、碰撞检测

9、结束界面

10、程序运行

  1. windows光标定位与隐藏

COORD 是window API中定义的一种结构体,表示一个字符在控制台屏幕上的坐标。定义为:

typedef struct_COORD{

SHORT X;  横坐标

SHORT Y;  纵坐标

}COORD;

定位函数gotoxy,gotoDelete

void gotoprint(int x, int y)

{

    gotoxy(x, y);

    printf("■");

}

// 删除蛇尾

void gotoDelete(int x, int y)

{

    gotoxy(x, y);

    printf("  ");

}

2. 欢迎界面

使用gotoxy函数将光标定义到页面相应的位置输出欢迎信息,页面左上角为(0,0)坐标。

将用户输入的姓名信息保存下来以便后面使用。

用户输入姓名后清屏,进入游戏界面。

void welcome()

{

    gotoxy(10, 5);

    printf("/**********************************************/");

    gotoxy(14, 8);

    printf("WELCOME TO THE GAME OF RETRO SNAKE");

    gotoxy(14, 12);

    printf("wasd控制蛇身上下左右运动,反向移动等同于吃到自己导致游戏结束,除了wasd任意键暂停");

    gotoxy(10, 20);

    printf("/**********************************************/");

    gotoxy(14, 16);

    printf("请输入姓名:");

    scanf("%s",username);

    system("cls");        

}

​​​​​​​3.打印围墙

贪吃蛇围墙只需要定位到相应位置输出"■"即可,本次游戏的围墙为57*27

坐标图如下图所示:

 

void creatgraph()

{

int i;

// 横向围墙 58 / 2 == 29列

for (i = 0; i < 58; i += 2)

    {

        gotoprint(i, 0);

        gotoprint(i, 26);

    }

    // 竖向围墙  1-25中间有25行 + 头尾两行  27行

    for (i = 1; i < 26; i++)

    {

        gotoprint(0, i);

        gotoprint(56, i);

    }

    

    // 游戏信息

gotoxy(60,5);

printf("hello,%s",username);

gotoxy(60,15);

printf("您当前的得分为:");

gotoxy(76,15);

printf("%d",score);

}

打印完后显示一下游戏信息。

注意:

汉字宽度是字符宽度的两倍,高度相等。即一个汉字占两个字节,一个英文字母占一个字节。

因此,"■"输出一次占两个字节,纵向上占一个字节,底部自身携带一些空隙。

​​​​​​​4.产生食物

1、构造食物

struct Food

{

    int x;

    int y;

}food;

2、定位食物

保证食物必须在围墙内:

x坐标范围: [2,54] -- 并且只要偶数部分,不是偶数部分需要转化为接近的偶数

y坐标范围: [1,25]

保证食物和蛇身没有重合,如果重合需要再次生成新的位置。

3、输出食物

void creatfood()

{

// 随机产生一个食物的位置

int flag = 0;

while(!flag)

{

srand((unsigned int)time(NULL));

food.y = rand() % 25 + 1; // [1,25]

food.x = rand() % 53 + 2; // [2,54] -- 只要偶数部分

if(food.x % 2 != 0)

{

food.x = food.x+1; // 不是偶数部分转化为接近的偶数

}

// 不能与蛇身重复

snake *p = head;

while(p)

{

if(p->x == food.x && p->y == food.y)

{

// 重复 -- 重新生成坐标

break;

}

p = p->next;

}

flag = 1;  

}

gotoxy(food.x, food.y);

    printf("吃");

}

​​​​​​​5.初始化蛇身

蛇身构造

typedef struct Snakes

{

    int x;

    int y;

    struct Snakes* next;

}snake;

snake* head;    // 声明蛇头指针

绘制初始化的蛇身

void initSnake()

{

head = (snake*)malloc(sizeof(snake));

    snake* p = (snake*)malloc(sizeof(snake));

    snake* q = (snake*)malloc(sizeof(snake));

    head->x = 16;

    head->y = 15;    

    p->x = 16;

    p->y = 16;

   

    q->x = 16;

    q->y = 17;

    head->next = p;

    p->next = q;

    q->next = NULL;

    

    //绘制初始蛇身

gotoprint(16,15);

gotoprint(16,16);

gotoprint(16,17);    

}

​​​​​​​6.响应按键

getch()为输入字符函数,但是是阻塞的,如果用户不输入进步向下进行,不满足蛇身自动向前运动的需求,因此替换为_kbhit()进行非阻塞的输入监听,监听到键盘输入后,使用_getch从缓冲区中将输入的字符读出来放入全局变量ch中。

另外,当程序执行时,循环一次需要进行一次碰撞检测,没有碰撞时,还需要进行蛇身的移动。

int clickControl()

{

while(1)

{

// 检测是否碰撞边界

if(collisionDetect() == 1) return 0;

// _kbhit监听键盘有输入-非阻塞

if(_kbhit())

{

ch = _getch();  

}

// 移动身体

movingBody();  

}

return 1;

}

​​​​​​​7.蛇身运动

蛇身运动即:消除蛇尾节点,根据按键方向确定好新节点坐标,将新节点添加到蛇头位置即可。

移动后可以加一些延迟,使用Sleep(xx); xx以ms为单位。

void movingBody()

{

// 坐标修改

int x = head->x;

int y = head->y;

switch(ch)

{

case UP:

y-=1;

break;

case DOWN:

y+=1;

break;

case LEFT:

x-=2;

break;

case RIGHT:

x+=2;

break;

default:

ch = 0;

break;

}

if(ch)

{

// 消除尾节点

snake *p = head;

while(p->next->next != NULL)

{

p = p->next;

}

snake *q = p->next;

gotoDelete(q->x,q->y);

free(q);

p->next = NULL;

// 新坐标结点置为头结点

snake* newNode = (snake*)malloc(sizeof(snake));

newNode->x = x;

newNode->y = y;

newNode->next = head;

head = newNode;

// 绘制蛇头

gotoprint(x,y);

// 延迟

Sleep(500);

}

}

注意:如果用户输入的是除了 wasd以外的按键则暂停游戏。

​​​​​​​8.碰撞检测

碰撞检测主要有三种:

  1. 蛇身和边界
  2. 蛇头和蛇身
  3. 蛇头碰到食物

                重新生成一个食物

                在蛇身后面添加一个尾巴,可以不给x,y,因为下次移动的时候会直接将它释放掉。

                加分

                在分值位置更新分数

int collisionDetect()

{

// 检测蛇头和边界

if(head->x == 0 || head->x == 56 || head->y == 0 || head->y == 26)

{

Finish();

return 1;

}

// 蛇头和蛇身

snake *p = head->next;

while(p != NULL)

{

if(head->x == p->x && head->y == p->y)

{

// 碰撞-结束

Finish();

return 1;

}

p = p->next;

}

// 碰到食物

if(head->x == food.x && head->y == food.y)

{

creatfood();

// 添加一个尾巴

snake* newNode = (snake*)malloc(sizeof(snake));

newNode->next = NULL;

snake *p = head;

while(p->next != NULL)

{

p = p->next;

}

p->next = newNode;

score += 10;

gotoxy(76,15);

printf("%d",score);

}

return 0;

}

​​​​​​​9.结束界面

清屏

显示用户名和得分

void Finish()

{

system("cls");

    gotoxy(10, 10);

    printf("/**********************************************/");

    gotoxy(10, 20);

    printf("/**********************************************/");

    gotoxy(15, 14);

    printf("GAME   OVER      o(* ̄▽ ̄*)o");    

    gotoxy(18, 16);

    printf("%s,    你的得分 %d ",username, score);

    gotoxy(15, 18);

    printf("还不错哦,     继续努力O(∩_∩)O");

    gotoxy(0, 27);

    // 释放空间

    snake* p = head, *q;

    while (p != NULL) {

        q = p->next;

        free(p);

        p = q;

    }

    system("pause");

}

​​​​​​​10.运行程序

int main()

{

welcome();

creatgraph();

creatfood();

initSnake();

clickControl();

return 0;

}