# 用C++语言实现贪吃蛇游戏

xiaoxiao2021-02-28  9

//蛇身单个节点 struct SNAKE { bool IsSurvivor; //当前结点是否存在（被画） int coor_x; //节点横坐标 int coor_y; //节点纵坐标 SNAKE *link; //指向下一个节点的指针 //构造函数 SNAKE(int x, int y, bool survivor = true,SNAKE *link = NULL) { //初始化坐标值,赋值方式为tail派生 coor_x = x; coor_y = y; } };

//蛇精灵类定义——基于单链表实现蛇身 class SnakeSprite { public: SnakeSprite(int x = 300, int y = 200); ~SnakeSprite() { delete snakeHead; } bool addTail(); //蛇尾增加长度 void drawThisSnake(); //绘制当前蛇身 void positionAction(); //完成蛇身移动(更新每个节点的坐标) void turnLeft(); //蛇头的基本转向 void turnRight(); void turnUp(); void turnDown(); void recordCurrentDirection(int d = LEFT); //记录蛇的当前运动方向，借助枚举类 int getDirection(); bool IsDeath(); //是否碰撞草丛，是蛇死亡返回true，否则返回false void getCurrentPosRect(RECT &rect); void getCurrentCoor(int &x, int &y); protected: int len; //蛇身长度_以块为单位 SNAKE *snakeHead; //蛇头指针 SNAKE *tail; //蛇尾指针 SNAKE *beforeTail; //尾巴节点的前一个节点，方便移动 int directions; };

//输入控制 if (Key_Down(DIK_UP) && !Key_Down(DIK_RIGHT) && !Key_Down(DIK_LEFT) && !Key_Down(DIK_DOWN)) { theSnake.turnUp(); if (DOWN != theSnake.getDirection()) { theSnake.recordCurrentDirection(UP); } } if (Key_Down(DIK_RIGHT) && !Key_Down(DIK_UP) && !Key_Down(DIK_LEFT) && !Key_Down(DIK_DOWN)) { theSnake.turnRight(); if (LEFT != theSnake.getDirection()) { theSnake.recordCurrentDirection(RIGHT); } } if (Key_Down(DIK_LEFT) && !Key_Down(DIK_UP) && !Key_Down(DIK_RIGHT) && !Key_Down(DIK_DOWN)) { theSnake.turnLeft(); if (RIGHT != theSnake.getDirection()) { theSnake.recordCurrentDirection(LEFT); } } if (Key_Down(DIK_DOWN) && !Key_Down(DIK_UP) && !Key_Down(DIK_RIGHT) && !Key_Down(DIK_LEFT)) { theSnake.turnDown(); if (UP != theSnake.getDirection()) { theSnake.recordCurrentDirection(DOWN); } } //蛇类的成员函数 void SnakeSprite::turnDown() { //向下转头 if (directions != UP) { snakeHead->coor_y += 1; } } void SnakeSprite::turnLeft() { //想左转头 if (directions != RIGHT) { snakeHead->coor_x -= 1; } } void SnakeSprite::turnRight() { //向右转头 if (directions != LEFT) { snakeHead->coor_x += 1; } } void SnakeSprite::turnUp() { if (directions != DOWN) { snakeHead->coor_y -= 1; } }

3》怎样实现蛇的移动？

//蛇类的成员函数 void SnakeSprite::positionAction() { //实现蛇的自动运动,即依次更新每个节点内坐标的值 if (UP == directions) { snakeHead->coor_y--; } if (DOWN == directions) { snakeHead->coor_y++; } if (LEFT == directions) { snakeHead->coor_x--; } if (RIGHT == directions) { snakeHead->coor_x++; } SNAKE *current = snakeHead; int LEN = len; for (int i = 1; i < len; i ++) { current = snakeHead; for (int j = 1; j < LEN - 1 && len >= 3; j ++) { //令current循环到指定位置 current = current->link; } current->link->coor_x = current->coor_x; current->link->coor_y = current->coor_y; LEN--; } }

