[项目设计] 拿捏----贪吃蛇(简洁版)(C语言设计)

🌟作者介绍:友友们好我是钓鱼的猫猫,可以叫我小猫💕

作者主页钓鱼的猫猫-CSDN博客🎉

👀项目专栏项目_钓鱼的猫猫的博客-CSDN博客🎉

贪吃蛇需要提前知识

        🎈 1. 从零拿捏链表(一)—— 无头单向不循环链表(单链表)-CSDN博客

        🎈 2.动态内存管理-CSDN博客

        🎈 3.Windows控制台编程核心API详解:从入门到应用-CSDN博客

        🎈 4.C标准库 ---- locale.h-CSDN博客

🎉小猫和友友们一样喜欢代码🤭。很荣幸能向大家分享我的所学所感,和大家一起进步,成为合格的卷王。✨如果文章有错误,欢迎在评论区✏️指正。那么开始今天的学习吧!😘

目录

总游戏思路

一. 初始化

1.1 游戏的设置

1.1.1 光标定位

1.1.2 窗口设置

1.1.3 光标的隐藏

1.1.4 开始界面

1.1.5 地图创建

1.2 游戏的信息

1.2.1 贪吃蛇的初始化

1.2.2 其他信息初始化

1.3 食物的初始化

1.3.1 生成食物的坐标

1.3.2 创建食物节点

1.3.3 打印食物

二. 游戏的运行

2.1 固定信息打印

2.2 按键检测

2.3 蛇的移动

2.3.1 蛇的移动

2.3.2 判断下一个节点是否是食物

2.3.3 下个节点是食物

2.3.4 下个节点不是食物

2.3.2 是否撞墙

2.3.4 是否撞自己

三. 游戏的结束

3.1 游戏的结束

四.总代码

4.1 Game.c

4.2 Snake.h

4.3 Snake.c

五. 最后


总游戏思路

思路大概是这个思路

        一.游戏的各种初始化

                1.1 游戏的设置(大小,光标,提示,地图)

                1.2 游戏的信息(游戏的状态,贪吃蛇的信息,食物的信息)

                1.3 食物的初始化

        二.游戏的运行

                2.1 打印固定的信息(打印什么按键对于蛇的移动)

                2.2 检测按键状态

                2.3 蛇的移动(蛇的移动,是否结束的判断)

        三.游戏的结束

                3.1 游戏结束的提示信息

                3.2 释放内存空间

        这个时候有读者会问为什么要食物初始化,因为吃了食物之后,这个节点会变成蛇的节点,因此初始了蛇的节点,也要初始食物的节点

一. 初始化

1.1 游戏的设置

1.1.1 光标定位

void SetPos(int x, int y)
{

//获得句柄
    HANDLE houtput = NULL;
    houtput = GetStdHandle(STD_OUTPUT_HANDLE);

//光标移动到x,y位置
    COORD pos = { x,y };
    SetConsoleCursorPosition(houtput, pos);
}

void SetPos(int x, int y)
{
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x,y };
	SetConsoleCursorPosition(houtput, pos);
}

1.1.2 窗口设置

        控制这个窗口的大小,还有左上角的信息

                system("mode con lines=30 cols=100 ");   //30行,100列     
                system("title CSDN 钓鱼的猫猫");                //左上角信息

	system("mode con lines=30 cols=100 ");
	system("title CSDN 钓鱼的猫猫");

1.1.3 光标的隐藏

光标的隐藏

界面无光标闪烁

//获得句柄

        HANDLE houtput = NULL;
        houtput = GetStdHandle(STD_OUTPUT_HANDLE);

//设置光标隐藏信息

        CONSOLE_CURSOR_INFO cur_info = { 0 };
        GetConsoleCursorInfo(houtput, &cur_info);
        cur_info.bVisible = false;
        SetConsoleCursorInfo(houtput,&cur_info);

HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);

CONSOLE_CURSOR_INFO cur_info = { 0 };
GetConsoleCursorInfo(houtput, &cur_info);
cur_info.bVisible = false;
SetConsoleCursorInfo(houtput,&cur_info);

1.1.4 开始界面

