贪吃蛇

本文介绍了一个基于控制台的简单贪吃蛇游戏实现。通过使用C++编程语言,详细展示了游戏规则、蛇的移动逻辑及食物生成机制,并考虑了边界碰撞与自身碰撞检测。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这次用控制台实现简单的贪吃蛇游戏(面向过程设计)。
首先,明确游戏规则。游戏初始状态是有一睹围墙和一条由几个点组成的“蛇”,还有一个食物,其实就是打印地图和创建一个链表。代码分别如下:

  1. 游戏初始化
void Pos(short x,short y)//设置光标位置函数,上一节讲过。
{
    COORD pos;
    pos.X=x;
    pos.Y=y;
    HANDLE hOutput=GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hOutput,pos);
}

void CreateMap()//打印地图
{
    //打印上下边框,这里每次自增2是因为控制台的一行和一列
    //的宽度不同,且恰好有两倍关系。
    for(int i=0;i<57;i+=2)
    {
        Pos(i,0);
        printf("■");
        Pos(i,24);
        printf("■");
    }
    for(int i=1;i<24;i++)//打印左右边框
    {
        Pos(0,i);
        printf("■");            
        Pos(56,i);
        printf("■");    
    }
    return;
}

void PrintSnake()//打印蛇
{
    struct Body *p=body001;
    while(p!=NULL)//判断是否指向蛇尾
    {
        Pos(p->x,p->y);
        printf("■");
        p=p->next;
    }//从蛇头打印到蛇尾
    return ;
}

int Food=0; //1表明有食物,0表明没食物
short food_x,food_y;//食物坐标
void CreateFood()//创建新食物
{
    if(Food==0)
    {       
        srand((int)time(0));
        food_x=2+rand()%53;//x的取值范围[2,54]
        if(food_x%2==1)
            food_x--;
        food_y=1+rand()%23;//y的取值范围[1,23]
        Food=1;
    }
}

//接下来定义结构体从而创建链表。
struct Body//定义蛇身结构体
{
    short x,y;//蛇身结点的坐标
    Body *next;//指向下一个结点的指针
};

Body *body001;//蛇头指针,判断方向,宜做全局变量
void Init()//游戏初始化
{
    Body *body002,*body003,*body004;
    body001=new Body;//创建4个蛇身结构体
    body002=new Body;
    body003=new Body;
    body004=new Body;
    body001->next=body002;//把蛇身连接起来,形成链表
    body002->next=body003;
    body003->next=body004;
    body004->next=NULL;
    Pos(body001->x=12,body001->y=8);printf("■");//设定并打印蛇身的初始位置
    Pos(body002->x=10,body002->y=8);printf("■");
    Pos(body003->x=8,body003->y=8);printf("■");
    Pos(body004->x=6,body004->y=8);printf("■");
    CreateFood();//创建食物
}

步骤1总结,有4个函数块和1个结构体和4个全局变量,
声明如下:
void Pos(short x,short y);//设置光标位置函数,上一节讲过。
void CreateMap();//打印地图
void PrintSnake();//打印蛇
void CreateFood();//创建食物坐标
void Init();//游戏初始化
struct Body;//定义蛇身结构体
Body *body001;//蛇头指针
int Food=0; //1表明有食物,0表明没食物
short food_x,food_y;//食物坐标
好了,现在进行一个关键步骤,就是蛇的移动问题。我想的是,蛇的移动分为两种情况,就是蛇的下一步有没有食物。若没有食物,则在下一步位置创建蛇身结构体,作为蛇头,然后把蛇尾删除。
若是有食物,则在食物位置创建结构体,作为蛇头,不删除蛇尾。
这一步代码包含蛇的一步移动问题。

