C语言小游戏快速入门--推箱子

一、前言

这是最简单的推箱子了,非常适合初学者。只需要学会一点数组和函数的知识就可以实现。代码的逻辑也不是很难理,就是交换位置。博主实现的推箱子增加了一些花样关卡和颜色,相信很多朋友看完都会受益匪浅。这里我们还是沿用弹跳小球的框架(至于为什么总是沿用这个框架,因为弹跳小球是博主写的第一个小项目,直接搬代码比全部手写快多了),并在原来的框架上增加推箱子所需要的玩法描述。之后加上关卡地图和游戏逻辑就可以实现推箱子啦!先看一下代码的运行效果吧。

推箱子游戏代码运行效果!

二、游戏运行环境

这里就是需要更改一下字符集,否则编译器会报错的。先说报错显示,然后再说更改步骤。这里只讲一种方法。

编译器报错显示:

字符集报错警告

第一步找到解决方案资源管理器选中你所创建的项目:

第一步

第二步:

第二步

最后结果:

无波浪线

三、代码实现逻辑

代码逻辑这里是先解释一些难点,然后在附上源码

1. game.h

头文件这里是放一些函数的声明和宏定义。

#pragma once//防止头文件重复包含
#include<stdio.h>
#include<windows.h>
#include<conio.h>

#define row 10
#define col 10

HANDLE hStdin;//控制台句柄


void HideCursor();//隐藏光标函数
int color(int c);//设置颜色
void gotoxy(int x, int y);//设置光标的位置
void prompt();//提示框
void initMap();//加载当前关卡地图
void people_move();//
void IsWin();//胜利判断函数
void draw_map(int arr[row][col],int x,int y);//打印地图
void play_game();//开始游戏
void game_help();//游戏帮助
void update();//更新日志
void showmenu();//游戏菜单

2. game.c

这里放的是代码的实现逻辑,这里需要讲的就是人物推箱子的逻辑。其他的包括地图设计和胜负判断看代码应该都不难理解。 人物推箱子逻辑就是让这两个位置交换嘛,下面用图来帮助理解。

人物推箱子逻辑

实现的代码:

void people_move()//判断人物移动代码
{
	int x1 = 0;
	int y1 = 0;
	for (int i=0;i<row;i++)
	{
		for (int j=0;j<col;j++)
		{
			if (2==map[i][j]||6==map[i][j])
			{
				x1 = j;
				y1 = i;
			}
		}
	}
		switch (_getch())
		{
		case 'w':
		case 'W'://上
		case 72:
			if (0==map[y1-1][x1]||4==map[y1-1][x1])
			{
				map[y1][x1] -= 2;
				map[y1 - 1][x1] += 2;
			}
			else if (3==map[y1-1][x1]||7== map[y1 - 1][x1])
			{
				if (0==map[y1 - 2][x1]||4==map[y1-2][x1])
				{
					map[y1][x1] -= 2;
					map[y1 - 1][x1] -= 1;
					map[y1 - 2][x1] += 3;
				}
			}
			break;
		case 's':
		case 'S'://下
		case 80:
			if (0 == map[y1 + 1][x1] || 4 == map[y1 + 1][x1])
			{
				map[y1][x1] -= 2;
				map[y1 + 1][x1] += 2;
			}
			else if (3 == map[y1 + 1][x1] || 7 == map[y1 + 1][x1])
			{
				if (0 == map[y1 +2][x1] || 4 == map[y1 + 2][x1])
				{
					map[y1][x1] -= 2;
					map[y1 + 1][x1] -= 1;
					map[y1 +2][x1] += 3;
				}
			}
			break;
		case 'a'://左
		case 'A':
		case 75:
			if (0==map[y1][x1-1]||4==map[y1][x1-1])
			{
				map[y1][x1] -= 2;
				map[y1][x1 - 1] += 2;
			}
			else if (3 == map[y1][x1 - 1] || 7 == map[y1][x1 - 1])
			{
				if (0 == map[y1][x1 - 2] || 4 == map[y1][x1 - 2])
				{
					map[y1][x1] -= 2;
					map[y1][x1 - 1] -= 1;
					map[y1][x1 - 2] += 3;
				}
			}
			break;
		case 'd':
		case 'D'://右
		case 77:
			if (0 == map[y1][x1 + 1] || 4 == map[y1][x1 + 1])
			{
				map[y1][x1] -= 2;
				map[y1][x1 + 1] += 2;
			}
			else if (3 == map[y1][x1 + 1] || 7 == map[y1][x1 + 1])
			{
				if (0 == map[y1][x1 + 2] || 4 == map[y1][x1 + 2])
				{
					map[y1][x1] -= 2;
					map[y1][x1 + 1] -= 1;
					map[y1][x1 + 2] += 3;
				}
			}
			break;
		case 27://ESC
			system("cls");
			exit(0);
			break;
		}
	}

