贪吃蛇小游戏(带BGM)

学习完链表之后,利用所学的知识写了一个史上最挫版的贪吃蛇,虽然想再提升一下,加了BGM(背景音乐)但游戏体验还是极差。等学习了图形界面之后再加上游戏界面之后应该游戏体验应该会提升一丢丢吧!
贪吃蛇游戏的主要要解决的问题:

  1. 怎样控制蛇的移动
  2. 蛇吃到食物后蛇的身子怎样加长
  3. 蛇的活动区域
  4. 在蛇的活动区域内怎样随机生成食物
  5. 游戏的结束条件
    主要要解决的就是这几个问题,那直接上代码,用代码来体现这几个问题是如何化解的。
    项目中要用到的数据包含在头文件DataType里面

DataType.h

#pragma once
//在所在的坐标系的坐标
typedef struct Position
{
    int x;
    int y;
}Position;
//构成蛇的每一个节点
typedef struct SnakeNode
{
    Position pos;//坐标
    struct SnakeNode* Pnext;//指向下一个节点
}SnakeNode;
//蛇可能的前进方向
typedef enum Direction
{
    UP,
    DOWN,
    LEFT,
    RIGHT
}Direction;
//蛇本身
typedef struct Snake
{
    SnakeNode *Phead;//蛇头
    SnakeNode*Ptail;//蛇尾
    Direction direction;//蛇要走的方向
}Snake;
//游戏本身所需要的数据
typedef struct Game
{
    Snake snake;
    int score;
    int Perfoodscore;
    Position Foodposition;
    int Sleeptime;
    int speed;
}Game;

game.c