void SnakeMove()//蛇身移动、刷新蛇身和食物及其坐标
{
    Body *p_food;//下一格有食物
    Body *p_forward;//下一格没食物
    Body *q;//蛇身移动一格时的临时指针
    p_forward=new Body;   
    if(1==dir)//向上移动
    {
        if(body001->x==food_x && body001->y-1==food_y)//下一格有食物
        {
            p_food=new Body;
            p_food->x=food_x;
            p_food->y=food_y;
            p_food->next=body001;
            body001=p_food;
            score+=5;
            Pos(62,3);printf("分数:%d",score);
            Food=0;
            CreateFood();
        }
        else//下一格没有食物
        {
            p_forward->x=body001->x;//横坐标不变
            p_forward->y=body001->y-1;//纵坐标-1
            p_forward->next=body001;
            body001=p_forward;
            q=body001;
            while((q->next)->next!=NULL)//使q指向倒数第二个结点
                q=q->next;
            Pos((q->next)->x,(q->next)->y);//移动光标到蛇尾
            printf(" ");//删除蛇尾
            free(q->next); //释放蛇尾
            q->next=NULL;//使倒数第二个结点成为新的蛇尾
        }//else
    }


    if(2==dir)//向下移动
    {

        if(body001->x==food_x && body001->y+1==food_y)//下一格有食物
        {
            p_food=new Body;
            p_food->x=food_x;
            p_food->y=food_y;
            p_food->next=body001;
            body001=p_food;
            score+=5;
            Pos(62,3);printf("分数:%d",score);
            Food=0;
            CreateFood();
        }
        else//下一格没有食物
        {
            p_forward->x=body001->x;//横坐标不变
            p_forward->y=body001->y+1;//纵坐标+1
            p_forward->next=body001;
            body001=p_forward;
            q=body001;
            while((q->next)->next!=NULL)//使q指向倒数第二个结点
                q=q->next;
            Pos((q->next)->x,(q->next)->y);//移动光标到蛇尾
            printf(" ");//删除蛇尾
            free(q->next); //释放蛇尾
            q->next=NULL;//使倒数第二个结点成为新的蛇尾
        }//else
    }


    if(3==dir)//向左移动
    {
        if(body001->x-2==food_x && body001->y==food_y)//下一格有食物
        {
            p_food=new Body;
            p_food->x=food_x;
            p_food->y=food_y;
            p_food->next=body001;
            body001=p_food;
            score+=5;
            Pos(62,3);printf("分数:%d",score);
            Food=0;
            CreateFood();
        }
        else//下一格没有食物
        {
            p_forward->x=body001->x-2;//横坐标-2
            p_forward->y=body001->y;//纵坐标不变
            p_forward->next=body001;
            body001=p_forward;
            q=body001;
            while((q->next)->next!=NULL)//使q指向倒数第二个结点
                q=q->next;
            Pos((q->next)->x,(q->next)->y);//移动光标到蛇尾
            printf(" ");//删除蛇尾
            free(q->next); //释放蛇尾
            q->next=NULL;//使倒数第二个结点成为新的蛇尾
        }
    }


    if(4==dir)//向右移动
    {
        if(body001->x+2==food_x && body001->y==food_y)//下一格有食物
        {
            p_food=new Body;
            p_food->x=food_x;
            p_food->y=food_y;
            p_food->next=body001;
            body001=p_food;
            score+=5;
            Food=0;
            Pos(62,3);printf("分数:%d",score);
            CreateFood();
        }
        else//下一格没有食物
        {
            p_forward->x=body001->x+2;//横坐标=2
            p_forward->y=body001->y;//纵坐标不变
            p_forward->next=body001;
            body001=p_forward;
            q=body001;
            while((q->next)->next!=NULL)//使q指向倒数第二个结点
                q=q->next;
            Pos((q->next)->x,(q->next)->y);//移动光标到蛇尾
            printf(" ");//删除蛇尾
            free(q->next); //释放蛇尾
            q->next=NULL;//使倒数第二个结点成为新的蛇尾
        }
    }
    Pos(food_x,food_y);printf("■");//打印食物
    PrintSnake();//打印蛇
} 

其实这一步是整个游戏的核心,每走一步就重新打印蛇,意味着这个函数需要放在一个循环里面。
接下来实现游戏规则:不能碰墙和自己。
代码如下:

//游戏规则检测
int TouchSelf()//判断是否咬到自己
{
    struct Body *p_tself;
    p_tself=body001;
    while(!p_tself->next==NULL)
    {
        p_tself=p_tself->next;
        if(p_tself->x==body001->x && p_tself->y==body001->y)
            return 1;
    }
    return 0;
} 

int TouchWall()//不可穿墙
{
    if(body001->x==0 || body001->x==56 || body001->y==0 || body001->y==24)
        return 1;
    else 
        return 0;
}

OK,有了一步移动,有了规则检测,有了食物随机产生,有了墙面,可以进行游戏的循环,每一步循环,我要得到玩家的按键方向,没有则是继续上一步的方向或是初始方向。然后有了方向就移动一步,顺便检测是否撞墙和撞到自己,没有就进行下一个循坏。
两个循坏的时间间隔则是蛇运动的快慢了。

