假期休闲,来发贪吃蛇!(Win32控制台版)

假期无事,整理电脑文件的时候发现了以前大二时做的坦克小游戏,心血来潮,决定再来一发贪吃蛇。

游戏玩法不必多说,主要是算法实现绘制过程

首先,利用一个二维数组 mp[][] 来存储地图信息,其中的值表示:

0:空

1:被蛇覆盖

2:食物

有了这个地图数组,生成随机食物的时候就可以避免生成到蛇身上。

那蛇的身体如何存储呢?也很简单,用队列(存储每一个小格的坐标信息)。

队列的头尾方向与蛇的头尾方向正好相反。

蛇每走一步,在蛇头方向的下一位置画一个小方格,同时把该位置放置到队列尾端。取出队列第一个元素,也即是蛇的尾部的坐标,把该位置的小方格清掉。

画一个小格,擦掉一个小格,再画一个,再擦一个……如此反复循环,小蛇就会爬了~

代码不多,写到一个文件里了:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <windows.h>
#include <queue>

#ifdef _MSC_VER  // M$的编译器要给予特殊照顾
#if _MSC_VER <= 1200  // VC6及以下版本
#error 你是不是还在用VC6?!
#else  // VC6以上版本
#if _MSC_VER >= 1600  // 据说VC10及以上版本有stdint.h了
#include <stdint.h>
#else  // VC10以下版本,自己定义int8_t和uint16_t
typedef signed char int8_t;
typedef unsigned short uint16_t;
#endif
#ifndef __cplusplus  // 据说VC都没有stdbool.h,不用C++编译,自己定义bool
typedef int bool;
#define true 1
#define false 0
#endif
#endif
#else  // 其他的编译器都好说
#include <stdint.h>
#ifndef __cplusplus  // 不用C++编译,需要stdbool.h里的bool
#include <stdbool.h>
#endif
#endif

using namespace std;

HANDLE g_hConsoleOutput;  // 控制台输出句柄

// 方向
enum { DIR_UP = 0, DIR_LEFT, DIR_DOWN, DIR_RIGHT };

// 绘画开始位置
const int START_ROW = 3;
const int START_COL = 3;

// 蛇的初始值
const int START_X = 5;
const int START_Y = 6;
const int START_LENGTH = 5;

// 地图大小
const int WIDTH = 32;
const int HEIGHT = 24;

// 定位到游戏池中的方格
#define gotoxyInPool(x, y) gotoxyWithFullwidth(x+START_ROW, y+START_COL)

int mp[HEIGHT][WIDTH];

typedef struct Point
{
	int x, y;
} Point;

typedef struct Snake  // 这个结构体存储游戏相关数据
{
	Point head;
	queue<Point>body;
	int dir;
public :
	void setDir();
	Point getHead();
	int nextStep();
} Snake;

// =============================================================================
typedef struct SnakeControl  // 这个结构体存储控制相关数据
{
	// 游戏池内每格的颜色
	// 由于此版本是彩色的,仅用游戏池数据无法存储颜色信息
	// 当然,如果只实现单色版的,就没必要用这个数组了
	int8_t color[28][16];
	bool dead;  // 挂
	bool pause;  // 暂停
	unsigned score;  // 得分(这个也可以用蛇的长度表示)
} SnakeControl;

// =============================================================================
// 以全角定位到某点
void gotoxyWithFullwidth(short y, short x);

void initGame(Snake * snake, SnakeControl * control);
void printSnake(Snake * snake, SnakeControl * control);
void createFood(Snake * snake);
void GoToNextStep(Snake * snake, SnakeControl * control, int dir);
void keydownControl(Snake * snake, SnakeControl * control, int key);
void runGame(Snake * snake, SnakeControl * control);
void printPrompting();
void printPoolBorder();
void printScore(const Snake *snake, const SnakeControl *control);

