C#学习-魔塔项目记录(2021.12.13)

最近在学各种算法,魔塔落下了。。。

今天更新一下项目进度,基本的逻辑都已经做完了!

总体情况,新增了一个Monster类和Bag类,还有Autor类来充当Player和Monster的父类,

来看下吧:

 先看下Autor类里有什么

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

namespace Mota
{
    class Actor
    {
        private int _hp;
        private int _atk;
        private int _def;
        private int hit_rate;
        //公有的字段,下面都设成了属性,给了get set

        public int Hp 
        {
            get
            {
                return _hp;
            }
            set
            {
                if (value < 0)
                {
                    value = 0;
                }
                _hp = value;
            }
        }

        public int Atk
        {
            get
            {
                return _atk;
            }
            set
            {
                _atk = value;
            }
        }

        public int Def 
        {
            get 
            {
                return _def;
            }
            set
            {
                if (value < 0)
                {
                    value = 0;
                }
                _def = value;
            }
        }

        public int Hit_Rate
        {
            get
            {
                return hit_rate;
            }
            set
            {
                if (value < 0)
                {
                    value = 0;
                }
                hit_rate = value;
            }
        }
        //字段属性


        public void Attack(Actor actor)
        {
            Random r = new Random();
            int num = r.Next(1, 101);

            if(num <= this.Hit_Rate)
            {
                actor._hp -= (this._atk - actor._def);
            }
            else
            {
                Console.WriteLine("Attack missed");
            }
        }//公有的Attack方法,我在这里加了个命中率的判断因素
    }
   

}

 看下Monster(有干货)

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

namespace Mota
{
    enum MonsterType
    {
        GreenSlime = 1,
        BlueSlime,
        YellowSlime,
        Hilichurl
    }//对怪物种类进行枚举,最近有玩原神,怪物起名史莱姆和丘丘人了就~

    class Monster : Actor
    {
        public static Monster CreateMonster(int id)
        {
            string[] reader =  File.ReadAllLines("ActorData/MonsterData.txt");
            //读表(配表后附),把所有数据存进一个reader的string数组

            Type t = Type.GetType("Mota.Monster");
            Type[] ctorParams = new Type[0];
            ConstructorInfo ci = t.GetConstructor(ctorParams);
            Monster curMonster = (Monster)ci.Invoke(new object[0]);
            //利用反射创建对象,纯粹是学了反射这里用一下
            
            string[] propertyNames = reader[0].Split("\t");
             //把reader里的第0行的元素,也就是Monster类(继承自Actor)的属性,
             //按照"\t"(Tab键)分割,单独取出来放进一个strting数组         

            for (int i = 1; i < reader.Length; i++)
            {
                string[] valueReaders = reader[i].Split("\t");
                //把reader里第1行往下的值,一次拿出来分割好再放进一个string数组

                if(id.ToString() == valueReaders[0])//按照怪物的 id 赋值
                {
                    for (int j = 1; j < propertyNames.Length; j++)
                    {
                        PropertyInfo pi = t.GetProperty(propertyNames[j]);
                        pi.SetValue(curMonster, int.Parse(valueReaders[j]));
                    }//给属性赋值
                    break;
                }
            }
            return curMonster;
        }
    }//这段整体是用了读配置表和反射来实现动态创建Monster对象,
     //Monster是地图上的元素,只有玩家遇到怪物的时候,才会创建相应的怪物类型开始互相攻击
}

Monster配表:

 这段会有点复杂,但是后面用起来很方便,加属性或者怪物种类只要往配表里追加就好。

Player类也有更新很多内容,看一下

using Mota;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

namespace Mota
{
    enum Direction
    {
        None,
        Up,
        Down,
        left,
        right
    }

    struct Position
    {
        public int x;
        public int y;
    }

    class Player : Actor, IKeyDown
    {
        Direction dir = Direction.None;

        public Position Pos = new Position();

        public void Init()
        {
            InputManager.Instance.AddObserverList(this);
        }

        private static Player player;

        public static Player Instance
        {
            get
            {
                if (player == null)
                {
                    player = new Player(500, 55, 5, 90);
                }
                return player;
            }
        }
         //创建Player对象,emmm...
         //这里我发现好像 Player 被当做单例用过不能再创建新的对象了,
         //按普通再在Player类里new对象出来会报错 stack overflow 堆栈溢出
         //干脆再写了个单例形式来创建对象...尴尬
         //感觉是有点奇怪,希望有大神告诉我该咋弄。。。。

        public void Update()
        {
            MoveAndFight();
        }