void GameCircle()//游戏循环
{
    while(1)
    {
        if (1== TouchWall())//不可穿墙
             break;//撞墙Game Over
        if(1== TouchSelf() )//判断蛇是否咬到自己
            break;//咬到自己Game Over
        //判断用户的按键输入
        if(GetAsyncKeyState(VK_UP) && dir!=2)
            dir=1;
        else if(GetAsyncKeyState(VK_DOWN) && dir!=1)
            dir=2;
        else if(GetAsyncKeyState(VK_LEFT) && dir!=4)
            dir=3;
        else if(GetAsyncKeyState(VK_RIGHT) && dir!=3)
            dir=4;
        SnakeMove();//蛇移动一格 
        Sleep(sleeptime);//设定两次移动之间的时间间隔
    } 
} 
void EndImage() //游戏结束画面
{
    system("cls");
    Pos(30,9);printf("游戏结束,欢迎再来!");
    Pos(30,11);printf("你的得分:%d",score);
    Pos(28,13);
    system("pause");
}

整个游戏具备的条件都有了,在主函数里面实现。如下:

int main()
{
    CreateMap();
    Init();
    GameCircle();
    EndImage();
    return 0;
}

嗯,这样结束了。
整段代码如下。

#include<windows.h>
#include<conio.h>
#include<cstdlib>
#include<time.h>
#include<iostream>
using namespace std;

void Pos(short x,short y)//设置光标位置函数,上一节讲过。
{
    COORD pos;
    pos.X=x;
    pos.Y=y;
    HANDLE hOutput=GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hOutput,pos);
}

void CreateMap()//打印地图
{
    for(int i=0;i<57;i+=2)//打印上下边框,这里每次自增2是因为控制台的一行和一列的宽度不同,且恰好有两倍关系。
    {
        Pos(i,0);
        printf("■");
        Pos(i,24);
        printf("■");
    }
    for(int i=1;i<24;i++)//打印左右边框
    {
        Pos(0,i);
        printf("■");            
        Pos(56,i);
        printf("■");    
    }
    return;
}

//接下来定义结构体从而创建链表。
struct Body//定义蛇身结构体
{
    short x,y;//蛇身结点的坐标
    Body *next;//指向下一个结点的指针
};

int Food=0; //1表明有食物,0表明没食物
short food_x,food_y;//食物坐标
void CreateFood()//创建新食物
{
    if(Food==0)
    {       
        srand((int)time(0));
        food_x=2+rand()%53;//x的取值范围[2,54]
        if(food_x%2==1)
            food_x--;
        food_y=1+rand()%23;//y的取值范围[1,23]
        Food=1;
    }
}
Body *body001;//蛇头指针
void Init()//游戏初始化
{
    struct Body *body002,*body003,*body004;//定义四个指向蛇身的指针
    body001=new Body;//创建4个蛇身结构体
    body002=new Body;
    body003=new Body;
    body004=new Body;
    body001->next=body002;//把蛇身连接起来,形成链表
    body002->next=body003;
    body003->next=body004;
    body004->next=NULL;
    Pos(body001->x=12,body001->y=8);printf("■");//设定并打印蛇身的初始位置
    Pos(body002->x=10,body002->y=8);printf("■");
    Pos(body003->x=8,body003->y=8);printf("■");
    Pos(body004->x=6,body004->y=8);printf("■");
    CreateFood();//打印食物
}