两个页面信息的打印

        void WelcomeToGame(void)
        {
            SetPos(40, 14);
            printf("欢迎来到贪吃蛇游戏\n");
            SetPos(42, 20);

            system("pause");
            system("cls");
            SetPos(35, 14);
            printf("用↑ ↓ ← →控制蛇的移动 F3加速 F4减速\n");
    
            SetPos(35, 15);
            printf("加速更高分数\n");
            SetPos(39, 16);
            system("pause");
            system("cls");

        }

void WelcomeToGame(void)
{
	SetPos(40, 14);
	printf("欢迎来到贪吃蛇游戏\n");
	SetPos(42, 20);

	system("pause");
	system("cls");
	SetPos(35, 14);
	printf("用↑ ↓ ← →控制蛇的移动 F3加速 F4减速\n");
	
	SetPos(35, 15);
	printf("加速更高分数\n");
	SetPos(39, 16);
	system("pause");
	system("cls");

}

1.1.5 地图创建

地图的贪吃蛇的边框打印

        

        void CreateMap(void)
        {


            //上
            for (int i = 0; i < 29; i++)
            {
                wprintf(L"%lc", L'□');
            }

            //下
            SetPos(0, 26);
            for (int i = 0; i < 29; i++)
            {
                wprintf(L"%lc", L'□');
            }

            //左
            for (int i = 1; i <= 25; i++)
            {
                SetPos(0, i);
                wprintf(L"%lc", L'□');
            }


            //右
            for (int i = 1; i <= 25; i++)
            {
                SetPos(56, i);
                wprintf(L"%lc", L'□');
            }


        }

void CreateMap(void)
{
	//上
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", L'□');
	}

	//下
	SetPos(0, 26);
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", L'□');
	}

	//左
	for (int i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", L'□');
	}
	//右
	for (int i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", L'□');
	}
	
}

1.2 游戏的信息

1.2.1 贪吃蛇的初始化

        ps->_pSnake = NULL;
        //一.贪吃蛇的初始化
        //①.申请五个节点

        SnakeSingleNode* newnode = NULL;
        for (int i = 0; i < 5; i++)
        {
            newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
            if (newnode == NULL)
            {
                perror("malloc");
                return;
            }

            //设置节点信息
            newnode->x = POS_X + 2 * i;
            newnode->y = POS_Y;
            newnode->next = NULL;  // 关键修复:初始化 next 指针
            if (ps->_pSnake == NULL)//如果只有一个节点
            {
                ps->_pSnake = newnode;
            }
            else//如果很多节点
            {
                //头插
                newnode->next = ps->_pSnake;
                ps->_pSnake = newnode;
            }
        }

//②插入完要打印
        SnakeSingleNode* cur = ps->_pSnake;
        while (cur)
        {
            //光标定位
            SetPos(cur->x, cur->y);
            wprintf(L"%lc", L'●');
            cur = cur->next;
        }

ps->_pSnake = NULL;
//一.贪吃蛇的初始化
//①.申请五个节点
SnakeSingleNode* newnode = NULL;
for (int i = 0; i < 5; i++)
{
	newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}

	//设置节点信息
	newnode->x = POS_X + 2 * i;
	newnode->y = POS_Y;
	newnode->next = NULL;  // 关键修复:初始化 next 指针
	if (ps->_pSnake == NULL)//如果只有一个节点
	{
		ps->_pSnake = newnode;
	}
	else//如果很多节点
	{
		//头插
		newnode->next = ps->_pSnake;
		ps->_pSnake = newnode;
	}
}

//②插入完要打印
SnakeSingleNode* cur = ps->_pSnake;
while (cur)
{
	//光标定位
	SetPos(cur->x, cur->y);
	wprintf(L"%lc", L'●');
	cur = cur->next;
}

1.2.2 其他信息初始化

    //二.其他信息初始化
    //①.开始方向为向右

    ps->_dir = RIGHT;

    //②开始分数为0
    ps->_score = 0;

    //③每个食物的分数
    ps->_food_weight = 10;

    //④蛇的速度
    ps->_sleep_time = 100;

    //⑤游戏的状态
    ps->status = OK;

//二.其他信息初始化
//①.开始方向为向右
ps->_dir = RIGHT;