int main()
{
	Snake snake;
	SnakeControl control;

	CONSOLE_CURSOR_INFO cursorInfo = { 1, FALSE };  // 光标信息
	g_hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);  // 获取控制台输出句柄
	SetConsoleCursorInfo(g_hConsoleOutput, &cursorInfo);  // 设置光标隐藏
	SetConsoleTitleA("贪吃蛇");

	printPrompting();

	while (1)
	{
		SetConsoleTextAttribute(g_hConsoleOutput, 0x07);
		system("cls");
		initGame(&snake,&control);  // 初始化游戏
		printPrompting();  // 显示提示信息
		printPoolBorder();  // 显示游戏池边界

		runGame(&snake,&control);  // 运行游戏

		SetConsoleTextAttribute(g_hConsoleOutput, 0xF0);
	    char * str = "Game Over";
		int len = strlen(str);
		// 居中显示
		int x = HEIGHT / 2 + START_ROW ;
		int y = WIDTH / 2 + START_COL - len/4;
		gotoxyWithFullwidth(x, y);
		printf("%s",str);
		SetConsoleTextAttribute(g_hConsoleOutput, 0x07);
		system("pause > nul");
		system("cls");
	}

	gotoxyWithFullwidth(0, 0);
	CloseHandle(g_hConsoleOutput);
	system("pause > nul");

	return 0;
}

// =============================================================================
// 以全角定位到某点
void gotoxyWithFullwidth(short y, short x)
{
	static COORD cd;

	cd.X = (short)(x << 1);
	cd.Y = y;
	SetConsoleCursorPosition(g_hConsoleOutput, cd);
}

void initGame(Snake * snake, SnakeControl * control)
{
	//清空数据
	memset(mp, 0, sizeof mp);
	while (!snake->body.empty()) snake->body.pop();

	//出生点
	Point next;
	next.x = START_X ;
	next.y = START_Y ;
	snake->dir = DIR_RIGHT;
	snake->head.x = next.x;
	snake->head.y = next.y;
	for (int i = 0; i < START_LENGTH; ++i)snake->body.push(next);

	//生成食物
	createFood(snake);

	//控制信息
	control->dead = false;
	control->pause = false;
	control->score = 0;
}

//生成随机食物
void createFood(Snake * snake)
{
	int x = 0, y = 0;
	do
	{
		x = rand() % HEIGHT;
		y = rand() % WIDTH;
	} while (mp[x][y] == 1); // 不能生成到蛇的身体上面
	mp[x][y] = 2;
	gotoxyInPool(x, y);
	// 用相应颜色,显示一个实心方块
	SetConsoleTextAttribute(g_hConsoleOutput, 0xA);
	printf("■");

}

void GoToNextStep(Snake * snake, SnakeControl * control, int dir)
{
	int x = snake->head.x;
	int y = snake->head.y;
	Point next;

	switch (dir)
	{
	case DIR_UP:
		next.x = x - 1;
		next.y = y;
		break;
	case DIR_DOWN:
		next.x = x + 1;
		next.y = y;
		break;
	case DIR_LEFT:
		next.x = x;
		next.y = y - 1;
		break;
	case DIR_RIGHT:
		next.x = x;
		next.y = y + 1;
		break;
	default:
		break;
	}

	if (next.x >= 0 && next.x <  HEIGHT && next.y >= 0 && next.y < WIDTH )
	{
		if (mp[next.x][next.y] == 1)
		{
			control->dead = true;
			return;
		}

		bool isEat = mp[next.x][next.y] == 2;

		Point last = snake->body.front();
		snake->head.x = next.x;
		snake->head.y = next.y;
		snake->body.push(next);
		snake->dir = dir;

		// 画下一个头的位置
		mp[next.x][next.y] = 1;
		gotoxyInPool(next.x , next.y );
		SetConsoleTextAttribute(g_hConsoleOutput, 0xF);
		printf("■"); 

		// 如果没有吃到食物,删掉尾巴的痕迹
		if (!isEat)
		{
			snake->body.pop();
			mp[last.x][last.y] = 0;

			gotoxyInPool(last.x , last.y );
			SetConsoleTextAttribute(g_hConsoleOutput, 0xA);
			printf("%2s", "");
		}
		else
		{
			createFood(snake);
			printScore(snake, control);
		}
	}
	else
	{
		control->dead = true;
	}
}