这里为了实现关卡地图,用到了三维数组来储存地图。这里好像并没有啥好讲的,就是每次开始游戏的时候需要在主函数调用一次加载关卡函数就完了。level是我定义的全局变量,用来表示当前到了第几关。我这里只实现了三关。想要实现更多关卡的话可以自己实现。

//加载当前关卡地图
void initMap()
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			map[i][j] = Map3[level-1][i][j];
		}
	}
}

这里的胜负判断比较简单,就是判断当前地图上没有箱子就是胜利!并加载下一关。

//胜利判断函数
void IsWin()
{
	//如果整个地图中没有箱子则通过此关卡
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (map[i][j] == 3)
			{ //如果在地图中发现一个箱子则判断为此关卡为不通过
				return;
			}
		}
	}
	color(15);
	printf("游戏胜利!\n");
	system("pause"); //暂停窗口
	if (level<3)
	{
		level++; //通过当前关卡后,进入下一关
		initMap(); //并加载下一关的地图
		system("cls");
	}
	else
	{
		color(12);
		system("cls");
		exit(0); //退出程序
	}
}

游戏源码:

#include"game.h"

/*******************************************
*
* @数字0表示空地
* @数字1表示墙
* @数字2表示人物
* @数字3表示箱子
* @数字4表示终点
* @数字6表示人物到达终点
* @数字7表示箱子到达终点
*
********************************************/
int Map3[3][row][col] =
{
	{ //第一关
		0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 4, 0, 2, 0, 1,
		1, 0, 0, 0, 3, 3, 3, 0, 0, 1,
		1, 0, 4, 1, 1, 4, 1, 1, 4, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 3, 0, 0, 0, 1,
		1, 0, 0, 3, 0, 4, 0, 0, 0, 1,
		1, 1, 1, 1, 0, 0, 0, 0, 1, 0,
		0, 0, 0, 1, 1, 1, 1, 1, 1, 0,
	},
	{ //第二关
		0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
		0, 1, 4, 0, 0, 0, 4, 4, 1, 0,
		0, 1, 4, 0, 0, 0, 3, 4, 1, 0,
		1, 1, 1, 0, 0, 0, 0, 3, 1, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 3, 0, 0, 0, 0, 3, 0, 1,
		1, 0, 1, 1, 3, 1, 1, 1, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 2, 0, 0, 0, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	},
	{ //第三关
		1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 4, 4, 1, 0, 0, 0, 1, 1,
		1, 0, 0, 0, 1, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 3, 0, 3, 0, 0, 1,
		1, 0, 0, 0, 1, 0, 0, 0, 0, 1,
		1, 1, 3, 0, 1, 0, 0, 0, 0, 1,
		1, 1, 4, 0, 4, 0, 1, 4, 0, 1,
		1, 1, 1, 1, 3, 3, 1, 1, 1, 1,
		0, 0, 0, 1, 0, 2, 1, 0, 0, 0,
		0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
	}
};
int map[row][col] = { 0 };
int level = 1; //记录当前关卡,默认为第1关