//②开始分数为0
ps->_score = 0;

//③每个食物的分数
ps->_food_weight = 10;

//④蛇的速度
ps->_sleep_time = 100;

//⑤游戏的状态
ps->status = OK;

1.3 食物的初始化

1.3.1 生成食物的坐标

    
    //一.生成食物的坐标
    int x = 0; int y = 0;
    
again:
    do
    {
        //①生成随机的坐标
        x = rand() % 53 + 2;
        y = rand() % 25 + 1;
        
    } while (x % 2 != 0);//让坐标必须是x为2的倍数

    //③x 和 y 不能是蛇的节点
    SnakeSingleNode* cur = ps->_pSnake;
    while (cur != NULL)
    {
        if ((x == cur->x) && (y == cur->y))
        {
            goto again;
        }
        cur = cur->next;
    }

	
	//一.生成食物的坐标
	int x = 0; int y = 0;
	
again:
	do
	{
		//①生成随机的坐标
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
		
	} while (x % 2 != 0);//让坐标必须是x为2的倍数

	//③x 和 y 不能是蛇的节点
	SnakeSingleNode* cur = ps->_pSnake;
	while (cur != NULL)
	{
		if ((x == cur->x) && (y == cur->y))
		{
			goto again;
		}
		cur = cur->next;
	}

1.3.2 创建食物节点

    //二.此时已经创建好节点
    //创建食物的节点

    SnakeSingleNode * newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
    if (newnode == NULL)
    {
        perror("malloc");
        return;
    }

    newnode->x = x;
    newnode->y = y;

    newnode->next = NULL;

	//二.此时已经创建好节点
	//创建食物的节点
	SnakeSingleNode * newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}

	newnode->x = x;
	newnode->y = y;

	newnode->next = NULL;

1.3.3 打印食物

//三.创建完节点应该打印食物信息
        SetPos(x, y);
        wprintf(L"%lc", L'★');


//四.记录该信息
        ps->_pfood = newnode;

//三.创建完节点应该打印食物信息
SetPos(x, y);
wprintf(L"%lc", L'★');


//四.记录该信息
ps->_pfood = newnode;

二. 游戏的运行

2.1 固定信息打印

void PrintfHelpInit(void)
{
    SetPos(64, 13);
    wprintf(L"%ls", L"1. 不能穿墙\n");
    SetPos(64, 14);
    wprintf(L"%ls", L"2. ↑ ↓ ←  → 控制蛇的移动\n");
    SetPos(64, 15);
    wprintf(L"%ls", L"3. 按F3加速,按F4减速\n");
    SetPos(64, 16);
    wprintf(L"%ls", L"4. 按Esc退出,按空格暂停\n");
    SetPos(64, 18);
    wprintf(L"%ls", L"CSDN 钓鱼的猫猫\n");
}

void PrintfHelpInit(void)
{
	SetPos(64, 13);
	wprintf(L"%ls", L"1. 不能穿墙\n");
	SetPos(64, 14);
	wprintf(L"%ls", L"2. ↑ ↓ ←  → 控制蛇的移动\n");
	SetPos(64, 15);
	wprintf(L"%ls", L"3. 按F3加速,按F4减速\n");
	SetPos(64, 16);
	wprintf(L"%ls", L"4. 按Esc退出,按空格暂停\n");
	SetPos(64, 18);
	wprintf(L"%ls", L"CSDN 钓鱼的猫猫\n");
}

2.2 按键检测

