赞
踩
需求:在控制台上,跑一个贪吃蛇,可以按照上下左右方向移动,可以吃食物增加长度,撞墙或者撞到自身结束游戏
Windows.h 中有一个可以控制光标位置的函数
SetConsoleCursorPosition(HANDLE,COORD);
使用方式如下:
#include<stdio.h>
#include<windows.h>
int main(){
COORD coord; //先初始化一个coord结构体,结构体里面有两个元素,X和Y
//表示光标的坐标
coord.X = 20; //给坐标赋值
coord.Y = 7;
//然后应用到函数中,HANDLE参数为固定写法
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
putchar('h'); //打印一个h,看看是否是在20,7这样的坐标
return 0;
}
运行结果:
可以实现根据坐标实现光标移动打印
有这个函数,贪吃蛇出现在屏幕的任意地方就可以实现了
以上函数使用完,发现总有“按任意键退出程序”这种东西,我们可以在代码最后,简单的使用一个getchar,意思是我这里还没结束,还要等键盘输入呢,你程序结束的信息不要出来。
int main(){
COORD coord;
coord.X = 20;
coord.Y = 7;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
putchar('h');
getchar();
return 0;
}
可以看到,getchar是可以解决问题的,但是h后面总有一个光标,闪来闪去的,很烦,解决一下
可以在main中最前面加入此段代码,意为让光标不可见
CONSOLE_CURSOR_INFO cci; 声明一个控制台光标信息结构体
cci.dwSize = sizeof(cci); 将里面的信息改一下,
cci.bVisible = FALSE;
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cci); 再设置回去
可以看到,光标没有了
贪吃蛇玩法是,按一下方向键,他拐一下弯,如果不按,他就一直走,
使用getchar函数作为接收键盘输入的话,getchar 他是一个阻塞函数,键盘不输入,他就停在那里等着键盘输入了。
解决办法是使用_getch()和_kbhit()函数组合
_kbhit函数的作用是检查看见是否被按下,按下范围非0值,没有按下返回0,该函数是一个非阻塞函数,不管有没有按键按下,都会立即响应 返回
_getch()函数是从控制台中获取输入的字符,获取输入的字符后,并不会在控制台上显示该字符,
举个例子,我需要控制台在我的键盘不动的时候,一直打印“走”,当按“w”“s”“a”“d”的时候,打印“拐弯”
#include<stdio.h> #include<windows.h> #include<conio.h> int main(){ char ch; while(1){ if(kbhit()){ //键盘有输入 ch = _getch(); if(ch=='w'||ch=='s'||ch=='a'||ch=='d'){ printf("拐弯\n"); } }else{ //键盘没有输入 printf("走\n"); } Sleep(1000); } return 0; }
有了以上这些参考资料,实现贪吃蛇游戏就成为可能,我们先分析一下怎么个思路
先界定一个贪吃蛇的活动范围,定义一个宽和高,WIDE和HIGH
定义贪吃蛇结构体:一个字段为它的长度size,另一个字段是坐标数组,这个坐标也可以定义为一个结构体body,所以一个贪吃蛇就是由size个body组成的数组
食物可以随机出现,而且只有一个坐标组成,可以用一个长度为2的int数组表示,
玩游戏的时候,将整个过程放在一个while循环中,每移动一步,计算一次坐标,按照坐标,绘制一次蛇身体,并且判断是否撞墙,是否撞到自己的身体,和是否撞到食物,撞到食物,则size+1
有了上面的分析,可以简单的写一个伪代码
定义蛇的身体结构体,蛇结构体(size+身体数组)
确定蛇的活动范围
初始化蛇的size和初始位置坐标
初始化食物的坐标
开始玩游戏
根据坐标在控制台上画蛇
控制方向(不可以向反方向移动)
检查是否撞墙
检查是否撞到自己身体
碰到食物
长度+1
重新初始化食物
更新蛇的位置(根据移动方向,更新蛇头坐标,蛇身体位置的其他坐标也更新)
简单的代码测试一下宽和高,然后宏定义为常量
#define WIDE 78
#define HIGH 24
定义蛇的结构体
struct BODY{
int X;
int Y;
};
struct SNAKE{
int size;
struct BODY body[WIDE*HIGH]; //将蛇的长度定义为最大
}snake;
初始化蛇的长度和初始坐标
void InitSnake(){
snake.size = 3;
snake.body[0].X = WIDE/2;
snake.body[0].Y = HIGH/2;
snake.body[1].X = WIDE/2-1;
snake.body[1].Y = HIGH/2;
snake.body[2].X = WIDE/2-2;
snake.body[2].Y = HIGH/2;
}
初始化食物
void InitFood(){
food[0] = rand()%WIDE;
food[1] = rand()%HIGH;
}
因为该动作每吃掉一个食物会再次调用一次,所以将srand操作放到了main中
初始化墙:
void InitWall(){
for(int i=0;i<=HIGH;i++){
for(int j = 0;j<=WIDE;j++){
if(i==HIGH){
putchar('=');
}else if(j==WIDE){
putchar('=');
}else{
putchar(' ');
}
}
putchar('\n');
}
}
画蛇和画食物的过程,因为每次都需要调用,所以分离出来一个函数
void ShowUI(){ COORD coord; //处理蛇尾巴 coord.X = lx; coord.Y = ly; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord); putchar(' '); //显示蛇的位置 for(int i=0;i<snake.size;i++){ coord.X = snake.body[i].X; coord.Y = snake.body[i].Y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord); if(i==0){ putchar('@'); }else{ putchar('*'); } } //显示食物位置 coord.X = food[0]; coord.Y = food[1]; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord); putchar('#'); }
开始玩游戏
void PlayGame(){ char key = 'D'; //初始化一个刚开始移动的方向 while(snake.body[0].X<WIDE && snake.body[0].Y<HIGH && snake.body[0].X>=0 && snake.body[0].Y>=0){ //只要在墙内,就可以循环 ShowUI(); //根据坐标画蛇和食物 //方向控制 while(_kbhit()){ //如果有按键 key = _getch(); //获取到按键 switch(lastkey){ //和当前蛇的运行方向相比较,如果相反,则还按之前的方向运行,不理会 case 'D':case 'd': if(key=='a' || key=='A'){ key = 'D'; } break; case 'S':case 's': if(key=='w' || key=='W'){ key = 'S'; } break; case 'A':case 'a': if(key=='D' || key=='d'){ key = 'A'; } break; case 'W':case 'w': if(key=='s' || key=='S'){ key = 'W'; } break; } } lastkey = key; switch(key){ //根据方向按键,计算出蛇头下一步的偏移量 case 'D':case 'd':dx=1;dy=0;break; case 'S':case 's':dx=0;dy=1;break; case 'A':case 'a':dx=-1;dy=0;break; case 'W':case 'w':dx=0;dy=-1;break; } //是否和自身碰撞 for(int i = 1;i<snake.size;i++){ if(snake.body[0].X == snake.body[i].X && snake.body[0].Y == snake.body[i].Y){ return; } } //蛇头和食物碰撞 if(snake.body[0].X == food[0] && snake.body[0].Y == food[1]){ InitFood(); //重新初始化食物 snake.size++; score++; } //获取蛇尾的坐标,等下次画蛇的时候直接置为空格 lx = snake.body[snake.size-1].X; ly = snake.body[snake.size-1].Y; //蛇身体更新位置 for(int i = snake.size-1;i>0;i--){ snake.body[i].X = snake.body[i-1].X; snake.body[i].Y = snake.body[i-1].Y; } //蛇头更新坐标 snake.body[0].X+=dx; snake.body[0].Y+=dy; Sleep(100); //system("cls"); } }
以上代码就编写完了,整体文件查看代码
snake.h
#ifndef __SNACK_H__ #define __SNACK_H__ #define WIDE 78 #define HIGH 24 struct BODY{ // 蛇坐标 int X; int Y; }; struct SNAKE{ int size; struct BODY body[WIDE*HIGH]; }snake; int food[2] = {0}; //食物坐标 int score; int dx; //蛇头坐标偏移量 int dy; int lx; //蛇尾坐标 int ly; char lastkey = 'D'; void InitFood(); void InitSnake(); void ShowUI(); void PlayGame(); void InitWall(); #endif // __SNACK_H__
main.c
#include <stdio.h> #include <stdlib.h> #include "snack.h" #include <time.h> #include <Windows.h> #include <conio.h> int main() { //去掉控制台光标 CONSOLE_CURSOR_INFO cci; cci.bVisible = FALSE; cci.dwSize = sizeof(cci); SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cci); srand((size_t)time(NULL)); InitSnake(); InitFood(); InitWall(); PlayGame(); getchar(); return 0; } void InitFood(){ food[0] = rand()%WIDE; food[1] = rand()%HIGH; } void InitSnake(){ snake.size = 3; snake.body[0].X = WIDE/2; snake.body[0].Y = HIGH/2; snake.body[1].X = WIDE/2-1; snake.body[1].Y = HIGH/2; snake.body[2].X = WIDE/2-2; snake.body[2].Y = HIGH/2; } void ShowUI(){ COORD coord; //显示蛇的位置 coord.X = lx; coord.Y = ly; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord); putchar(' '); for(int i=0;i<snake.size;i++){ coord.X = snake.body[i].X; coord.Y = snake.body[i].Y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord); if(i==0){ putchar('@'); }else{ putchar('*'); } } //显示食物位置 coord.X = food[0]; coord.Y = food[1]; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord); putchar('#'); } void PlayGame(){ char key = 'D'; while(snake.body[0].X<WIDE && snake.body[0].Y<HIGH && snake.body[0].X>=0 && snake.body[0].Y>=0){ ShowUI(); //方向控制 while(_kbhit()){ key = _getch(); switch(lastkey){ case 'D':case 'd': if(key=='a' || key=='A'){ key = 'D'; } break; case 'S':case 's': if(key=='w' || key=='W'){ key = 'S'; } break; case 'A':case 'a': if(key=='D' || key=='d'){ key = 'A'; } break; case 'W':case 'w': if(key=='s' || key=='S'){ key = 'W'; } break; } } lastkey = key; switch(key){ case 'D':case 'd':dx=1;dy=0;break; case 'S':case 's':dx=0;dy=1;break; case 'A':case 'a':dx=-1;dy=0;break; case 'W':case 'w':dx=0;dy=-1;break; } //是否和自身碰撞 for(int i = 1;i<snake.size;i++){ if(snake.body[0].X == snake.body[i].X && snake.body[0].Y == snake.body[i].Y){ return; } } //蛇和食物碰撞 if(snake.body[0].X == food[0] && snake.body[0].Y == food[1]){ InitFood(); snake.size++; score++; } //获取蛇尾的坐标 lx = snake.body[snake.size-1].X; ly = snake.body[snake.size-1].Y; //蛇更新位置 for(int i = snake.size-1;i>0;i--){ snake.body[i].X = snake.body[i-1].X; snake.body[i].Y = snake.body[i-1].Y; } snake.body[0].X+=dx; snake.body[0].Y+=dy; Sleep(100); //system("cls"); } } void InitWall(){ for(int i=0;i<=HIGH;i++){ for(int j = 0;j<=WIDE;j++){ if(i==HIGH){ putchar('='); }else if(j==WIDE){ putchar('='); }else{ putchar(' '); } } putchar('\n'); } }
运行结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。