【C语言】贪吃蛇

 一,涉及知识点:

结构体链表,动态分配内存,键盘输入检测,设置光标。

 二,实现逻辑

1,涉及多个结构体,分别定义蛇,方向,结点,位置坐标,游戏 
2,结点串联形成链表,遍历获取成员坐标,打印符号得到蛇身。 
3,不断的加头,去尾,重新遍历坐标,再打印形成蛇的移动。
4,食物产生的位置判定,不能越界,也不能与蛇身体重合。 
5,蛇的转向判定,一条规则,不允许倒退。
6,转向的实现,跟行进方向决定新的关节坐标(当前头的上下左右) 
7,死亡检测,是否头节点坐标是否与墙壁重合,是否与身体其他关节重合。 
8,加速减速,设置刷新休眠时间实现。 
9,可以设置光标,就能实现制定位置打印制定符号。

源码

//Model.h  结构体头文件
#pragma once

typedef enum Direction
{
	UP, DOWN, LEFT, RIGHT
}Direction;
typedef struct Position
{
	int x;
	int y;
}Position;
typedef struct Node
{
	Position pos;
	struct Node *next;
}Node;
typedef struct Snake
{
	Node *head;
	Node *tail;
	Direction direction;
}Snake;
typedef struct Game
{
	Snake snake;
	Position food;

	int width;
	int height;
}Game;
//View.h
#pragma once
#include "Model.h"

void SetPos(int X, int Y);
void DisplayWall(int width, int height);
void DisplaySnake(const Snake *pSnake);
void DisplayFood(const Position *pFood);
void CleanSnakeNode(const Position *pPos);
void DisplaySnakeNode(const Position *pPos);
void GameDescription1();
void GameDescription2();
//View.c
#include "View.h"
#include "Model.h"
#include <Windows.h>
#include <stdio.h>

void ViewInit()
{
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	//CONSOLE_CURSOR_INFO 这个结构体包含的是控制台光标的信息
	CONSOLE_CURSOR_INFO ConsoleCursorInfo;
	GetConsoleCursorInfo(hOutput, &ConsoleCursorInfo);
	ConsoleCursorInfo.bVisible = FALSE;
	SetConsoleCursorInfo(hOutput, &ConsoleCursorInfo);
}
//设置光标位置
void SetPos(int X, int Y)
{
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD coord = { X, Y };
	SetConsoleCursorPosition(hOutput, coord);
}

static void TranslateSetPos(int x, int y)
{
	int X = 2 * (x + 1);
	int Y = y + 1;

	SetPos(X, Y);
}

void DisplayWall(int width, int height)
{
	ViewInit();
	// 上
	SetPos(0, 0);
	for (int i = 0; i < width + 2; i++) {
		printf("◆");
	}
	// 下
	SetPos(0, height + 1);
	for (int i = 0; i < width + 2; i++) {
		printf("◆");
	}
	// 左
	for (int i = 0; i < height + 2; i++) {
		SetPos(0, i);
		printf("◆");
	}
	// 右
	for (int i = 0; i < height + 2; i++) {
		SetPos(2 * (width + 1), i);
		printf("◆");
	}
	
}

void DisplaySnake(const Snake *pSnake)
{
	for (Node *node = pSnake->tail; node != NULL; node = node->next) {
		TranslateSetPos(node->pos.x, node->pos.y);
		printf("■");
	}
}

void DisplayFood(const Position *pFood)
{
	TranslateSetPos(pFood->x, pFood->y);
	printf("■");
}

void CleanSnakeNode(const Position *pPos)
{
	TranslateSetPos(pPos->x, pPos->y);
	printf(" ");
}

