C#编程挑战:从零开始构建贪吃蛇游戏

在编程的世界里,贪吃蛇游戏是一个经典且广受欢迎的入门级项目,它不仅能够锻炼编程基础,还能加深对游戏开发的理解。本文将带你从零开始,使用C#语言和.NET框架,一步步构建一个简单的贪吃蛇游戏。

在这里插入图片描述


在这里插入图片描述

项目展示

按键操作:
方向控制: WSAD
确定键: J
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


在这里插入图片描述

代码实现

一、游戏对象和场景更新接口

1.游戏类 —— Game

using CSharp核心实践小游戏_贪食蛇.Class2;
namespace CSharp核心实践小游戏_贪食蛇.Class1
{
    /// <summary>
    /// 场景类型枚举
    /// </summary>
    enum E_SceneType
    {
        /// <summary>
        /// 开始场景
        /// </summary>
        Begin,
        /// <summary>
        /// 游戏场景
        /// </summary>
        Game,
        /// <summary>
        /// 结束场景
        /// </summary>
        End,
    }
    internal 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();
            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;
            }
        }
    }
}

2.场景更新接口 ——ISceneUpdate

namespace CSharp核心实践小游戏_贪食蛇.Class1
{
    /// <summary>
    /// 场景更新接口
    /// </summary>
    internal interface ISceneUpdate
    {
        void Update();
    }
}

三、实现多场景切换

1.游戏场景类

在这里插入图片描述

2.开始和结束场景基类

using CSharp核心实践小游戏_贪食蛇.Class1;
namespace CSharp核心实践小游戏_贪食蛇.Class2
{
    abstract internal class BeginOrEndBaseScene : ISceneUpdate
    {
        protected int nowSelIndex = 0;
        protected string strTitle;
        protected string strOne;
        public abstract void EnterJDoSomthing();
        public void Update()
        {
            // 开始和结束场景的游戏逻辑
            // 选择当前的对象 然后监听键盘输入 wsj
            Console.ForegroundColor = ConsoleColor.White;
            // 显示标题
            Console.SetCursorPosition(Game.w / 2 - strTitle.Length, 5);
            Console.Write(strTitle);
            // 显示下方的选项
            Console.SetCursorPosition(Game.w / 2 - strOne.Length, 8);
            Console.ForegroundColor = nowSelIndex == 0 ? ConsoleColor.Red : ConsoleColor.White;
            Console.Write(strOne);
            Console.SetCursorPosition(Game.w / 2 - 4, 10);
            Console.ForegroundColor = nowSelIndex == 1 ? ConsoleColor.Red : ConsoleColor.White;
            Console.Write("结束游戏");
            // 检测输入
            switch (Console.ReadKey(true).Key)
            {
                case ConsoleKey.W:
                    --nowSelIndex;
                    if (nowSelIndex < 0)
                    {
                        nowSelIndex = 0;
                    }
                    break;
                case ConsoleKey.S:
                    ++nowSelIndex;
                    if (nowSelIndex > 1)
                    {
                        nowSelIndex = 1;
                    }
                    break;
                case ConsoleKey.J:
                    EnterJDoSomthing();
                    break;
            }
        }
    }
}

3.开始场景

using CSharp核心实践小游戏_贪食蛇.Class1;
namespace CSharp核心实践小游戏_贪食蛇.Class2
{
    internal class BeginScene : BeginOrEndBaseScene
    {
        public BeginScene()
        {
            strTitle = "贪食蛇";
            strOne = "开始游戏";
        }
        public override void EnterJDoSomthing()
        {
            // 按J键做什么的逻辑
            if(nowSelIndex == 0)
            {
                Game.ChangeScene(E_SceneType.Game);
            }
            else
            {
                Environment.Exit(0);
            }
        }
    }
}

4.结束场景

using CSharp核心实践小游戏_贪食蛇.Class1;
using CSharp核心实践小游戏_贪食蛇.Class6;
namespace CSharp核心实践小游戏_贪食蛇.Class2
{
    internal class EndScene : BeginOrEndBaseScene
    {
        public EndScene()
        {
            Snake.Num = 0;
            strTitle = "游戏结束";
            strOne = "回到开始界面";
        }
        public override void EnterJDoSomthing()
        {
            // 按J键做什么的逻辑
            if (nowSelIndex == 0)
            {
                Game.ChangeScene(E_SceneType.Begin);
            }
            else
            {
                Environment.Exit(0);
            }
        }
    }
}

四、游戏场景逻辑实现

1.游戏对象基类的实现