        public Player(int hp, int atk, int def, int hit_rate)
        {
            this.Atk = atk;
            this.Def = def;
            this.Hp = hp;
            this.Hit_Rate = hit_rate;
        }
         //这个其实应该在父类 Actor 里写,但是我的怪物是调用无参构造器动态创建对象的,
         //只有玩家需要这个构造器,就在这里写了,也只要写一遍

        public Player() { }

        public void MoveAndFight()//我给这个方法加入了战斗元素,之前是单纯的Move
        {
            Position p = Pos;

            switch (dir)
            {
                case Direction.None:
                    return;
                case Direction.Up:
                    p.x--;
                    break;
                case Direction.Down:
                    p.x++;
                    break;
                case Direction.left:
                    p.y--;
                    break;
                case Direction.right:
                    p.y++;
                    break;
                default:
                    break;
            }

            int mapData = Singlton<SceneManager>.Instance.GetcurMapData(p);

            switch (mapData)
            {
                case (int)GameMapElement.Wall:
                    return;
                case (int)GameMapElement.Way:
                    PrintPlayer(0);
                    Pos = p;
                    break;
                case (int)Key.YellowKey:
                    PrintPlayer(0);
                    Pos = p;
                    Bag.Instance.yellowKey++;
                    UpDateMap();//根据玩家走到哪里,更新地图元素,方法在下面
                    break;//在地图里加入了钥匙元素
                case (int)GameMapElement.YellowDoor:
                case (int)GameMapElement.RedDoor:
                case (int)GameMapElement.BlueDoor:
                    if (Bag.Instance.yellowKey > 0)
                    {
                        PrintPlayer(0);
                        Pos = p;
                        Bag.Instance.yellowKey--;
                        UpDateMap();//根据玩家走到哪里,更新地图元素
                    }
                    else
                    {
                        return;
                    }
                    break;//不同颜色的钥匙开不同颜色的门,暂时第一关只有黄色的门
                case (int)GameMapElement.UpStairs:
                    SceneManager.Instance.MoveToNextMap();
                    break;
                case (int)MonsterType.GreenSlime:
                case (int)MonsterType.BlueSlime:
                case (int)MonsterType.YellowSlime:
                case (int)MonsterType.Hilichurl:
                    PrintPlayer(0);
                    Pos = p;                   SceneManager.Instance.mapList[SceneManager.Instance.curMapIndex].mapDate[Pos.x, Pos.y] = 0;
                    dir = Direction.None;
                    Fight(mapData);//Fight方法在下面
                    return;
            }
             //玩家遇到怪物,开始战斗,这里我是把怪物的枚举转换成int值用了好几个地方,
             //当做了地图的元素,也用来创建怪物对象,非常方便
             //并且把怪物脚下数据改成 0 下次打印赋空,
             //这里应该还要加个判断,这串代码有点长,
             //原因是地图数据也是读表的,并且放在了两个不同的类里。。。。

            PrintPlayer(1);

            dir = Direction.None;
        }

        public void ObserverOnKeyDown(ConsoleKey Key)
        {
            switch (Key)
            {
                case ConsoleKey.W:
                case ConsoleKey.UpArrow:
                    dir = Direction.Up;
                    break;
                case ConsoleKey.S:
                case ConsoleKey.DownArrow:
                    dir = Direction.Down;
                    break;
                case ConsoleKey.A:
                case ConsoleKey.LeftArrow:
                    dir = Direction.left;
                    break;
                case ConsoleKey.D:
                case ConsoleKey.RightArrow:
                    dir = Direction.right;
                    break;
                default:
                    break;
            }
        }
        public void PrintPlayer(int isPrint)
        {
            if (isPrint == 0)
            {
                GameManager.Instance.SetCursor(Pos.x, Pos.y);
                Console.Write("  ");
            }
            else if (isPrint == 1)
            {
                GameManager.Instance.SetCursor(Pos.x, Pos.y);
                Console.Write("♂");
            }
        }

        public void Fight(int monsterId)
        {
            SceneManager.Instance.SwitchToBattleScene(monsterId);
            Monster curMonster = Monster.CreateMonster(monsterId);

            Console.WriteLine("按任意键开始攻击(默认玩家先手)");
            Console.ReadKey();

            while (true)
            {
                player.Attack(curMonster);
                Console.WriteLine("你开始攻击");
                if (curMonster.Hp <= 0)
                {
                    Console.WriteLine("你赢了");
                    break;
                }

                curMonster.Attack(player);
                Console.WriteLine("{0}开始攻击", (MonsterType)monsterId);
                if (player.Hp <= 0)
                {
                    Console.WriteLine("你输了,游戏GG");
                    break;
                }
            }
            SceneManager.Instance.BackToGameScene();//打完换回游戏场景
        }//战斗,需要补充一下场景,暂时是文字版

