14.蛇吃食物

14.游戏场景-蛇吃食物


14.1 知识点

蛇类中创建判断是否和蛇重合方法和蛇吃食物方法。判断是否和蛇重合方法在要写的食物类的实现随机食物位置方法中调用判断。蛇吃食物方法在游戏场景类的实现游戏场景里的更新方法中绘制完蛇类检测完是否检测撞墙或撞身体后调用。吃到了后就应该让食物位置再随机,增加蛇身体的长度。

// 蛇类 继承绘制接口
class Snake : IDraw
{
    // 蛇的身体数组
    SnakeBody[] bodys;

    // 当前蛇的长度
    int snakeBodysLength;

    // 当前移动的方向
    E_MoveDir dir;

    // 蛇类构造函数
    public Snake(int x, int y)
    {
        // 粗暴地申明200个空间,游戏中基本不会出现蛇长度达到200个身体
        bodys = new SnakeBody[200];

        // 声明蛇头
        bodys[0] = new SnakeBody(E_SnakeBody_Type.Head, x, y);

        // 初始时只有蛇头,初始化为1
        snakeBodysLength = 1;

        // 初始化默认移动的方向向右
        dir = E_MoveDir.Right;
    }

    // 蛇类绘制方法
    public void Draw()
    {
        // 遍历蛇身体数组,逐个绘制蛇身体
        for (int i = 0; i < snakeBodysLength; i++)
        {
            bodys[i].Draw();
        }
    }

    // 蛇移动方法
    public void Move()
    {
        // 蛇移动前,擦除蛇尾,也就是最后一个位置,否则有残影
        SnakeBody lastBody = bodys[snakeBodysLength - 1];
        Console.SetCursorPosition(lastBody.pos.x, lastBody.pos.y);
        Console.Write("  "); // 擦屁股

        // 蛇头移动
        switch (dir)
        {
            case E_MoveDir.Up:
                --bodys[0].pos.y;
                break;
            case E_MoveDir.Down:
                ++bodys[0].pos.y;
                break;
            case E_MoveDir.Left:
                bodys[0].pos.x -= 2;
                break;
            case E_MoveDir.Right:
                bodys[0].pos.x += 2;
                break;
        }
    }

    // 蛇改变移动方向方法
    public void ChangeDir(E_MoveDir dir)
    {
        // 蛇只有头部的时候,可以直接左转右,右转左,上转下,下转上
        // 蛇有身体时,这种情况就不能直接转

        if (
            // 假如传进来的方向就等于现在的方向
            dir == this.dir
            ||
            // 或者,蛇长度大于0且想要直接180度转向
            (
                snakeBodysLength > 1
                &&
                (this.dir == E_MoveDir.Left && dir == E_MoveDir.Right ||
                 this.dir == E_MoveDir.Right && dir == E_MoveDir.Left ||
                 this.dir == E_MoveDir.Up && dir == E_MoveDir.Down ||
                 this.dir == E_MoveDir.Down && dir == E_MoveDir.Up)
            )
           )
        {
            // 直接返回
            return;
        }

        // 记录外面传入的方向,之后就会按照这个方向去移动
        this.dir = dir;
    }

    // 检测蛇是否撞墙撞身体后结束游戏
    public bool CheckEnd(Map map)
    {
        // 是否和墙体位置重合
        for (int i = 0; i < map.walls.Length; i++)
        {
            if (bodys[0].pos == map.walls[i].pos)
            {
                return true;
            }
        }

        // 是否和自己的身体重合
        for (int i = 1; i < snakeBodysLength; i++)
        {
            if (bodys[0].pos == bodys[i].pos)
            {
                return true;
            }
        }

        return false;
    }

    // 判断是否和蛇重合的方法
    public bool CheckSamePos(Position p)
    {
        for (int i = 0; i < snakeBodysLength; i++)
        {
            if (bodys[i].pos == p)
            {
                return true;
            }
        }
        return false;
    }