绘制接口
namespace CSharp核心实践小游戏_贪食蛇.Class3
{
    internal interface IDraw
    {
        void Draw();
    }
}
游戏对象类
namespace CSharp核心实践小游戏_贪食蛇.Class3
{
    abstract internal class GameObject : IDraw
    {
        // 游戏对象位置
        public Position pos;
        // 可以继承接口后把接口中的行为变成抽象行为
        // 供子类去实现 因为是抽象行为所以子类中是必须去实现
        public abstract void Draw();
    }
}
位置结构体
namespace CSharp核心实践小游戏_贪食蛇.Class3
{
    struct Position
    {
        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;
        }
    }
}

2.继承游戏对象基类的对象

地图墙壁类
using CSharp核心实践小游戏_贪食蛇.Class3;
namespace CSharp核心实践小游戏_贪食蛇.Class4
{
    internal 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.Write("■");
        }
    }
}
食物类
using CSharp核心实践小游戏_贪食蛇.Class1;
using CSharp核心实践小游戏_贪食蛇.Class3;
using CSharp核心实践小游戏_贪食蛇.Class6;
namespace CSharp核心实践小游戏_贪食蛇.Class4
{
    internal 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);
            }
        }
    }
}
蛇身子类
using CSharp核心实践小游戏_贪食蛇.Class3;
namespace CSharp核心实践小游戏_贪食蛇.Class4
{
    /// <summary>
    /// 蛇身体类型
    /// </summary>
    enum E_SnakeBody_Type
    {
        /// <summary>
        /// 头
        /// </summary>
        Head,
        /// <summary>
        /// 身体
        /// </summary>
        Body,
    }
    internal 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 ? "●" : "◎");
        }
    }
}

3.地图对象

using CSharp核心实践小游戏_贪食蛇.Class1;
using CSharp核心实践小游戏_贪食蛇.Class3;
using CSharp核心实践小游戏_贪食蛇.Class4;
namespace CSharp核心实践小游戏_贪食蛇.Class5
{
    internal class Map : IDraw
    {
        public Wall[] walls;
        public Map()
        {
            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();
            }
        }
    }
}
在GameScene实现Map中的方法

在这里插入图片描述

4.蛇对象

在这里插入图片描述

在这里插入图片描述

五、蛇对象移动


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson3;
using 贪吃蛇.Lesson4;
 
namespace 贪吃蛇.Lesson6
{
    /// <summary>
    /// 蛇的移动方向
    /// </summary>
    enum E_MoveDir
    {
        Up,
        Down,
        Left,
        Right,
    }
 
    class Snake : IDraw
    {
        SnakeBody[] bodys;
        //记录当前蛇的长度
        int nowNum;
        //当前移动方向
        E_MoveDir dir;
 
        public Snake(int x, int y)
        {
            //粗暴的方法 直接申明200个空间 来装蛇身体的数组
            bodys = new SnakeBody[200];
 
            bodys[0] = new SnakeBody(E_SnakeBody_Type.Head, x, y);
            nowNum = 1;
 
            dir = E_MoveDir.Down;
        }
 
        public void Draw()
        {
            //画一节一节的身子
            for (int i = 0; i < nowNum; i++)
            {
                bodys[i].Draw();
            }
        }
 
        #region Lesson7 蛇的移动
        public void Move()
        {
            //移动前
            //擦除最后一个位置
            Console.SetCursorPosition(bodys[nowNum - 1].pos.x, bodys[nowNum - 1].pos.y);
            Console.WriteLine("  ");
 
            //再移动
            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;
                default:
                    break;
            }
 
        }
 
        #endregion
 
    }
}

在这里插入图片描述

六、蛇对象改变移动方向


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson3;
using 贪吃蛇.Lesson4;
 
namespace 贪吃蛇.Lesson6
{
    /// <summary>
    /// 蛇的移动方向
    /// </summary>
    enum E_MoveDir
    {
        Up,
        Down,
        Left,
        Right,
    }
 
    class Snake : IDraw
    {
        SnakeBody[] bodys;
        //记录当前蛇的长度
        int nowNum;
        //当前移动方向
        E_MoveDir dir;
 
        public Snake(int x, int y)
        {
            //粗暴的方法 直接申明200个空间 来装蛇身体的数组
            bodys = new SnakeBody[200];
 
            bodys[0] = new SnakeBody(E_SnakeBody_Type.Head, x, y);
            nowNum = 1;
 
            dir = E_MoveDir.Down;
        }
 