        public void UpDateMap()
        {
            SceneManager.Instance.PrintGameInfo();
            SceneManager.Instance.mapList[SceneManager.Instance.curMapIndex].mapDate[Pos.x, Pos.y] = 0;
        }
         //根据玩家走到哪里,更新地图元素,因为像钥匙和门这种地图元素,
         //玩家走过去就得把他们数据改掉,写个方法方便使用
         //“PrintGameInfo” 方法在场景管理类里



    }
}

SceneManager类更新

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

namespace Mota
{
    class SceneManager:Singlton<SceneManager>
    {
        public List<GameMap> mapList = new List<GameMap>();

        public int curMapIndex = 0;

        public void Init()
        {
            if (Directory.Exists("GameMap"))
            {
                string[] fileNames = Directory.GetFiles("GameMap");
                foreach (var fileName in fileNames)
                {
                    mapList.Add(new GameMap(fileName));
                }
            }
            else
            {
                Console.WriteLine("GameMap文件夹不存在!!!");
            }

            PrintScene();
            PrintGameInfo();
        }

        public void Update()
        {
            Singlton<Player>.Instance.Update();
        }


        public int GetcurMapData(Position p)
        {
           return mapList[curMapIndex].GetMapData(p);
        }


        public void PrintScene()
        {
            mapList[curMapIndex].PrintMap();
            mapList[curMapIndex].SetPlayerToStartPos();
            Singlton<Player>.Instance.PrintPlayer(1);
        }

        public void MoveToNextMap()
        {
            Console.Clear();
            curMapIndex++;
            PrintScene();
            PrintGameInfo();
        }

        public void SwitchToBattleScene(int MonsterType)
        {
            Console.Clear();
            Singlton<BattleScene>.Instance.Init(MonsterType);
            InputManager.Instance.RemoveObserverList(Singlton<Player>.Instance);
        }

        public void BackToGameScene()
        {
            if (Player.Instance.Hp > 0)
            {
                Console.Clear();
                mapList[curMapIndex].PrintMap();
                Singlton<Player>.Instance.PrintPlayer(1);
                PrintGameInfo();
                InputManager.Instance.AddObserverList(Singlton<Player>.Instance);
            }
        }//2021.12.13更新:战斗结束切换回游戏场景


        public void PrintGameInfo()
        {
            GameManager.Instance.SetCursor(0, 20);
            Console.ForegroundColor = ConsoleColor.DarkCyan;
            Console.WriteLine("玩家属性");
            Console.ResetColor();
            GameManager.Instance.SetCursor(2, 20);
            Console.WriteLine("HP : {0}",Player.Instance.Hp);
            GameManager.Instance.SetCursor(2, 26);
            Console.WriteLine("ATK : {0}",Player.Instance.Atk);
            GameManager.Instance.SetCursor(4, 20);
            Console.WriteLine("DEF : {0}", Player.Instance.Def);
            GameManager.Instance.SetCursor(4, 26);
            Console.WriteLine("HIT_RATE : {0}", Player.Instance.Hit_Rate);
            GameManager.Instance.SetCursor(6, 20);
            Console.ForegroundColor = ConsoleColor.DarkCyan;
            Console.WriteLine("背包");
            Console.ResetColor();
            GameManager.Instance.SetCursor(8, 20);
            Console.WriteLine("YellowKey : {0}", Bag.Instance.yellowKey);
            GameManager.Instance.SetCursor(10, 20);
            Console.WriteLine("RedKey : {0}", Bag.Instance.redKey);
            GameManager.Instance.SetCursor(12, 20);
            Console.WriteLine("BlueKey : {0}", Bag.Instance.blueKey);
            GameManager.Instance.SetCursor(14, 20);
            Console.WriteLine("Heart : {0}", Bag.Instance.heart);
            GameManager.Instance.SetCursor(18, 0);
            Console.Write("操作说明 : ");
            GameManager.Instance.SetCursor(20, 0);
            Console.WriteLine("1、按 上↑ 下↓ 左← 右→ 或 W A S D 键控制玩家");

        }//2021.12.13更新:
         //主要就是加了这一块的内容,游戏的各种说明,还有玩家属性包括道具数量的打印。

    }
}

GameMap类

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

