c++ 实现贪吃蛇(含技术难点解析和完整代码)

0.参考资料

借鉴了这位大佬的博客及代码,在其基础上进行了修改,特此鸣谢。
https://blog.csdn.net/qq_45359344/article/details/103254166

1 技术难点

1.1 关于光标的移动

这一步需要手写一个gotoxy的函数,其中涉及到的一些知识点见下链接:
关于COORD
https://zhidao.baidu.com/question/243419995.html
关于handle
https://blog.csdn.net/qq_31967569/article/details/102726894
这里需要注意的是COORD里的x,y是指的横纵坐标,而我们(或者只有我)平常在使用二维数组时习惯按照线性代数中矩阵的表达形式,即第一维放纵坐标,第二维放横坐标。所以这里的x,y要反着赋值(这个bug d了好久qwq)

void gotoxy(int x,int y)
{
    COORD pos;//COORD是一种自带结构体,表示一个字符在控制台屏幕上的坐标
    HANDLE han=GetStdHandle(STD_OUTPUT_HANDLE); //从标准输出设备里取出一个句柄
    pos.X=y,pos.Y=x;
    SetConsoleCursorPosition(han,pos);//定位光标的函数
}

1.2 关于蛇的移动

主要有两种实现方式,一种使用指针加链表,一种使用数组。笔者选择的是数组的方式,这种方法代码量略大,且较为丑陋,但是易于理解。每次操作后只需要清空蛇尾(如果吃到食物就不用),打印蛇头,以及将蛇身的坐标顺次移动。

1.2.1 从键盘上读取输入

用kbhit()和getch()函数

1.2.2 蛇的移动

需要注意的是如果此时蛇是向上移动的话,点击向下移动是无效的。所以需要将蛇的运动状态记录下来。而在写switch,case时注意要写default,即不进行操作时,保持原运动状态。

1.3 食物的生成

一个简单的srand制造随机数就可以实现。需要注意的是,需要判断食物是否和当前蛇的位置重合,方法是暴力(枚举蛇的每一块)。可以写成一个函数来实现。

2.完整代码

#include<cstdio>
#include<iostream>
#include<ctime>
#include<stdlib.h>
#include<windows.h>
#include <conio.h>
using namespace std;
#define frame_width 50
#define frame_height 25

typedef struct{
    int x,y;
} Food;
typedef struct{
    int x[100],y[100],len,state;
} Snake;

void gotoxy(int x,int y);  //最重要的一个函数,控制光标的位置
void print_map();
void get_newfood();//生成新食物
bool check_foodinsnake();//检查新食物有没有在蛇身上
void move_snake();
void check_foodeating();
bool check_snakealive();


//需要用到的全局变量
int score;
Snake snake;
Food food;
bool check_eaten;

int main()
{
    system("color 0B");
    do
    {
        system("cls");
        print_map();
        score=0,check_eaten=0;
        //贪吃蛇的每回合运行控制
        while(1)
        {
            check_foodeating();//system("pause");
            move_snake();
            Sleep(max(50,300-score));//控制速度(与长度呈反比)
            if(!check_snakealive())
                break;
        }
        printf("Game Over!\n");
        printf("1:Restart\t2:exit\n");
        char com2;
        cin>>com2;
        if(com2=='2')
            break;
    }while(1);
}

void gotoxy(int x,int y)
{
    COORD pos;//COORD是一种自带结构体,表示一个字符在控制台屏幕上的坐标
    HANDLE han=GetStdHandle(STD_OUTPUT_HANDLE); //从标准输出设备里取出一个句柄
    pos.X=y,pos.Y=x;
    SetConsoleCursorPosition(han,pos);//定位光标的函数
}

void print_map()
{
    //打印墙壁
    for(int i=0;i<frame_height;i++)
    {
        gotoxy(i,0);
        printf("#");
        gotoxy(i,frame_width-1);//因为这个标记是长度,从零开始所以最后要减1
        printf("#");
    }
    for(int i=0;i<frame_width;i++)
    {
        gotoxy(0,i);
        printf("#");
        gotoxy(frame_height-1,i);
        printf("#");
    }

    //蛇身初始化
    snake.len=3;
    snake.state='w';
    snake.x[1]=frame_height/2;
    snake.y[1]=frame_width/2;
    gotoxy(snake.x[1],snake.y[1]);
    printf("@");
    for(int i=2;i<=snake.len;i++)
    {
        snake.x[i]=snake.x[i-1]+1;
        snake.y[i]=snake.y[i-1];
        gotoxy(snake.x[i],snake.y[i]);
        printf("@");
    }

    //打印初始食物
    get_newfood();

    //打印右边状态栏
    gotoxy(2,frame_width+3);
    printf("WELCOME TO THE GAME OF RETRO SNAKE");
    gotoxy(4,frame_width+3);
    printf("UP:   w");
    gotoxy(6,frame_width+3);
    printf("DOWN: s");
    gotoxy(8,frame_width+3);
    printf("LEFT: a");
    gotoxy(10,frame_width+3);
    printf("RIGHT:d");
    gotoxy(12,frame_width+3);
    printf("Your score:%d",score);
    gotoxy(28,frame_width+3);
    printf("Made by jokersio");
}

