当前位置:   article > 正文

C语言学习第016课——项目实训_`define wide = 7

`define wide = 7

贪吃蛇项目

需求:在控制台上,跑一个贪吃蛇,可以按照上下左右方向移动,可以吃食物增加长度,撞墙或者撞到自身结束游戏

参考资料:

一、控制光标的位置和显示

Windows.h 中有一个可以控制光标位置的函数

SetConsoleCursorPosition(HANDLE,COORD);
  • 1

使用方式如下:

#include<stdio.h>
#include<windows.h>

int main(){
    COORD coord;		//先初始化一个coord结构体,结构体里面有两个元素,X和Y
    					//表示光标的坐标
    coord.X = 20;		//给坐标赋值
    coord.Y = 7;
    //然后应用到函数中,HANDLE参数为固定写法
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
    putchar('h');		//打印一个h,看看是否是在20,7这样的坐标
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

运行结果:
在这里插入图片描述
可以实现根据坐标实现光标移动打印
有这个函数,贪吃蛇出现在屏幕的任意地方就可以实现了
以上函数使用完,发现总有“按任意键退出程序”这种东西,我们可以在代码最后,简单的使用一个getchar,意思是我这里还没结束,还要等键盘输入呢,你程序结束的信息不要出来。

int main(){
    COORD coord;
    coord.X = 20;
    coord.Y = 7;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
    putchar('h');
    getchar();
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述
可以看到,getchar是可以解决问题的,但是h后面总有一个光标,闪来闪去的,很烦,解决一下
可以在main中最前面加入此段代码,意为让光标不可见

	CONSOLE_CURSOR_INFO cci;		声明一个控制台光标信息结构体
	cci.dwSize = sizeof(cci);		将里面的信息改一下,
	cci.bVisible = FALSE;
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cci);		再设置回去
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述
可以看到,光标没有了

二、控制键盘输入

贪吃蛇玩法是,按一下方向键,他拐一下弯,如果不按,他就一直走,
使用getchar函数作为接收键盘输入的话,getchar 他是一个阻塞函数,键盘不输入,他就停在那里等着键盘输入了。
解决办法是使用_getch()和_kbhit()函数组合
_kbhit函数的作用是检查看见是否被按下,按下范围非0值,没有按下返回0,该函数是一个非阻塞函数,不管有没有按键按下,都会立即响应 返回
_getch()函数是从控制台中获取输入的字符,获取输入的字符后,并不会在控制台上显示该字符,
举个例子,我需要控制台在我的键盘不动的时候,一直打印“走”,当按“w”“s”“a”“d”的时候,打印“拐弯”

#include<stdio.h>
#include<windows.h>
#include<conio.h>
int main(){
    char ch;
    while(1){
        if(kbhit()){
            //键盘有输入
            ch = _getch();
            if(ch=='w'||ch=='s'||ch=='a'||ch=='d'){
                printf("拐弯\n");
            }
        }else{
            //键盘没有输入
            printf("走\n");
        }
        Sleep(1000);
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

有了以上这些参考资料,实现贪吃蛇游戏就成为可能,我们先分析一下怎么个思路

分析

先界定一个贪吃蛇的活动范围,定义一个宽和高,WIDE和HIGH
定义贪吃蛇结构体:一个字段为它的长度size,另一个字段是坐标数组,这个坐标也可以定义为一个结构体body,所以一个贪吃蛇就是由size个body组成的数组
食物可以随机出现,而且只有一个坐标组成,可以用一个长度为2的int数组表示,
玩游戏的时候,将整个过程放在一个while循环中,每移动一步,计算一次坐标,按照坐标,绘制一次蛇身体,并且判断是否撞墙,是否撞到自己的身体,和是否撞到食物,撞到食物,则size+1

伪代码

有了上面的分析,可以简单的写一个伪代码

定义蛇的身体结构体,蛇结构体(size+身体数组)
确定蛇的活动范围
初始化蛇的size和初始位置坐标
初始化食物的坐标
开始玩游戏
	根据坐标在控制台上画蛇
	控制方向(不可以向反方向移动)
	检查是否撞墙
	检查是否撞到自己身体
	碰到食物
		长度+1
		重新初始化食物
	更新蛇的位置(根据移动方向,更新蛇头坐标,蛇身体位置的其他坐标也更新)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

代码

简单的代码测试一下宽和高,然后宏定义为常量

#define WIDE 78
#define HIGH 24
  • 1
  • 2

定义蛇的结构体

struct BODY{
    int X;
    int Y;
};

struct SNAKE{
    int size;
    struct BODY body[WIDE*HIGH];	//将蛇的长度定义为最大
}snake;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

初始化蛇的长度和初始坐标

void InitSnake(){
    snake.size = 3;
    snake.body[0].X = WIDE/2;
    snake.body[0].Y = HIGH/2;

    snake.body[1].X = WIDE/2-1;
    snake.body[1].Y = HIGH/2;

    snake.body[2].X = WIDE/2-2;
    snake.body[2].Y = HIGH/2;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

初始化食物

void InitFood(){

    food[0] = rand()%WIDE;
    food[1] = rand()%HIGH;
}
  • 1
  • 2
  • 3
  • 4
  • 5

因为该动作每吃掉一个食物会再次调用一次,所以将srand操作放到了main中
初始化墙:

void InitWall(){
    for(int i=0;i<=HIGH;i++){
        for(int j = 0;j<=WIDE;j++){
            if(i==HIGH){
                putchar('=');
            }else if(j==WIDE){
                putchar('=');
            }else{
                putchar(' ');
            }
        }
        putchar('\n');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

画蛇和画食物的过程,因为每次都需要调用,所以分离出来一个函数

void ShowUI(){
    COORD coord;
    //处理蛇尾巴
    coord.X = lx;
    coord.Y = ly;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
    putchar(' ');
    //显示蛇的位置
    for(int i=0;i<snake.size;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 = food[0];
    coord.Y = food[1];
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
    putchar('#');
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

开始玩游戏

void PlayGame(){
    char key = 'D';			//初始化一个刚开始移动的方向
    while(snake.body[0].X<WIDE && snake.body[0].Y<HIGH
          && snake.body[0].X>=0 && snake.body[0].Y>=0){				
          //只要在墙内,就可以循环
        ShowUI();	//根据坐标画蛇和食物
        //方向控制
        while(_kbhit()){	//如果有按键
            key = _getch();		//获取到按键
            switch(lastkey){		//和当前蛇的运行方向相比较,如果相反,则还按之前的方向运行,不理会
                case 'D':case 'd':
                    if(key=='a' || key=='A'){
                        key = 'D';
                    }
                    break;
                case 'S':case 's':
                    if(key=='w' || key=='W'){
                        key = 'S';
                    }
                    break;
                case 'A':case 'a':
                    if(key=='D' || key=='d'){
                        key = 'A';
                    }
                    break;
                case 'W':case 'w':
                    if(key=='s' || key=='S'){
                        key = 'W';
                    }
                    break;
            }
        }
        lastkey = key;		
         switch(key){	//根据方向按键,计算出蛇头下一步的偏移量
             case 'D':case 'd':dx=1;dy=0;break;
             case 'S':case 's':dx=0;dy=1;break;
             case 'A':case 'a':dx=-1;dy=0;break;
             case 'W':case 'w':dx=0;dy=-1;break;
         }
        //是否和自身碰撞
        for(int i = 1;i<snake.size;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[0] && snake.body[0].Y == food[1]){
            InitFood();	//重新初始化食物
            snake.size++;
            score++;
        }
        //获取蛇尾的坐标,等下次画蛇的时候直接置为空格
        lx = snake.body[snake.size-1].X;
        ly = snake.body[snake.size-1].Y;
        //蛇身体更新位置
        for(int i = snake.size-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+=dx;
        snake.body[0].Y+=dy;
        Sleep(100);
        //system("cls");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

以上代码就编写完了,整体文件查看代码
snake.h

#ifndef __SNACK_H__
#define __SNACK_H__
#define WIDE 78
#define HIGH 24

struct BODY{		// 蛇坐标
    int X;
    int Y;
};

struct SNAKE{
    int size;
    struct BODY body[WIDE*HIGH];
}snake;

int food[2] = {0};	//食物坐标
int score;	
int dx;			//蛇头坐标偏移量
int dy;
int lx;			//蛇尾坐标
int ly;
char lastkey = 'D';

void InitFood();
void InitSnake();
void ShowUI();
void PlayGame();
void InitWall();
#endif // __SNACK_H__

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

main.c


#include <stdio.h>
#include <stdlib.h>
#include "snack.h"
#include <time.h>
#include <Windows.h>
#include <conio.h>
int main()
{
    //去掉控制台光标
    CONSOLE_CURSOR_INFO cci;
    cci.bVisible = FALSE;
    cci.dwSize = sizeof(cci);
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cci);

    srand((size_t)time(NULL));
    InitSnake();
    InitFood();
    InitWall();
    PlayGame();
    getchar();
    return 0;
}

void InitFood(){

    food[0] = rand()%WIDE;
    food[1] = rand()%HIGH;
}
void InitSnake(){
    snake.size = 3;
    snake.body[0].X = WIDE/2;
    snake.body[0].Y = HIGH/2;

    snake.body[1].X = WIDE/2-1;
    snake.body[1].Y = HIGH/2;

    snake.body[2].X = WIDE/2-2;
    snake.body[2].Y = HIGH/2;
}
void ShowUI(){
    COORD coord;
    //显示蛇的位置
    coord.X = lx;
    coord.Y = ly;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
    putchar(' ');
    for(int i=0;i<snake.size;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 = food[0];
    coord.Y = food[1];
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
    putchar('#');
}
void PlayGame(){
    char key = 'D';
    while(snake.body[0].X<WIDE && snake.body[0].Y<HIGH
          && snake.body[0].X>=0 && snake.body[0].Y>=0){
        ShowUI();
        //方向控制
        while(_kbhit()){
            key = _getch();
            switch(lastkey){
                case 'D':case 'd':
                    if(key=='a' || key=='A'){
                        key = 'D';
                    }
                    break;
                case 'S':case 's':
                    if(key=='w' || key=='W'){
                        key = 'S';
                    }
                    break;
                case 'A':case 'a':
                    if(key=='D' || key=='d'){
                        key = 'A';
                    }
                    break;
                case 'W':case 'w':
                    if(key=='s' || key=='S'){
                        key = 'W';
                    }
                    break;
            }
        }
        lastkey = key;
         switch(key){
             case 'D':case 'd':dx=1;dy=0;break;
             case 'S':case 's':dx=0;dy=1;break;
             case 'A':case 'a':dx=-1;dy=0;break;
             case 'W':case 'w':dx=0;dy=-1;break;
         }
        //是否和自身碰撞
        for(int i = 1;i<snake.size;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[0] && snake.body[0].Y == food[1]){
            InitFood();
            snake.size++;
            score++;
        }
        //获取蛇尾的坐标
        lx = snake.body[snake.size-1].X;
        ly = snake.body[snake.size-1].Y;
        //蛇更新位置
        for(int i = snake.size-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+=dx;
        snake.body[0].Y+=dy;
        Sleep(100);
        //system("cls");
    }
}

void InitWall(){
    for(int i=0;i<=HIGH;i++){
        for(int j = 0;j<=WIDE;j++){
            if(i==HIGH){
                putchar('=');
            }else if(j==WIDE){
                putchar('=');
            }else{
                putchar(' ');
            }
        }
        putchar('\n');
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143

运行结果:
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/626575
推荐阅读
相关标签
  

闽ICP备14008679号