namespace Mota
{
    enum GameMapElement
    {
        Way = 0,
        Wall = 10,
        UpStairs = 38,
        DownStairs = 39,
        YellowDoor = 46,
        RedDoor = 47,
        BlueDoor = 48
    }
    
    
    class GameMap:Singlton<GameMap>
    {
        public int[,] mapDate;
        int row;
        int col;
        Position startPos;
        Position endPos;

        public GameMap() { }

        public GameMap(string path)
        {
            string[] lines = File.ReadAllLines(path);
            //调用 File 类里的 ReadAllLines 方法,
            //读取文件里的内容传入一个string数组(文件路径以参数形式传入)
            string[] line0 = lines[0].Split(",");//第零行按照“,”分隔,来读取地图行列
            row = int.Parse(line0[0]);
            col = int.Parse(line0[1]);
            startPos = new Position() { x = int.Parse(line0[2]), y = int.Parse(line0[3]) };
            endPos = new Position() { x = int.Parse(line0[4]), y = int.Parse(line0[5]) };
//2021.12.13更新:增加了初始和结束未知的坐标,也是通过读配置表来获取的(配置表有更新)

            mapDate = new int[row, col];

            for (int i = 0; i < row; i++)
            {
                string curLine = lines[i + 1];
                string[] datas = curLine.Split(",");
                //从第一行开始按照“,”分割来读取数据,一共有roe行
                //读取后把每一行的所有数据数据存入 datas 数组备用

                for (int j = 0; j < col; j++)
                {
                    mapDate[i, j] = int.Parse(datas[j]);
                    //把 datas 数组里的数据返回给mapData
                }
            }
        }

       /* 
        public void Init()
        {
            InputManager.Instance.AddObserverList(this);//把地图添加为观察者
        }
       */

        public int GetMapData(Position p)
        {
            return mapDate[p.x, p.y];
        }

        public void PrintMap()
        {
            for (int i = 0; i < mapDate.GetLength(0); i++)
            {
                for (int j = 0; j < mapDate.GetLength(1); j++)
                {
                    switch (mapDate[i,j])
                    {
                        case (int)GameMapElement.Wall:
                            Console.Write("■");
                            break;
                        case (int)GameMapElement.YellowDoor:
                            Console.ForegroundColor = ConsoleColor.DarkYellow;
                            Console.Write("◇");
                            Console.ResetColor();
                            break;
                        case (int)GameMapElement.UpStairs:
                            Console.Write("△");
                            break;
                        case (int)Props.Ruby:
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.Write("◆");
                            Console.ResetColor();
                            break;
                        case (int)Props.YellowKey:
                            Console.ForegroundColor = ConsoleColor.DarkYellow;
                            Console.Write("卍");
                            Console.ResetColor();
                            break;
                        case (int)GameMapElement.Way:
                            Console.Write("  ");
                            break;
                        case (int)MonsterType.GreenSlime:
                            Console.ForegroundColor = ConsoleColor.Green;
                            Console.Write("¤");
                            Console.ResetColor();
                            break;
                        case (int)MonsterType.YellowSlime:
                            Console.ForegroundColor = ConsoleColor.Yellow;
                            Console.Write("¤");
                            Console.ResetColor();
                            break;
                        case (int)Props.Heart:
                            Console.ForegroundColor = ConsoleColor.DarkRed;
                            Console.Write("*");
                            Console.ResetColor();
                            break;
                        default:
                            Console.WriteLine("没有这个地图元素" + mapDate[i, j]);
                            break;
                    }
                }
                Console.WriteLine();
            }
        }
         //2021.12.13更新
         //主要就是打印地图的逻辑改了一下,改成直接用字符串打印,方便给各种地图元素加上各种颜色

        public void SetPlayerToStartPos()
        {
            Singlton<Player>.Instance.Pos = startPos;
        }

        public void SetPlayerToEndPos()
        {
            Singlton<Player>.Instance.Pos = endPos;
        }//2021.12.2更新:两个方法,把人物坐标设到初始和结束位置


    }
    
}

Bag背包类:

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

namespace Mota
{
    enum Props
    {
        YellowKey = 40,
        RedKey = 41,
        BlueKey = 42,
        Heart = 43,
        Ruby = 44,
        sapphire = 45
    }

    class Bag:Singlton<Bag>
    {
        public int yellowKey;
        public int redKey;
        public int blueKey;
        public int heart;
    }
}

//这里比较简单,主要是对道具的罗列,用个枚举就好,同步更新到地图上

基本就这样了~我的魔塔快完成啦!

控制台上的效果是这样的:卍代表钥匙可以开门  ¤是怪  ◇是门  ◆是宝石加攻击和防御

*是心 游戏需要收集的。

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值