//二.检测按键
do
{
    //①打印总分数和食物的分值
    SetPos(64, 10);
    printf("总分数:%d\n", ps->_score);
    SetPos(64,11);
    printf("当前食物分数:%2d\n",ps->_food_weight);

    //②检测按键
    if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)//判断向上但是不能突变反方向按键
    {
        ps->_dir = UP;
    }
    else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)//判断向下但是不能突变反方向按键
    {

        ps->_dir = DOWN;
    }
    else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)//判断向左但是不能突变反方向按键
    {
        ps->_dir = LEFT;
    }
    else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)//判断向右但是不能突变反方向按键
    {
        ps->_dir = RIGHT;
    }
    else if (KEY_PRESS(VK_SPACE) )//判断空格按键
    {
        Pause();
    }
    else if (KEY_PRESS(VK_ESCAPE))//判断是否退出按键
    {
        ps->status = END_NORMAL;
    }
    else if (KEY_PRESS(VK_F3))//判断F3按键
    {
        //加速=》时间变短
        if (ps->_sleep_time > 80)
        {
            ps->_sleep_time -= 30;
            ps->_food_weight += 2;
        }
    }
    else if (KEY_PRESS(VK_F4))//判断F4按键
    {
        //减速=》时间变长
        if (ps->_food_weight > 2)
        {
            ps->_sleep_time += 30;
            ps->_food_weight -= 2;
        }
    }
    //蛇的移动
    SnakeMove(ps);
    Sleep(ps->_sleep_time);

} while (ps->status == OK);

//二.检测按键
do
{
	//①打印总分数和食物的分值
	SetPos(64, 10);
	printf("总分数:%d\n", ps->_score);
	SetPos(64,11);
	printf("当前食物分数:%2d\n",ps->_food_weight);

	//②检测按键
	if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)
	{
		ps->_dir = UP;
	}
	else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)
	{

		ps->_dir = DOWN;
	}
	else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
	{
		ps->_dir = LEFT;
	}
	else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
	{
		ps->_dir = RIGHT;
	}
	else if (KEY_PRESS(VK_SPACE) )
	{
		Pause();
	}
	else if (KEY_PRESS(VK_ESCAPE))
	{
		ps->status = END_NORMAL;
	}
	else if (KEY_PRESS(VK_F3))
	{
		//加速=》时间变短
		if (ps->_sleep_time > 80)
		{
			ps->_sleep_time -= 30;
			ps->_food_weight += 2;
		}
	}
	else if (KEY_PRESS(VK_F4))
	{
		//减速=》时间变长
		if (ps->_food_weight > 2)
		{
			ps->_sleep_time += 30;
			ps->_food_weight -= 2;
		}
	}
	//蛇的移动
	SnakeMove(ps);
	Sleep(ps->_sleep_time);

} while (ps->status == OK);

2.3 蛇的移动

2.3.1 蛇的移动

//蛇的移动函数
void SnakeMove(Snake* ps)
{
    //1.创建一个节点
    SnakeSingleNode* newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
    if (newnode == NULL)
    {
        perror("malloc");
        return;
    }
    //移动
    switch (ps->_dir)
    {
    case UP:
        newnode->x = ps->_pSnake->x;
        newnode->y = ps->_pSnake->y - 1;
        break;
    case DOWN:
        newnode->x = ps->_pSnake->x;
        newnode->y = ps->_pSnake->y + 1;
        break;
    case LEFT:
        newnode->x = ps->_pSnake->x-2;
        newnode->y = ps->_pSnake->y;
        break;
    case RIGHT:
        newnode->x = ps->_pSnake->x + 2;
        newnode->y = ps->_pSnake->y;
        break;
    }

    //2.检测下一个节点是否是食物
    if (NextIsFood(newnode, ps))
    {
        EatFood(newnode, ps);
    }
    else
    {
        NoFood(newnode, ps);
    }

    //3.检测是否撞墙
    KillByWall(ps);

    //4.检测是否撞自己
    KillBySelf(ps);

}

//蛇的移动函数
void SnakeMove(Snake* ps)
{
	//1.创建一个节点
	SnakeSingleNode* newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}
	//移动
	switch (ps->_dir)
	{
	case UP:
		newnode->x = ps->_pSnake->x;
		newnode->y = ps->_pSnake->y - 1;
		break;
	case DOWN:
		newnode->x = ps->_pSnake->x;
		newnode->y = ps->_pSnake->y + 1;
		break;
	case LEFT:
		newnode->x = ps->_pSnake->x-2;
		newnode->y = ps->_pSnake->y;
		break;
	case RIGHT:
		newnode->x = ps->_pSnake->x + 2;
		newnode->y = ps->_pSnake->y;
		break;
	}

	//2.检测下一个节点是否是食物
	if (NextIsFood(newnode, ps))
	{
		EatFood(newnode, ps);
	}
	else
	{
		NoFood(newnode, ps);
	}

	//3.检测是否撞墙
	KillByWall(ps);

	//4.检测是否撞自己
	KillBySelf(ps);

}