        public void Draw()
        {
            //画一节一节的身子
            for (int i = 0; i < nowNum; i++)
            {
                bodys[i].Draw();
            }
        }
 
        #region Lesson7 蛇的移动
        public void Move()
        {
            //移动前
            //擦除最后一个位置
            Console.SetCursorPosition(bodys[nowNum - 1].pos.x, bodys[nowNum - 1].pos.y);
            Console.WriteLine("  ");
 
            //再移动
            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;
                default:
                    break;
            }
 
        }
 
        #endregion
 
        #region Lesson8 改变方向
        public void ChangeDir(E_MoveDir dir)
        {
            //只有头部的时候 可以直接左转右 右转左 上转下 下转上
            //有身体时 这些情况就不能直接转
            if (this.dir == dir || 
                nowNum > 1 && 
                (this.dir == E_MoveDir.Up && dir == E_MoveDir.Down || 
                this.dir == E_MoveDir.Down && dir == E_MoveDir.Up || 
                this.dir == E_MoveDir.Left && dir == E_MoveDir.Right || 
                this.dir == E_MoveDir.Right && dir == E_MoveDir.Left))
            {
                return;
            }
 
            //只要没有 return 就记录外面传入的方向 之后就会按照这个方向去移动
            this.dir = dir;
        }
        #endregion
 
    }
}
在GameScene里面实现调用
using CSharp核心实践小游戏_贪食蛇.Class1;
using CSharp核心实践小游戏_贪食蛇.Class4;
using CSharp核心实践小游戏_贪食蛇.Class5;
using CSharp核心实践小游戏_贪食蛇.Class6;
namespace CSharp核心实践小游戏_贪食蛇.Class2
{
    internal class GameScene : ISceneUpdate
    {
        Map map;
        Snake snake;
        Food food;
        int updateIndex = 0;
        public GameScene()
        {
            map = new Map();
            snake = new Snake(40, 10);
            food = new Food(snake);
            Console.ForegroundColor = ConsoleColor.White;
            Console.SetCursorPosition(2, 1);
            Console.Write("当前得分:0");
        }
        public void Update()
        {
            if (updateIndex % 5555 == 0)
            {
                map.Draw();
                food.Draw();
                snake.Move();
                snake.Draw();
                // 检测是否撞墙
                if(snake.CheckEnd(map))
                {
                    // 结束逻辑
                    Game.ChangeScene(E_SceneType.End);
                }
                snake.CheckEatFood(food);
                updateIndex = 0;
            }
            ++updateIndex;
            // 在控制台中 检测玩家输入 让程序不被检测卡主
            // 判断有没有键盘输入 如果有才为 true
            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;
                }
            }
        }
    }
}

七、撞墙撞身体结束游戏


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson3;
using 贪吃蛇.Lesson4;
using 贪吃蛇.Lesson5;
 
namespace 贪吃蛇.Lesson6
{
    /// <summary>
    /// 蛇的移动方向
    /// </summary>
    enum E_MoveDir
    {
        Up,
        Down,
        Left,
        Right,
    }
 
    class Snake : IDraw
    {
        SnakeBody[] bodys;
        //记录当前蛇的长度
        int nowNum;
        //当前移动方向
        E_MoveDir dir;
 
        public Snake(int x, int y)
        {
            //粗暴的方法 直接申明200个空间 来装蛇身体的数组
            bodys = new SnakeBody[200];
 
            bodys[0] = new SnakeBody(E_SnakeBody_Type.Head, x, y);
            nowNum = 1;
 
            dir = E_MoveDir.Down;
        }
 
        public void Draw()
        {
            //画一节一节的身子
            for (int i = 0; i < nowNum; i++)
            {
                bodys[i].Draw();
            }
        }
 
        #region Lesson7 蛇的移动
        public void Move()
        {
            //移动前
            //擦除最后一个位置
            Console.SetCursorPosition(bodys[nowNum - 1].pos.x, bodys[nowNum - 1].pos.y);
            Console.WriteLine("  ");
 
            //再移动
            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;
                default:
                    break;
            }
 
        }
 
        #endregion
 
        #region Lesson8 改变方向
        public void ChangeDir(E_MoveDir dir)
        {
            //只有头部的时候 可以直接左转右 右转左 上转下 下转上
            //有身体时 这些情况就不能直接转
            if (this.dir == dir || 
                nowNum > 1 && 
                (this.dir == E_MoveDir.Up && dir == E_MoveDir.Down || 
                this.dir == E_MoveDir.Down && dir == E_MoveDir.Up || 
                this.dir == E_MoveDir.Left && dir == E_MoveDir.Right || 
                this.dir == E_MoveDir.Right && dir == E_MoveDir.Left))
            {
                return;
            }
 
            //只要没有 return 就记录外面传入的方向 之后就会按照这个方向去移动
            this.dir = dir;
        }
        #endregion
 
