16.代码汇总

16.代码汇总


16.1 主入口

using System;
using 贪食蛇.Lesson1;

namespace 贪食蛇
{
    /// <summary>
    /// 游戏入口
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            //new出一个游戏类
            Game game = new Game();

            //开始游戏
            game.Start();
        }
    }
}

16.2 游戏类

using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson2;

namespace 贪食蛇.Lesson1
{
    /// <summary>
    /// 场景类型枚举
    /// </summary>
    enum E_SceneType
    {
        /// <summary>
        /// 开始场景
        /// </summary>
        Begin,
        /// <summary>
        /// 游戏场景
        /// </summary>
        Game,
        /// <summary>
        /// 结束场景
        /// </summary>
        End,
    }

    /// <summary>
    /// 游戏类
    /// </summary>
    class Game
    {
        //游戏窗口宽高
        public const int w = 80;
        public const int h = 20;

        //游戏帧更新接口变量 用来标记 当前选中的场景
        public static ISceneUpdate nowScene;

        //构造函数
        //初始化控制台和场景
        public Game()
        {
            //初始化控制台
            Console.CursorVisible = false;
            Console.SetWindowSize(w, h);
            Console.SetBufferSize(w, h);

            //初始化场景
            ChangeScene(E_SceneType.Begin);
        }

        //游戏开始的方法
        public void Start()
        {
            //游戏主循环 主要负责 游戏场景逻辑的更新
            while (true)
            {
                //判断当前游戏场景不为空 就更新
                if( nowScene != null )
                {
                    nowScene.Update();
                }
            }
        }

        //场景切换方法
        public static void ChangeScene(E_SceneType type)
        {
            //切场景之前 应该把上一个场景的绘制内容擦掉
            Console.Clear();

            //根据场景枚举new出新的对应的场景类
            switch (type)
            {
                case E_SceneType.Begin:
                    nowScene = new BeginScene();
                    break;
                case E_SceneType.Game:
                    nowScene = new GameScene();
                    break;
                case E_SceneType.End:
                    nowScene = new EndScene();
                    break;
            }
        }
    }
}

16.3 游戏帧更新接口

using System;
using System.Collections.Generic;
using System.Text;

namespace 贪食蛇.Lesson1
{
    
    /// <summary>
    /// 游戏帧更新接口
    /// </summary>
    interface ISceneUpdate
    {
        /// <summary>
        /// 更新方法
        /// </summary>
        void Update();
    }
}

16.4 开始场景和结束场景基类

using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;

namespace 贪食蛇.Lesson2
{
    //开始场景和结束场景基类 是抽象类 继承游戏帧更新接口
    abstract class BeginOrEndBaseScene : ISceneUpdate
    {
        //标题名字
        protected string strTitle;

        //选项索引 当前选择的是选项几 从0开始
        protected int nowSelectIndex = 0;

        //选项0的名字
        protected string strZero;

        //选项1的名字
        protected const string strOne = "结束游戏";

        //抽象方法 按j键进入不同选项后 做的事情 
        public abstract void EnterJDoSomthing();

        //实现开始场景和结束场景基类里的更新方法
        public void Update()
        {
            //开始和结束场景的 游戏逻辑 
            //选择当前的选项 然后 监听 键盘输入 wsj

            //设置字体颜色
            Console.ForegroundColor = ConsoleColor.White;

            //设置标题显示位置 打印标题
            Console.SetCursorPosition(Game.w / 2 - strTitle.Length, 5);
            Console.Write(strTitle);

            //设置选项0和选项1显示位置 打印选项0和选项1 根据选项索引设置选项颜色
            Console.SetCursorPosition(Game.w / 2 - strZero.Length, 8);
            Console.ForegroundColor = nowSelectIndex == 0 ? ConsoleColor.Red : ConsoleColor.White;
            Console.Write(strZero);
            Console.SetCursorPosition(Game.w / 2 - 4, 10);
            Console.ForegroundColor = nowSelectIndex == 1 ? ConsoleColor.Red : ConsoleColor.White;
            Console.Write(strOne);

            //检测输入 ws上下切换当前选项 j确认进入当前选项
            switch(Console.ReadKey(true).Key)
            {

                case ConsoleKey.W:
                    --nowSelectIndex;
                    if( nowSelectIndex < 0 )
                    {
                        nowSelectIndex = 0;
                    }
                    break;

                case ConsoleKey.S:
                    ++nowSelectIndex;
                    if (nowSelectIndex > 1)
                    {
                        nowSelectIndex = 1;
                    }
                    break;

                case ConsoleKey.J:
                    EnterJDoSomthing();
                    break;
            }

        }
    }
}

16.5 开始场景类

using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;