2.3.2 判断下一个节点是否是食物

int NextIsFood(SnakeSingleNode* newnode, Snake* ps)
{
    if (ps->_pfood->x == newnode->x && ps->_pfood->y == newnode->y)
        return 1;
    else return 0;
}

int NextIsFood(SnakeSingleNode* newnode, Snake* ps)
{
	if (ps->_pfood->x == newnode->x && ps->_pfood->y == newnode->y)
		return 1;
	else return 0;
}

2.3.3 下个节点是食物

void EatFood(SnakeSingleNode* newnode, Snake* ps)
{
    //头插法
    ps->_pfood ->next = ps->_pSnake;
    ps->_pSnake = ps->_pfood;

    free(newnode);
    newnode = NULL;

    SnakeSingleNode* cur = ps->_pSnake;
    while (cur)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", L'●');
        cur = cur->next;
    }

    //加分
    ps->_score += ps->_food_weight;
    //重新创建食物
    FoodInit(ps);
}

void EatFood(SnakeSingleNode* newnode, Snake* ps)
{
	//头插法
	ps->_pfood ->next = ps->_pSnake;
	ps->_pSnake = ps->_pfood;

	free(newnode);
	newnode = NULL;

	SnakeSingleNode* cur = ps->_pSnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", L'●');
		cur = cur->next;
	}

	//加分
	ps->_score += ps->_food_weight;
	//重新创建食物
	FoodInit(ps);
}

2.3.4 下个节点不是食物

void NoFood(SnakeSingleNode* newnode, Snake* ps)
{
    //头插法
    newnode->next = ps->_pSnake;
    ps->_pSnake = newnode;


    //把最后两个位置打印成两个空格
    //最后一个节点释放掉

    SnakeSingleNode* cur = ps->_pSnake;
    while (cur->next->next != NULL)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", L'●');
        cur = cur->next;
    }

    SetPos(cur->next->x, cur->next->y);
    printf("  ");
    free(cur->next);
    cur->next = NULL;
}

void NoFood(SnakeSingleNode* newnode, Snake* ps)
{
	//头插法
	newnode->next = ps->_pSnake;
	ps->_pSnake = newnode;


	//把最后两个位置打印成两个空格
	//最后一个节点释放掉
	SnakeSingleNode* cur = ps->_pSnake;
	while (cur->next->next != NULL)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", L'●');
		cur = cur->next;
	}

	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	free(cur->next);
	cur->next = NULL;
}

2.3.2 是否撞墙

//如果撞墙
void KillByWall(Snake* ps)
{
    if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56
        || ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
    {
        ps->status = KILL_BY_WALL;
    }
}

//如果撞墙
void KillByWall(Snake* ps)
{
	if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56
		|| ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
	{
		ps->status = KILL_BY_WALL;
	}
}

2.3.4 是否撞自己

