C#核心--实践小项目(贪吃蛇)

发布时间:2024年01月12日

C#核心实践小项目 -- 贪吃蛇

必备知识点--多脚本文件

(可观看CSharp核心--52集进行了解)

必备知识点--UML类图

必备知识点--七大原则

贪吃蛇

项目展示

控制方向的是:WSAD

确定键是:J

需求分析(UML类图)

自个先写--贪吃蛇

结合自己所学进行开发(UML类图是老师提供的,因为自己暂时还不太会绘制,主要是代码逻辑还不够清晰)

补充知识点:

检测键盘是否激活

Console.KeyAvailable == true;

按照UML类图逐个去写逐个去实现(但是有些模块我没有用上)

主要精力是放在了功能实现上

下面是我实现的过程

一、万事开头难--游戏类

(我先确定了开始着手的地方--游戏类,因为它是所有类和方法的汇聚地)

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


//游戏类

namespace 自个先写CSharp核心小项目_贪吃蛇_
{
    enum E_Scene
    {
        BeginID,
        GamingID,
        FinishID,
    }

    class Game
    {
        public static int x;
        public static int y;
        public static E_Scene scene = new E_Scene();
        Begin begin = new Begin();
        Finish finish = new Finish();

        public Game()
        {
            x = 100;
            y = 30;
            scene = E_Scene.BeginID;
        }

        //初始化控制台
        public void Consoles()
        {
            //隐藏光标
            Console.CursorVisible = false;
            //设置舞台大小
            Console.SetWindowSize(x, y);
            Console.SetBufferSize(x, y);

        }

        //游戏主循环
        public void MajorCycle()
        {
            while (true)
            {
                //思考一下,为什么把开始场景和结束场景的类申明放在外面,而游戏场景的类申明放循环里面
                //因为开始结束场景是一成不变的,只需申明一次就够用了
                //而游戏场景进入一次就会执行出结果出来,每次结果都将不一样,所以每次都得重新申明
                switch (scene)
                {
                    case E_Scene.BeginID:
                        Console.Clear();
                        begin.newers();
                        break;
                    case E_Scene.GamingID:
                        Console.Clear();
                        GameScene gameScene = new GameScene();
                        gameScene.newers();
                        break;
                    case E_Scene.FinishID:
                        Console.Clear();
                        finish.newers();
                        break;
                    default:
                        break;
                }
            }
        }

        //场景切换
        public void SceneMove()
        {

        }

    }
}
二、游戏帧更新接口
1.更新接口

2.开始和结束场景基类

开始场景类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

//开始场景

namespace 自个先写CSharp核心小项目_贪吃蛇_
{
    class Begin : BeginAndFinish
    {
        public Begin()
        {
            str = "贪吃蛇";
            str1 = "开始游戏";
            str2 = "结束游戏";
        }

        //重写更新方法
        public override void newers()
        {
            Console.SetCursorPosition(48, 10);
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine(str);
            Console.SetCursorPosition(47, 13);
            Console.ForegroundColor = key == 1 ? ConsoleColor.Red : ConsoleColor.White;
            Console.WriteLine(str1);
            Console.SetCursorPosition(47, 15);
            Console.ForegroundColor = key == 2 ? ConsoleColor.Red : ConsoleColor.White;
            Console.WriteLine(str2);

            char c = Console.ReadKey(true).KeyChar;
            switch (c)
            {
                case 'W':
                case 'w':
                    key = 1;
                    break;
                case 'S':
                case 's':
                    key = 2;
                    break;
                case 'J':
                case 'j':
                    if (key == 2)
                    {
                        //关闭控制台
                        Environment.Exit(0);
                    }
                    Game.scene = (E_Scene)key;
                    break;
                default:
                    break;
            }
        }
    }
}
结束场景类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

//结束场景

namespace 自个先写CSharp核心小项目_贪吃蛇_
{
    class Finish : BeginAndFinish
    {
        string str0;
        public static int num;

        public Finish()
        {
            key = 0;
            str = "游戏结束";
            str0 = "本次游戏的长度为:";
            str1 = "回到开始界面";
            str2 = "结束游戏";
        }