        #region Lesson9 撞墙撞身体结束逻辑
        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 < nowNum; i++)
            {
                if (bodys[0].pos == bodys[i].pos)
                {
                    return true;
                }
            }
 
            return false;
        }
        #endregion
 
    }
}

八、蛇吃食物

using CSharp核心实践小游戏_贪食蛇.Class1;
using CSharp核心实践小游戏_贪食蛇.Class3;
using CSharp核心实践小游戏_贪食蛇.Class4;
using CSharp核心实践小游戏_贪食蛇.Class5;
namespace CSharp核心实践小游戏_贪食蛇.Class6
{
    #region Class7 蛇移动
    /// <summary>
    /// 蛇的移动方向
    /// </summary>
    enum E_MoveDir
    {
        /// <summary>
        /// 上
        /// </summary>
        Up,
        /// <summary>
        /// 下
        /// </summary>
        Down,
        /// <summary>
        /// 左
        /// </summary>
        Left,
        /// <summary>
        /// 右
        /// </summary>
        Right,
    }
    #endregion
    internal class Snake : IDraw
    {
        SnakeBody[] bodys;
        // 来记录当前蛇的长度
        int nowNum;
        public static int Num = 0;
        // 当前移动的方向
        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);
            nowNum = 1;
            dir = E_MoveDir.Right;
        }
        public void Draw()
        {
            // 画一节一节的身子
            for (int i = 0; i < nowNum; i++)
            {
                bodys[i].Draw();
            }
        }
        #region Class7 蛇的移动
        public void Move()
        {
            // 移动前
            // 擦除最后一个位置
            // 擦屁股
            SnakeBody lastbody = bodys[nowNum - 1];
            Console.SetCursorPosition(lastbody.pos.x, lastbody.pos.y);
            Console.Write("  ");
            // 在蛇头移动之前 从蛇尾开始 不停的让后一个的位置 等于前一个的位置
            for (int i = nowNum - 1; i > 0;i--)
            {
                bodys[i].pos = bodys[i - 1].pos;
            }
            // 再动
            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 Class8 改变方向
        public void ChangeDir(E_MoveDir dir)
        {
            // 只有头部的时候 可以直接左转右 右转左 上转下 下转上
            // 有身体时 这种情况就不能直接转
            if( dir == this.dir || 
                nowNum > 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 就记录外面传入的方向 之后就会按照这个方向去移动
            this.dir = dir;
        }
        #endregion
        #region Class9 撞墙撞身体结束逻辑
        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 < nowNum; i++)
            {
                if (bodys[0].pos == bodys[i].pos)
                {
                    return true;
                }
            }
            return false;
        }
        #endregion
        #region Class10 吃食物相关
        // 通过传入一个位置 来判断这个位置是不是和蛇重合
        public bool CheckSamePos(Position p)
        {
            for(int i = 0; i < nowNum; 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();
                Console.ForegroundColor = ConsoleColor.White;
                Console.SetCursorPosition(2, 1);
                Console.Write("当前得分:" + Snake.Num);
                if (Snake.Num >= 20)
                {
                    Console.SetCursorPosition(14, 1);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("★");
                }
                if (Snake.Num >= 40)
                {
                    Console.SetCursorPosition(16, 1);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("★");
                }
                if (Snake.Num >= 60)
                {
                    Console.SetCursorPosition(18, 1);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("★");
                }
                if (Snake.Num >= 80)
                {
                    Console.SetCursorPosition(20, 1);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("★");
                }
                if (Snake.Num >= 100)
                {
                    Console.SetCursorPosition(22, 1);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("★");
                }
                if (Snake.Num >= 200)
                {
                    Game.ChangeScene(E_SceneType.End);
                }
            }
        }
        #endregion
        
    }
}

九、蛇长身体