    // 蛇吃食物方法
    public void CheckEatFood(Food food)
    {
        // 判断蛇是否吃到食物
        if (bodys[0].pos == food.pos)
        {
            // 吃到了,让食物位置再随机,增加蛇身体的长度
            food.RandomPos(this);

            // 增加蛇身体长度
            // ...
        }
    }
}

食物类中实现随机食物位置方法逻辑,不能和蛇重合,假如还是重合的话使用递归重新随机位置直到不重合为止。在食物的构造函数中调用。

// 食物类 继承游戏对象类
class Food : GameObject
{
    // 食物类构造函数
    public Food(Snake snake)
    {
        // 食物的位置不能和蛇重合,要有一定规则的随机出食物的位置
        RandomPos(snake);
    }

    // 实现食物的绘制方法
    public override void Draw()
    {
        // 设置颜色和输出位置,输出一个食物符号
        Console.SetCursorPosition(pos.x, pos.y);
        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.Write("¤");
    }

    // 随机位置的方法
    public void RandomPos(Snake snake)
    {
        // 随机位置在墙内
        Random r = new Random();
        int x = r.Next(2, Game.w / 2 - 1)* 2;
        int y = r.Next(1, Game.h - 4);

        // 设置食物位置
        pos = new Position(x, y);

        // 如果与蛇重合,则递归重新设置食物位置
        if (snake.CheckSamePos(pos))
        {
            RandomPos(snake);
        }
    }
}

游戏场景类中创建食物类变量。游戏场景类构造函数中初始化一个食物。在游戏场景类的实现游戏场景里的更新方法中绘制完蛇类检测完是否检测撞墙或撞身体后调用蛇吃食物方法。

// 游戏场景类 继承游戏帧更新接口
class GameScene : ISceneUpdate
{
    // 地图类变量,管理所有墙壁
    Map map;

    // 蛇类变量,管理蛇相关
    Snake snake;

    // 食物类变量
    Food food;

    // while循环次数,判断当前while循环进了多少次
    int whileLoopTimes = 0;

    // 游戏场景类构造函数
    public GameScene()
    {
        // 初始化地图类变量
        map = new Map();

        // 初始化一个蛇
        snake = new Snake(40, 10);

        // 初始化一个食物
        food = new Food(snake);
    }

    // 实现游戏场景里的更新方法,在游戏类中的start方法中的while循环中调用
    public void Update()
    {
        // while循环运行的太快,每次进一次while绘制一次的话,游戏速度太快,要降速
        // 用while循环次数来判断,每当进入while循环10000次后,再进行一次绘制
        // 可以理解为每10000帧更新一次
        if (whileLoopTimes % 10000 == 0)
        {
            // 绘制地图类
            map.Draw();

            // 绘制食物
            food.Draw();

            // 蛇移动,先移动再绘制蛇
            snake.Move();

            // 绘制蛇类
            snake.Draw();

            // 检测是否撞墙或撞身体
            if (snake.CheckEnd(map))
            {
                // 切换为结束场景
                Game.ChangeScene(E_SceneType.End);
            }

            // 判断是否吃到食物
            snake.CheckEatFood(food);

            // 实际update了,重写开始计数
            whileLoopTimes = 0;
        }

        // 每进一次while循环加一
        ++whileLoopTimes;

        // 检测输入输出,不能再间隔帧(就是while循环一万次)里面去处理,应该每次都检测,这样才准确,否则会有延迟
        if (Console.KeyAvailable)
        {
            // 根据输入
            switch (Console.ReadKey(true).Key)
            {
                case ConsoleKey.W:
                    snake.ChangeDir(E_MoveDir.Up);
                    break;
                case ConsoleKey.A:
                    snake.ChangeDir(E_MoveDir.Left);
                    break;
                case ConsoleKey.S:
                    snake.ChangeDir(E_MoveDir.Down);
                    break;
                case ConsoleKey.D:
                    snake.ChangeDir(E_MoveDir.Right);
                    break;
            }
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