        //重写更新方法
        public override void newers()
        {
            Console.SetCursorPosition(47, 10);
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine(str);
            Console.SetCursorPosition(42, 12);
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine(str0 + num);
            Console.SetCursorPosition(45, 15);
            Console.ForegroundColor = key == 0 ? ConsoleColor.Red : ConsoleColor.White;
            Console.WriteLine(str1);
            Console.SetCursorPosition(47, 17);
            Console.ForegroundColor = key == 2 ? ConsoleColor.Red : ConsoleColor.White;
            Console.WriteLine(str2);

            char c = Console.ReadKey(true).KeyChar;
            switch (c)
            {
                case 'W':
                case 'w':
                    key = 0;
                    break;
                case 'S':
                case 's':
                    key = 2;
                    break;
                case 'J':
                case 'j':
                    if (key == 2)
                    {
                        //关闭控制台
                        Environment.Exit(0);
                    }
                    Game.scene = (E_Scene)key;
                    break;
                default:
                    break;
            }
        }

    }
}

(这里面其实可以把这些方法提取到开始和结束场景基类里面的,但我懒,没有去整!!!)

3.游戏场景类

(第二个大类,游戏里的墙壁、食物、蛇、各种方法等等都汇聚在这个类中)

三、游戏场景中的各类
1.绘制接口

2.游戏对象类

(讲真的这个类没怎么用上,具体怎么用我还得看看老师是怎么用的)

3.位置结构体

(这个是完全没有用上!!)

4.地图墙壁类

5.食物类

6.蛇类--(最复杂的类)

蛇身体类--没用上

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

//蛇类

namespace 自个先写CSharp核心小项目_贪吃蛇_
{
    //移动方向枚举
    enum E_Move
    {
        Up,
        Down,
        Left,
        Right,
    }

    class Snake
    {
        string snakeHead = "●";
        string snakeBody = "◎";
        int x = 10;
        int y = 5;
        E_Move move = E_Move.Down;
        char c;
        Foods foods = new Foods();
        int bodyNum = 0;
        //标识符
        int[] num1 = new int[10000];
        int[] num2 = new int[10000];
        //打印出长度
        public string longs = "当前长度为:";

        //蛇绘制
        public void SnakePlan()
        {
            //打印长度
            Console.SetCursorPosition(2, 1);
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(longs + bodyNum);

            Finish.num = bodyNum;

            //蛇头的绘制
            Console.SetCursorPosition(x, y);
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(snakeHead);

            //蛇身的绘制
            for (int i = 0; i < bodyNum; i++)
            {
                Console.SetCursorPosition(num1[i], num2[i]);
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine(snakeBody);
            }
        }

        //蛇清除
        public void SnakeClear()
        {
            //打印长度清除
            Console.SetCursorPosition(2, 1);
            Console.WriteLine("               ");
            //蛇头的清除
            Console.SetCursorPosition(x, y);
            Console.WriteLine("  ");

            //蛇身的清除
            for (int i = 0; i < bodyNum; i++)
            {
                Console.SetCursorPosition(num1[i], num2[i]);
                Console.WriteLine("  ");
            }
        }

        //蛇转向
        public void SnakeTurn()
        {
            //老师漏讲的知识点,Console.KeyAvailable -- 检测键盘是否被激活
            if (Console.KeyAvailable == true)
            {
                c = Console.ReadKey(true).KeyChar;
                switch (c)
                {
                    case 'W':
                    case 'w':
                        if (move == E_Move.Down && bodyNum != 0)
                        {
                            move = E_Move.Down;
                        }
                        else
                        {
                            move = E_Move.Up;
                        }
                        break;
                    case 'S':
                    case 's':
                        if (move == E_Move.Up && bodyNum != 0)
                        {
                            move = E_Move.Up;
                        }
                        else
                        {
                            move = E_Move.Down;
                        }
                        break;
                    case 'A':
                    case 'a':
                        if (move == E_Move.Right && bodyNum != 0)
                        {
                            move = E_Move.Right;
                        }
                        else
                        {
                            move = E_Move.Left;
                        }
                        break;
                    case 'D':
                    case 'd':
                        if (move == E_Move.Left && bodyNum != 0)
                        {
                            move = E_Move.Left;
                        }
                        else
                        {
                            move = E_Move.Right;
                        }
                        break;
                    default:
                        break;
                }
            }
            
        } 

        //吃食物

        //死亡

        