//如果撞自己
void KillBySelf(Snake* ps)
{
    SnakeSingleNode* cur = ps->_pSnake->next;
    while (cur)
    {
        if(cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
        {
            ps->status = KILL_BY_SELF;
            break;
        }
        cur = cur->next;
    }
}

//如果撞自己
void KillBySelf(Snake* ps)
{
	SnakeSingleNode* cur = ps->_pSnake->next;
	while (cur)
	{
		if(cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
		{
			ps->status = KILL_BY_SELF;
			break;
		}
		cur = cur->next;
	}
}

三. 游戏的结束

3.1 游戏的结束

//游戏的状态
void GameOver(Snake* ps)
{
    SetPos(24, 12);
    switch (ps->status)
    {
    case END_NORMAL:
        printf("您的游戏正常结束\n");
        break;
    case KILL_BY_WALL:
        printf("您的游戏撞上墙\n");
        break;
    case KILL_BY_SELF:
        printf("您的游戏撞到了自己\n");
        break;
    }
    //释放蛇的节点
    SnakeSingleNode* cur = ps->_pSnake;
    while (cur)
    {
        SnakeSingleNode* del = cur;
        cur = cur->next;
        free(del);
    }
}

 

//游戏的状态
void GameOver(Snake* ps)
{
	SetPos(24, 12);
	switch (ps->status)
	{
	case END_NORMAL:
		printf("您的游戏正常结束\n");
		break;
	case KILL_BY_WALL:
		printf("您的游戏撞上墙\n");
		break;
	case KILL_BY_SELF:
		printf("您的游戏撞到了自己\n");
		break;
	}
	//释放蛇的节点
	SnakeSingleNode* cur = ps->_pSnake;
	while (cur)
	{
		SnakeSingleNode* del = cur;
		cur = cur->next;
		free(del);
	}
}

四.总代码

4.1 Game.c

#include "Snake.h"
Snake message;
void game()
{
	//一.游戏各种的初始化
	Snake* ps = &message;
	
	//①游戏的设置(大小,光标,提示,地图)
	GameInit();

	//②游戏的信息(游戏的状态,贪吃蛇的信息,食物的信息)
	SnakeInit(ps);

	//③食物的初始化
	FoodInit(ps);


	//二.游戏的运行
	GameRun(ps);

	//游戏的结束
	GameOver(ps);

}


int main()
{
	setlocale(LC_ALL, "");
	srand((unsigned int)time(NULL));
	game();

	return 0;
}

4.2 Snake.h

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <time.h>
#include <locale.h>
#include <stdbool.h>
#define POS_X 24
#define POS_Y 5

#define KEY_PRESS(vk) ((GetAsyncKeyState(vk) & 1 ) ? 1 : 0 )

//游戏的状态
enum GAME_STATUS
{
	OK,				//正常
	KILL_BY_WALL,	//蛇撞墙
	KILL_BY_SELF,	//蛇撞自己
	END_NORMAL		//正常退出
};


//蛇的方向
enum Direction
{
	UP = 1,
	DOWN = 2,
	LEFT = 3,
	RIGHT = 4
};

//蛇的节点信息
typedef struct SnakeSingleNode
{
	int x;
	int y;
	struct SnakeSingleNode* next;
}SnakeSingleNode;


//游戏的各种信息
typedef struct Snake
{
	//一.系统的信息
	//①游戏的状态
	enum GAME_STATUS status;

	//二.蛇的信息
	//①找到蛇头
	SnakeSingleNode* _pSnake;

	//②蛇的方向
	enum Direction _dir;

	//③蛇的速度
	int _sleep_time;


	//三.食物信息
	//①食物的节点
	SnakeSingleNode* _pfood;

	//②当前食物的分数
	int _food_weight;

	//③吃掉食物的总分数
	int _score;

}Snake;

void GameInit(void);
void WelcomeToGame(void);

void SetPos(int x, int y);
void CreateMap(void);
void SnakeInit(Snake* ps);
void FoodInit(Snake* ps);

void GameRun(Snake* ps);
void PrintfHelpInit(void);
void Pause(void);
int NextIsFood(SnakeSingleNode* newnode, Snake* ps);
void NoFood(SnakeSingleNode* newnode, Snake* ps);

void EatFood(SnakeSingleNode* newnode, Snake* ps);
void KillByWall(Snake * ps);
void KillBySelf(Snake* ps);
void GameOver(Snake *ps);


4.3 Snake.c

#include "Snake.h"

//光标定位
void SetPos(int x, int y)
{
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x,y };
	SetConsoleCursorPosition(houtput, pos);
}

void CreateMap(void)
{
	//上
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", L'□');
	}

	//下
	SetPos(0, 26);
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", L'□');
	}

	//左
	for (int i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", L'□');
	}
	//右
	for (int i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", L'□');
	}
}



//环境界面的打印
void WelcomeToGame(void)
{
	SetPos(40, 14);
	printf("欢迎来到贪吃蛇游戏\n");
	SetPos(42, 20);

	system("pause");
	system("cls");
	SetPos(35, 14);
	printf("用↑ ↓ ← →控制蛇的移动 F3加速 F4减速\n");
	
	SetPos(35, 15);
	printf("加速更高分数\n");
	SetPos(39, 16);
	system("pause");
	system("cls");

}

