您的位置:首页 > 编程语言 > C语言/C++

【C语言】贪吃蛇

2018-10-06 20:56 92 查看

 一,涉及知识点:

[code]结构体链表,动态分配内存,键盘输入检测,设置光标。

 二,实现逻辑

[code]1,涉及多个结构体,分别定义蛇,方向,结点,位置坐标,游戏
2,结点串联形成链表,遍历获取成员坐标,打印符号得到蛇身。
3,不断的加头,去尾,重新遍历坐标,再打印形成蛇的移动。
4,食物产生的位置判定,不能越界,也不能与蛇身体重合。
5,蛇的转向判定,一条规则,不允许倒退。
6,转向的实现,跟行进方向决定新的关节坐标(当前头的上下左右)
7,死亡检测,是否头节点坐标是否与墙壁重合,是否与身体其他关节重合。
8,加速减速,设置刷新休眠时间实现。
9,可以设置光标,就能实现制定位置打印制定符号。

源码

[code]//Model.h  结构体头文件
#pragma once

typedef enum Direction
{
UP, DOWN, LEFT, RIGHT
}Direction;
typedef struct Position
{
int x;
int y;
}Position;
typedef struct Node
{
Position pos;
struct Node *next;
}Node;
typedef struct Snake
{
Node *head;
Node *tail;
Direction direction;
}Snake;
typedef struct Game
{
Snake snake;
Position food;

int width;
int height;
}Game;
[code]//View.h
#pragma once
#include "Model.h"

void SetPos(int X, int Y);
void DisplayWall(int width, int height);
void DisplaySnake(const Snake *pSnake);
void DisplayFood(const Position *pFood);
void CleanSnakeNode(const Position *pPos);
void DisplaySnakeNode(const Position *pPos);
void GameDescription1();
void GameDescription2();
[code]//View.c
#include "View.h"
#include "Model.h"
#include <Windows.h>
#include <stdio.h>

void ViewInit()
{
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//CONSOLE_CURSOR_INFO 这个结构体包含的是控制台光标的信息
CONSOLE_CURSOR_INFO ConsoleCursorInfo;
GetConsoleCursorInfo(hOutput, &ConsoleCursorInfo);
ConsoleCursorInfo.bVisible = FALSE;
SetConsoleCursorInfo(hOutput, &ConsoleCursorInfo);
}
//设置光标位置
void SetPos(int X, int Y)
{
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD coord = { X, Y };
SetConsoleCursorPosition(hOutput, coord);
}

static void TranslateSetPos(int x, int y)
{
int X = 2 * (x + 1);
int Y = y + 1;

SetPos(X, Y);
}

void DisplayWall(int width, int height)
{
ViewInit();
// 上
SetPos(0, 0);
for (int i = 0; i < width + 2; i++) {
printf("◆");
}
// 下
SetPos(0, height + 1);
for (int i = 0; i < width + 2; i++) {
printf("◆");
}
// 左
for (int i = 0; i < height + 2; i++) {
SetPos(0, i);
printf("◆");
}
// 右
for (int i = 0; i < height + 2; i++) {
SetPos(2 * (width + 1), i);
printf("◆");
}

}

void DisplaySnake(const Snake *pSnake)
{
for (Node *node = pSnake->tail; node != NULL; node = node->next) {
TranslateSetPos(node->pos.x, node->pos.y);
printf("■");
}
}

void DisplayFood(const Position *pFood)
{
TranslateSetPos(pFood->x, pFood->y);
printf("■");
}

void CleanSnakeNode(const Position *pPos)
{
TranslateSetPos(pPos->x, pPos->y);
printf(" ");
}

void DisplaySnakeNode(const Position *pPos)
{
TranslateSetPos(pPos->x, pPos->y);
printf("■");
}
//游戏说明界面
void GameDescription1()
{
SetPos(40, 14);
printf("欢迎进入贪吃蛇游戏!\n\n");
system("pause");
system("color 5e");
system("cls");
SetPos(10, 14);
printf("您将通过键盘控制蛇的移动: 上↑  下↓  左 ←  右→  加速F1  减速F2  暂停space  退出Esc");
SetPos(10, 15);
printf("吃到食物身体会变长\n");
SetPos(10, 16);
printf("撞到身体或墙壁游戏都会结束\n");
system("pause");
system("cls");
}
void GameDescription2()
{
SetPos(64, 15);
printf("不能穿墙,不能咬到自己\n");
SetPos(64, 16);
printf("用↑ ↓ ← → 控制蛇的移动.");
SetPos(64, 17);
printf("F1 为加速,F2 为减速\n");
SetPos(64, 18);
printf("ESC :退出游戏    space:暂停游戏.");
}
[code]//Controller.c
#include "Model.h"
#include "View.h"
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <stdbool.h>
#include <time.h>