        //蛇移动 -- (包含了蛇绘制、蛇转向、吃食物)
        public void SnakeMove()
        {
            if(foods.x == 0 || foods.y == 0)
            {
                foods.Plan();
            }
            SnakeTurn();
            switch (move)
            {
                case E_Move.Up:
                    SnakeClear();
                    y -= 1;
                    //判断是否死亡
                    //撞墙死亡
                    if (y == 0)
                    {
                        Game.scene = E_Scene.FinishID;
                        GameScene.bo = false;
                        break;
                    }
                    //撞身体死亡
                    for (int i = 0; i < bodyNum; i++)
                    {
                        if(num1[i] == x && num2[i] == y)
                        {
                            Game.scene = E_Scene.FinishID;
                            GameScene.bo = false;
                            break;
                        }
                    }

                    if (foods.x == x && foods.y == y)
                    {
                        foods.Plan();
                        //给个判断,让生成的food不会出现在有蛇身体的位置上
                        for (int i = 0; i < bodyNum; i++)
                        {
                            if (foods.x == num1[i] && foods.y == num2[i])
                            {
                                Console.SetCursorPosition(foods.x, foods.y);
                                Console.WriteLine("  ");
                                foods.Plan();
                                i = 0;
                            }
                        }

                        bodyNum += 1;
                        for (int i = bodyNum - 1; i > 0; i--)
                        {
                            num1[i] = num1[i - 1];
                            num2[i] = num2[i - 1];
                        }
                        num1[0] = x;
                        num2[0] = y + 1;
                    }
                    else
                    {
                        for (int i = bodyNum - 1; i > 0; i--)
                        {
                            num1[i] = num1[i - 1];
                            num2[i] = num2[i - 1];
                        }
                        num1[0] = x;
                        num2[0] = y + 1;
                    }
                    SnakePlan();
                    
                    break;
                case E_Move.Down:
                    SnakeClear();
                    y += 1;
                    //判断是否死亡
                    //撞墙死亡
                    if (y == 29)
                    {
                        Game.scene = E_Scene.FinishID;
                        GameScene.bo = false;
                        break;
                    }
                    //撞身体死亡
                    for (int i = 0; i < bodyNum; i++)
                    {
                        if (num1[i] == x && num2[i] == y)
                        {
                            Game.scene = E_Scene.FinishID;
                            GameScene.bo = false;
                            break;
                        }
                    }

                    if (foods.x == x && foods.y == y)
                    {
                        foods.Plan();
                        //给个判断,让生成的food不会出现在有蛇身体的位置上
                        for (int i = 0; i < bodyNum; i++)
                        {
                            if (foods.x == num1[i] && foods.y == num2[i])
                            {
                                Console.SetCursorPosition(foods.x, foods.y);
                                Console.WriteLine("  ");
                                foods.Plan();
                                i = 0;
                            }
                        }

                        bodyNum += 1;
                        for (int i = bodyNum - 1; i > 0; i--)
                        {
                            num1[i] = num1[i - 1];
                            num2[i] = num2[i - 1];
                        }
                        num1[0] = x;
                        num2[0] = y - 1;
                    }
                    else
                    {
                        for (int i = bodyNum - 1; i > 0; i--)
                        {
                            num1[i] = num1[i - 1];
                            num2[i] = num2[i - 1];
                        }
                        num1[0] = x;
                        num2[0] = y - 1;
                    }
                    SnakePlan();
                    break;
                case E_Move.Left:
                    SnakeClear();
                    x -= 2;
                    //判断是否死亡
                    //撞墙死亡
                    if (x == 0)
                    {
                        Game.scene = E_Scene.FinishID;
                        GameScene.bo = false;
                        break;
                    }
                    //撞身体死亡
                    for (int i = 0; i < bodyNum; i++)
                    {
                        if (num1[i] == x && num2[i] == y)
                        {
                            Game.scene = E_Scene.FinishID;
                            GameScene.bo = false;
                            break;
                        }
                    }

                    if (foods.x == x && foods.y == y)
                    {
                        foods.Plan();
                        //给个判断,让生成的food不会出现在有蛇身体的位置上
                        for (int i = 0; i < bodyNum; i++)
                        {
                            if (foods.x == num1[i] && foods.y == num2[i])
                            {
                                Console.SetCursorPosition(foods.x, foods.y);
                                Console.WriteLine("  ");
                                foods.Plan();
                                i = 0;
                            }
                        }

                        bodyNum += 1;
                        for (int i = bodyNum - 1; i > 0; i--)
                        {
                            num1[i] = num1[i - 1];
                            num2[i] = num2[i - 1];
                        }
                        num1[0] = x + 2;
                        num2[0] = y;
                    }
                    else
                    {
                        for (int i = bodyNum - 1; i > 0; i--)
                        {
                            num1[i] = num1[i - 1];
                            num2[i] = num2[i - 1];
                        }
                        num1[0] = x + 2;
                        num2[0] = y;
                    }
                    SnakePlan();
                    break;
                case E_Move.Right:
                    SnakeClear();
                    x += 2;
                    //判断是否死亡
                    //撞墙死亡
                    if (x == 98)
                    {
                        Game.scene = E_Scene.FinishID;
                        GameScene.bo = false;
                        break;
                    }
                    //撞身体死亡
                    for (int i = 0; i < bodyNum; i++)
                    {
                        if (num1[i] == x && num2[i] == y)
                        {
                            Game.scene = E_Scene.FinishID;
                            GameScene.bo = false;
                            break;
                        }
                    }

                    if (foods.x == x && foods.y == y)
                    {
                        foods.Plan();
                        //给个判断,让生成的food不会出现在有蛇身体的位置上
                        for (int i = 0; i < bodyNum; i++)
                        {
                            if (foods.x == num1[i] && foods.y == num2[i])
                            {
                                Console.SetCursorPosition(foods.x, foods.y);
                                Console.WriteLine("  ");
                                foods.Plan();
                                i = 0;
                            }
                        }

                        bodyNum += 1;
                        for (int i = bodyNum - 1; i > 0; i--)
                        {
                            num1[i] = num1[i - 1];
                            num2[i] = num2[i - 1];
                        }
                        num1[0] = x - 2;
                        num2[0] = y;
                    }
                    else
                    {
                        for (int i = bodyNum - 1; i > 0; i--)
                        {
                            num1[i] = num1[i - 1];
                            num2[i] = num2[i - 1];
                        }
                        num1[0] = x - 2;
                        num2[0] = y;
                    }
                    SnakePlan();
                    break;
                default:
                    break;
            }
        }

    }
}
全部代码文件:看资源
实现视频展示:

