C# 扫雷游戏 纯控制台 附源码

盆友在学C#+U3D,作为一枚java开发,我也研究了一下,估计以后也会更这方面的内容。百度能搜到的内容,我从来不写。
先贴两张效果图:
大小动态定义
密度动态定义
游戏界面
F1开启作弊模式(在左上角显示是否是地雷)
废话不多说,先贴代码,再盘出逻辑。
扫雷游戏的类

using System;
using System.Threading;

namespace ConsoleGame
{
    class FindBomb
    {
        Square[,] table ;

        //起始坐标
        int x;
        int y;
        //选中的坐标
        int chooseX = 1;
        int chooseY = 1;
        //背景颜色
        ConsoleColor foreColor;
        ConsoleColor backColor;
        //布雷密度
        int bombPersent;
        //DEBUG模式
        bool debug = false;
        //开始时间
        DateTime dt;
        //重绘标志位
        bool needPaint = true;
        //时间重绘标志位
        string timeLastPaint;


        public FindBomb(int x, int y, ConsoleColor foreColor, ConsoleColor backColor,int size, int bombPersent)
        {
            this.x = x;
            this.y = y;
            this.foreColor = foreColor;
            this.backColor = backColor;
            this.table = new Square[size + 2, size + 2];
            this.bombPersent = bombPersent;
        }

        public void Start()
        {
            BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 38, 5, "\t\t扫雷游戏", 50);
            BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 38, 7, "地图边长"+(table.GetLength(0)-2)+ "格 布雷密度"+bombPersent+"%", 50);
            BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Gray, 38, 8, "移动光标:↑↓←→  标记:SPACE  翻开:ENTER", 50);
            BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 38, 10, "玩法说明:", 50);
            BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 38, 11, "翻开所有非地雷的方格即可完成游戏", 50);
            BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 38, 12, "不小心翻到地雷则游戏失败", 50);
            BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 38, 13, "临近地雷的方格,会显示附近的地雷数量", 50);
            BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 38, 28, "F1:开启/关闭DEBUG作弊模式 Esc:退出游戏", 50);
            PutBombs(bombPersent);
            PrintTable();
            dt = DateTime.Now;
            //开启计时线程
            ThreadStart clock = new ThreadStart(Timer);
            Thread thread = new Thread(clock);
            thread.Start();

            if (ReadKeyBoard())
            {
                thread.Abort();
                //success
                AfterAll(true);
            }
            else
            {
                thread.Abort();
                //lose
                AfterAll(false);
            }
        }

        public void Timer()
        {
            while (true)
            {
                PrintTable();
            }
        }

        public bool ReadKeyBoard()
        {
            while (true)
            {
                ConsoleKey ck = Console.ReadKey(true).Key;
                switch (ck)
                {
                    case ConsoleKey.Backspace:
                        break;
                    case ConsoleKey.Spacebar://标记
                        if (!table[chooseX, chooseY].isOpen)
                        {
                            table[chooseX, chooseY].isMarked = !table[chooseX, chooseY].isMarked;//标志位取反
                        }
                        break;
                    case ConsoleKey.Enter:
                        if (TryOpen(chooseX, chooseY))//打开
                        {
                            table[chooseX, chooseY].isMarked = false;
                            return false;//如果打开了Bomb则返回
                        }//如果没打开Bomb,检测是否胜利
                        if (IsSuccess())
                        {
                            return true;
                        }
                        break;
                    case ConsoleKey.LeftArrow:
                        if (chooseX > 1)
                            chooseX--;
                        break;
                    case ConsoleKey.UpArrow:
                        if (chooseY > 1)
                            chooseY--;
                        break;
                    case ConsoleKey.RightArrow:
                        if (chooseX < table.GetLength(0) - 2)
                            chooseX++;
                        break;
                    case ConsoleKey.DownArrow:
                        if (chooseY < table.GetLength(1) - 2)
                            chooseY++;
                        break;
                    case ConsoleKey.F1:
                        debug = !debug;
                        break;
                    case ConsoleKey.Escape:
                        return false;
                    default:
                        break;
                }
                needPaint = true;
            }
        }

        /// <summary>
        /// 判断是否已经完成
        /// </summary>
        public bool IsSuccess()
        {
            for (int i = 1; i < table.GetLength(0) - 1; i++)
            {
                for (int j = 1; j < table.GetLength(1) - 1; j++)
                {
                    if (table[i,j].isBomb==false && table[i, j].isOpen==false)
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// 填入或重置所有方块
        /// </summary>
        /// <param name="isBombPersent">标记为true的几率</param>
        public void PutBombs(int isBombPersent)
        {
            Random random = new Random();
            for (int i = 1; i < table.GetLength(0) - 1; i++)//忽略外围
            {
                for (int j = 1; j < table.GetLength(1) - 1; j++)
                {
                    if (random.Next() % 100 < isBombPersent)
                    {
                        table[i, j] = new Square(true);
                    }
                    else
                    {
                        table[i, j] = new Square(false);
                    }
                }
            }//设置好了Bomb,设置count
            for (int i = 1; i < table.GetLength(0) - 1; i++)
            {
                for (int j = 1; j < table.GetLength(1) - 1; j++)
                {
                    if (!table[i, j].isBomb)//如果不是bomb,计算count
                    {
                        table[i, j].count = CountNumb(i, j);
                    }
                }
            }
        }
        /// <summary>
        /// 计算某坐标周边Bomb数
        /// </summary>
        /// <returns>周边Bomb的数量</returns>
        private int CountNumb(int x, int y)
        {
            int result = 0;
            if (table[x - 1, y - 1].isBomb)
                result++;
            if (table[x, y - 1].isBomb)
                result++;
            if (table[x + 1, y - 1].isBomb)
                result++;
            if (table[x - 1, y].isBomb)
                result++;
            if (table[x + 1, y].isBomb)
                result++;
            if (table[x - 1, y + 1].isBomb)
                result++;
            if (table[x, y + 1].isBomb)
                result++;
            if (table[x + 1, y + 1].isBomb)
                result++;
            return result;
        }

        /// <summary>
        /// 绘制排查中的Table
        /// </summary>
        public void PrintTable()
        {
            string timePass = "用时: " + (DateTime.Now - dt).Minutes + "分 " + (DateTime.Now - dt).Seconds + "秒   ";
            if (!timePass.Equals(timeLastPaint))//如果绘制的时间想同,则不会绘制,避免闪烁
            {
                timeLastPaint = timePass;
                BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 40, 29, timePass, 0);
            }
            
            if (needPaint)
            {
                needPaint = false;

                Console.ResetColor();
                Console.SetCursorPosition(2, 1);
                if (debug)
                {
                    
                    Console.WriteLine("当前选择{2},{3}  计数:{4}  {5}     ", x, y, chooseX, chooseY, table[chooseX, chooseY].count, table[chooseX, chooseY].isBomb ? "炸弹" : "空地");
                }
                else
                {
                    Console.WriteLine("                                   ");
                }
                for (int x = 1; x < table.GetLength(0) - 1; x++)//外围不绘制
                {
                    for (int y = 1; y < table.GetLength(1) - 1; y++)
                    {
                        Console.SetCursorPosition(this.x + x * 2, this.y + y);
                        if (x == chooseX && y == chooseY)//如果正被选中
                        {
                            Console.ForegroundColor = foreColor;//不交换
                            Console.BackgroundColor = backColor;
                        }
                        else
                        {
                            Console.ForegroundColor = backColor;//交换前景色和背景色
                            Console.BackgroundColor = foreColor;
                        }
                        if (table[x, y].isOpen == false)//如果没有被开启
                        {
                            if (table[x, y].isMarked == false)
                            {
                                Console.Write('□');
                            }
                            else
                            {
                                Console.Write('※');
                            }
                        }
                        else//如果已经开启
                        {
                            if (table[x, y].count == 0)
                            {
                                Console.Write("  ");
                            }
                            else
                            {
                                Console.Write(table[x, y].count + " ");
                            }
                        }
                    }
                }
            }
        }

        public void AfterAll(bool isSuccess)
        {
            string timePass = (DateTime.Now - dt).Minutes + "分 " + (DateTime.Now - dt).Seconds + "秒";

            for (int x = 1; x < table.GetLength(0) - 1; x++)//外围不绘制
            {
                for (int y = 1; y < table.GetLength(1) - 1; y++)
                {
                    Console.SetCursorPosition(this.x + x * 2, this.y + y);
                    if (x == chooseX && y == chooseY)//如果正被选中
                    {
                        Console.ForegroundColor = foreColor;//不交换
                        Console.BackgroundColor = backColor;
                    }
                    else
                    {
                        Console.ForegroundColor = backColor;//交换前景色和背景色
                        Console.BackgroundColor = foreColor;
                    }
                    if (table[x, y].isBomb && table[x, y].isMarked)//如果是正确标记的地雷
                    {
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.Write('√');
                    }else if (table[x, y].isBomb == false && table[x, y].isMarked)//标错的
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.Write('×');
                    }else if (table[x, y].isBomb)//未标记的地雷
                    {
                        Console.ForegroundColor = ConsoleColor.DarkYellow;
                        Console.Write('○');
                    }
                    else if (table[x, y].isOpen)
                    {
                        if (table[x, y].count == 0)
                        {
                            Console.Write("  ");
                        }
                        else
                        {
                            Console.Write(table[x, y].count + " ");
                        }
                    }
                    else
                    {
                        Console.Write("□");
                    }
                    Console.ResetColor();
                    
                }
            }
            if (isSuccess)
            {
                BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 15, 3, "你成功了,用时" + timePass + " 请按任意键继续", 50);
            }
            else
            {
                BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 15, 3, "游戏结束,耗时" + timePass + " 请按任意键继续", 50);
            }
            Console.ReadKey();
        }

        /// <summary>
        /// 设置开启状态,并返回isBomb属性
        /// </summary>
        /// <returns></returns>
        public bool TryOpen(int x, int y)
        {
            //如果是空的 则向四周传递
            if (x>0 && y>0 && x < table.GetLength(0) - 1 && y < table.GetLength(1) - 1)
            {
                if (table[x, y].isOpen == false && table[x, y].count == 0 && table[x, y].isBomb == false)
                {
                    table[x, y].isOpen = true;
                    TryOpen(x - 1, y - 1);//↖
                    TryOpen(x, y - 1);    //↑
                    TryOpen(x + 1, y - 1);//↗
                    TryOpen(x - 1, y);    //←
                    TryOpen(x + 1, y);    //→
                    TryOpen(x - 1, y + 1);//↙
                    TryOpen(x, y + 1);    //↓
                    TryOpen(x + 1, y + 1);//↘
                }
                table[x, y].isOpen = true;
            }
            return table[x, y].isBomb;
        }    }
    /// <summary>
    /// 单个方块结构体
    /// </summary>
    public struct Square
    {
        public Square(bool isBomb)
        {
            this.isBomb = isBomb;
            isMarked = false;
            isOpen = false;
            count = 0;
        }
        public bool isBomb;
        public bool isMarked;
        public bool isOpen;
        public int count;
    }
}

调用方式:

 /// <summary>
        /// 扫雷调用
        /// </summary>
        public static bool PlayFindBombs()
        {
            BoxUtil.CoverBackendColor(ConsoleColor.Black, 60, 31);//擦除内容
            BoxUtil.TypeWords(ConsoleColor.DarkRed, ConsoleColor.Black, 14, 2, StrConst.MINE_CLEARANCE, 0);//绘制暗色LOGO
            //选择大小
            Thread.Sleep(500);
            BoxUtil.PrintBox(baseColor, baseColor, 20, 10, 40, 20, ' ', 20);//绘制内边框
            BoxUtil.TypeWords(ConsoleColor.Red, ConsoleColor.Black, 28, 12, "请选择大小", 0);
            BoxUtil.TypeWords(ConsoleColor.Red, ConsoleColor.Black, 14, 2, StrConst.MINE_CLEARANCE, 0);//绘制亮色LOGO
            ChooseList FindBombSizeMod = new ChooseList(new ListView[] {
                new ListView(6 ,"6  X 6  -适合学龄前儿童"),
                new ListView(10,"10 X 10 -大小和厕所接近"),
                new ListView(14,"14 X 14 -标准面积的擂台"),
                new ListView(22,"22 X 22 -谁选谁是大傻子")}, 23, 14);

            int sizeFlag = FindBombSizeMod.RefreshAndChoose();
            BoxUtil.CoverBackendColor(ConsoleColor.Black, 21, 11, 40, 21);//擦除内容

            //选择密度
            BoxUtil.TypeWords(ConsoleColor.Red, ConsoleColor.Black, 28, 12, "请选择密度", 0);
            ChooseList FindBombPersentMod = new ChooseList(new ListView[] {
                new ListView(10,"10%  -工兵有我方间谍"),
                new ListView(20,"20%  -军费被领导贪污"),
                new ListView(30,"30%  -训练有素的敌军"),
                new ListView(40,"45%  -敌军地雷不要钱")}, 24, 14);

            int persentFlag = FindBombPersentMod.RefreshAndChoose();//显示选择列表
            BoxUtil.CoverBackendColor(ConsoleColor.Black, 60, 31);//擦除内容
            BoxUtil.TypeWords(ConsoleColor.Black, ConsoleColor.Black, 14, 2, StrConst.MINE_CLEARANCE, 0);//绘制黑色LOGO
            FindBomb fb = new FindBomb(10, 5, ConsoleColor.White, ConsoleColor.Black, sizeFlag, persentFlag);//按大小创建地图
            fb.Start();//按密度排布地雷,并开始
            return ShowRetry();
        }

这里有点长,BoxUtil和ChooseList是我自己封装的绘图和选框工具类
反正java是这么玩的
缩略一下,就是这样即可

FindBomb fb = new FindBomb(10, 5, ConsoleColor.White, ConsoleColor.Black, 18/*边长18*18*/, 10/*密度10%*/);
            fb.Start();

工具类我在最下面贴出吧,其实可以用Console.Write和Console.SetCursorPosition函数替换掉

本游戏的特点:
1.完全使用原生和控制台实现。
2.布雷密度和地图大小可调节。
3.操作使用上下左右,并且基本复刻了原版扫雷。
4.F1开启debug模式,可以作弊。
5.多线程显示游戏进行时间。
6.缺点,没有保存、暂停、排行榜功能(因为懒)
简单说明一下逻辑和用到的方法:
1.显示扫雷的框框:由于右下角实时显示游戏时间,所以使用了一个标志位来判断是否进行绘图,绘图的条件是:1.如果秒数变了,绘制。2.如果按下了上下左右等按键,绘制。3.如果秒数没变,上下左右也没按,不绘制。
2.扫雷框框的结构:一个二维数组,java的小伙伴们注意了,是C#的二维数组喔,C#还有个交错数组。每一个数组元素是一个Square结构体。
那说说Square结构体的设计:

public struct Square
    {
        public Square(bool isBomb)
        {
            this.isBomb = isBomb;
            isMarked = false;
            isOpen = false;
            count = 0;
        }
        public bool isBomb;
        public bool isMarked;
        public bool isOpen;
        public int count;
    }

通过一系列布尔值,标注了是否为炸弹isBomb,是否被玩家标记isMarked,是否被翻开isOpen,以及如果不是炸弹,那么数字应该是几count。他的构造方法是为二维矩阵布置地雷时调用。

那么地雷如何布置的呢,使用了Random,根据创建类对象时提供的密度作为比例布置地雷

诶呦,忘记说了,这里用了一个巧妙的设计,来避免数组下标越界异常(称呼来自java,c#是不是叫这个不清楚)

简单的来理解一下,首先熟悉一下扫雷的规则
如果中间的黑色方块不是炸弹
如图,如果一个黑色的小方块不是炸弹,那么我们需要在其中显示他周围炸弹的数量,如2表示他周边8个格子中有2个炸弹
中间的“1”表示他周围有一个炸弹
那么就会出现数组下标越界的麻烦事
画的有点丑
对,当你在处理边缘上的点的时候,比如当前选择x,y这个点,判断x-1,y-1这个点时,C#会告诉你x数组没那么长,你这个x-1不在数组下标范围里。

那如何化解这个尴尬,显然,可以通过if来判断,如果这个点在边缘上,我们就使用另一套判别计算方法
使用多种计算方法
那么你需要写几种计算方法呢,9种,分别是黄色的-正常,绿色的-不去找他的y-1,等等等,这显然非常麻烦。

这个办法虽然繁琐,但是是可行的,他可以简单一些,我们加上if判断,如果这个点的x-1已经小于0了,不处理,如果大于length了也不处理。
那么每个点的8个周边点,请你都套上最少带一个&&的if语句

这样是不是简单了一些,至少我不用写9种判别方法了,但是他可以更容易,比如我如果只处理黄色部分呢
黄色部分是显示内容
于是我想到了这种设计,如图,黄色既是布雷区域,也是显示区域,相当于对于一个长度为[x,y]的二维数组,只显示 1→x-1,1→y-1的部分,外围部分设置为空地。这样在计数的时候,也只处理黄色部分的方块,看看他周围有几个雷,而不用担心数组下标越界的问题。
所以我的显示区,布雷区,计数区,都是黄色区域,通过加宽加高这个二维数组,绕开了下标越界。

于是乎一切都变得简单了,剩下的内容主要也只剩显示了
显示我只写了一个方法,在AfterAll(bool isScuess)中,这个方法通过一个布尔来区分是游戏状态还是游戏结束状态,绘出游戏结束状态:
游戏结束
如图如果我翻到了炸弹,或者翻完了框框,会显示当前选择,炸弹位置,标错的标记,标对的标记。

这块其实很纠结,写一个方法有点乱,写两个方法代码又大量重复,最终我写成了一个方法
本人的注释写的很清晰 个人认为代码可读性也基本良好
再贴一遍:

public void AfterAll(bool isSuccess)
        {
            string timePass = (DateTime.Now - dt).Minutes + "分 " + (DateTime.Now - dt).Seconds + "秒";

            for (int x = 1; x < table.GetLength(0) - 1; x++)//外围不绘制
            {
                for (int y = 1; y < table.GetLength(1) - 1; y++)
                {
                    Console.SetCursorPosition(this.x + x * 2, this.y + y);
                    if (x == chooseX && y == chooseY)//如果正被选中
                    {
                        Console.ForegroundColor = foreColor;//不交换
                        Console.BackgroundColor = backColor;
                    }
                    else
                    {
                        Console.ForegroundColor = backColor;//交换前景色和背景色
                        Console.BackgroundColor = foreColor;
                    }
                    if (table[x, y].isBomb && table[x, y].isMarked)//如果是正确标记的地雷
                    {
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.Write('√');
                    }else if (table[x, y].isBomb == false && table[x, y].isMarked)//标错的
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.Write('×');
                    }else if (table[x, y].isBomb)//未标记的地雷
                    {
                        Console.ForegroundColor = ConsoleColor.DarkYellow;
                        Console.Write('○');
                    }
                    else if (table[x, y].isOpen)
                    {
                        if (table[x, y].count == 0)
                        {
                            Console.Write("  ");
                        }
                        else
                        {
                            Console.Write(table[x, y].count + " ");
                        }
                    }
                    else
                    {
                        Console.Write("□");
                    }
                    Console.ResetColor();
                    
                }
            }
            if (isSuccess)
            {
                BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 15, 3, "你成功了,用时" + timePass + " 请按任意键继续", 50);
            }
            else
            {
                BoxUtil.TypeWords(ConsoleColor.Blue, ConsoleColor.Black, 15, 3, "游戏结束,耗时" + timePass + " 请按任意键继续", 50);
            }
            Console.ReadKey();
        }

大意是,类变量chooseX,chooseY记录了当前选择的点,如果是当前选择的点,则交换前景色和背景色,如果不是,则判断是否没开,显示方块,是否开了,显示空白或数字。如果是游戏结束状态呢,则显示开没开,标没标错,已经翻开的显示数字,没翻开的依旧显示方块。晕

好,显示问题也解决一部分了,在一个就是多线程实时显示游戏时间的问题,由于控制台的特性,我们不能单纯使用两个线程来分别绘制时间和扫雷块,必须全图一次绘制,类似于“垂直同步”,这是因为控制台似乎是单线程的,如果分别绘制,会有这样的问题
在这里插入图片描述
这张图是我模拟出来的,不过大概就是这个意思,画的很像了,本该在右下角显示的时间,却显示到这里来了。这是因为两个线程同步运行产生的安全问题,本质是Console只有一个类,他是相当于控制台是单线程调用的
比如:
A线程 – 逐一定位在table的某个方块上,并绘制方块
B线程 – 定位到右下角,绘制游戏时间

那么会有这种情况:
B线程先定位到右下角 – B线程刚刚输出“用时:”
A线程抢占了控制台资源 – 定位到左边扫雷框 – A线程输出了一个框框
B线程抢回了控制台资源 – B线程继续输出几分几秒

由于控制台的定位已经被A线程定位到左侧扫雷框了,所以B线程的打印接在了A线程后面。

于是就发生了线程安全问题,我们可以通过锁机制,让A线程抢占锁资源,绘制完成再释放锁资源,或者通过将绘制过程整合为一个线程,来避免线程的安全问题。我使用了后者

是时候说明游戏整体逻辑了:
整体逻辑
进入游戏首先new出数组,布雷,并把他画出来,开启计时线程,每秒更新标志位
然后ReadKeyBoard()方法是一个死循环,只有在游戏成功或失败时才会 返回true或者false,获取他的返回值的过程就是整个游戏过程,他会在每次你敲下按键时判断你赢没赢,或者死没死。
PrintTable方法在标志位改变时绘制屏幕,也就是时间过去了一秒或者你按下了按键。死循环执行,游戏结束时停止执行。

ReadKeyBoard,IsSuccess,PutBombs等方法,不再赘述,逻辑简单,注释清晰。

最后说个TryOpen的传递。
传递
如图所示,我只翻开了一块,却打开了一大片,通过分析得知,如果你翻开了一块计数为0的方块,他会向四周传递,一直传递到有计数的方块为止。所以我设计了TryOpen方法

        /// <summary>
        /// 设置开启状态,并返回isBomb属性
        /// </summary>
        /// <returns></returns>
        public bool TryOpen(int x, int y)
        {
            //如果是空的 则向四周传递
            if (x>0 && y>0 && x < table.GetLength(0) - 1 && y < table.GetLength(1) - 1)
            {
                if (table[x, y].isOpen == false && table[x, y].count == 0 && table[x, y].isBomb == false)
                {
                    table[x, y].isOpen = true;
                    TryOpen(x - 1, y - 1);//↖
                    TryOpen(x, y - 1);    //↑
                    TryOpen(x + 1, y - 1);//↗
                    TryOpen(x - 1, y);    //←
                    TryOpen(x + 1, y);    //→
                    TryOpen(x - 1, y + 1);//↙
                    TryOpen(x, y + 1);    //↓
                    TryOpen(x + 1, y + 1);//↘
                }
                table[x, y].isOpen = true;
            }
            return table[x, y].isBomb;
        }
    }

不难看出这是一个递归调用方法,其实这个方法耗费了本架构师好长时间,他看起来简单,写起来还真不容易,原则上是传递打开方块这个方法,如果计数是0,要继续传递,直到把空的一片全翻开。
这里有个递归的坑,即如果A是空的,传给了他边上的B,B也是空的,又会传给A,这样死循环,来回来去的传,会栈溢出,解决很简单,在合适的时机将isOpen设置为true,并且不传递给isOpen的方块即可。
记得限制传递范围,不要传递到外圈,不然又要下标越界。

基本写到这了,为了方便拿来主义能拿来就用,我贴一下BoxUtil工具类中用到的方法

/// <summary>
        /// 模拟打字机效果,一个字一个字的敲出文本
        ///另,只贴出了重载方法中最大的一个,最后一个delay可以设置为0则无延迟
        /// </summary>
        public static void TypeWords(ConsoleColor foreColor, ConsoleColor backColor, int startX, int startY, string words,int delay)
        {
            Console.ForegroundColor = foreColor;
            Console.BackgroundColor = backColor;
            int X = startX * 2;
            int Y = startY;
            Console.SetCursorPosition(X, Y);

            char[] ch = words.ToCharArray();

            foreach (char c in ch)
            {
                if (c=='\r')
                {
                    Y++;
                    Console.SetCursorPosition(X, Y);
                }
                else if(c == '\n'){
                    continue;
                }
                else
                {
                    Console.Write(c);
                }
                Thread.Sleep(delay);
            }

            Console.ResetColor();
        }

如此可以简单重现出本游戏了,祝君c#学习愉快

  • 8
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

控场的朴哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值