namespace 贪食蛇.Lesson2
{
    //开始场景类 继承开始场景和结束场景基类
    class BeginScene : BeginOrEndBaseScene
    {
        //开始场景类构造函数
        public BeginScene()
        {
            strTitle = "贪食蛇";
            strZero = "开始游戏";
        }

        //按J键做什么的逻辑
        public override void EnterJDoSomthing()
        {
            //选项0是开始游戏
            if (nowSelectIndex == 0)
            {
                //进入游戏场景
                Game.ChangeScene(E_SceneType.Game);
            }
            //选项1是结束游戏
            else
            {
                Environment.Exit(0);
            }
        }
    }
}

16.6 结束场景类

using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;

namespace 贪食蛇.Lesson2
{
    //结束场景类 继承开始场景和结束场景基类
    class EndScene : BeginOrEndBaseScene
    {
        //结束场景类构造函数
        public EndScene()
        {
            strTitle = "结束游戏";
            strZero = "回到开始界面";
        }

        //按J键做什么的逻辑
        public override void EnterJDoSomthing()
        {
            //选项0是回到开始界面
            if (nowSelectIndex == 0)
            {
                Game.ChangeScene(E_SceneType.Begin);
            }
            //选项1是结束游戏
            else
            {
                Environment.Exit(0);
            }
        }
    }
}

16.7 游戏场景类

using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;
using 贪食蛇.Lesson4;
using 贪食蛇.Lesson5;
using 贪食蛇.Lesson6;

namespace 贪食蛇.Lesson2
{
    //游戏场景类 继承游戏帧更新接口
    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循环一万次)里面去处理 应该每次都检测 这样才准确 否则会有延迟

            //假如检测玩家输入用 Console.ReadKey(true).Key 程序会卡住等待输入
            //因为Console.ReadKey(true).Key会一直等

            //想在控制台中 检测玩家输入 让程序不被检测卡住
            //使用 Console.KeyAvailable
            //判断 有没有键盘输入 如果有 才为true 否则一直false 就不会被卡住

            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;
                }
            }


        }
    }
}

16.8 绘制接口

using System;
using System.Collections.Generic;
using System.Text;

namespace 贪食蛇.Lesson3
{
    // 绘制接口
    interface IDraw
    {
        // 绘制方法
        void Draw();
    }
}

16.9 位置结构体

using System;
using System.Collections.Generic;
using System.Text;

namespace 贪食蛇.Lesson3
{
    //位置结构体
    struct Position
    {
        //位置信息xy坐标
        public int x;
        public int y;

        //位置构造函数 必须初始化所有变量
        public Position(int x, int y)
        {
            this.x = x;
            this.y = y;
        }


        //贪食蛇中 肯定是存在 位置的比较 
        //各个游戏对象 都会去比较位置是不是重合
        
        //运算符重载 重载==和!=运算符 
        public static bool operator ==(Position p1, Position p2)
        {
            if( p1.x == p2.x && p1.y == p2.y)
            {
                return true;
            }
            return false;
        }

        public static bool operator !=(Position p1, Position p2)
        {
            if (p1.x == p2.x && p1.y == p2.y)
            {
                return false;
            }
            return true;
        }
    }
}

16.10 游戏对象基类

using System;
using System.Collections.Generic;
using System.Text;

namespace 贪食蛇.Lesson3
{
    //游戏对象基类 是抽象类 继承绘制接口
    abstract class GameObject : IDraw
    {
        //游戏对象位置
        public Position pos;

        //可以继承接口后 把接口中的行为 编程 抽象行为
        //供子类去实现 因为是抽象行为 所以子类中是必须去实现
        public abstract void Draw();
    }
}

16.11 食物类

using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;
using 贪食蛇.Lesson3;
using 贪食蛇.Lesson6;

namespace 贪食蛇.Lesson4
{
    //食物类 继承游戏对象类
    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语句 递归的重新设置食物的位置
            if(snake.CheckSamePos(pos))
            {
                RandomPos(snake);
            }

        } 
    }
}

16.12 蛇身子类

using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson3;

namespace 贪食蛇.Lesson4
{
    /// <summary>
    /// 蛇身体类型
    /// </summary>
    enum E_SnakeBody_Type
    {
        /// <summary>
        /// 头
        /// </summary>
        Head,
        /// <summary>
        /// 身体
        /// </summary>
        Body,
    }

    //蛇身子类 继承游戏对象类
    class SnakeBody :GameObject
    {
        //蛇身体类型变量 用于判断自己这个蛇身子类的身体类型
        private E_SnakeBody_Type type;

        //蛇身子类构造函数 要初始化蛇身体类型和位置
        public SnakeBody( E_SnakeBody_Type type, int x, int y )
        {
            this.type = type;
            this.pos = new Position(x, y);
        }