void keydownControl(Snake * snake, SnakeControl * control, int key)
{
	if (key == 13)  // 暂停/解除暂停
	{
		control->pause = !control->pause;

		if (control->pause)
		{
			SetConsoleTextAttribute(g_hConsoleOutput, 0xE);
			gotoxyWithFullwidth(5, 38);
			printf("      已暂停      ");
			return;
		}
		else
		{
			//清楚提示信息
			SetConsoleTextAttribute(g_hConsoleOutput, 0xE);
			gotoxyWithFullwidth(5, 38);
			printf("%18s","");

			//显示得分信息
			printScore(snake, control);
		}
	}

	if (control->pause)  // 暂停状态,不作处理
	{
		return;
	}

	int dir = 0;
	switch (key)
	{
	case ‘w‘: case ‘W‘: case ‘8‘: case 72:  // 上
		dir = DIR_UP;
		break;
	case ‘a‘: case ‘A‘: case ‘4‘: case 75:  // 左
		dir = DIR_LEFT;
		break;
	case ‘d‘: case ‘D‘: case ‘6‘: case 77:  // 右
		dir = DIR_RIGHT;
		break;
	case ‘s‘: case ‘S‘: case ‘2‘: case 80:  // 下
		dir = DIR_DOWN;
		break;
	default:
		return;
	}

	// 不允许掉头
	if (snake->dir != dir && (snake->dir + dir) % 2 == 0)
	{
		return;
	}
	snake->dir = dir;
}

void runGame(Snake * snake, SnakeControl * control)
{
	int ch;
	clock_t clockLast, clockNow;

	clockLast = clock();  // 计时
	printScore(snake, control);  // 显示游戏池

	while (!control->dead)  // 没挂
	{
		while (_kbhit())  // 有键按下
		{
			ch = _getch();
			if (ch == 27)  // Esc键
			{
				return;
			}
			keydownControl(snake, control, ch);  // 处理按键
		}

		if (!control->pause)  // 未暂停
		{
			clockNow = clock();  // 计时
								 // 两次记时的间隔超过0.45秒
			if (clockNow - clockLast > 0.15F * CLOCKS_PER_SEC)
			{
				clockLast = clockNow;
				GoToNextStep(snake, control, snake->dir);;  // 蛇自动往前走
			}
		}
	}
}

void printPrompting()
{
	// 边框
	SetConsoleTextAttribute(g_hConsoleOutput, 0xF);
	gotoxyWithFullwidth(3, 37);
	printf("┏━━━━━━━━━┓");
	gotoxyWithFullwidth(4, 37);
	printf("┃%18s┃", "", "");
	gotoxyWithFullwidth(5, 37);
	printf("┃%18s┃", "", "");
	gotoxyWithFullwidth(6, 37);
	printf("┃%18s┃", "", "");
	gotoxyWithFullwidth(7, 37);
	printf("┗━━━━━━━━━┛");

	SetConsoleTextAttribute(g_hConsoleOutput, 0xB);
	gotoxyWithFullwidth(10, 37);
	printf("■控制说明:");
	gotoxyWithFullwidth(12, 38);
	printf("□向左移动:← A 4");
	gotoxyWithFullwidth(13, 38);
	printf("□向右移动:→ D 6");
	gotoxyWithFullwidth(14, 38);
	printf("□向下移动:↓ S 2");
	gotoxyWithFullwidth(15, 38);
	printf("□向上移动:↑ W 8");

	gotoxyWithFullwidth(26, 37);
	printf("■By: woffee 17.01.20");

}

// =============================================================================
// 显示得分信息
void printScore(const Snake *snake, const SnakeControl *control)
{
	SetConsoleTextAttribute(g_hConsoleOutput, 0xE);
	gotoxyWithFullwidth(5, 41);
	printf("得分:%d", snake->body.size());
}

// =============================================================================
// 显示游戏边界
void printPoolBorder()
{
	int x, y;
	SetConsoleTextAttribute(g_hConsoleOutput, 0xF0);

	x = START_ROW-1, y = START_COL-1;
	for (int i = 0; i <= WIDTH ; ++i) { gotoxyWithFullwidth(x, y); printf("%2s", ""); y++; }

	x = START_ROW + HEIGHT, y = START_COL;
	for (int i = 0; i <= WIDTH ; ++i) { gotoxyWithFullwidth(x, y); printf("%2s", ""); y++; }

	x = START_ROW, y = START_COL-1;
	for (int i = 0; i <= HEIGHT; ++i) { gotoxyWithFullwidth(x, y); printf("%2s", ""); x++; }

	x = START_ROW-1, y = START_COL + WIDTH;
	for (int i = 0; i <= HEIGHT; ++i) { gotoxyWithFullwidth(x, y); printf("%2s", ""); x++; }
}

  

时间: 2024-10-06 19:29:57

假期休闲,来发贪吃蛇!(Win32控制台版)的相关文章

贪吃蛇(修改Ⅰ版)

