项目实践---贪吃蛇小游戏(下)

发布于:2024-04-24 ⋅ 阅读:(19) ⋅ 点赞:(0)

  • 对于贪吃蛇小游戏,最主要的还是主函数部分,这里就和大家一一列举出来,
  • 上一章已经写过头文件了,这里就不多介绍了。

  • 首先就是打印桌面,也就是背景,则对应的代码为:
void SetPos(short x, short y)
{
	//获取标准输出设备的句柄
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);


	//定位光标的位置
	COORD pos = { x,y };
	SetConsoleCursorPosition(houtput, pos);
}


void WelcomeToGame()
{
	system("cls");
	SetPos(40, 14);
	wprintf(L"欢迎来到贪吃蛇小游戏\n");
	SetPos(42, 20);
	system("pause");
	system("cls");
	SetPos(35, 14);
	wprintf(L"用 w.s.a.d来控制蛇的移动,按q加速,e减速\n");
	SetPos(35, 15);
	wprintf(L"加速能够得到更高的分数\n");

	SetPos(42, 20);
	system("pause");
	system("cls");

}

  • 然后就是打印墙体,在打印墙体之前,要先声明一下类型,用#define,来定义。则对应的代码为:
void CreateMap()
{
	//上
	int i = 0;
	SetPos(0, 0);
	for (i = 0; i < 58; i ++)
	{
		wprintf(L"%lc", WALL);
	}

	//下
	SetPos(0, 26);
	for (i = 0; i < 58; i++) 
	{
		wprintf(L"%lc", WALL);
	}

	//左
	for (i = 1; i <= 26; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}

	//右
	for (i = 1; i <= 26; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc",WALL);

	}
}

  • 然后就是创建蛇身(用链表来实现)和食物,则代码为:
    void InitSnake(pSnake ps)
    {
    	int i = 0;
    	pSnakeNode cur = NULL;
    
    	for (i = 0; i < 6; i++)
    	{
    		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
    		if (cur == NULL)
    		{
    			perror("InitSnake()::malloc");
    			return;
    		}
    		cur->next = NULL;
    		cur->x = POS_X + 2 * i;
    		cur->y = POS_Y;
    
    		//头插法插入链表
    
    		if (ps->_pSnake == NULL)  //空链表
    		{
    			ps->_pSnake = cur;
    		}
    		else  //非空
    		{
    			cur->next = ps->_pSnake;
    			ps->_pSnake = cur;
    		}
    	}
    
    	cur = ps->_pSnake;
    	while (cur)
    	{
    		SetPos(cur->x, cur->y);
    		wprintf(L"%lc", BODY);
    		cur = cur->next;
    	}
    
    
    	//设置贪吃蛇得属性
    	ps->_dir = RIGHT; //默认向右
    	ps->_score = 0;
    	ps->_food_weight = 10;
    	ps->_sleep_time = 200;   //单位是ms
    	ps->_status = OK;
    }
    
    
    void CreateFood(pSnake ps)
    {
    	int x = 0;
    	int y = 0;
    
    
    	//生成2的倍数
    	//x:2~53
    	//y:1~25
    
    again:
    	do
    	{
    		x = rand() % 53 + 2;
    		y = rand() % 25 + 1;
    	} while (x % 2 != 0);
    
    
    	//x和y的坐标不能和蛇的身体坐标冲突
    
    
    	pSnakeNode cur = ps->_pSnake;
    	while(cur)
    	{
    		if (x == cur->x && y == cur->y)
    		{
    			goto again;
    		}
    
    		cur = cur->next;
    	}
    
    	//创建食物的节点
    
    	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
    	if (pFood == NULL)
    	{
    		return;
    	}
    	pFood->x = x;
    	pFood->y = y;
    	pFood->next = NULL;
    
    	SetPos(x, y);//定义位置
    
    	wprintf(L"%lc", FOOD);
    	ps->_pFood = pFood;
    }

    有点小多,请耐心看完哦。

  • 蛇的移步和游戏的初始化,则代码为:
    void GameStart(pSnake ps)
    {
    	//1.先设置窗口得大小,再光标隐藏
    	system("mode con cols = 100 lines = 30");
    	system("title 贪吃蛇");
    	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
    
    	//隐藏光标操作
    	CONSOLE_CURSOR_INFO CursorInfo;
    	GetConsoleCursorInfo(houtput, &CursorInfo);//获取控制台光标信息
    	CursorInfo.bVisible = false; //隐藏控制台光标
    	SetConsoleCursorInfo(houtput, &CursorInfo);  //设置控制台光标状态
    
    	//2.打印环境界面和功能介绍
    	WelcomeToGame();
    
    	//3.绘制地图
    	CreateMap();
    
    	//4.创建蛇
    	InitSnake(ps);
    
    	//5.创建食物
    	CreateFood(ps);
    }
    
    void SnakeMove(pSnake ps)
    {
    	//创建一个节点,表示蛇即将到的下一个节点
    	pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
    	if (pNextNode == NULL)
    	{
    		perror("SnakeMove()::malloc()");
    		return;
    	}
    	switch (ps->_dir)
    	{
    	case UP:
    		pNextNode->x = ps->_pSnake->x;
    		pNextNode->y = ps->_pSnake->y - 1;
    		break;
    
    	case DOWN:
    		pNextNode->x = ps->_pSnake->x;
    		pNextNode->y = ps->_pSnake->y + 1;
    		break;
    
    	case LEFT:
    		pNextNode->x = ps->_pSnake->x-2;
    		pNextNode->y = ps->_pSnake->y;
    		break;
    
    	case RIGHT:
    		pNextNode->x = ps->_pSnake->x+2;
    		pNextNode->y = ps->_pSnake->y;
    		break;
    	}
    
    	//检测下一个坐标处是否是食物
    	if (NextIsFood(pNextNode, ps))
    	{
    		EatFood(pNextNode, ps);
    	}
    	else
    	{
    		NoFood(pNextNode, ps);
    	}
    }

  • 判断下一个坐标是否是食物和下一个位置是食物和下一个位置不是食物,就吃掉食物:则代码为:

    int NextIsFood(pSnakeNode pn, pSnake ps)
    {
    	return (ps->_pFood->x == pn->x && ps->_pFood->y == pn->y);
    }
    
    void EatFood(pSnakeNode pn, pSnake ps)
    {
    	//头插法
    	ps->_pFood->next = ps->_pSnake;
    	ps->_pSnake = ps->_pFood;
    
    	//释放下一个位置的节点
    	free(pn);
    	pn = NULL;
    
    
    	pSnakeNode cur = ps->_pSnake;
    
    	//打印蛇
    	while (cur)
    	{
    		SetPos(cur->x, cur->y);
    		wprintf(L"%lc", BODY);
    		cur = cur->next;
    	}
    	ps->_score += ps->_food_weight;
    
    	//重新创建食物
    	CreateFood(ps);
    }
    
    void NoFood(pSnakeNode pn, pSnake ps)
    {
    	//头插法
    	pn->next = ps->_pSnake;
    	ps->_pSnake = pn;
    
    	pSnakeNode cur = ps->_pSnake;
    	while (cur->next->next != NULL)
    	{
    		SetPos(cur->x, cur->y);
    		wprintf(L"%lc", BODY);
    		cur = cur->next;
    	}
    
    	//把最后一个节点打印成空格
    	SetPos(cur->next->x, cur->next->y);
    	printf(" ");
    
    	//释放最后一个节点
    	free(cur->next);
    
    	//把倒数第二个节点的地址置为NULL
    	cur->next = NULL;
    }

    确实多,请耐心看完。

  • 游戏运行的逻辑,则代码为:
    void GameRun(pSnake ps)
    {
    	//打印帮助信息
    	PrintHelpInfo();
    
    
    	do
    	{
    		//打印总分数和食物的分值
    		SetPos(64, 10);
    		printf("总分数:%d\n", ps->_score);
    		SetPos(64, 11);
    		printf("当前食物的分数:%d\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 pause()
    {
    	while (1)
    	{
    		Sleep(200);
    		if(KEY_PRESS(VK_SPACE))
    		{
    			break;
    		}
    	}
    }
    
    void KillByWall(pSnake 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(pSnake ps)
    {
    	pSnakeNode 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 GameEnd(pSnake ps)
    {
    	SetPos(24, 12);
    	switch (ps->_status)
    	{
    	case END_NORMAL:
    		wprintf(L"你主动退出\n");
    		break;
    	case KILL_BY_WALL:
    		wprintf(L"您撞上自己了 ,游戏结束!\n");
    		break;
    	case KILL_BY_SELF:
    		wprintf(L"您撞墙了,游戏结束!\n");
    		break;
    	}
    
    
    	//释放蛇身的链表
    
    	pSnakeNode cur = ps->_pSnake;
    	while (cur)
    	{
    		pSnakeNode del = cur;
    		cur = cur->next;
    		free(del);
    	}
    }
    
    

  • 这贪吃蛇的主函数代码确实多,但也是最重要一部分。请大家耐心看完哦。