C#核心实践项目(自个先写)--贪吃蛇

总结:

代码有点屎山,but跑起来就好!!!

还是知识点运用的不够,很多没用上。

跟着老师实现--贪吃蛇

一.需求分析 -- 就是UML类图

二.游戏对象和场景更新接口
1.游戏类 ---- Game?

场景类型枚举 ---- E_SceneType

2.场景更新接口 ---- ISceneUpdate

三、实现多场景切换
1.游戏场景类

2.开始和结束场景基类

3.开始场景

将Game类中的nowScene 改为静态的

将Game类中的场景切换方法也改静态的

4.结束场景

Game中的调用也改

四、游戏场景逻辑实现
1.游戏对象基类的实现
绘制接口

游戏对象类

位置结构体

2.继承游戏对象基类的对象
地图墙壁类

食物类

蛇身子类

3.地图对象

在GameScene实现Map中的方法

到这里可以实现的功能有:三个场景

4.蛇对象

5.蛇对象移动 -- (Lesson7 部分)
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

    }
}

6.蛇对象改变移动方向(Lesson8 部分)
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里面实现调用

7.撞墙撞身体结束游戏 -- (Lesson9 部分)
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

    }
}

GameScene中调用

8.蛇吃食物

Snake类里面添加的相关方法 -- (Lesson10 部分)

GameScene类中调用

9.蛇长身体

Snake类中添加方法

(加了Lesson11--长身体AddBody方法,在Lesson10吃食物方法里面调用了AddBody方法,然后在Lesson7中添加了蛇尾跟着蛇头移动的逻辑处理)

至此跟着老师进行的制作的功能都已实现

视频展示

C#核心实践--贪吃蛇(老师实现的)

完整代码在资源里。

总结一下下

还是得多敲多练,多想想怎么让代码更精简,逻辑怎么更清晰!

多挤些时间啊!

文章来源:https://blog.csdn.net/SunsetSunup/article/details/135373603
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。