        //实现蛇身子类的绘制方法
        public override void Draw()
        {
            //设置位置 根据蛇身体类型变量 设置身体的颜色和对应的身体类型的符号
            Console.SetCursorPosition(pos.x, pos.y);
            Console.ForegroundColor = type == E_SnakeBody_Type.Head ? ConsoleColor.Yellow : ConsoleColor.Green;
            Console.Write(type == E_SnakeBody_Type.Head ? "●" : "◎");
        }
    }

}

16.13 地图墙壁类

using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson3;

namespace 贪食蛇.Lesson4
{
    //地图墙壁类 继承游戏对象类
    class Wall : GameObject
    {
        //地图墙壁类构造函数
        public Wall(int x, int y)
        {
            //直接设置前的位置就行
            pos = new Position(x, y);
        }

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

16.14 地图类

using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;
using 贪食蛇.Lesson3;
using 贪食蛇.Lesson4;

namespace 贪食蛇.Lesson5
{
    //地图类 继承绘制接口
    class Map : IDraw
    {
        //墙壁数组
        public Wall[] walls;

        //地图类构造函数 初始化墙壁数组 new出中的每一堵墙
        public Map()
        {

            //new出墙壁数组
            walls = new Wall[Game.w + (Game.h - 3)*2];
            
            //设置墙的索引 用来计数画了多少堵墙
            int index = 0;

            //四次遍历 画四周的墙

            for (int i = 0; i < Game.w; i+=2)
            {
                walls[index] = new Wall(i, 0);
                ++index;
            }

            for (int i = 0; i < Game.w; i+=2)
            {
                walls[index] = new Wall(i, Game.h - 2);
                ++index;
            }

            for (int i = 1; i < Game.h - 2; i++)
            {
                walls[index] = new Wall(0, i);
                ++index;
            }

            for (int i = 1; i < Game.h - 2; i++)
            {
                walls[index] = new Wall(Game.w - 2, i);
                ++index;
            }
        }

        //实现地图类绘制方法 绘制墙壁数组中的所有墙
        public void Draw()
        {
            //遍历墙壁数组中的所有墙 逐个调用墙的绘制方法进行绘制
            for (int i = 0; i < walls.Length; i++)
            {
                walls[i].Draw();
            }
        }
    }
}

16.15 蛇类

using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson3;
using 贪食蛇.Lesson4;
using 贪食蛇.Lesson5;

namespace 贪食蛇.Lesson6
{
    #region Lesson7 蛇移动
    /// <summary>
    /// 蛇的移动方向
    /// </summary>
    enum E_MoveDir
    {
        /// <summary>
        /// 上
        /// </summary>
        Up,
        /// <summary>
        /// 下
        /// </summary>
        Down,
        /// <summary>
        /// 左
        /// </summary>
        Left,
        /// <summary>
        /// 右
        /// </summary>
        Right,
    }
    #endregion

    //蛇类 继承绘制接口
    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();
            }
        }

        #region Lesson7 蛇的移动

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

            #region Lesson11 长身体后 蛇身体移动

            //在蛇头移动之前 从蛇尾开始 不停的 让后一个的位置 等于前一个的位置
            //遍历蛇身体
            for (int i = snakeBodysLength - 1; i > 0; i--)
            {
                bodys[i].pos = bodys[i - 1].pos;
            }

            #endregion

            //蛇头移动
            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;
            }
        }
        #endregion

        #region Lesson8 改变蛇的方向

        //蛇改变移动方向方法
        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
                return;
            }

            //只要没有return 就记录外面传入的方向 之后就会按照这个方向去移动
            this.dir = dir;
        }
        #endregion

        #region Lesson9 撞墙撞身体结束逻辑

        //检测蛇是否撞墙撞身体后 结束游戏
        //在update中每次绘制完检测 要传入地图
        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;
        }
        #endregion

        #region Lesson10 吃食物相关

        //判断是否和蛇重合方法
        //通过传入一个位置 来判断这个位置 是不是和蛇重合
        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);

                //长身体
                AddBody();
            }
        }
        #endregion

        #region Lesson11 长身体

        //蛇长身体方法
        private void AddBody()
        {
            //获得当前蛇尾的蛇身子类变量
            SnakeBody frontBody = bodys[snakeBodysLength - 1];

            //吃完食物后 先长出身体 但是先不画出来 下一帧再画
            bodys[snakeBodysLength] = new SnakeBody(E_SnakeBody_Type.Body, frontBody.pos.x, frontBody.pos.y);

            //增加蛇身体的总长度
            ++snakeBodysLength;
        }
        #endregion
    }
}


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

×

喜欢就点赞,疼爱就打赏