void SnakeInit(Snake *pSnake)
{
//链表中头结点是蛇的尾巴,尾结点是蛇的头
pSnake->tail = NULL;
for (int i = 0; i < 3; i++){
Node *node = (Node *)malloc(sizeof(Node));
//从蛇头开始创建结点
node->pos.x = 7 + i;
node->pos.y = 2;

if (i == 0){
pSnake->head = node;
}
node->next = pSnake->tail;
pSnake->tail = node;
}
pSnake->direction = LEFT;
}
bool IsOverLap(Position pos,Snake *pSnake)
{
Node *node;
for (node = pSnake->tail; node != pSnake->head; node = node->next){
if (node->pos.x == pos.x&&node->pos.y == pos.y){
return true;
}
}
return false;
}
//生成食物
Position GenerateFood(int width, int height, Snake *pSnake)
{
Position pos;
SetPos(1, 1);
do
{
pos.x = rand() % width;
pos.y = rand() % height;
} while (IsOverLap(pos, pSnake));
DisplayFood(&pos);
return pos;
}
//初始化Game
void GameInit(Game *pGame)
{
pGame->width = 28;
pGame->height = 27;
//初始化蛇
SnakeInit(&pGame->snake);
//初始化食物
//pGame->food = GenerateFood(pGame->width, pGame->height, &pGame->snake);
}
//得到蛇即将要走的坐标
Position GetNextPosition(const Snake *pSnake)
{
Position next = pSnake->head->pos;
switch (pSnake->direction){
case UP:
next.y -= 1;
break;
case DOWN:
next.y += 1;
break;
case LEFT:
next.x -= 1;
break;
case RIGHT:
next.x += 1;
break;
}
return next;
}
//蛇头上加一格
void AddHead(Snake *pSnake, Position nextPos)
{
Node *node = (Node *)malloc(sizeof(Node));
node->pos = nextPos;
node->next = NULL;
pSnake->head->next = node;
pSnake->head = node;
DisplaySnakeNode(&nextPos);//注意这里传参
}
//蛇尾减一格
void RemoveTail(Snake *pSnake)
{
Node *tail = pSnake->tail;
pSnake->tail = pSnake->tail->next;
CleanSnakeNode(&tail->pos);
free(tail);
}
//判断蛇能不能吃到食物
bool IsEat(Position food, Position next)
{
return food.x == next.x&&food.y == next.y;
}
//判断蛇是否撞墙
bool KilledByWall(const Snake *pSnake,int width,int height)
{
int x = pSnake->head->pos.x;
int y = pSnake->head->pos.y;
if (x>=0 && x<width && y>=0 && y<=height){
return false;
}
return true;
}
//判断蛇是否撞到自己
bool KilledBySelf(const Snake *pSnake,Position nextPos)
{
Node *node = pSnake->tail;
for (node = pSnake->tail; node != pSnake->head; node = node->next){
if (node->pos.x == nextPos.x&&node->pos.y == nextPos.y){
return true;
}
}
return false;
}
//判断死没死
bool GameOver(const Snake *pSnake,Position nextPos,int width,int height)
{
if (KilledByWall(pSnake,width,height)){
printf("撞墙了\n");
return true;
}
if (KilledBySelf(pSnake,nextPos)){
printf("自杀了\n");
return true;
}
else{
return false;
}
}
//暂停游戏
void PauseGame()
{
while (1)
{
Sleep(300);
if (GetAsyncKeyState(VK_SPACE))
{
break;
}
}
}
void GameRun()
{
Game game;
int score = 0,            //得分
add = 10,             //加分
sleeptime = 200;      //正常速度
GameInit(&game);

GameDescription1();
DisplayWall(game.width,game.height);
DisplaySnake(&game.snake);
game.food = GenerateFood(game.width, game.height, &game.snake);
GameDescription2();
while (1)
{
SetPos(64, 10);
printf("得分:%d ", score);
SetPos(64, 11);
printf("当前食物分值:%d分", add);
if (game.snake.direction != DOWN&&GetAsyncKeyState(VK_UP)){
game.snake.direction = UP;
}
else if (game.snake.direction != UP&&GetAsyncKeyState(VK_DOWN)){
game.snake.direction = DOWN;
}
else if (game.snake.direction != RIGHT&&GetAsyncKeyState(VK_LEFT)){
game.snake.direction = LEFT;
}
else if (game.snake.direction != LEFT&&GetAsyncKeyState(VK_RIGHT)){
game.snake.direction = RIGHT;
}
else if (GetAsyncKeyState(VK_F1))   //加速
{
if (sleeptime > 50)
{
sleeptime -= 30;
add = add + 2;
if (sleeptime == 320)
{
add = 2;//防止减到1之后再加回来有错
}
}
}
else if (GetAsyncKeyState(VK_F2))   //减速
{
if (sleeptime < 350)
{
sleeptime = sleeptime + 30;
add = add - 2;
if (sleeptime == 350)
{
add = 1; //保证最低分为1
}
}
}
else if (GetAsyncKeyState(VK_SPACE)){
//暂停
PauseGame();
}
else if (GetAsyncKeyState(VK_ESCAPE))
{
//退出
system("cls");
SetPos(18,12 );
printf("退出游戏\n");
break;
}
Position nextPos = GetNextPosition(&game.snake);
if (IsEat(game.food,nextPos)){
//头上加一格
AddHead(&game.snake,nextPos);
score += add;
//重新生成一个食物
game.food = GenerateFood(game.width, game.height, &game.snake);
}
else{
//头上加一格
AddHead(&game.snake,nextPos);
//尾巴减一格
RemoveTail(&game.snake);
}
if (GameOver(&game.snake,nextPos,game.width,game.height)){
break;
}
Sleep(sleeptime);
}

}

//测试控制光标的位置
void DemoSetPos()
{
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD coord;

for (int i = 0; i < 6; i++) {
coord.X = 2 * i;
coord.Y = i;
SetConsoleCursorPosition(hOutput, coord);

printf("%02d", i);
}
}

int main()
{
//DemoSetPos();
srand((unsigned)time(NULL));
system("title ★★贪吃蛇★★");   //改变命令窗口名字
GameRun();

system("pause");
system("cls");

return 0;

}

 

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: