c#用控制台实现贪吃蛇小游戏

写在前面:

        有一天突发奇想,如果我只用算法实现基本逻辑,让控制台输出图像,不用任何组件,只用一门编程语言,能否实现一个可以人机交互的小游戏,抱着试一试的想法,博主用c#控制台程序复原了贪吃蛇玩法的小游戏。

目录

一、可视化界面

1.创建地图

 二、贪吃蛇基本逻辑

1.蛇的定义

2.控制移动

 3.创建食物

 三、大功告成


一、可视化界面

        众所周知,控制台程序通常没有可视化的界面,只是通过字符串来显示或者监控程序,在使用控制台写程序时,我们只关心数据,不在乎界面。但是,如果我们偏偏反其道而行之,用控制台可视化形式将游戏界面动态反馈给玩家,该怎么办呢。

        在c#中,我们用Console.WriteLine()在控制台打印一行数据,如果我们将它嵌套在一个循环里面,那么它会不停的在控制台一行一行的打印数据。此时,对于输出的某一行来说,可以看作动画里面的一帧。(动画是因为通过连续播放许多帧静止的画面,使眼睛产生连续动作的错觉。其实说白了就是人的视觉停留造成了动画会动。)

        明白了这一点,我们写一个while循环,循环打印一段代码,并且,每次循环结束,让代码停顿0.5秒,使得人眼能跟上动态的帧率。实现一个石头从高处慢慢降落的过程。

代码演示:

int stonex = 10;//石头x初始值
int stoney = 0;//石头y初始值
while (true)
{
    for (int y = 0; y < 20; y++)
    {
        for (int x = 0; x < 20; x++)
        {
            if (y == 0||y==19)
            {
                Console.Write("-");
                continue;
            }
            if (x == 0 || x == 19)
            {
                Console.Write("|");
                continue;
            }
            if (x == stonex && y == stoney)
            {
                Console.Write("0");//输出石头
                continue;
            }
            Console.Write(" ");
        }
        Console.WriteLine();
    }
    stoney++;//石头y值+1
    System.Threading.Thread.Sleep(500);//停顿0.5秒
}

1.创建地图

明白原理之后,我们只要定义一个二维数组,用来存储地图的数据。再定义一个地图显示的方法,按照预定好的规则循环输出就好了。

public static void ShowMap(int[,] map)
{
    for (int y = 0; y < map.GetLength(0); y++)
    {
        for (int x = 0; x < map.GetLength(1); x++)
        {
            if (x == 0 || x == map.GetLength(1) - 1)
            {
                Console.Write('|');
                continue;
            }
            if (y == 0 || y == map.GetLength(0) - 1)
            {
                Console.Write('-');
                continue;
            }
            if (map[y, x] == 0)
            {
                Console.Write(" ");
                continue;
            }
            if (map[y, x] == 1)
            {
                Console.Write(0);
                continue;
            }
            if (map[y, x] == 6)
            {
                Console.Write(0);
                continue;
            }

        }
        Console.WriteLine();
    }
}
}

 二、贪吃蛇基本逻辑

        玩家操控一条蛇,上下左右控制蛇的方向,寻找吃的东西,每吃一口就能得到一定的积分,而且蛇的身子会越吃越长,身子越长玩的难度就越大,不能碰墙,不能咬到自己的身体,更不能咬自己的尾巴。

1.蛇的定义

首先定义一个“蛇“类,我们可以把蛇看成若干个节点,对于每个节点来说,都有一个前节点和尾节点,同时还有一个当前节点的位置。、

public class Snake
{
    private Direction dir;//当前位置
    public Snake qianqu;//前节点
    public Snake houji;//后节点

    //构造函数
    public Snake(Direction dir)
    {
        Dir = dir;
    }
    public Direction Dir { get => dir; set => dir = value; }
}

2.控制移动

把蛇按节点分开来看之后,我们其实只用操作头节点,给定一个方向,头节点向该方向移动,而对于除了头节点以外的每个节点,我们要做的是,将后一个节点位置移动到前一个节点位置。这样,就实现了贪吃蛇的移动。

public static void Move(Direction dir,ref Snake snake,ref int[,] map)
{
    if (snake.houji != null) {
        Move(dir,ref snake.houji,ref map);
    }
    if (snake.qianqu == null)
    {
        snake.Dir.X += dir.X;
        snake.Dir.Y += dir.Y;
    }
    snake.Dir.X = snake.qianqu.Dir.X;
    snake.Dir.Y = snake.qianqu.Dir.Y;

}

移动方法定义好以后,我们只用传入一个方向,就可以改变蛇移动的方向,但控制台程序是单线程的程序,程序要怎么知道什么时候接收参数改变方向呢,这时候就用到多线程,第一个线程就是主函数,用来运行游戏的主要逻辑,第二个程序用来接收用户的输入,当用户按下按键之后,立马改变蛇的方向。

public static void TancsMain()
{
    direction = new Direction();

    ThreadStart start = new ThreadStart(run);
    Thread startThread = new Thread(start);
    startThread.Start();
    while (true)
    {
        ConsoleKeyInfo x = Console.ReadKey();
        if (x != null)
        {
            switch (x.KeyChar)
            {
                case 's':
                    if (direction.X != -1)
                    {
                        direction.Y = 0;
                        direction.X = 1;
                    }
                    break;
                case 'w':
                    if (direction.X != 1)
                    {
                        direction.Y = 0;
                        direction.X = -1;
                    }
                    break;
                case 'a':
                    if (direction.Y != 1)
                    {
                        direction.Y = -1;
                        direction.X = 0;
                    }
                    break;
                case 'd':
                    if (direction.Y != -1)
                    {
                        direction.Y = 1;
                        direction.X = 0;
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

 3.创建食物

用随机数随机创建一个食物位置,当蛇吃到食物时,创建一个节点。

public static void CreatFood(ref int[,] map)
{
    for (int y = 0; y < map.GetLength(0); y++)
    {
        for (int x = 0; x < map.GetLength(1); x++)
        {
            if (map[y, x] == 6)
            {
                return;
            }
        }
    }
    Random random = new Random();
    random.Next(1, 19);
    map[random.Next(1, 19), random.Next(1, 39)] = 6;

}

 

 三、大功告成

到此、通过控制台实现贪吃蛇的小游戏就结束啦,博主也只是初学者,在枯燥的学习当中给自己找点乐子,如果看完了本篇文章,有兴趣的小伙伴也可以尝试做一个玩玩,实现的方法有很多,也不局限于我的思路。完整代码附上:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleApp1
{
    class Tancs
    {
        public static Direction direction;

public static void TancsMain()
{
    direction = new Direction();

    ThreadStart start = new ThreadStart(run);
    Thread startThread = new Thread(start);
    startThread.Start();
    while (true)
    {
        ConsoleKeyInfo x = Console.ReadKey();
        if (x != null)
        {
            switch (x.KeyChar)
            {
                case 's':
                    if (direction.X != -1)
                    {
                        direction.Y = 0;
                        direction.X = 1;
                    }
                    break;
                case 'w':
                    if (direction.X != 1)
                    {
                        direction.Y = 0;
                        direction.X = -1;
                    }
                    break;
                case 'a':
                    if (direction.Y != 1)
                    {
                        direction.Y = -1;
                        direction.X = 0;
                    }
                    break;
                case 'd':
                    if (direction.Y != -1)
                    {
                        direction.Y = 1;
                        direction.X = 0;
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

        public static void CreatFood(ref int[,] map)
        {
            for (int y = 0; y < map.GetLength(0); y++)
            {
                for (int x = 0; x < map.GetLength(1); x++)
                {
                    if (map[y, x] == 6)
                    {
                        return;
                    }
                }
            }
            Random random = new Random();
            random.Next(1, 19);
            map[random.Next(1, 19), random.Next(1, 39)] = 6;

        }
        public static void run()
        {
            direction.X = 1;
            direction.Y = 0;
            int[,] map = new int[20, 40];
            ShowMap(map);
            Console.ReadLine();
            Snake snake = new Snake(new Direction());
            snake.Dir.X = 3;
            snake.Dir.Y = 8;
            Snake snake2 = new Snake(new Direction());
            snake2.Dir.X = 2;
            snake2.Dir.Y = 8;
            Snake snake3 = new Snake(new Direction());
            snake2.Dir.X = 1;
            snake2.Dir.Y = 8;
            Snake snake4 = new Snake(new Direction());
            snake2.Dir.X = 0;
            snake2.Dir.Y = 8;
            snake.houji = snake2;
            snake2.qianqu = snake;
            snake2.houji = snake3;
            snake3.qianqu = snake2;
            snake3.houji = snake4;
            snake4.qianqu = snake3;


            while (true)
            {
                Move(direction,ref snake,ref map);
                ClearMap(ref map);
                CreatFood(ref map);
                ChangeMap(snake, ref map);
                ShowMap(map);
                System.Threading.Thread.Sleep(500);
            }
        }
        public static void Move(Direction dir,ref Snake snake,ref int[,] map)
        {
            if (snake.houji != null) {
                Move(dir,ref snake.houji,ref map);
            }
            if (snake.qianqu == null)
            {
                snake.Dir.X += dir.X;
                snake.Dir.Y += dir.Y;
                if (map[snake.Dir.X, snake.Dir.Y] == 6)
                {
                    map[snake.Dir.X, snake.Dir.Y] = 0;
                    Snake temp = snake;
                    while (temp.houji != null)
                    {
                        temp = temp.houji;
                    }
                    Snake snake2 = new Snake(new Direction());
                    temp.houji = snake2;
                    snake2.qianqu = temp;
                    snake2.Dir.X = temp.Dir.X;
                    snake2.Dir.Y = temp.Dir.Y;
                }
                return;
            }
            snake.Dir.X = snake.qianqu.Dir.X;
            snake.Dir.Y = snake.qianqu.Dir.Y;

        }
        public static void ChangeMap(Snake snake,ref int[,] map)
        {
            map[snake.Dir.X, snake.Dir.Y] = 1;
            if (snake.houji != null)
            {
                ChangeMap(snake.houji, ref map);
            }
        }
        public static void ClearMap(ref int[,] map)
        {
            for (int y = 0; y < map.GetLength(0); y++)
            {
                for (int x = 0; x < map.GetLength(1); x++)
                {
                    if (map[y, x] != 6)
                    {
                        map[y, x] = 0;
                    }
                    
                }
            }
        }
        public static void ShowMap(int[,] map)
        {
            for (int y = 0; y < map.GetLength(0); y++)
            {
                for (int x = 0; x < map.GetLength(1); x++)
                {
                    if (x == 0 || x == map.GetLength(1) - 1)
                    {
                        Console.Write('|');
                        continue;
                    }
                    if (y == 0 || y == map.GetLength(0) - 1)
                    {
                        Console.Write('-');
                        continue;
                    }
                    if (map[y, x] == 0)
                    {
                        Console.Write(" ");
                        continue;
                    }
                    if (map[y, x] == 1)
                    {
                        Console.Write(0);
                        continue;
                    }
                    if (map[y, x] == 6)
                    {
                        Console.Write(0);
                        continue;
                    }

                }
                Console.WriteLine();
            }
        }
    }
    public class Snake
    {
        private Direction dir;//当前位置
        public Snake qianqu;//前节点
        public Snake houji;//后节点

        //构造函数
        public Snake(Direction dir)
        {
            Dir = dir;
        }
        public Direction Dir { get => dir; set => dir = value; }
    }

    public class Direction
    {
        private int x;
        private int y;

        public int X { get => x; set => x = value; }
        public int Y { get => y; set => y = value; }
    }

}

 

  • 12
    点赞
  • 94
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值