贪吃蛇游戏

贪食蛇(也叫贪吃蛇)是一款经典的小游戏。初始是像素版本,后来又衍生出3D版本、多人对战版本等。
这里使用C语言来实现DOS下的贪吃蛇小游戏。

开发工具:Visual Studio 2019
开发语言:C语言

游戏开始后,会在中间位置出现一条只有一个个节点的贪吃蛇,并随机出现一个食物。
贪吃蛇开始移动后,和大部分游戏一样,你可以通过W、A、S、D四个键来控制移动方向。
当贪吃蛇出界或者撞到自己时,游戏结束。
游戏源码如下所示:
snake.h

#ifndef __SNAKE_H__
#define __SNAKE_H__

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <conio.h>
#include <Windows.h>

#define WIDTH 60    // 宽度
#define HEIGHT 20   // 高度
// 身体的对象
struct BODY {
	int X;    // x坐标
	int Y;    // y坐标
};

// 定义蛇对象
struct SNACK {
	struct BODY body[WIDTH * HEIGHT];	// 蛇的最大长度
	int length;	// 蛇的长度
} snake;

// 定义食物对象
struct FOOD {
	int X;
	int Y;
} food;

int score = 0;  // 分数

int keyX = 0;	// 用户按下WASD按键得到的坐标值
int keyY = 0;

int lastX = 0;		// 蛇尾的坐标。
int lastY = 0;

int sleepSecond = 400;

// 声明函数
void initSnack(void);
void initFood(void);
void initUI(void);
void playGame(void);
void initWall(void);
void showScore(void);
#endif

Snake.c

#define _CRT_SECURE_NO_WARNINGS
#include "snake.h"

int main() {
	// 去除光标。
	CONSOLE_CURSOR_INFO cci;
	cci.dwSize = sizeof(cci);
	cci.bVisible = FALSE;  // TRUE :
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);

	srand(time(NULL)); // 置随机种子
	initSnack();	// 初始化蛇
	initFood();		// 初始化食物
	initWall();		// 画墙
	initUI();		// 初始化UI
	playGame();		// 启动游戏
	showScore();	// 打印分数
	system("pause");
	return EXIT_SUCCESS;
}

// 开始游戏
void playGame(void) {
	char key = 'd';
	// 判断蛇是否撞墙
	while (snake.body[0].X >= 0 && snake.body[0].X < WIDTH
		&& snake.body[0].Y >= 0 && snake.body[0].Y < HEIGHT){
		// 刷新UI
		initUI();
		// 接收用户按键输入 A S D W
		if (_kbhit()){	// 不阻塞判断用户输入
			key = _getch();
		}
		switch (key) {
			case 'w': keyX = 0; keyY = -1; break;
			case 's': keyX = 0; keyY = +1; break;
			case 'd': keyX = +1; keyY = 0; break;
			case 'a': keyX = -1; keyY = 0; break;
			default: break;
		}

		// 如果蛇头和蛇身体的位置完全一致,则判定为相撞
		for (size_t i = 1; i < snake.length; i++) {
			if (snake.body[0].X == snake.body[i].X
				&& snake.body[0].Y == snake.body[i].Y) {
				return;		// 游戏结束。
			}
		}

		// 蛇头撞食物
		if (snake.body[0].X == food.X && snake.body[0].Y == food.Y){
			// 食物消失,身体增长,加分,加速
			initFood();
			snake.length++;
			score += 10;
			sleepSecond -= 10;	// 加速
		}

		// 存储蛇尾坐标
		lastX = snake.body[snake.length - 1].X;
		lastY = snake.body[snake.length - 1].Y;

		// 蛇移动就是前一节身体赋值给后一节即可
		for (size_t i = snake.length - 1; i > 0; i--) {
			snake.body[i].X = snake.body[i - 1].X;
			snake.body[i].Y = snake.body[i - 1].Y;
		}
		// 蛇头坐标根据用户按键而改变
		snake.body[0].X += keyX;
		snake.body[0].Y += keyY;
		Sleep(sleepSecond);
	}
	return;
}

// 初始化墙
void initWall(void) {
	for (size_t i = 0; i <= HEIGHT; i++){
		for (size_t j = 0; j <= WIDTH; j++){
			if (j == WIDTH) {
				printf("|");
			} else if (i == HEIGHT) {
				printf("_");
			} else {
				printf(" ");
			}
		}
		printf("\n");
	}
}

// 显示分数
void showScore(void) {
	// 将光标默认位置移动至 不干扰游戏的任意位置。
	COORD coord;

	coord.X = 0;
	coord.Y = HEIGHT + 2;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);

	printf("Game Over!!!\n");
	printf("成绩为:%d\n\n\n", score);
}

// 初始化蛇
void initSnack(void) {
	snake.length = 2;
	// 初始化蛇头的位置
	snake.body[0].X = WIDTH / 2;
	snake.body[0].Y = HEIGHT / 2;
	// 初始化一节蛇身的位置
	snake.body[1].X = (WIDTH / 2) - 1;
	snake.body[1].Y = HEIGHT / 2;
	return;
}

// 初始化界面控件
void initUI(void) {
	// 画蛇
	COORD coord = {0};    // 光标移动的位置
	for (size_t i = 0; i < snake.length; i++) {
		coord.X = snake.body[i].X;
		coord.Y = snake.body[i].Y;
		SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
		if (i == 0)
			putchar('@');
		else
			putchar('*');
	}

	// 去除蛇尾
	coord.X = lastX;
	coord.Y = lastY;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	putchar(' ');

	// 画食物
	coord.X = food.X;
	coord.Y = food.Y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	putchar('&');
}

// 初始化食物
void initFood(void) {
	food.X = rand() % WIDTH;	// 0 - 59
	food.Y = rand() % HEIGHT;	// 0 - 20
	return;
}