#pragma once
#include"DataType.h"
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<Windows.h>
#include<time.h>
#include"View.h"
#define WIDTH 28
#define HEIGHT 27
#include<mmsystem.h>//音乐播放所需要的头文件
#pragma comment(lib,"winmm.lib")//音乐播放所需要的文件库
//蛇本身自己的初始化
void SnakeInit(Snake*Psnake)
{
    assert(Psnake != NULL);
    Position pos;
    SnakeNode*Pnode;
    Psnake->Phead =Psnake->Ptail= NULL;
    int i = 0;
    int x = 0;
    int y = 0;
    //蛇刚开始时所在的坐标为(5,5)、(6,5)、(7,5)
    for (i = 0; i < 3; i++)
    {
        x = 5 + i;
        y = 5;
        pos.x = x;
        pos.y = y;
        Pnode = (SnakeNode*)malloc(sizeof(SnakeNode));
        assert(Pnode != NULL);
        Pnode->pos = pos;
        Pnode->Pnext = Psnake->Phead;
        Psnake->Phead = Pnode;
    }
    Psnake->Ptail = Psnake->Phead->Pnext->Pnext;
    Psnake->direction = RIGHT;
}
//判断生成的食物的坐标是否和蛇身子重叠
int IsOverlapSnake(int x,int y,Snake* Psnake)
{
    assert(Psnake != NULL);
    SnakeNode*Pnode = Psnake->Phead;
    //遍历蛇身的所有节点,对比每个节点的坐标是否与生成的食物坐标相同
    while (Pnode!= NULL)
    {
        if (Pnode->pos.x == x&&Pnode->pos.y == y)
        {
            return 1;
        }
        Pnode = Pnode->Pnext;
    }
    return 0;
}
//生成的食物
/*
条件:
1.不能和蛇身子重叠
2.不能生成在边界上
3.随机生成
*/
void GenerateFood(Snake*Psnake,Position *Posfood,int width,int heigth)
{
    assert(Psnake != NULL);

    int x = 0;
    int y = 0;
    do
    {
        //不能生成在边界上且随机生成
        x = rand() % width;
        y = rand() % heigth;
    } while (IsOverlapSnake(x,y,Psnake));
    Posfood->x = x;
    Posfood->y = y;
}
void GameInit(Game*Pgame)
{
    assert(Pgame != NULL);
    SnakeInit(&(Pgame->snake));
    GenerateFood(&(Pgame->snake), &(Pgame->Foodposition), WIDTH, HEIGHT);
    Pgame->score = 0;
    Pgame->Perfoodscore = 10;
    Pgame->Sleeptime = 500;
    Pgame->speed = 0;
}
Position GetNextPos(Snake*Psnake)
{
    Position NextPos = Psnake->Phead->pos;
    switch (Psnake->direction)
    {
    case UP:
        NextPos.y -= 1;
        break;
    case DOWN:
        NextPos.y += 1;
        break;
    case LEFT:
        NextPos.x -= 1;
        break;
    case RIGHT:
        NextPos.x += 1;
        break;
    }
    return NextPos;
}
//让蛇往前走
//在蛇的头部插入一个新节点
void AddSnakeHead(Snake*Psnake,Position NextPos)
{
    assert(Psnake != NULL);
    SnakeNode*Pnode = (SnakeNode*)malloc(sizeof(SnakeNode));
    assert(Pnode != NULL);
    Pnode->pos = NextPos;
    Pnode->Pnext = Psnake->Phead;
    Psnake->Phead = Pnode;
    PrintSnakeBlock(NextPos);
}
//让蛇往前走
//删除蛇的尾巴
void RemoveSnakeTail(Snake*Psnake)
{
    assert(Psnake != NULL);
    SnakeNode*Pnode = Psnake->Phead;
    while (Pnode->Pnext->Pnext != NULL)
    {
        Pnode = Pnode->Pnext;
    }
    CleanBlock(Pnode->Pnext->pos);
    free(Pnode->Pnext);
    Pnode->Pnext = NULL;

}
//判断蛇是否吃到了食物,比较蛇要前进的下一个坐标与食物坐标是否重叠,若重叠表示吃到食物,否则没有
int IsEatFood(Position NextPos, Position FoodPosition)
{
    if (NextPos.x == FoodPosition.x&&NextPos.y == FoodPosition.y)
    {
        return 1;
    }
    return 0;
}
//判断蛇要前进的下一个坐标是否在边界
int IsKilledByWall(Position NextPos, int width, int height)
{
    if (NextPos.x < 0)
    {
        return 1;
    }
    if (NextPos.x >= width)
    {
        return 1;
    }
    if (NextPos.y < 0)
    {
        return 1;
    }
    if (NextPos.y >= height)
    {
        return 1;
    }
    return 0;
}
//判断蛇要前进的下一个坐标是否与蛇身的某个节点相重合
int IsKilledBySelf(Snake*Psnake)
{
    SnakeNode*Pnode = Psnake->Phead->Pnext;
    Position head = Psnake->Phead->pos;
    while (Pnode != NULL)
    {
        if (head.x==Pnode->pos.x&&head.y==Pnode->pos.y)
        {
            return 1;
        }
        Pnode = Pnode->Pnext;
    }
    return 0;

}
void Pause()//按space键暂停游戏
{
    while (1)
    {
        if (GetAsyncKeyState(VK_SPACE))
        {
            break;
        }
        Sleep(300);
    }
}
void Speed(Game *Pgame)//加速
{
    if (GetAsyncKeyState(VK_RETURN))
    {
        Pgame->speed +=100;
    }
}
//背景音乐
DWORD WINAPI Fun(LPVOID lpPrarmter)
{
    while (1)
    {
        PlaySound(TEXT("D:\\代码Code\\贪吃蛇\\Debug\\贪吃蛇大作战-夺宝模式 - 小旭音乐003.wav"), NULL, SND_FILENAME | SND_SYNC);
    }
}
void GameRun()
{
    //初始化工作
    Position NextPos;
    Game game;
    GameInit(&game);
    PrintGameInformation(&game);
    PrintFood(game.Foodposition);
    PrintSnake(&(game.snake));
    while (1)
    {
        //播放背景音乐
        HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);
        CloseHandle(hThread);
        //玩游戏
        //按方向键后蛇会改变方向
        if (GetAsyncKeyState(VK_UP) && game.snake.direction != DOWN)
        {
            game.snake.direction = UP;
        }
        else if (GetAsyncKeyState(VK_DOWN) && game.snake.direction != UP)
        {
            game.snake.direction = DOWN;
        }
        else if (GetAsyncKeyState(VK_LEFT) && game.snake.direction != RIGHT)
        {
            game.snake.direction = LEFT;
        }
        else if (GetAsyncKeyState(VK_RIGHT) && game.snake.direction != LEFT)
        {
            game.snake.direction = RIGHT;
        }
        else if (GetAsyncKeyState(VK_SPACE))
        {
            Pause();
        }
        else if (GetAsyncKeyState(VK_RETURN))
        {
            Speed(&game);
        }
        else if (GetAsyncKeyState(VK_ESCAPE))
        {
            break;
        }
        NextPos = GetNextPos(&(game.snake));
        //蛇吃到食物的时候
        if (IsEatFood(NextPos, game.Foodposition))
        {
            //1.蛇身子要变长
            AddSnakeHead(&(game.snake), NextPos);
            //2.生成食物
            GenerateFood(&(game.snake), &(game.Foodposition), WIDTH, HEIGHT);
            PrintFood(game.Foodposition);
            //3.加分
            game.score += game.Perfoodscore;
            PrintScore(&game);
        }
        //没吃到食物时让蛇前进,不动方向键的时候蛇会自己前进(擦除蛇身子的最后一个节点并且在头部插入一个新的节点相当于蛇往前走了)
        else
        {
            RemoveSnakeTail(&(game.snake));
            AddSnakeHead(&(game.snake), NextPos);
        }
        //退出游戏
        //撞墙
        if (IsKilledByWall(NextPos, WIDTH, HEIGHT))
        {
            break;
        }
        //撞到自己
        if (IsKilledBySelf(&(game.snake)))
        {
            break;
        }
        Sleep(game.Sleeptime-game.speed);
    }
}
int main()
{
    srand((unsigned)time(NULL));
    system("mode con cols=100 lines=35");

    PrintWelcome();

    PrintWall(WIDTH, HEIGHT);
    GameRun();
    PrintDeathMessage(WIDTH, HEIGHT);
    system("pause");
    return 0;
}