void HideCursor()//隐藏光标函数
{
	CONSOLE_CURSOR_INFO cursor;
	cursor.bVisible = FALSE;
	cursor.dwSize = sizeof(cursor);
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorInfo(handle, &cursor);
}
void gotoxy(int x, int y)    //设置光标的位置
{     
	COORD c;    
    c.X=x-1;  
    c.Y=y-1;     
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c);
}   
int color(int c)//设置颜色
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);
	return 0;//改变颜色的函数
}
void prompt()
{
	
	gotoxy(21, 1);
	color(15);
	printf("||------------------||\n");
	gotoxy(21, 2);
	printf("||   提       示    ||\n");
	gotoxy(21, 3);
	printf("||                  ||\n");
	gotoxy(21, 4);
	printf("||①按wasd或↑←↓→||\n");
	gotoxy(21, 5);
	printf("|| 可以移动人物方向 ||\n");
	gotoxy(21, 6);
	printf("||                  ||\n");
	gotoxy(21, 7);
	printf("||②退出请按ESC     ||\n");
	gotoxy(21, 8);
	printf("||                  ||\n");
	gotoxy(21, 9);
	printf("||③当前关卡为第  关||\n");
	gotoxy(21, 10);
	printf("||                  ||\n");
	gotoxy(21, 11); 
	printf("||④祝您游戏愉快!  ||\n");
	gotoxy(21, 12);
	printf("||                  ||\n");
	gotoxy(21, 13);
	printf("||------------------||\n");
	color(12);
	gotoxy(38, 9);
	printf("%d", level);
	gotoxy(21, 13);
	printf("\n");
}

//加载当前关卡地图
void initMap()
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			map[i][j] = Map3[level-1][i][j];
		}
	}
}
void people_move()
{
	int x1 = 0;
	int y1 = 0;
	for (int i=0;i<row;i++)
	{
		for (int j=0;j<col;j++)
		{
			if (2==map[i][j]||6==map[i][j])
			{
				x1 = j;
				y1 = i;
			}
		}
	}
		switch (_getch())
		{
		case 'w':
		case 'W':
		case 72:
			if (0==map[y1-1][x1]||4==map[y1-1][x1])
			{
				map[y1][x1] -= 2;
				map[y1 - 1][x1] += 2;
			}
			else if (3==map[y1-1][x1]||7== map[y1 - 1][x1])
			{
				if (0==map[y1 - 2][x1]||4==map[y1-2][x1])
				{
					map[y1][x1] -= 2;
					map[y1 - 1][x1] -= 1;
					map[y1 - 2][x1] += 3;
				}
			}
			break;
		case 's':
		case 'S':
		case 80:
			if (0 == map[y1 + 1][x1] || 4 == map[y1 + 1][x1])
			{
				map[y1][x1] -= 2;
				map[y1 + 1][x1] += 2;
			}
			else if (3 == map[y1 + 1][x1] || 7 == map[y1 + 1][x1])
			{
				if (0 == map[y1 +2][x1] || 4 == map[y1 + 2][x1])
				{
					map[y1][x1] -= 2;
					map[y1 + 1][x1] -= 1;
					map[y1 +2][x1] += 3;
				}
			}
			break;
		case 'a':
		case 'A':
		case 75:
			if (0==map[y1][x1-1]||4==map[y1][x1-1])
			{
				map[y1][x1] -= 2;
				map[y1][x1 - 1] += 2;
			}
			else if (3 == map[y1][x1 - 1] || 7 == map[y1][x1 - 1])
			{
				if (0 == map[y1][x1 - 2] || 4 == map[y1][x1 - 2])
				{
					map[y1][x1] -= 2;
					map[y1][x1 - 1] -= 1;
					map[y1][x1 - 2] += 3;
				}
			}
			break;
		case 'd':
		case 'D':
		case 77:
			if (0 == map[y1][x1 + 1] || 4 == map[y1][x1 + 1])
			{
				map[y1][x1] -= 2;
				map[y1][x1 + 1] += 2;
			}
			else if (3 == map[y1][x1 + 1] || 7 == map[y1][x1 + 1])
			{
				if (0 == map[y1][x1 + 2] || 4 == map[y1][x1 + 2])
				{
					map[y1][x1] -= 2;
					map[y1][x1 + 1] -= 1;
					map[y1][x1 + 2] += 3;
				}
			}
			break;
		case 27:
			system("cls");
			exit(0);
			break;
		}
}
void draw_map(int arr[row][col], int x, int y)//打印地图
{
	for (int i=0;i<x;i++)
	{
		for (int j=0;j<y;j++)
		{
			if (1 == arr[i][j])
			{
				color(9);
				printf("■");
			}
			else if (2 == arr[i][j])
			{
				color(10);
				printf("♀");
			}
			else if (3 == arr[i][j])
			{
				color(14);
				printf("■");
			}
			else if (4 == arr[i][j])
			{
				color(13);
				printf("★");
			}
			else if (6 == arr[i][j])
			{
				color(10);
				printf("♀");
			}
			else if (7 == arr[i][j])
			{
				color(12);
				printf("●");
			}
			else
			{
				printf("  ");
			}
		}
		printf("\n");
	}
}
//胜利判断函数
void IsWin()
{
	//如果整个地图中没有箱子则通过此关卡
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (map[i][j] == 3)
			{ //如果在地图中发现一个箱子则判断为此关卡为不通过
				return;
			}
		}
	}
	color(15);
	printf("游戏胜利!\n");
	system("pause"); //暂停窗口
	if (level<3)
	{
		level++; //通过当前关卡后,进入下一关
		initMap(); //并加载下一关的地图
		system("cls");
	}
	else
	{
		color(12);
		system("cls");
		exit(0); //退出程序
	}
}

