12.结束流程
12.1 知识点
主要操作
发现随机创建的方块有可能一出来就重合,所以将随机生成的方块的 y 的位置往上移。
在屏幕外就写死不绘制和不擦除。
只要在动态墙壁添加处发现位置顶满,就结束,切换到结束界面。
在结束界面发现输入还能进行,因为输入线程没有停止,所以要加个标识控制输入线程的关闭。
在游戏场景类中编写一个关闭线程的方法,让标识为 false 即可。
在地图类的构造函数中传入游戏场景,切换场景时调用游戏场景停止线程的方法。
同时发现切完场景直接 break 的话后面的绘制方法还会走完,将 break 改成 return 即可。
12.2 知识点代码
Map
using System;
using System.Collections.Generic;
using System.Text;
namespace CSharp实践教学
{
class Map : IDraw
{
//必须要初始化才能往里面装东西
//固定墙壁
private List<DrawObject> walls = new List<DrawObject>();
//必须要初始化才能往里面装东西
//动态墙壁
public List<DrawObject> dynamicWalls = new List<DrawObject>();
private GameScene nowGameScene;
#region Lesson6 为了外部能快速得到地图边界
//动态墙壁的宽容量 一行可以有多少个
public int w;
public int h;
#endregion
#region Lesson11 跨层
//记录每一行有多少个小方块的容器
//索引对应的就是行号
private int[] recordInfo;
#endregion
//重载一次无参构造 去初始化我们的固定墙壁
public Map( GameScene scene)
{
this.nowGameScene = scene;
//为了方便外部得到地图的高的边界 直接在此记录 避免修改代码时多处修改
h = Game.h - 6;
//这个就代表对应每行的计数初始化 默认都为0
//0~Game.h-7
recordInfo = new int[h];
w = 0;
//在绘制 横向的固定墙壁
for (int i = 0; i < Game.w; i+=2)
{
walls.Add(new DrawObject(E_DrawType.Wall, i, h));
++w;
}
w -= 2;
for (int i = 0; i < h; i++)
{
walls.Add(new DrawObject(E_DrawType.Wall, 0, i));
walls.Add(new DrawObject(E_DrawType.Wall, Game.w - 2, i));
}
}
public void Draw()
{
//绘制固定墙壁
for (int i = 0; i < walls.Count; i++)
{
walls[i].Draw();
}
//绘制动态墙壁 有才绘制
for (int i = 0; i < dynamicWalls.Count; i++)
{
dynamicWalls[i].Draw();
}
}
//清楚动态墙壁
public void ClearDraw()
{
//绘制动态墙壁 有才绘制
for (int i = 0; i < dynamicWalls.Count; i++)
{
dynamicWalls[i].ClearDraw();
}
}
/// <summary>
/// 提供给外部添加动态方块的函数
/// </summary>
/// <param name="walls"></param>
public void AddWalls( List<DrawObject> walls )
{
for (int i = 0; i < walls.Count; i++)
{
//传递方块进来时 把其类型改成 墙壁类型
walls[i].ChangeType(E_DrawType.Wall);
dynamicWalls.Add(walls[i]);
//在动态墙壁添加处 发现 位置顶满了 就结束
if( walls[i].pos.y <= 0 )
{
//关闭输入线程
this.nowGameScene.StopThread();
//场景切换 切换到结束界面
Game.ChangeScene(E_SceneType.End);
return;
}
//进行添加动态墙壁的计数
//根据索引来得到行
//h 是 Game.h - 6
//y 最大为 Game.h - 7
recordInfo[h - 1 - walls[i].pos.y] += 1;
}
//先把之前的动态小方块擦掉
ClearDraw();
//检测移除
CheckClear();
//再绘制动态小方块
Draw();
}
#region Lesson11 跨层
/// <summary>
/// 检测是否跨层
/// </summary>
public void CheckClear()
{
List<DrawObject> delList = new List<DrawObject>();
//要选择记录行中有多少个方块的容器
//数组
//判断这个一行是否满(方块)
//遍历数组 检测数组里面存的数
//是不是w-2
for (int i = 0; i < recordInfo.Length; i++)
{
//必须满足条件 才证明满了
//小方块计数 == w(这个w已经是去掉了左右两边的固定墙壁)
if (recordInfo[i] == w)
{
//1.这一行的所有小方块移除
for (int j = 0; j < dynamicWalls.Count; j++)
{
//当前通过动态方块的y计算它在哪一行 如果行号
//和当前记录索引一致 就证明 应该移除
if (i == h - 1 - dynamicWalls[j].pos.y)
{
//移除这个方块 为了安全移除 添加一个记录列表
delList.Add(dynamicWalls[j]);
}
//2.要这一行之上的所有小方块下移一个单位
//如果当前的这个位置 是该行以上 那就该小方块 下移一格
else if (h - 1 - dynamicWalls[j].pos.y > i)
{
++dynamicWalls[j].pos.y;
}
}
//移除待删除的小方块
for (int j = 0; j < delList.Count; j++)
{
dynamicWalls.Remove(delList[j]);
}
//3.记录小方块数量的数组从上到下迁移
for (int j = i; j < recordInfo.Length - 1; j++)
{
recordInfo[j] = recordInfo[j + 1];
}
//置空最顶的计数
recordInfo[recordInfo.Length - 1] = 0;
//跨掉一行后 再次去从头检测是否跨层
CheckClear();
break;
}
}
}
#endregion
}
}
GameScene
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace CSharp实践教学
{
class GameScene : ISceneUpdate
{
Map map;
BlockWorker blockWorker;
bool isRunning;
#region Lesson10 输入线程
Thread inputThread;
#endregion
public GameScene()
{
map = new Map(this);
blockWorker = new BlockWorker();
#region Lesson10 输入线程
isRunning = true;
inputThread = new Thread(CheckInputThread);
//设置成后台线程 声明周期随主线程决定
inputThread.IsBackground = true;
//开启线程
inputThread.Start();
#endregion
}
#region Lesson10 输入线程
private void CheckInputThread()
{
while (isRunning)
{
if (Console.KeyAvailable)
{
//为了避免影响主线程 在输入后加锁
lock (blockWorker)
{
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.LeftArrow:
//判断能不能变形
if (blockWorker.CanChange(E_Change_Type.Left, map))
blockWorker.Change(E_Change_Type.Left);
break;
case ConsoleKey.RightArrow:
//判断能不能变形
if (blockWorker.CanChange(E_Change_Type.Right, map))
blockWorker.Change(E_Change_Type.Right);
break;
case ConsoleKey.A:
if (blockWorker.CanMoveRL(E_Change_Type.Left, map))
blockWorker.MoveRL(E_Change_Type.Left);
break;
case ConsoleKey.D:
if (blockWorker.CanMoveRL(E_Change_Type.Right, map))
blockWorker.MoveRL(E_Change_Type.Right);
break;
case ConsoleKey.S:
//向下动
if (blockWorker.CanMove(map))
blockWorker.AutoMove();
break;
}
}
}
}
}
#endregion
public void StopThread()
{
isRunning = false;
inputThread = null;
//在某些c#版本中 会直接报错 没用
//inputThread.Abort();
}
public void Update()
{
//锁里面不要包含 休眠 不然会影响别人
lock(blockWorker)
{
//地图绘制
map.Draw();
//搬运工绘制
blockWorker.Draw();
//自动向下移动
if (blockWorker.CanMove(map))
blockWorker.AutoMove();
}
//用线程休眠的形式
Thread.Sleep(200);
}
}
}
DrawObject
using System;
using System.Collections.Generic;
using System.Text;
namespace CSharp实践教学
{
/// <summary>
/// 绘制类型 根据不同类型 改变绘制的方块的颜色
/// </summary>
enum E_DrawType
{
/// <summary>
/// 墙壁
/// </summary>
Wall,
/// <summary>
/// 正方形方块
/// </summary>
Cube,
/// <summary>
/// 直线
/// </summary>
Line,
/// <summary>
/// 坦克
/// </summary>
Tank,
/// <summary>
/// 左梯子
/// </summary>
Left_Ladder,
/// <summary>
/// 右梯子
/// </summary>
Right_Ladder,
/// <summary>
/// 左长梯子
/// </summary>
Left_Long_Ladder,
/// <summary>
/// 右长梯子
/// </summary>
Right_Long_Ladder,
}
class DrawObject : IDraw
{
public Position pos;
public E_DrawType type;
public DrawObject(E_DrawType type)
{
this.type = type;
}
public DrawObject(E_DrawType type, int x, int y):this(type)
{
this.pos = new Position(x, y);
}
public void Draw()
{
//屏幕外不用再绘制
if (pos.y < 0)
return;
Console.SetCursorPosition(pos.x, pos.y);
switch (type)
{
case E_DrawType.Wall:
Console.ForegroundColor = ConsoleColor.Red;
break;
case E_DrawType.Cube:
Console.ForegroundColor = ConsoleColor.Blue;
break;
case E_DrawType.Line:
Console.ForegroundColor = ConsoleColor.Green;
break;
case E_DrawType.Tank:
Console.ForegroundColor = ConsoleColor.Cyan;
break;
case E_DrawType.Left_Ladder:
case E_DrawType.Right_Ladder:
Console.ForegroundColor = ConsoleColor.Magenta;
break;
case E_DrawType.Left_Long_Ladder:
case E_DrawType.Right_Long_Ladder:
Console.ForegroundColor = ConsoleColor.DarkGray;
break;
}
Console.Write("■");
}
#region Lesson6 添加一个清楚绘制的方法
public void ClearDraw()
{
//屏幕外不用再绘制
if (pos.y < 0)
return;
Console.SetCursorPosition(pos.x, pos.y);
Console.Write(" ");
}
#endregion
/// <summary>
/// 这是切换方块类型 主要用于搬砖下落到地图时 把搬砖类型编程墙壁类型
/// </summary>
/// <param name="type"></param>
public void ChangeType(E_DrawType type)
{
this.type = type;
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com