简单的字符游戏

制作字符游戏贪吃蛇的学习记录

        c语言简洁强大,我们可以利用它写出许多程序,甚至制作一些小游戏,比如贪吃蛇。笔者在第一次用c语言制作贪吃蛇游戏时,头都大了,因为完全不懂该从何处下手开始写代码。后来借鉴了前辈的经验才明白,需要用到自顶向下、逐步求精的方法,将一个贪吃蛇游戏细分为几个小问题:打印地图、蛇的移动、gameover的判定、食物的产生。然后再慢慢化简各个小问题中的难题。在这里给大家记录下笔者的学习记录,若能给大家一点帮助则是万幸。

1.打印地图

        打印地图我们首先要构造一个地图,笔者的想法是构建一个整数型的二维数组用来表示地图:0表示空地(“ ”),1表示蛇头(“H”),2表示蛇身(“X”),3表示墙壁(“*
”),4表示食物("$")。这样做的好处就是每次打印新地图时可以用同一个函数,同时也便于后续的一些判定。以下是地图的构造。
int map[22][42] = { 0 };//地图全部初始化为空地,最好把map设为全局变量
 	for (int i = 0; i < 22; i++)
	{
	 	for (int j = 0; j < 42; j++)
		if (i == 0 || i == 21 || j == 0 || j == 41)
			map[i][j] = 3;
	}//设置墙壁
	map[1][5] = 1;//设置蛇头
	map[1][1] = 2; map[1][2] = 2; map[1][3] = 2; map[1][4] = 2;//设置蛇身
      接下来再写一个打印地图的函数,让蛇每次移动后打印新地图。