void play_game()
{
	HideCursor();
	while (1)
	{
	  gotoxy(1, 1);
	  draw_map(map,row,col);
	  prompt();
	  people_move(map, row, col);
	  gotoxy(1, 1);
	  draw_map(map, row, col);
	  prompt();
	  IsWin(); 
	}
}
static void game_help_menu()//游戏帮助菜单
{
	printf("\n");
	printf("游戏操作:\n");
	printf("\n");
	printf("①:按w a s d 或 ↑ ←↓ →\n");
	printf("\n");
	printf("  可以上下左右控制箱子\n");
	printf("\n");
	printf("\n");
	printf("\n");
	printf("(按1返回,按任意键退出)\n");
	printf("请选择:>");
}
void game_help()//游戏帮助
{
	game_help_menu();
	switch (_getch())
	{
	case '1':
		break;
	default:
		exit(0);
		break;
	}
}
static void update_menu()//更新日志菜单
{
	printf("(暂无)\n");
	printf("\n");
	printf("\n");
	printf("\n");
	printf("(按1返回,按任意键退出)\n");
	printf("请选择:>");
}
void update()//更新日志
{
	update_menu();
	switch (_getch())
	{
	case '1':
		break;
	default:
		exit(0);
		break;
	}
}
void showmenu()//游戏菜单
{
	printf("|----------------------|\n");
	printf("|      1.开始游戏      |\n");
	printf("|----------------------|\n");
	printf("|      2.游戏帮助      |\n");
	printf("|----------------------|\n");
	printf("|      3.更新日志      |\n");
	printf("|----------------------|\n");
	printf("|      0.退出游戏      |\n");
	printf("|----------------------|\n");
	printf("\n");
	printf("(温馨提醒:请按照菜单相应的选项选择)");
	printf("\n");
	printf("请输入你的选择:>");
}

3. test.c

这里主要实现一些函数的调用。

#include"game.h"


int main()
{
	SetConsoleTitle("推箱子游戏");
	//控制台窗口菜单栏上的字
	system("mode con cols=42 lines=16");
	//控制台的大小,这里可以按需要自己设置
	initMap();
	do
	{
		system("cls");
		showmenu();
		switch (_getch())
		{
		case'1':
			system("cls");
			play_game();//开始游戏
			break;
		case'2':
			system("cls");
			game_help();//游戏帮助
			break;
		case'3':
			system("cls");
			update();//更新日志
			break;
		case'0':
			exit(0);//退出游戏
			break;
		}

	} while ('0');

	return 0;
}

四、总结

推箱子实现还是很简单。也能知道前面学好框架有多重要。

  • 19
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

食落鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值