bool SnakeSprite::IsDeath() { //判断是否超出规定范围 67<x<468/87<y<470 if (snakeHead->coor_x > 67 && snakeHead->coor_x < 460 && snakeHead->coor_y > 87 && snakeHead->coor_y < 470) { return false; } else { return true; } }

//FOOD CLASS class Food { protected: int coor_x; //食物出现的横坐标 int coor_y; //纵坐标 public: Food(int x = 100, int y = 100); bool drawThisFood(bool &again); //绘制当前食物 bool checkFoodPosition(); //检查当前食物出现的位置是否合法（即不能与蛇体重合) void getRandCoor(int & x, int & y); //食物的随机坐标生成 }; //APPLE CLASS class Apple : public Food { //苹果是Food的一种 private: int color; //扩展功能，标定当前Apple的颜色 public: bool beenCollision(RECT snakeRect); //检测apple是否被碰撞到，是返回true否则返回false void getCurrentPosRect(RECT &rect); //得到当前的位置矩形 //bool beenCollision2(int x, int y); }; Food::Food(int x, int y) : coor_x(x), coor_y(y) {} void Apple::getCurrentPosRect(RECT &rect) { RECT currentRect = {coor_x, coor_y, coor_x + 19, coor_y + 22}; rect = currentRect; } bool Apple::beenCollision(RECT snakeRect) { RECT rect_apple, rect; getCurrentPosRect(rect_apple); if (IntersectRect(&rect, &rect_apple, &snakeRect)) { return true; } else { return false; } } bool Food::drawThisFood(bool &again) { //绘制当前食物到屏幕 int posX , posY ; if (again) { getRandCoor(posX, posY); again = false; } RECT rectApple = { 0, 0, 19, 22 }; D3DXVECTOR3 position(coor_x, coor_y, 0); D3DCOLOR red = D3DCOLOR_XRGB(255, 255, 255); spriteoj->Draw(apple, &rectApple, NULL, &position, red); return true; } void Food::getRandCoor(int & x, int & y) { //指定范围内的随机函数生成器 srand((unsigned)time(NULL)); //随机种子，以系统时间作为基数 x = foodAllowPosX + (rand() % 350); y = foodAllowPosY + (rand() % 330); coor_x = x; coor_y = y; } bool Food::checkFoodPosition() { //检查食物位置的合法性 return true; }

void Food::getRandCoor(int & x, int & y) { //指定范围内的随机函数生成器 srand((unsigned)time(NULL)); //随机种子，以系统时间作为基数 x = foodAllowPosX + (rand() % 350); y = foodAllowPosY + (rand() % 330); coor_x = x; coor_y = y; }

4，不足之处 上面的代码尽管解决了一些核心的游戏逻辑，但是依然存在不足。计分系统由于食物出现的位置不当而暴增、食物万一出现在蛇身上怎么办？而我在食物位置的合法性检查上只是返回了个字面量true！希望我们一块交流和改进。

5，资源链接 说明：你休想直接复制粘贴上面的代码块来放在编译器里运行它，并且天真的等待游戏画面的出现，因为我早说过这些需要相应工具的支持！你甚至不能运行起版本2的EXE程序，因为可能需要DirectX的游戏环境，而恰好你的机器上没有！但是版本1的贪吃蛇是可以运行起来的，源码也是可以编译的（如果你的编译器正常的话！），因为他真的是用纯语言做的，当然性能也会有不足。 贪吃蛇版本一资源链接：http://pan.baidu.com/s/1c2EUVc8 密码：na6m 贪吃蛇版本二资源链接：http://pan.baidu.com/s/1hsb7k92 密码：6sbg

6，版权声明 游戏中的音乐、图片、图标等资源均来源于网络，仅供学习之用。 借鉴数目：《游戏编程入门》、《Windows游戏编程大师技巧》