bool check_foodinsnake()
{
    for(int i=1;i<=snake.len;i++)
        if(snake.x[i]==food.x&&snake.y[i]==food.y)
            return 1;
    return 0;
}

void get_newfood()
{
    do{
        srand(time(0));
        food.x=rand()%(frame_height-2)+1;
        food.y=rand()%(frame_width-2)+1;
    }while(check_foodinsnake());
    gotoxy(food.x,food.y);
    cout<<"$";
}

void move_snake()
{
    char com;
    while(kbhit())//键盘有输入
        com=getch();//从控制台读取一个字符,但不显示在屏幕上
    //没有吃到去除蛇尾
    if(!check_eaten)
    {
        gotoxy(snake.x[snake.len],snake.y[snake.len]);
        printf(" ");
    }
    //将除蛇头外的其他部分向前移动
    for(int i=snake.len;i>1;i--)
        snake.x[i]=snake.x[i-1],
        snake.y[i]=snake.y[i-1];
    //移动蛇头
    switch(com)
    {
        case 'w':
        {
            if(snake.state=='s') //如果命令与当前方向相反不起作用
                snake.x[1]++;
            else
                snake.x[1]--,snake.state='w';
            break;
        }
        case 's':
        {
            if(snake.state=='w')
                snake.x[1]--;
            else
                snake.x[1]++,snake.state='s';
            break;
        }
        case 'a':
        {
            if(snake.state=='d')
                snake.y[1]++;
            else
                snake.y[1]--,snake.state='a';
            break;
        }
        case 'd':
        {
            if(snake.state=='a')
                snake.y[1]--;
            else
                snake.y[1]++,snake.state='d';
            break;
        }
        default: //按其余键保持状态前进
        {
            if(snake.state=='s')
                snake.x[1]++;
            else if(snake.state=='w')
                snake.x[1]--;
            else if(snake.state=='d')
                snake.y[1]++;
            else if(snake.state=='a')
                snake.y[1]--;
            break;
        }
    }
    gotoxy(snake.x[1],snake.y[1]);
    printf("@");
    check_eaten=0;
    gotoxy(frame_height,0);
}

void check_foodeating()
{
    if(snake.x[1]==food.x&&snake.y[1]==food.y)
    {
        score+=10;
        check_eaten=1;
        gotoxy(12,frame_width+3);
        printf("Your score:%d",score);
        snake.len++;
        get_newfood();
    }
}

bool check_snakealive()
{
    //检查有没有撞到墙
    if(snake.x[1]==0||snake.x[1]==frame_height-1||snake.y[1]==0||snake.y[1]==frame_width-1)//撞墙
           return 0;
    //检查有没有吃到自己
    for(int i=2;i<=snake.len;i++)
        if(snake.x[i]==snake.x[1]&&snake.y[i]==snake.y[1])
            return 0;
    return 1;
}

#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <windows.h> #define MAX_X 20 #define MAX_Y 20 int x, y, fruit_x, fruit_y, score, gameover; int tail_x[100], tail_y[100], ntail; enum eDirection { STOP = , LEFT, RIGHT, UP, DOWN }; enum eDirection dir; void Setup() { gameover = ; dir = STOP; x = MAX_X / 2; y = MAX_Y / 2; fruit_x = rand() % MAX_X; fruit_y = rand() % MAX_Y; score = ; } void Draw() { system("cls"); for (int i = ; i < MAX_X + 2; i++) printf("#"); printf("\n"); for (int i = ; i < MAX_Y; i++) { for (int j = ; j < MAX_X; j++) { if (j == ) printf("#"); if (i == y && j == x) printf("O"); else if (i == fruit_y && j == fruit_x) printf("F"); else { int print = ; for (int k = ; k < ntail; k++) { if (tail_x[k] == j && tail_y[k] == i) { printf("o"); print = 1; } } if (!print) printf(" "); } if (j == MAX_X - 1) printf("#"); } printf("\n"); } for (int i = ; i < MAX_X + 2; i++) printf("#"); printf("\n"); printf("Score: %d\n", score); } void Input() { if (_kbhit()) { switch (_getch()) { case 'a': dir = LEFT; break; case 'd': dir = RIGHT; break; case 'w': dir = UP; break; case 's': dir = DOWN; break; case 'x': gameover = 1; break; } } } void Logic() { int prev_x = tail_x[]; int prev_y = tail_y[]; int prev2_x, prev2_y; tail_x[] = x; tail_y[] = y; for (int i = 1; i < ntail; i++) { prev2_x = tail_x[i]; prev2_y = tail_y[i]; tail_x[i] = prev_x; tail_y[i] = prev_y; prev_x = prev2_x; prev_y = prev2_y; } switch (dir) { case LEFT: x--; break; case RIGHT: x++; break; case UP: y--; break; case DOWN: y++; break; default: break; } if (x >= MAX_X) x = ; else if (x < ) x = MAX_X - 1; if (y >= MAX_Y) y = ; else if (y < ) y = MAX_Y - 1; for (int i = ; i < ntail; i++) if (tail_x[i] == x && tail_y[i] == y) gameover = 1; if (x == fruit_x && y == fruit_y) { score += 10; fruit_x = rand() % MAX_X; fruit_y = rand() % MAX_Y; ntail++; } } int main() { Setup(); while (!gameover) { Draw(); Input(); Logic(); Sleep(50); } return ; }
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值