图形界面的头文件view.h
view.h

#pragma once
#include"DataType.h"
//设置文本颜色
void Color(int color);
void Move(int X, int Y);
//打印边界墙,这里的width和height值的指的是游戏内的坐标
void PrintWall(int width, int height);
//擦除一格
void CleanBlock(Position Pos);
//打印一节蛇
void PrintSnakeBlock(Position Pos);
//打印食物
void PrintFood(Position FoodPos);
//打印整条蛇
void PrintSnake(Snake*Psnake);
//打印欢迎界面
void PrintWelcome();
//打印死亡信息
void PrintDeathMessage(int width, int height);
//打印游戏相关信息
void PrintGameInformation();
//打印得分
void PrintScore(Game*pGame);

处理游戏界面 View.c
View.c

#include<Windows.h>
#include<stdio.h>
#include"DataType.h"
#pragma comment(lib,"Winmm.lib")
#define WIDTH 28
#define HEIGHT 27
#include <mmsystem.h>
//移动光标
//COORED:The COORD structure defines the coordinates of a character cell in a console screen buffer. 
//The origin of the coordinate system (0,0) is at the top, left cell of the buffer.
void Move(int X, int Y)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord;
    coord.X = X;
    coord.Y = Y;
    SetConsoleCursorPosition(hStdout, coord);
}
void Color(int color)
{
    HANDLE consolehwnd = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(consolehwnd, color);
}
//隐藏光标
void HideCursor()
{
    CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
//打印墙
void PrintWall(int width, int height)
{
    Color(8);
    int i;
    Move(0, 0);

    for (i = 0; i < width + 2; i++)
    {
        Sleep(30);
        printf("█");
    }

    Move(0, height + 1);
    for (i = 0; i < width + 2; i++)
    {
        Sleep(30);

        printf("█");
    }
    for (i = 0; i < height; i++)
    {
        Sleep(30);

        Move(0, i + 1);
        printf("█");
    }
    for (i = 0; i < height; i++)
    {
        Sleep(30);

        Move(2 * (width + 1), i + 1);
        printf("█");
    }
}
void PrintFood(Position Food)
{
    Color(FOREGROUND_RED);
    Move(2*(Food.x + 1), Food.y + 1);
    printf("★");
}
void CleanBlock(Position pos)
{
    Move(2 * (pos.x + 1), pos.y + 1);
    printf("  ");
}
void PrintSnakeBlock(Position pos)
{
    Color(FOREGROUND_GREEN);

    Move(2 * (pos.x + 1), pos.y + 1);
    printf("●");
}
void PrintSnake(Snake*Psnake)
{
    SnakeNode*Pnode;
    Pnode = Psnake->Phead;
    while (Pnode != NULL)
    {
        PrintSnakeBlock(Pnode->pos);
        Pnode = Pnode->Pnext;
    }
}
void PrintWelcome()
{
    Move(WIDTH / 2, HEIGHT/2);
    printf("Welcome to Gluttonous Snake");
    HideCursor();
    system("pause");
    system("cls");
}

void PrintDeathMessage(int width, int height)
{
    Color(FOREGROUND_BLUE | FOREGROUND_GREEN);
    Move(width / 2 +7 , height / 2);
    printf("Game Over");
    HideCursor();

}
void PrintScore(Game*pGame)
{
    Color(FOREGROUND_BLUE | FOREGROUND_GREEN);
    Move(65, HEIGHT / 2 + 6);

    printf("得分:score=%d", pGame->score);
    HideCursor();

}
void PrintGameInformation(Game*Pgame)
{
    Color(FOREGROUND_BLUE | FOREGROUND_GREEN);


    Move(65, HEIGHT / 2);
    printf("↑:向上移动");
    Move(65, HEIGHT / 2+1);
    printf("↓:向下移动");
    Move(65, HEIGHT / 2 + 2);
    printf("→:向右移\n");
    Move(65, HEIGHT / 2 + 3);
    printf("←:向左移动");
    Move(65, HEIGHT / 2 + 4);
    printf("规则:每个食物加10分\n");
    Move(65, HEIGHT / 2 + 5);

    printf("回车键可以加速");
    HideCursor();

}

这里在处理游戏界面时调用了一些Windows的系统接口函数来实现,还有就是我自己加的这个BGM是没有移植性的,所以要想加上BGM需要去调用系统中播放音乐的函数PlaySound(),在函数要将你的音乐所在的路径传过去,音乐的格式是.wav的格式,我自己用mp3格式试了好久都不行才转的wav格式。
效果图:
这里写图片描述

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这里提供一份使用Windows API实现的C++贪吃蛇小游戏有颜色。主要使用了控制台颜色和键盘输入事件的处理。 ```c++ #include <iostream> #include <conio.h> #include <windows.h> using namespace std; // 定义控制台颜色 const int COLOR_RED = 12; const int COLOR_GREEN = 10; const int COLOR_YELLOW = 14; const int COLOR_WHITE = 7; // 定义每个方块的大小 const int BLOCK_SIZE = 20; // 定义窗口的大小 const int WINDOW_WIDTH = 800; const int WINDOW_HEIGHT = 600; // 定义蛇的最大长度 const int MAX_SNAKE_LENGTH = 100; // 定义蛇的初始位置和大小 const int INIT_SNAKE_X = 300; const int INIT_SNAKE_Y = 300; const int INIT_SNAKE_LENGTH = 3; // 定义食物的大小 const int FOOD_SIZE = 20; // 定义游戏的速度 const int GAME_SPEED = 100; // 定义方向常量 const int DIR_UP = 1; const int DIR_DOWN = 2; const int DIR_LEFT = 3; const int DIR_RIGHT = 4; // 定义结构体表示蛇的每个方块 struct SnakeBlock { int x, y; }; // 定义全局变量 int g_direction = DIR_UP; int g_snakeLength = INIT_SNAKE_LENGTH; SnakeBlock g_snake[MAX_SNAKE_LENGTH]; int g_foodX, g_foodY; // 初始化蛇 void initSnake() { for (int i = 0; i < INIT_SNAKE_LENGTH; i++) { g_snake[i].x = INIT_SNAKE_X; g_snake[i].y = INIT_SNAKE_Y + i * BLOCK_SIZE; } } // 绘制方块 void drawBlock(int x, int y, int color) { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos = {x, y}; SetConsoleCursorPosition(hConsole, pos); SetConsoleTextAttribute(hConsole, color); cout << " "; } // 绘制蛇 void drawSnake() { for (int i = 0; i < g_snakeLength; i++) { drawBlock(g_snake[i].x, g_snake[i].y, COLOR_GREEN); } } // 擦除蛇 void eraseSnake() { for (int i = 0; i < g_snakeLength; i++) { drawBlock(g_snake[i].x, g_snake[i].y, COLOR_WHITE); } } // 移动蛇 void moveSnake() { // 记录蛇尾位置,用于后面擦除 int tailX = g_snake[g_snakeLength - 1].x; int tailY = g_snake[g_snakeLength - 1].y; // 后面的方块依次移到前面的方块的位置 for (int i = g_snakeLength - 1; i > 0; i--) { g_snake[i].x = g_snake[i - 1].x; g_snake[i].y = g_snake[i - 1].y; } // 根据方向移动蛇头 switch (g_direction) { case DIR_UP: g_snake[0].y -= BLOCK_SIZE; break; case DIR_DOWN: g_snake[0].y += BLOCK_SIZE; break; case DIR_LEFT: g_snake[0].x -= BLOCK_SIZE; break; case DIR_RIGHT: g_snake[0].x += BLOCK_SIZE; break; } // 如果蛇头和食物重合,蛇长度加1 if (g_snake[0].x == g_foodX && g_snake[0].y == g_foodY) { g_snakeLength++; // 随机生成新的食物位置 g_foodX = rand() % (WINDOW_WIDTH / BLOCK_SIZE) * BLOCK_SIZE; g_foodY = rand() % (WINDOW_HEIGHT / BLOCK_SIZE) * BLOCK_SIZE; } // 擦除蛇尾 drawBlock(tailX, tailY, COLOR_WHITE); } // 绘制食物 void drawFood() { drawBlock(g_foodX, g_foodY, COLOR_RED); } // 处理键盘输入事件 void handleInput() { if (_kbhit()) { char ch = _getch(); switch (ch) { case 'w': case 'W': if (g_direction != DIR_DOWN) { g_direction = DIR_UP; } break; case 's': case 'S': if (g_direction != DIR_UP) { g_direction = DIR_DOWN; } break; case 'a': case 'A': if (g_direction != DIR_RIGHT) { g_direction = DIR_LEFT; } break; case 'd': case 'D': if (g_direction != DIR_LEFT) { g_direction = DIR_RIGHT; } break; } } } // 判断游戏是否结束 bool isGameOver() { // 如果蛇头超出边界,游戏结束 if (g_snake[0].x < 0 || g_snake[0].x >= WINDOW_WIDTH || g_snake[0].y < 0 || g_snake[0].y >= WINDOW_HEIGHT) { return true; } // 如果蛇头和蛇身重合,游戏结束 for (int i = 1; i < g_snakeLength; i++) { if (g_snake[0].x == g_snake[i].x && g_snake[0].y == g_snake[i].y) { return true; } } return false; } int main() { // 隐藏光标 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cursorInfo = {1, false}; SetConsoleCursorInfo(hConsole, &cursorInfo); // 设置窗口大小 HWND hwnd = GetConsoleWindow(); RECT rect; GetWindowRect(hwnd, &rect); MoveWindow(hwnd, rect.left, rect.top, WINDOW_WIDTH, WINDOW_HEIGHT, true); // 初始化随机数种子 srand(GetTickCount()); // 初始化蛇和食物 initSnake(); g_foodX = rand() % (WINDOW_WIDTH / BLOCK_SIZE) * BLOCK_SIZE; g_foodY = rand() % (WINDOW_HEIGHT / BLOCK_SIZE) * BLOCK_SIZE; // 游戏循环 while (true) { // 绘制蛇和食物 drawSnake(); drawFood(); // 处理键盘输入事件 handleInput(); // 移动蛇 moveSnake(); // 判断游戏是否结束 if (isGameOver()) { break; } // 等待一段时间,控制游戏速度 Sleep(GAME_SPEED); // 擦除蛇 eraseSnake(); } // 显示游戏结束信息 COORD pos = {WINDOW_WIDTH / 2 - 5, WINDOW_HEIGHT / 2}; SetConsoleCursorPosition(hConsole, pos); SetConsoleTextAttribute(hConsole, COLOR_YELLOW); cout << "Game Over" << endl; return 0; } ``` 注意,在Windows下使用控制台颜色时,需要使用Windows API中的相关函数。例如,`GetStdHandle`用于获取标准输出句柄,`SetConsoleCursorPosition`用于设置控制台光标位置,`SetConsoleTextAttribute`用于设置控制台文本属性(包括颜色)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值