//信息的初始化
void SnakeInit(Snake* ps)
{
	ps->_pSnake = NULL;
	//一.贪吃蛇的初始化
	//①.申请五个节点
	SnakeSingleNode* newnode = NULL;
	for (int i = 0; i < 5; i++)
	{
		newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
		if (newnode == NULL)
		{
			perror("malloc");
			return;
		}

		//设置节点信息
		newnode->x = POS_X + 2 * i;
		newnode->y = POS_Y;
		newnode->next = NULL;  // 关键修复:初始化 next 指针
		if (ps->_pSnake == NULL)//如果只有一个节点
		{
			ps->_pSnake = newnode;
		}
		else//如果很多节点
		{
			//头插
			newnode->next = ps->_pSnake;
			ps->_pSnake = newnode;
		}
	}

	//②插入完要打印
	SnakeSingleNode* cur = ps->_pSnake;
	while (cur)
	{
		//光标定位
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", L'●');
		cur = cur->next;
	}

	//二.其他信息初始化
	//①.开始方向为向右
	ps->_dir = RIGHT;

	//②开始分数为0
	ps->_score = 0;

	//③每个食物的分数
	ps->_food_weight = 10;

	//④蛇的速度
	ps->_sleep_time = 100;

	//⑤游戏的状态
	ps->status = OK;

}


void GameInit(void)
{
	//一.设置
	//①大小
	system("mode con lines=30 cols=100 ");
	system("title CSDN 钓鱼的猫猫");

	//②光标隐藏
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	
	CONSOLE_CURSOR_INFO cur_info = { 0 };
	GetConsoleCursorInfo(houtput, &cur_info);
	cur_info.bVisible = false;
	SetConsoleCursorInfo(houtput,&cur_info);

	//二.提示信息
	//①开始界面
	WelcomeToGame();
	//②地图创建
	CreateMap();

}

void FoodInit(Snake* ps)
{
	
	//一.生成食物的坐标
	int x = 0; int y = 0;
	
again:
	do
	{
		//①生成随机的坐标
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
		
	} while (x % 2 != 0);//让坐标必须是x为2的倍数

	//③x 和 y 不能是蛇的节点
	SnakeSingleNode* cur = ps->_pSnake;
	while (cur != NULL)
	{
		if ((x == cur->x) && (y == cur->y))
		{
			goto again;
		}
		cur = cur->next;
	}
	
	//二.此时已经创建好节点
	//创建食物的节点
	SnakeSingleNode * newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}

	newnode->x = x;
	newnode->y = y;

	newnode->next = NULL;

	//三.创建完节点应该打印食物信息
	SetPos(x, y);
	wprintf(L"%lc", L'★');


	//四.记录该信息
	ps->_pfood = newnode;

}

void PrintfHelpInit(void)
{
	SetPos(64, 13);
	wprintf(L"%ls", L"1. 不能穿墙\n");
	SetPos(64, 14);
	wprintf(L"%ls", L"2. ↑ ↓ ←  → 控制蛇的移动\n");
	SetPos(64, 15);
	wprintf(L"%ls", L"3. 按F3加速,按F4减速\n");
	SetPos(64, 16);
	wprintf(L"%ls", L"4. 按Esc退出,按空格暂停\n");
	SetPos(64, 18);
	wprintf(L"%ls", L"CSDN 钓鱼的猫猫\n");
}

//暂停函数
void Pause(void)
{
	while (1)
	{
		Sleep(200);
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
	}
}

int NextIsFood(SnakeSingleNode* newnode, Snake* ps)
{
	if (ps->_pfood->x == newnode->x && ps->_pfood->y == newnode->y)
		return 1;
	else return 0;
}

void EatFood(SnakeSingleNode* newnode, Snake* ps)
{
	//头插法
	ps->_pfood ->next = ps->_pSnake;
	ps->_pSnake = ps->_pfood;

	free(newnode);
	newnode = NULL;

	SnakeSingleNode* cur = ps->_pSnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", L'●');
		cur = cur->next;
	}

	//加分
	ps->_score += ps->_food_weight;
	//重新创建食物
	FoodInit(ps);
}