void new_map()
{
	for (int i = 0; i < 22; i++)
	{
		for (int j = 0; j < 42; j++)
		{
			if (map[i][j] == 0)
				printf(" ");//空地
			if (map[i][j] == 1)
				printf("H");//蛇头
			if (map[i][j] == 2)
				printf("X");//蛇身
			if (map[i][j] == 3)
				printf("*");//墙壁
			if (map[i][j] == 4)
				printf("$");//食物
		}
		printf("\n");
	}

2.蛇的移动

      蛇如何移动是整个游戏中最关键的一部分,若是一开始就考虑整条蛇的移动是很困难的,于是笔者先从最简单的头部的移动开始写代码:用x、y表示头的横纵坐标,读入相应的方向字符后对坐标进行相应的加减就好了,如下

while (1)//死循环,之后可加入gameover的判定
	{
		map[y][x] = 0;//旧蛇头归零
		scanf_s("%d", &m);
		switch (m)
		{
		case 'w': y -= 1; break; 
		case 's': y += 1; break; 
		case 'a': x -= 1; break; 
		case 'd': x += 1; break; 
		default:
			break;
		}//移动
		map[y][x] = 1;//新蛇头
	}
       接着再考虑蛇身的移动,这里许多前辈使用了数据构造的办法,无奈在下学得不精,不得不放弃使用另一种方式来实现蛇的身体构造。但是一些思想还是通用的,蛇身的移动其实就是蛇头的位置变成蛇身,然后蛇的尾巴消失。这样一来就豁然开朗了,笔者只需要利用一个二维数据存储每个蛇身的坐标位置,再分别用两个指针指向蛇的脖子(头的下一节)和蛇尾,每次移动就读入旧蛇头即新蛇脖的坐标,蛇尾坐标归零后再指向下一个蛇尾即可。代码形式如下
int head = 4, tail = 1,body[801][2] = { 0 };//笔者构建的地图为20×40,考虑蛇身最大长度所以设定了801
body[1][0] = 1; body[1][1] = 1; body[2][1] = 1; body[2][0] = 2; body[3][1] = 1; body[3][0] = 3; body[4][1] = 1; body[4][0] = 4;//初始蛇身的坐标,0代表x轴,1代表y轴
while (1)
	{
		map[y][x] = 2;//旧蛇头替换为蛇身
		head = (head + 1) % 801;//指向新蛇脖,准备读入数据,%801为了让数组循环
		body[head][0] = x; body[head][1] = y;//读入新蛇脖(头的下一节)
		scanf_s("%d", &m);
		switch (m)
		{
		case 'w': y -= 1; break; 
		case 's': y += 1; break; 
		case 'a': x -= 1; break; 
		case 'd': x += 1; break; 
		default:
			break;
		}
		map[y][x] = 1;
		map[body[tail][1]][body[tail][0]] = 0;//去掉蛇尾
		tail = (tail + 1) % 801;//指向新蛇尾
		new_map();//打印蛇移动后的新地图
	}

3.gameover的判定

        这个问题其实很简单,我们只需要判断蛇头的新坐标上的数字是不是为0就好了(如果是食物只需要多加一个分支而已),写一个函数_life(),给新蛇头的位置,若该位置为0就返回1,若为蛇身或墙壁就返回0,让循环结束(只需要把返回值的结果给while循环就好了),代码如下
int _life(int a)
{
	if (a == 4)
		return 2;//遇到食物,返回一个特殊值,便于后续判定。同理如果想增加游戏道具也可以用这种方法
	else
	{
		if (a == 0)
return 1;elsereturn 0;}}

        需要注意的是_life()函数的判断要在新蛇头生成前,即map[y][x] = 1;的上一条。

4.食物的生成

      我们需要设立一个变量来监控食物是否被吃,以及一个函数来在地图中随机生成食物。看似简单,但会牵扯到上面的许多函数。食物随机生成函数
void _food()
{
	int x = rand() % 40 + 1, y = rand() % 20 + 1;
	while (food == 0)
	{
		if (map[y][x] == 0)//找一个空地放食物
		{
			map[y][x] = 4;
			food = 1;//全局变量,监控食物是否存在
		}
		x = rand() % 40 + 1; y = rand() % 20 + 1;
	}
}
      在上面的_life()函数里吃掉食物时会返回为2,这就便于我们进行食物被吃、和蛇身变长的判定
if (life != 2){//life = _life(map[y][x])
			map[body[tail][1]][body[tail][0]] = 0;
			tail = (tail + 1) % 801;//没吃到食物,蛇尾消失
		}
		else food = 0;//吃到食物就跳过尾巴消失,则蛇自然变长,同时食物变量归零
        最后只需要把上面的内容整合下,并做些调整就可以制作出最简单的贪吃蛇游戏了,下面是完整的代码

  
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include <Windows.h>
#include <conio.h>


int map[22][42] = { 0 }, food = 0;


int _life(int a);
void move();
void _food();
void new_map();
int main()
{


	for (int i = 0; i < 22; i++)
	{
		for (int j = 0; j < 42; j++)
		if (i == 0 || i == 21 || j == 0 || j == 41)
			map[i][j] = 3;
	}
	map[1][5] = 1;
	map[1][1] = 2; map[1][2] = 2; map[1][3] = 2; map[1][4] = 2;
	move();
	return 0;
}
int _life(int a)
{
	if (a == 4)
		return 2;
	else
	{
		if (a == 0)
			return 1;
		else
			return 0;
	}
}
void move()
{
	int head = 4, tail = 1, x = 5, y = 1, body[801][2] = { 0 }, life = 1;
	body[1][0] = 1; body[1][1] = 1; body[2][1] = 1; body[2][0] = 2; body[3][1] = 1; body[3][0] = 3; body[4][1] = 1; body[4][0] = 4;
	char m = 'd', m1 = 'd';//设定初始的移动方向
	while (life)
	{
		_food();
		map[y][x] = 2;
		head = (head + 1) % 801;
		body[head][0] = x; body[head][1] = y;
		for (; _kbhit();) m = _getch();//用到<conio.h>库,让蛇在没有输入指令时保持原来的方向移动
		switch (m)
		{
		case 'w':if (m1 == 's'){ m = m1; y += 1; break; }
				 else{ y -= 1; break; }
		case 's':if (m1 == 'w'){ m = m1; y -= 1; break; }
				 else{ y += 1; break; }
		case 'a':if (m1 == 'd'){ m = m1; x += 1; break; }
				 else{ x -= 1; break; }
		case 'd':if (m1 == 's'){ m = m1; x -= 1; break; }
				 else{ x += 1; break; }
		default:m = m1;
			break;
		}
		m1 = m;//m1为了不让蛇回头
		life = _life(map[y][x]);
		map[y][x] = 1;
		if (life != 2){
			map[body[tail][1]][body[tail][0]] = 0;
			tail = (tail + 1) % 801;
		}
		else food = 0;
		Sleep(300);//用到 <Windows.h>屏幕刷新的间隔,可以控制蛇的速度
		system("cls");//刷新屏幕,让游戏看起来像在一个地图里运行的
		new_map();
	}
}
void _food()
{
	int x = rand() % 40 + 1, y = rand() % 20 + 1;
	while (food == 0)
	{
		if (map[y][x] == 0)
		{
			map[y][x] = 4;
			food = 1;
		}
		x = rand() % 40 + 1; y = rand() % 20 + 1;
	}
}
void new_map()
{
	for (int i = 0; i < 22; i++)
	{
		for (int j = 0; j < 42; j++)
		{
			if (map[i][j] == 0)
				printf(" ");
			if (map[i][j] == 1)
				printf("H");
			if (map[i][j] == 2)
				printf("X");
			if (map[i][j] == 3)
				printf("*");
			if (map[i][j] == 4)
				printf("$");
		}
		printf("\n");
	}
}



  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值