void DisplaySnakeNode(const Position *pPos)
{
	TranslateSetPos(pPos->x, pPos->y);
	printf("■");
}
//游戏说明界面
void GameDescription1()
{
	SetPos(40, 14);
	printf("欢迎进入贪吃蛇游戏!\n\n");
	system("pause");
	system("color 5e");
	system("cls");
	SetPos(10, 14);
	printf("您将通过键盘控制蛇的移动: 上↑  下↓  左 ←  右→  加速F1  减速F2  暂停space  退出Esc");
	SetPos(10, 15);
	printf("吃到食物身体会变长\n");
	SetPos(10, 16);
	printf("撞到身体或墙壁游戏都会结束\n");
	system("pause");
	system("cls");
}
void GameDescription2()
{
	SetPos(64, 15);
	printf("不能穿墙,不能咬到自己\n");
	SetPos(64, 16);
	printf("用↑ ↓ ← → 控制蛇的移动.");
	SetPos(64, 17);
	printf("F1 为加速,F2 为减速\n");
	SetPos(64, 18);
	printf("ESC :退出游戏    space:暂停游戏.");
}
//Controller.c
#include "Model.h"
#include "View.h"
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <stdbool.h>
#include <time.h>

void SnakeInit(Snake *pSnake)
{
	//链表中头结点是蛇的尾巴,尾结点是蛇的头
	pSnake->tail = NULL;
	for (int i = 0; i < 3; i++){
		Node *node = (Node *)malloc(sizeof(Node));
		//从蛇头开始创建结点
		node->pos.x = 7 + i;
		node->pos.y = 2;

		if (i == 0){
			pSnake->head = node;
		}
		node->next = pSnake->tail;
		pSnake->tail = node; 
	}
	pSnake->direction = LEFT;
}
bool IsOverLap(Position pos,Snake *pSnake)
{
	Node *node;
	for (node = pSnake->tail; node != pSnake->head; node = node->next){
		if (node->pos.x == pos.x&&node->pos.y == pos.y){
			return true;
		}
	}
	return false;
}
//生成食物
Position GenerateFood(int width, int height, Snake *pSnake)
{
	Position pos;
	SetPos(1, 1);
	do
	{
		pos.x = rand() % width;
		pos.y = rand() % height;
	} while (IsOverLap(pos, pSnake));
	DisplayFood(&pos);
	return pos;
}
//初始化Game
void GameInit(Game *pGame)
{
	pGame->width = 28;
	pGame->height = 27;
	//初始化蛇
	SnakeInit(&pGame->snake);
	//初始化食物
	//pGame->food = GenerateFood(pGame->width, pGame->height, &pGame->snake);
}
//得到蛇即将要走的坐标
Position GetNextPosition(const Snake *pSnake)
{
	Position next = pSnake->head->pos;
	switch (pSnake->direction){
	case UP:
		next.y -= 1;
		break;
	case DOWN:
		next.y += 1;
		break;
	case LEFT:
		next.x -= 1;
		break;
	case RIGHT:
		next.x += 1;
		break;
	}
	return next;
}
//蛇头上加一格
void AddHead(Snake *pSnake, Position nextPos)
{
	Node *node = (Node *)malloc(sizeof(Node));
	node->pos = nextPos;
	node->next = NULL;
	pSnake->head->next = node;
	pSnake->head = node;
	DisplaySnakeNode(&nextPos);//注意这里传参 
}
//蛇尾减一格
void RemoveTail(Snake *pSnake)
{
	Node *tail = pSnake->tail;
	pSnake->tail = pSnake->tail->next; 
	CleanSnakeNode(&tail->pos);
	free(tail);
}
//判断蛇能不能吃到食物
bool IsEat(Position food, Position next)
{
	return food.x == next.x&&food.y == next.y;
}
//判断蛇是否撞墙
bool KilledByWall(const Snake *pSnake,int width,int height)
{
	int x = pSnake->head->pos.x;
	int y = pSnake->head->pos.y;
	if (x>=0 && x<width && y>=0 && y<=height){
		return false;
	}
	return true;
}
//判断蛇是否撞到自己
bool KilledBySelf(const Snake *pSnake,Position nextPos)
{
	Node *node = pSnake->tail;
	for (node = pSnake->tail; node != pSnake->head; node = node->next){
		if (node->pos.x == nextPos.x&&node->pos.y == nextPos.y){
			return true;
		}
	}
	return false;
}
//判断死没死
bool GameOver(const Snake *pSnake,Position nextPos,int width,int height)
{
	if (KilledByWall(pSnake,width,height)){
		printf("撞墙了\n");
		return true;
	}
	if (KilledBySelf(pSnake,nextPos)){
		printf("自杀了\n");
		return true;
	}
	else{
		return false;
	}
}
//暂停游戏
void PauseGame()
{
	while (1)
	{
		Sleep(300);
		if (GetAsyncKeyState(VK_SPACE))
		{
			break;
		}
	}
}
void GameRun()
{
	Game game;
	int score = 0,            //得分
		add = 10,             //加分
		sleeptime = 200;      //正常速度
	GameInit(&game);
	
	GameDescription1();
	DisplayWall(game.width,game.height);
	DisplaySnake(&game.snake);
	game.food = GenerateFood(game.width, game.height, &game.snake);
	GameDescription2();
	while (1)
	{
		SetPos(64, 10);
		printf("得分:%d ", score);
		SetPos(64, 11);
		printf("当前食物分值:%d分", add);
		if (game.snake.direction != DOWN&&GetAsyncKeyState(VK_UP)){
			game.snake.direction = UP;
		}
		else if (game.snake.direction != UP&&GetAsyncKeyState(VK_DOWN)){
			game.snake.direction = DOWN;
		}
		else if (game.snake.direction != RIGHT&&GetAsyncKeyState(VK_LEFT)){
			game.snake.direction = LEFT;
		}
		else if (game.snake.direction != LEFT&&GetAsyncKeyState(VK_RIGHT)){
			game.snake.direction = RIGHT;
		}
		else if (GetAsyncKeyState(VK_F1))   //加速
		{
			if (sleeptime > 50)
			{
				sleeptime -= 30;
				add = add + 2;
				if (sleeptime == 320)
				{
					add = 2;//防止减到1之后再加回来有错
				}
			}
		}
		else if (GetAsyncKeyState(VK_F2))   //减速
		{
			if (sleeptime < 350)
			{
				sleeptime = sleeptime + 30;
				add = add - 2;
				if (sleeptime == 350)
				{
					add = 1; //保证最低分为1
				}
			}
		}
		else if (GetAsyncKeyState(VK_SPACE)){
			//暂停
			PauseGame();
		}
		else if (GetAsyncKeyState(VK_ESCAPE))
		{
			//退出
			system("cls");
			SetPos(18,12 );
			printf("退出游戏\n");
			break;
		}
		Position nextPos = GetNextPosition(&game.snake);
		if (IsEat(game.food,nextPos)){
            //头上加一格
			AddHead(&game.snake,nextPos);
			score += add;
			//重新生成一个食物
			game.food = GenerateFood(game.width, game.height, &game.snake);
		}
		else{
           //头上加一格
			AddHead(&game.snake,nextPos);
		   //尾巴减一格
			RemoveTail(&game.snake);
		}
		if (GameOver(&game.snake,nextPos,game.width,game.height)){
			break;
		}
		Sleep(sleeptime);
	}
	
}


//测试控制光标的位置
void DemoSetPos()
{
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD coord;

	for (int i = 0; i < 6; i++) {
		coord.X = 2 * i;
		coord.Y = i;
		SetConsoleCursorPosition(hOutput, coord);

		printf("%02d", i);
	}
}

int main()
{
	//DemoSetPos();
	srand((unsigned)time(NULL));
	system("title ★★贪吃蛇★★");   //改变命令窗口名字
	GameRun();
	
	system("pause");
	system("cls");
	
	return 0;

}

猜你喜欢

转载自blog.csdn.net/ch15029286692/article/details/82953832