//下一个是节点
void NoFood(SnakeSingleNode* newnode, Snake* ps)
{
	//头插法
	newnode->next = ps->_pSnake;
	ps->_pSnake = newnode;


	//把最后两个位置打印成两个空格
	//最后一个节点释放掉
	SnakeSingleNode* cur = ps->_pSnake;
	while (cur->next->next != NULL)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", L'●');
		cur = cur->next;
	}

	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	free(cur->next);
	cur->next = NULL;
}

//如果撞墙
void KillByWall(Snake* ps)
{
	if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56
		|| ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
	{
		ps->status = KILL_BY_WALL;
	}
}

//如果撞自己
void KillBySelf(Snake* ps)
{
	SnakeSingleNode* cur = ps->_pSnake->next;
	while (cur)
	{
		if(cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
		{
			ps->status = KILL_BY_SELF;
			break;
		}
		cur = cur->next;
	}
}







//蛇的移动函数
void SnakeMove(Snake* ps)
{
	//1.创建一个节点
	SnakeSingleNode* newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}
	//移动
	switch (ps->_dir)
	{
	case UP:
		newnode->x = ps->_pSnake->x;
		newnode->y = ps->_pSnake->y - 1;
		break;
	case DOWN:
		newnode->x = ps->_pSnake->x;
		newnode->y = ps->_pSnake->y + 1;
		break;
	case LEFT:
		newnode->x = ps->_pSnake->x-2;
		newnode->y = ps->_pSnake->y;
		break;
	case RIGHT:
		newnode->x = ps->_pSnake->x + 2;
		newnode->y = ps->_pSnake->y;
		break;
	}

	//2.检测下一个节点是否是食物
	if (NextIsFood(newnode, ps))
	{
		EatFood(newnode, ps);
	}
	else
	{
		NoFood(newnode, ps);
	}

	//3.检测是否撞墙
	KillByWall(ps);

	//4.检测是否撞自己
	KillBySelf(ps);

}


//游戏运行函数
void GameRun(Snake* ps)
{
	//一.首先是固定的打印信息
	PrintfHelpInit();

	//二.检测按键
	do
	{
		//①打印总分数和食物的分值
		SetPos(64, 10);
		printf("总分数:%d\n", ps->_score);
		SetPos(64,11);
		printf("当前食物分数:%2d\n",ps->_food_weight);

		//②检测按键
		if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)
		{
			ps->_dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)
		{

			ps->_dir = DOWN;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
		{
			ps->_dir = LEFT;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
		{
			ps->_dir = RIGHT;
		}
		else if (KEY_PRESS(VK_SPACE) )
		{
			Pause();
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			ps->status = END_NORMAL;
		}
		else if (KEY_PRESS(VK_F3))
		{
			//加速=》时间变短
			if (ps->_sleep_time > 80)
			{
				ps->_sleep_time -= 30;
				ps->_food_weight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//减速=》时间变长
			if (ps->_food_weight > 2)
			{
				ps->_sleep_time += 30;
				ps->_food_weight -= 2;
			}
		}

		SnakeMove(ps);
		Sleep(ps->_sleep_time);

	} while (ps->status == OK);
}

void GameOver(Snake* ps)
{
	SetPos(24, 12);
	switch (ps->status)
	{
	case END_NORMAL:
		printf("您的游戏正常结束\n");
		break;
	case KILL_BY_WALL:
		printf("您的游戏撞上墙\n");
		break;
	case KILL_BY_SELF:
		printf("您的游戏撞到了自己\n");
		break;
	}
	//释放蛇的节点
	SnakeSingleNode* cur = ps->_pSnake;
	while (cur)
	{
		SnakeSingleNode* del = cur;
		cur = cur->next;
		free(del);
	}
}

五. 最后

小猫尽力了,请见谅!😘

那么今天的学习就到这里了。友友们觉得不错的可以给个关注点赞或者收藏哦!😘感谢各位友友们的支持。希望各位大佬们自行检验哦,毕竟亲手操作让记忆更加深刻。

你的❤️点赞是我创作的动力的源泉
你的✨收藏是我奋斗的方向
你的🙌关注是对我最大的支持
你的✏️评论是我前进的明灯
创作不易,希望友友们支持一下小猫吧😘

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值