using CSharp核心实践小游戏_贪食蛇.Class1;
using CSharp核心实践小游戏_贪食蛇.Class3;
using CSharp核心实践小游戏_贪食蛇.Class4;
using CSharp核心实践小游戏_贪食蛇.Class5;
namespace CSharp核心实践小游戏_贪食蛇.Class6
{
    #region Class7 蛇移动
    /// <summary>
    /// 蛇的移动方向
    /// </summary>
    enum E_MoveDir
    {
        /// <summary>
        /// 上
        /// </summary>
        Up,
        /// <summary>
        /// 下
        /// </summary>
        Down,
        /// <summary>
        /// 左
        /// </summary>
        Left,
        /// <summary>
        /// 右
        /// </summary>
        Right,
    }
    #endregion
    internal class Snake : IDraw
    {
        SnakeBody[] bodys;
        // 来记录当前蛇的长度
        int nowNum;
        public static int Num = 0;
        // 当前移动的方向
        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);
            nowNum = 1;
            dir = E_MoveDir.Right;
        }
        public void Draw()
        {
            // 画一节一节的身子
            for (int i = 0; i < nowNum; i++)
            {
                bodys[i].Draw();
            }
        }
        #region Class7 蛇的移动
        public void Move()
        {
            // 移动前
            // 擦除最后一个位置
            // 擦屁股
            SnakeBody lastbody = bodys[nowNum - 1];
            Console.SetCursorPosition(lastbody.pos.x, lastbody.pos.y);
            Console.Write("  ");
            // 在蛇头移动之前 从蛇尾开始 不停的让后一个的位置 等于前一个的位置
            for (int i = nowNum - 1; i > 0;i--)
            {
                bodys[i].pos = bodys[i - 1].pos;
            }
            // 再动
            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 Class8 改变方向
        public void ChangeDir(E_MoveDir dir)
        {
            // 只有头部的时候 可以直接左转右 右转左 上转下 下转上
            // 有身体时 这种情况就不能直接转
            if( dir == this.dir || 
                nowNum > 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 就记录外面传入的方向 之后就会按照这个方向去移动
            this.dir = dir;
        }
        #endregion
        #region Class9 撞墙撞身体结束逻辑
        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 < nowNum; i++)
            {
                if (bodys[0].pos == bodys[i].pos)
                {
                    return true;
                }
            }
            return false;
        }
        #endregion
        #region Class10 吃食物相关
        // 通过传入一个位置 来判断这个位置是不是和蛇重合
        public bool CheckSamePos(Position p)
        {
            for(int i = 0; i < nowNum; 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();
                Console.ForegroundColor = ConsoleColor.White;
                Console.SetCursorPosition(2, 1);
                Console.Write("当前得分:" + Snake.Num);
                if (Snake.Num >= 20)
                {
                    Console.SetCursorPosition(14, 1);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("★");
                }
                if (Snake.Num >= 40)
                {
                    Console.SetCursorPosition(16, 1);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("★");
                }
                if (Snake.Num >= 60)
                {
                    Console.SetCursorPosition(18, 1);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("★");
                }
                if (Snake.Num >= 80)
                {
                    Console.SetCursorPosition(20, 1);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("★");
                }
                if (Snake.Num >= 100)
                {
                    Console.SetCursorPosition(22, 1);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("★");
                }
                if (Snake.Num >= 200)
                {
                    Game.ChangeScene(E_SceneType.End);
                }
            }
        }
        #endregion
        #region Class11 长身体
        private void AddBody()
        {
            SnakeBody frontBody = bodys[nowNum - 1];
            // 先长
            bodys[nowNum] = new SnakeBody(E_SnakeBody_Type.Body, frontBody.pos.x, frontBody.pos.y);
            // 再加长度
            ++nowNum;
            ++Num;
        }
        #endregion
    }
}

至此贪吃蛇制作的功能都已实现。


在这里插入图片描述

总结

通过本文的指导,你已经能够使用C#创建一个基本的贪吃蛇游戏了。这只是一个起点,你可以在此基础上继续探索和创新,比如添加计分板、游戏暂停和恢复、不同难度级别、声音效果、网络对战等,让你的游戏更加丰富和有趣。


在这里插入图片描述


在这里插入图片描述

📂 Unity 开发技能 | 目录索引

📂 Unity 常用插件 | 总目录

📂 Unity 开发资源 | 目录索引

📂 Unity 源码工程 | 总目录

🍉🍉🍉 如果觉得这篇文对你有帮助的话,请点个赞👍、收藏⭐️下吧,非常感谢! 💕💕💕


在这里插入图片描述

🥷🏻博主简介:20年软件开发经验,经历嵌入式驱动开发、Android开发、Unity游戏开发。积累收藏了大量游戏开发资源和素材,如有需要请联系我。

互3互推也请联系我…

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Unity打怪升级

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

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

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

打赏作者

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

抵扣说明:

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

余额充值