《C#零基础入门之百识百例》(四十)方法应用 -- 推箱子游戏 -- 源码分享
Posted 陈言必行
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《C#零基础入门之百识百例》(四十)方法应用 -- 推箱子游戏 -- 源码分享相关的知识,希望对你有一定的参考价值。
前言
本文属于C#零基础入门之百识百例系列文章。此系列文章旨在为学习C#语言的童鞋提供一套系统的学习路径。此系列文章都会通过【知识点】【练习题】的形式呈现。有任何问题,你都可以通过评论,私信等方式找到我,我会一对一解答你的问题。
系列文章目录:
《C#零基础入门之百识百例》 目录文章传送门
效果展示
一,关卡设计
首先我们要确定一下数组值的含义和其对应的表示:
数值 | 含义 | 符号 |
---|---|---|
0 | 空格 | “ ” 空字符串 |
1 | 墙体 | ■ 实心正方形 |
2 | 箱子 | □ 空心正方形 |
3 | 小人 | 人 汉字 |
4 | 目标 | ☆ 空心五角星 |
5 | 完成 | ★实心五角星 |
基本定义就是这样,详解在使用数组实例一文中: 《C#零基础入门之百识百例》(三十)数组应用 – 推箱子游戏 – 地图初始化,这里不在赘述。
二,地图初始化
主要逻辑:
- 定义两个二维数组表示关卡地图,map带有玩家(3),targeMap不带玩家(3)
- 遍历map将其值对应的地图含义转换为对应的图形,打印出来
/// <summary>
/// 推箱子
/// 地图数值含义:0:空地,1:墙体,2:箱子,3:人,4:目标,5:完成
/// </summary>
class Sokoban
static void Main(string[] args)
// 关卡初始化
InitMap();
// 刷新地图
UpdateMap();
Console.ReadLine();
// 本关卡地图
static int[,] map;
// 目标地图
static int[,] targeMap;
//玩家的初始坐标
static int y = 4, x = 5;
/// <summary>
/// 关卡初始化 -- 关卡数组,玩家位置
/// </summary>
static void InitMap()
y = 4;
x = 5;
map = new int[8, 9]
0,0,0,1,1,1,0,0,0,
0,0,0,1,4,1,0,0,0,
0,0,0,1,0,1,1,1,1,
0,1,1,1,2,0,2,4,1,
0,1,4,0,2,3,1,1,1,
0,1,1,1,1,2,1,0,0,
0,0,0,0,1,4,1,0,0,
0,0,0,0,1,1,1,0,0
;
targeMap = new int[8, 9]
0,0,0,1,1,1,0,0,0,
0,0,0,1,4,1,0,0,0,
0,0,0,1,0,1,1,1,1,
0,1,1,1,2,0,2,4,1,
0,1,4,0,2,0,1,1,1,
0,1,1,1,1,2,1,0,0,
0,0,0,0,1,4,1,0,0,
0,0,0,0,1,1,1,0,0
;
/// <summary>
/// 刷新地图显示
/// </summary>
static void UpdateMap()
// 清屏
Console.Clear();
Console.WriteLine("-------- 推箱子 --------");
Console.WriteLine();
// 遍历数组 -- 打印新地图
for (int i = 0; i < map.GetLength(0); i++)
for (int j = 0; j < map.GetLength(1); j++)
if (map[i, j] == 0)
Console.Write(" ");
if (map[i, j] == 1)
Console.Write("■");
if (map[i, j] == 2)
Console.Write("□");
if (map[i, j] == 3)
Console.Write("人![请添加图片描述](https://img-blog.csdnimg.cn/6a07bce3885340c6aa0b038e46858af9.gif)
");
if (map[i, j] == 4)
Console.Write("☆");
if (map[i, j] == 5)
Console.Write("★");
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("按 ↑←↓→ 控制玩家移动");
Console.WriteLine("输入 G 重新开始");
三,玩家移动
实现逻辑:
- 用户通过 “↑←↓→“ 来控制玩家移动,所以需要在循环中,检测键盘输入,然后处理对应的键盘输入逻辑
- 需要注意玩家移动的方向,对数组进行的计算逻辑。比如向上移动,则是对y-1; 这里需要理解一下
- 进行数组越界校验,不满足的值不进行处理,避免程序报错。
模拟玩家移动逻辑:
/// <summary>
/// 移动函数
/// </summary>
/// <param name="direction">移动方向</param>
static void Move(ConsoleKey direction)
// 本次玩家移动位置
int ox = 0, oy = 0;
// 根据方向进行偏移计算
switch (direction)
case ConsoleKey.UpArrow:
oy--;
break;
case ConsoleKey.DownArrow:
oy++;
break;
case ConsoleKey.LeftArrow:
ox--;
break;
case ConsoleKey.RightArrow:
ox++;
break;
default:
return;
// 玩家移动走 -- 还原地图
map[y, x] = targeMap[y, x];
y += oy;
x += ox;
// 做越界判断
if (y < map.GetLength(0) && y > 0 && x < map.GetLength(1) && x > 0)
map[y, x] = 3;
修改Main
函数如下,即可运行输入控制玩家移动
static void Main(string[] args)
// 关卡初始化
InitMap();
// 循环 -> 等待用户输入后执行一次
while (true)
// 刷新地图
UpdateMap();
// 接收用户输入操作
ConsoleKeyInfo keyInfo = Console.ReadKey();
// 主角移动
Move(keyInfo.Key);
Console.ReadLine();
移动效果:
四,玩家推箱子
玩家推箱子可以分解为两个步骤:**1. 玩家向前移动一步;2. 箱子向前移动一步。**而这里两个步骤又有各种条件限制,如前面是墙箱子不能动,箱子前面是墙不能动等等…
整理后的移动情况如下:(为方便描述将A1作为玩家移动的下一个目标点,A2作为下两个目标点)
- A1是
空地
或目标
--> 可以移动 - A1是
墙体
--> 不能移动 - A1是
箱子
或到目标点的箱子
--> 可以移动 --> 此时需要看能不能推动箱子,即A2点情况
- A2是空地
或目标点
--> 可以移动
- A2是墙体
,箱子
或达到目标的箱子
--> 不能移动
注意:当可以移动时,还需要考虑当前玩家位置是否目标位置,若是目标位置还需要还原会目标,若不是则还原为空格即可。
移动代码修改如下:
/// <summary>
/// 移动函数
/// </summary>
/// <param name="direction">移动方向</param>
static void Move(ConsoleKey direction)
// 玩家要移动的下一个位置的x,y
int nx1 = x, ny1 = y;
// 同方向的移动下两个位置的x,y
int nx2 = x, ny2 = y;
// 本次玩家移动位置
int ox = 0, oy = 0;
// 根据方向进行偏移计算
switch (direction)
case ConsoleKey.UpArrow:
ny1 -= 1;
ny2 -= 2;
oy--;
break;
case ConsoleKey.DownArrow:
ny1 += 1;
ny2 += 2;
oy++;
break;
case ConsoleKey.LeftArrow:
nx1 -= 1;
nx2 -= 2;
ox--;
break;
case ConsoleKey.RightArrow:
nx1 += 1;
nx2 += 2;
ox++;
break;
default:
return;
// 玩家的下一个坐标为空地,进行移动
if (map[ny1, nx1] == 0 || map[ny1, nx1] == 4)
map[ny1, nx1] = 3;
map[y, x] = 0;
// 玩家现在的坐标是目标点的坐标 -- 还原回去
if (targeMap[y, x] == 4)
map[y, x] = 4;
else
map[y, x] = 0;
y += oy;
x += ox;
// 玩家的下一个坐标为墙 不做处理
else if (map[ny1, nx1] == 1)
return;
// 玩家的下一个坐标为未到达目标的箱子或到达目标的箱子
else if (map[ny1, nx1] == 2 || map[ny1, nx1] == 5)
// 箱子的下一个目标为空地
if (map[ny2, nx2] == 0)
map[ny2, nx2] = 2;
map[ny1, nx1] = 3;
// 玩家现在的坐标是目标点的坐标 -- 还原回去
if (targeMap[y, x] == 4)
map[y, x] = 4;
else
map[y, x] = 0;
y += oy;
x += ox;
// 箱子的下一个目标为目标点
else if (map[ny2, nx2] == 4)
map[ny2, nx2] = 5;
map[ny1, nx1] = 3;
// 玩家现在的坐标是目标点的坐标 -- 还原回去
if (targeMap[y, x] == 4)
map[y, x] = 4;
else
map[y, x] = 0;
y += oy;
x += ox;
else
// 箱子的下一个目标为墙或空箱子或到达目标的箱子 -- 不做处理
//if (map[ny2, nx2] == 1 || map[ny2, nx2] == 2 || map[ny2, nx2] == 5)
五,游戏结束
判断游戏结束逻辑就比较简单了,当地图中没有箱子
和目标点
时,即可认为是游戏成功。代码实现如下:
/// <summary>
/// 游戏结束 -- 地图中没有箱子和目标点
/// </summary>
/// <returns></returns>
static bool IsGameOver()
for (int i = 0; i < map.GetLength(0); i++)
for (int j = 0; j < map.GetLength(1); j++)
if (map[i, j] == 2 || map[i, j] == 4)
return false;
return true;
游戏结束后,可处理根据用户操作进行重开或者进行下一关卡(本文未实现,其实逻辑不能,只是将地图设置为下一关卡即可)
修改Main函数
如下:
static void Main(string[] args)
// 关卡初始化
InitMap();
// 循环 -> 等待用户输入后执行一次
while (true)
// 刷新地图
UpdateMap();
// 游戏成功
if (IsGameOver())
Console.WriteLine("======== 恭喜过关 ========");
// Console.WriteLine("按下任意键重玩...");
// todo...下一关
// Console.ReadKey();
//break;
// 接收用户输入操作
ConsoleKeyInfo keyInfo = Console.ReadKey();
// 重玩
if (keyInfo.Key == ConsoleKey.G)
// 关卡初始化
InitMap();
// 刷新地图
UpdateMap();
else
// 主角移动
Move(keyInfo.Key);
Console.ReadLine();
六,源码分享
上面代码可以组成一个完成的推箱子游戏了,组合后的代码:
/// <summary>
/// 推箱子
/// </summary>
class Sokoban
/// <summary>
/// 本关卡地图
/// 值释义 -> 0:空格; 墙:1■; 箱子:2□; 人:3人; 目标:4☆; 完成:5★
/// </summary>
static int[,] map;
// 目标地图
static int[,] targeMap;
//玩家的初始坐标
static int y = 4, x = 5;
static void Main(string[] args)
// 关卡初始化
InitMap();
// 循环 -> 等待用户输入后执行一次
while (true)
// 刷新地图
UpdateMap();
// 游戏成功
if (IsGameOver())
Console.WriteLine("======== 恭喜过关 ========");
// Console.WriteLine("按下任意键重玩...");
// todo...下一关
// Console.ReadKey();
break;
// 接收用户输入操作
ConsoleKeyInfo keyInfo = Console.ReadKey();
// 重玩
if (keyInfo.Key == ConsoleKey.G)
《C#零基础入门之百识百例》(四十四)静态类 -- 温度单位转换
《C#零基础入门之百识百例》(四十六)类的索引 -- 数组索引器
《C#零基础入门之百识百例》(四十五)类的属性 -- 单例模式
《C#零基础入门之百识百例》(四十二)类的成员 -- 模拟计算器