修改:1.添加了菜单栏,并进行了监听 2.添加了通关的条件 3.添加了背景音乐(音频文件连接:http://yunpan.cn/cQjY4PzpkfSdz (提取码:2726)) 4.美观了蛇身 class SnakeMain代码: import java.applet.Applet; import java.applet.AudioClip; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.ev

贪吃蛇游戏趣味版

我在程序中加入了分数显示,三种特殊食物,将贪吃蛇的游戏逻辑写到了SnakeGame的类中,而不是在Snake类中. 特殊食物: 1.绿色:普通,吃了增加体型 2.红色:吃了减少体型 3.金色:吃了回到最初体型 4.变色食物:吃了会根据食物颜色改变蛇的颜色 游戏开始,出现普通绿色食物: 特殊变色食物,蛇吃了会变成食物的颜色: 金色食物,蛇吃了可以变回初始状态: #coding=UTF-8from Tkinter import *from random import randintimport tk

自己第一个控制台的游戏——贪吃蛇

一直想自己写个游戏玩玩,好像很厉害的样子,终于今天下定决心写了个最经典的休闲的小游戏--贪吃蛇,当然也有借鉴别人的程序,但是整个代码都是和别人不一样的,直接上代码吧: #include <conio.h> #include <iostream> #include <vector> #include <time.h> using namespace std; #define ROW 22 #define COL 22 struct Point { char c

游戏开发(一)——控制台 贪吃蛇

贪吃蛇游戏设计中主要需要注意的几点: 1:坐标的定义:定义为左上角为(0,0),向右为x正方向,向下为y正方向 2:蛇的设计, 蛇身:m_body,这里用的是链表(是之前写好的双链表),一个节点就是蛇身的一节 每节蛇身的属性包括x,y坐标:column_x,row_y,x表示在地图上的第几列,y表示在地图上的第几行 蛇有一个属性叫朝向,也就是当前在往上.下.左.右的哪个方向移动:m_forward 蛇的动作有:Turn,转向.转向有个判断,就是不能向相反的方向转,比如本来向上运动,按向下键,是无

【141030】VC++贪吃蛇游戏源码(Win32+API)

不错的贪吃蛇游戏,运用了Win32的API.完整源代码,在VS2005下编译通过.内附有编程要点,很好的学习范例. 游戏源码下载地址:点击下载

控制台小游戏-贪吃蛇,c++和c#版

说是c++版,其实只是用到了c++的cout和cin而已.这是我做的第二个控制台游戏,基本上每一行代码都加上了注释. 游戏嘛,我觉得重要的是了解他的思想,所以后期学了面向对象之后这个游戏的代码我也没有重新封装. 下面请看图 代码如下:我是用dev c++写的 1 //注释. ---星辰 2 3 #include <iostream> 4 #include<Windows.h> 5 #include<ctime> 6 #include<cstdlib> 7 #

基于控制台实现贪吃蛇游戏

1).引言 学习编程,我个人觉得最好的办法就是根据自己的水平不断的给自己设定一个小目标.而这个小目标就是一个有意思的项目,通过完成这个项目,对自己的成果(也包括失败的)进行分析总结,从中提炼出对应的技术并分享出来,不断的往复,如此,为的就是让我们永远保持编写程序的兴趣和热情,完了,还提高我们的技术.而本文就是总结自己的一个小目标(基于控制台实现的贪吃蛇游戏而写的总结) 2).技术实现 大家小时候一定玩过贪吃蛇的游戏.贪吃蛇游戏的控制过程其实也不复杂.简单的可以概括为以下4个部分. 1.1  .组

转载——C++控制台贪吃蛇代码

游戏截图: 以下是3个代码文件: Snake_Class.h文件: 1 #ifndef SNAKE 2 #define SNAKE 3 4 #include<windows.h> 5 #include<vector> 6 #include<string> 7 8 //标记界面和菜单项的ID 9 enum ID_ { ID_1=1, ID_2, ID_3, ID_4, ID_5, ID_6, ID_7 }; 10 11 //标记初级,中级,高级三种游戏等级 12 enum

C++控制台贪吃蛇代码

游戏截图: 以下是3个代码文件: Snake_Class.h文件: 1 #ifndef SNAKE 2 #define SNAKE 3 4 #include<windows.h> 5 #include<vector> 6 #include<string> 7 8 //标记界面和菜单项的ID 9 enum ID_ { ID_1=1, ID_2, ID_3, ID_4, ID_5, ID_6, ID_7 }; 10 11 //标记初级,中级,高级三种游戏等级 12 enum