------- 基于对C语言的学习,我想通过一个小游戏来告一段落
1 .模块设计
控制台的贪吃蛇小游戏,首先我认为可以分成视图模块,控制模块(键盘控制方向),蛇的移动模块
2.设计步骤
1.第一步当然是创建贪吃蛇结构体然后初始化贪吃蛇了,贪吃蛇有身体、有食物、有边界
贪吃蛇结构体代码如下:
typedef struct body { //蛇体;
int x;
int y;
}BODY;
typedef struct snake {
BODY list[WIDE * HIGH];
int size;
BODY FOOD;
COORD coord; //光标的位置
int dx; //蛇头方向
int dy; //蛇头方向
int score;
BODY tail;
}SNAKE;
COORD 位于windows.h文件中,是一个结构体,用于设置光标位置详情点击这个
1.边界通过尝试我姑且设计成
打印效果如下:
实现函数如下
void init_wall()
{
for (int i = 0; i <= HIGH; i++)
{
for (int j = 0; j <= WIDE; j++)
{
if (i == HIGH || j == WIDE)
{
printf("+");
}
else
{
printf(" ");
}
}
printf("\n");
}
}
2.食物初始化
食物随机生成但不能在蛇身上
关于srand详解
。。。。。。。
3.贪吃蛇整体初始化
void init_FOOD(SNAKE* snake) { //初始化food
srand(time(NULL));
snake->FOOD.x = rand() % 60;
snake->FOOD.y = rand() % 20;
for (int i = 0; i < snake->size; i++) { //食物不能在蛇身上
if (snake->FOOD.x == snake->list[i].x && snake->FOOD.y == snake->list[i].y)
init_FOOD(snake);
}
}
void init_SNAKE(SNAKE* snake) { //初始化蛇
snake->list[0].x = WIDE / 2; //初始化蛇头蛇尾
snake->list[0].y = HIGH/ 2;
snake->list[1].x = WIDE / 2-1; //初始化蛇尾
snake->list[1].y = HIGH / 2;
snake->tail = snake->list[1];
snake->size = 2; //初始化大小
init_FOOD(snake); //初始化食物
snake->dx = 1; //初始化蛇头方向
snake->dy = 0;
snake->score = 0; //初始化分数
}
3.第二步进行蛇的移动了
void move_snake(SNAKE *snake) {
snake->tail.x = snake->list[snake->size - 1].x; //在移动之前记录之前的蛇尾坐标蛇尾,以便于将其设置为空格
snake->tail.y = snake->list[snake->size - 1].y;
for (int i = snake->size-1; i > 0; i--) { //更新身体坐标,从后向前
snake->list[i] = snake->list[i - 1];
}
snake->list[0].x += snake->dx; //蛇头方向
snake->list[0].y += snake->dy; //蛇头方向
}
3.第三步进行蛇的显示了
通过COORD设置
void show_SNAKE(SNAKE *snake) {//展示贪吃蛇
//每次显示蛇都要注意设置光标的位置
for (int i = 0; i < snake->size; i++) {
snake->coord.X = snake->list[i].x;
snake->coord.Y = snake->list[i].y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
if (i == 0) printf("@");
else printf("*");
}
//显示食物
snake->coord.X = snake->FOOD.x;
snake->coord.Y = snake->FOOD.y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
printf("#");
snake->coord.X = snake->tail.x;//覆盖原来的蛇尾为空格
snake->coord.Y = snake->tail.y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
printf(" ");
}
由于打印之前的蛇尾还在显示屏所以要记录原先的蛇尾坐标,在打印新蛇之后,使用空格抹去原来的蛇尾
4.第四步通过键盘来控制方向
kbhit()函数用法 位于<conio.h> 文件
void control_snake(SNAKE* snake) {
char key = 0;
while (_kbhit()) { //判断是否按下建,没有则为零
key = _getch(); //获取键值
}
switch (key) {
case 'a':
snake->dx = -1;
snake->dy = 0;
break;
case 'd':
snake->dx = 1;
snake->dy = 0;
break;
case 'w':
snake->dx = 0;
snake->dy = -1;
break;
case 's':
snake->dx = 0;
snake->dy = 1;
break;
}
5.第五步开始游戏
void start_game(SNAKE* snake) {
while(snake->list[0].x<60&&snake->list[0].x>=0
&& snake->list[0].y < 20 && snake->list[0].x >= 0) {
//控制蛇的方向
control_snake(snake);
move_snake(snake);
//system("cls"); 用这个不好,会清理掉我的边界wall
show_SNAKE(snake);
//是否寄掉了
snake_eat_body(snake);
//是否吃到了食物
snake_eat_food(snake);
Sleep(200);
}
game_end(snake);
}
void game_end(SNAKE* snake) {
//system("cls"); 用这可不好,把我wall搞没了
snake->coord.X = WIDE/2; //更新光标
snake->coord.Y = HIGH/2;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
printf("游戏结束%d分",snake->score);
exit(0); //结束进程
}
void snake_eat_body(SNAKE* snake) {//自己撞到了自己要死
for (int i = 1; i < snake->size; i++) {
if (snake->list[0].x == snake->list[i].x && snake->list[0].y == snake->list[i].y)
game_end(snake);
}
}
void snake_eat_food(SNAKE* snake){ //吃食物边长
if (snake->list[0].x == snake->FOOD.x && snake->list[0].y == snake->FOOD.y) {//蛇头吃到了食物
snake->size++; //不用重新给坐标,因为下一次show_SNAKE的时候新的[size]使用了旧的[size]坐标
//蛇头按照方向改变坐标就行
//食物自动覆盖,消失了,只需要生成新食物
init_FOOD(snake);
snake->score += 10;
}
}
最后全部代码如下:(第一个C语言项目,哎呦终于写完了,这学期过一半,终于可以学算法啦笑哭~~~)
#include <stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<string.h>
#include <conio.h> //含按键设置函数
#define WIDE 60
#define HIGH 20
//初始化屏幕大小 OK
// 初始化蛇和食物,食物要求随机生成
//将蛇和食物显示在屏幕上
void init_ui(){ //初始化屏幕大小
int i=0, j=0;
for ( i = 0; i < HIGH; i++) {
for ( j = 0; j < WIDE; j++) {
printf("#");
}
printf("\n");
}
}
typedef struct body { //蛇体;
int x;
int y;
}BODY;
typedef struct snake {
BODY list[WIDE * HIGH];
int size;
BODY FOOD;
COORD coord; //光标的位置
int dx;
int dy;
int score;
BODY tail;
}SNAKE;
void init_FOOD(SNAKE* snake) { //初始化food
srand(time(NULL));
snake->FOOD.x = rand() % 60;
snake->FOOD.y = rand() % 20;
for (int i = 0; i < snake->size; i++) { //食物不能在蛇身上
if (snake->FOOD.x == snake->list[i].x && snake->FOOD.y == snake->list[i].y)
init_FOOD(snake);
}
}
void init_SNAKE(SNAKE* snake) { //初始化蛇
snake->list[0].x = WIDE / 2; //初始化蛇头蛇尾
snake->list[0].y = HIGH/ 2;
snake->list[1].x = WIDE / 2-1;
snake->list[1].y = HIGH / 2;
snake->tail = snake->list[1];
snake->size = 2; //初始化大小
init_FOOD(snake); //初始化食物
snake->dx = 1; //初始化方向
snake->dy = 0;
snake->score = 0; //初始化分数
}
void show_SNAKE(SNAKE *snake) {//展示贪吃蛇
//每次显示蛇都要注意设置光标的位置
for (int i = 0; i < snake->size; i++) {
snake->coord.X = snake->list[i].x;
snake->coord.Y = snake->list[i].y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
if (i == 0) printf("@");
else printf("*");
}
//显示食物
snake->coord.X = snake->FOOD.x;
snake->coord.Y = snake->FOOD.y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
printf("#");
snake->coord.X = snake->tail.x;//覆盖原来的蛇尾为空格
snake->coord.Y = snake->tail.y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
printf(" ");
}
void move_snake(SNAKE *snake) {
snake->tail.x = snake->list[snake->size - 1].x; //在移动之前记录之前的蛇尾坐标蛇尾,以便于将其设置为空格
snake->tail.y = snake->list[snake->size - 1].y;
for (int i = snake->size-1; i > 0; i--) { //更新身体坐标
snake->list[i] = snake->list[i - 1];
}
snake->list[0].x += snake->dx;
snake->list[0].y += snake->dy;
}
void control_snake(SNAKE* snake) {
char key = 0;
while (_kbhit()) { //判断是否按下建,没有则为零
key = _getch();
}
switch (key) {
case 'a':
snake->dx = -1;
snake->dy = 0;
break;
case 'd':
snake->dx = 1;
snake->dy = 0;
break;
case 'w':
snake->dx = 0;
snake->dy = -1;
break;
case 's':
snake->dx = 0;
snake->dy = 1;
break;
}
}void game_end(SNAKE* snake) {
//system("cls"); 用这可不好,把我wall搞没了
snake->coord.X = WIDE/2; //更新光标
snake->coord.Y = HIGH/2;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
printf("游戏结束%d分",snake->score);
exit(0); //结束进程
}
void snake_eat_body(SNAKE* snake) {
for (int i = 1; i < snake->size; i++) {
if (snake->list[0].x == snake->list[i].x && snake->list[0].y == snake->list[i].y)
game_end(snake);
}
}
void snake_eat_food(SNAKE* snake){
if (snake->list[0].x == snake->FOOD.x && snake->list[0].y == snake->FOOD.y) {//蛇头吃到了食物
snake->size++; //不用重新给坐标,因为下一次show_SNAKE的时候新的[size]使用了旧的[size]坐标
蛇头按照方向改变坐标就行
//食物自动覆盖,消失了,只需要生成新食物
init_FOOD(snake);
snake->score += 10;
}
}
void start_game(SNAKE* snake) {
while(snake->list[0].x<60&&snake->list[0].x>=0
&& snake->list[0].y < 20 && snake->list[0].x >= 0) {
//控制蛇的方向
control_snake(snake);
move_snake(snake);
//system("cls"); 用这个不好,会清理掉我的边界wall
show_SNAKE(snake);
//是否寄掉了
snake_eat_body(snake);
//是否吃到了食物
snake_eat_food(snake);
Sleep(200);
}
game_end(snake);
}
void init_wall()
{
for (int i = 0; i <= HIGH; i++)
{
for (int j = 0; j <= WIDE; j++)
{
if (i == HIGH || j == WIDE)
{
printf("+");
}
else
{
printf(" ");
}
}
printf("\n");
}
}
void hide_cur()
{
//隐藏控制台光标
CONSOLE_CURSOR_INFO cci;
cci.dwSize = sizeof(cci);
cci.bVisible = FALSE;
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
}
int main(){
hide_cur();//隐藏光标
//分配结构体贪吃蛇
SNAKE* snake = (SNAKE*)malloc(sizeof(SNAKE));
//初始化贪吃蛇
init_SNAKE(snake);
//初始化边界
init_wall();
start_game(snake);
return 0;
}