void PrintSnake()//打印蛇
{
    struct Body *p=body001;
    while(p!=NULL)//判断是否指向蛇尾
    {
        Pos(p->x,p->y);
        printf("■");
        p=p->next;
    }//从蛇头打印到蛇尾
    return ;
}
int dir=4;//蛇头方向1-up,2-down,3-left,4-right
int sleeptime=220;//每次移动之后的停顿时间
int score;//玩家得分
void SnakeMove()//蛇身移动、刷新蛇身和食物及其坐标
{
    Body *p_food;//下一格有食物
    Body *p_forward;//下一格没食物
    Body *q;//蛇身移动一格时的临时指针
    p_forward=new Body;   
    if(1==dir)//向上移动
    {
        if(body001->x==food_x && body001->y-1==food_y)//下一格有食物
        {
            p_food=new Body;
            p_food->x=food_x;
            p_food->y=food_y;
            p_food->next=body001;
            body001=p_food;
            score+=5;
            Pos(62,3);printf("分数:%d",score);
            Food=0;
            CreateFood();
        }
        else//下一格没有食物
        {
            p_forward->x=body001->x;//横坐标不变
            p_forward->y=body001->y-1;//纵坐标-1
            p_forward->next=body001;
            body001=p_forward;
            q=body001;
            while((q->next)->next!=NULL)//使q指向倒数第二个结点
                q=q->next;
            Pos((q->next)->x,(q->next)->y);//移动光标到蛇尾
            printf(" ");//删除蛇尾
            free(q->next); //释放蛇尾
            q->next=NULL;//使倒数第二个结点成为新的蛇尾
        }//else
    }


    if(2==dir)//向下移动
    {

        if(body001->x==food_x && body001->y+1==food_y)//下一格有食物
        {
            p_food=new Body;
            p_food->x=food_x;
            p_food->y=food_y;
            p_food->next=body001;
            body001=p_food;
            score+=5;
            Pos(62,3);printf("分数:%d",score);
            Food=0;
            CreateFood();
        }
        else//下一格没有食物
        {
            p_forward->x=body001->x;//横坐标不变
            p_forward->y=body001->y+1;//纵坐标+1
            p_forward->next=body001;
            body001=p_forward;
            q=body001;
            while((q->next)->next!=NULL)//使q指向倒数第二个结点
                q=q->next;
            Pos((q->next)->x,(q->next)->y);//移动光标到蛇尾
            printf(" ");//删除蛇尾
            free(q->next); //释放蛇尾
            q->next=NULL;//使倒数第二个结点成为新的蛇尾
        }//else
    }


    if(3==dir)//向左移动
    {
        if(body001->x-2==food_x && body001->y==food_y)//下一格有食物
        {
            p_food=new Body;
            p_food->x=food_x;
            p_food->y=food_y;
            p_food->next=body001;
            body001=p_food;
            score+=5;
            Pos(62,3);printf("分数:%d",score);
            Food=0;
            CreateFood();
        }
        else//下一格没有食物
        {
            p_forward->x=body001->x-2;//横坐标-2
            p_forward->y=body001->y;//纵坐标不变
            p_forward->next=body001;
            body001=p_forward;
            q=body001;
            while((q->next)->next!=NULL)//使q指向倒数第二个结点
                q=q->next;
            Pos((q->next)->x,(q->next)->y);//移动光标到蛇尾
            printf(" ");//删除蛇尾
            free(q->next); //释放蛇尾
            q->next=NULL;//使倒数第二个结点成为新的蛇尾
        }
    }


    if(4==dir)//向右移动
    {
        if(body001->x+2==food_x && body001->y==food_y)//下一格有食物
        {
            p_food=new Body;
            p_food->x=food_x;
            p_food->y=food_y;
            p_food->next=body001;
            body001=p_food;
            score+=5;
            Food=0;
            Pos(62,3);printf("分数:%d",score);
            CreateFood();
        }
        else//下一格没有食物
        {
            p_forward->x=body001->x+2;//横坐标=2
            p_forward->y=body001->y;//纵坐标不变
            p_forward->next=body001;
            body001=p_forward;
            q=body001;
            while((q->next)->next!=NULL)//使q指向倒数第二个结点
                q=q->next;
            Pos((q->next)->x,(q->next)->y);//移动光标到蛇尾
            printf(" ");//删除蛇尾
            free(q->next); //释放蛇尾
            q->next=NULL;//使倒数第二个结点成为新的蛇尾
        }
    }
    Pos(food_x,food_y);printf("■");//打印食物
    PrintSnake();//打印蛇
} 

int TouchSelf()//判断是否咬到自己
{
    struct Body *p_tself;
    p_tself=body001;
    while(!p_tself->next==NULL)
    {
        p_tself=p_tself->next;
        if(p_tself->x==body001->x && p_tself->y==body001->y)
            return 1;
    }
    return 0;
} 

int TouchWall()//不可穿墙
{
    if(body001->x==0 || body001->x==56 || body001->y==0 || body001->y==24)
        return 1;
    else 
        return 0;
}

void GameCircle()//游戏循环
{
    while(1)
    {
        if (1== TouchWall())//不可穿墙
             break;
        if(1== TouchSelf() )//判断蛇是否咬到自己
            break;//咬到自己Game Over


        //判断用户的按键输入
        if(GetAsyncKeyState(VK_UP) && dir!=2)
            dir=1;
        else if(GetAsyncKeyState(VK_DOWN) && dir!=1)
            dir=2;
        else if(GetAsyncKeyState(VK_LEFT) && dir!=4)
            dir=3;
        else if(GetAsyncKeyState(VK_RIGHT) && dir!=3)
            dir=4;
        SnakeMove();//蛇移动一格 
        Sleep(sleeptime);//设定两次移动之间的时间间隔
    } 
} 
void EndImage()
{
    system("cls");
    Pos(30,9);printf("游戏结束,欢迎再来!");
    Pos(30,11);printf("你的得分:%d",score);
    Pos(28,13);
    Sleep(5000);
}
int main()
{
    CreateMap();
    Init();
    GameCircle();
    EndImage();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值