我的demo保卫萝卜中的技术要点

管理类:

GameManager(单例),GameController(单例);

一些其他的管理类(PlayerManager,AudioSourceManager,FactoryManager)作为GameManager的成员变量存在(这样也可以保证只有一个存在,并且初始化在GameManager之后)

使用到的多种设计模式

复盘一下我用过的设计模式-CSDN博客

:: Scoll View 结合 GridLayoutGroup 组件可以实现组件的整齐排列

RectTransform

RectTransform 和 Transfrom 的区别

Inspector,Awake,OnEnable与Start之间微妙的关系

Inspector 早于 Awake 早于 OnEnable 早于 Start

地图编辑器 编辑类

MapTool类

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;

#if Tool
[CustomEditor(typeof(MapMaker))]
public class MapTool : Editor
{
    private MapMaker mapMaker;

    //关卡文件列表
    private List<FileInfo> fileList = new List<FileInfo>();
    private string[] fileNameList;

    //当前编辑的关卡索引
    private int selectIndex = -1;

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        if(Application.isPlaying)
        {
            mapMaker = MapMaker.Instance; 
            EditorGUILayout.BeginHorizontal();
            //获取操作的文件名
            fileNameList = GetNames(fileList);

            int currentIndex = EditorGUILayout.Popup(selectIndex, fileNameList);
            if (currentIndex != selectIndex)  //当前选择对象是否改变
            {
                selectIndex = currentIndex;

                //实例化地图的方法
                mapMaker.InitMap();
                //加载当前选择的Level文件
                mapMaker.LoadLevelFile(mapMaker.LoadLevelInfoFile(fileNameList[selectIndex]));
            }

            if (GUILayout.Button("读取关卡列表"))
            {
                LoadLevelFiles();

            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            if(GUILayout.Button("回复地图编辑器默认状态"))
            {
                mapMaker.RecoverTowerPoint();
            }

            if(GUILayout.Button("清除怪物路点"))
            {
                mapMaker.CLearMonsterPath();
            }

            EditorGUILayout.EndHorizontal();

            if(GUILayout.Button("保存当前关卡数据文件"))
            {
                mapMaker.SaveLevelFileByJson();
            }
        }
    }

    //加载关卡数据文件
    private void LoadLevelFiles()
    {
        CLearList();
        fileList = GetLevelFile();

    }

    //清楚文件列表
    private void CLearList()
    {
        fileList.Clear();
        selectIndex = -1;
    }

    //具体读取关卡列表的方法
    private  List<FileInfo> GetLevelFile()
    {
                                                                         //自动帮我们读后缀是.json的文件
        string[] files = Directory.GetFiles(Application.streamingAssetsPath + "/Json/Level/", "*.json");

        List<FileInfo> list = new List<FileInfo>();
        for (int i = 0; i < files.Length; i++)
        {
            FileInfo file = new FileInfo(files[i]);
            list.Add(file);
        }
        return list;
    }

    //获取关卡文件的名字
    private string[] GetNames(List<FileInfo> files)
    {
        List<string> names = new List<string>();
        foreach (FileInfo file in files)
        {
            names.Add(file.Name);
        }
        //将列表转成数组
        return names.ToArray();
    }


    
}
#endif

Mapmaker 类

cs 复制代码
using LitJson;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

/// <summary>
/// 地图编辑工具,游戏中作为地图加载产生工具
/// </summary>

public class MapMaker : MonoBehaviour
{
#if Tool
    public bool drawLine;  //是否画线
    public GameObject gridGo;  //格子的预制体

    private static MapMaker _instance;

    public static MapMaker Instance { get => _instance; }
#endif

    //地图的有关属性
    private float mapWidth;  //地图宽
    private float mapHeight;  //地图高

    //格子
    [HideInInspector]
    public float gridWidth;
    [HideInInspector]
    public float gridHeight;

    //当前关卡索引
    //[HideInInspector]
    public int bigLevelID;
    //[HideInInspector]
    public int levelID;

    //全部的格子对象
    public GridPoint[,] gridPoints;

    //行列数
    public const int yRow = 8;
    public const int xCloumn = 12;

    //怪物路径点
    [HideInInspector]
    public List<GridPoint.GridIndex> monsterPath;

    //怪物路径点的具体位置
    [HideInInspector]
    public List<Vector3> monsterPathPos;

    //关卡的背景道路渲染
    private SpriteRenderer bgSR;
    private SpriteRenderer roadSR;

    //每一波次产生的怪物ID列表
    public List<Round.RoundInfo> roundInfoList;

    [HideInInspector]
    public Carrot carrot;

    private void Awake()
    {
#if Tool
        _instance = this;
        InitMapMaker();
#endif

    }

    //初始化地图
    public void InitMapMaker()
    {
        Calculatesize();
        gridPoints = new GridPoint[xCloumn, yRow];
        monsterPath = new List<GridPoint.GridIndex>();
        for (int x = 0; x < xCloumn; x++)
        {
            for (int y = 0; y < yRow; y++)
            {
#if Tool
                GameObject itemGo = Instantiate(gridGo,transform.position,transform.rotation);
#endif

#if Game
                //问工厂要资源的方法
                GameObject itemGo = GameController.Instance.GetGameObjectResource("Grid");
#endif
                itemGo.transform.position = new Vector3(x * gridWidth - mapWidth / 2 + gridWidth / 2,
                    y * gridHeight - mapHeight / 2 + gridHeight / 2);
                itemGo.transform.SetParent(transform);
                gridPoints[x, y] = itemGo.GetComponent<GridPoint>();
                gridPoints[x, y].gridIndex.xIndex = x;
                gridPoints[x, y].gridIndex.yIndex = y;
            }
        }
        bgSR = transform.Find("BG").GetComponent<SpriteRenderer>();
        roadSR = transform.Find("Road").GetComponent<SpriteRenderer>();

    }

#if Game
    //加载地图
    public void LoadMap(int bigLevel, int level)
    {
        bigLevelID = bigLevel;
        levelID = level;
        LoadLevelFile(LoadLevelInfoFile("Level" + bigLevelID.ToString() + "_" + levelID.ToString() + ".json"));
        monsterPathPos = new List<Vector3>();
        for (int i = 0; i < monsterPath.Count; i++)
        {
            monsterPathPos.Add(gridPoints[monsterPath[i].xIndex, monsterPath[i].yIndex].transform.position);
        }

        //起始点与终止点
        GameObject startPointGo = GameController.Instance.GetGameObjectResource("startPoint");
        startPointGo.transform.position = monsterPathPos[0];
        startPointGo.transform.SetParent(transform);

        GameObject endPointGo = GameController.Instance.GetGameObjectResource("Carrot");
        endPointGo.transform.position = monsterPathPos[monsterPathPos.Count - 1] - new Vector3(0, 0, 1);
        endPointGo.transform.SetParent(transform);
        carrot = endPointGo.GetComponent<Carrot>();

    }


#endif

    //纠正位置
    public Vector3 CorrectPosition(float x, float y)
    {
        return new Vector3(x - mapWidth / 2 + gridWidth / 2, y - mapHeight / 2 + gridHeight / 2);
    }

    //计算地图格子宽高
    private void Calculatesize()
    {
        //视口坐标,左下角(0,0),右上角(1,1)
        Vector3 leftDown = new Vector3(0, 0);
        Vector3 rightUp = new Vector3(1, 1);

        //将视口转换为世界坐标
        Vector3 posOne = Camera.main.ViewportToWorldPoint(leftDown);
        Vector3 posTwo = Camera.main.ViewportToWorldPoint(rightUp);

        mapWidth = posTwo.x - posOne.x;
        mapHeight = posTwo.y - posOne.y;

        gridWidth = mapWidth / xCloumn;
        gridHeight = mapHeight / yRow;

    }

#if Tool
    //画格子用于辅助设计
    private void OnDrawGizmos()
    {
        if(drawLine)
        {
            Calculatesize();
            Gizmos.color = Color.green;

            //画行
            for(int y = 0; y <= yRow; y++)
            {
                Vector3 startPos = new Vector3(-mapWidth / 2, -mapHeight / 2 + y * gridHeight);
                Vector3 endPos = new Vector3(mapWidth / 2, -mapHeight / 2 + y * gridHeight);
                Gizmos.DrawLine(startPos, endPos);
            }

            //画列
            for(int x = 0;x <= xCloumn;x++)
            {
                Vector3 startPos = new Vector3(-mapWidth / 2 + x * gridWidth ,mapHeight / 2);
                Vector3 endPos = new Vector3(-mapWidth / 2 + x * gridWidth, -mapHeight / 2);
                Gizmos.DrawLine(startPos, endPos);
            }
        }
    }
#endif

    /// <summary>
    /// 有关地图编辑的方法
    /// </summary>

    //清除怪物路点
    public void CLearMonsterPath()
    {
        monsterPath.Clear();
    }

    //恢复地图编辑默认状态
    public void RecoverTowerPoint()
    {
        CLearMonsterPath();
        for (int x = 0; x < xCloumn; x++)
        {
            for (int y = 0; y < yRow; y++)
            {
                gridPoints[x, y].InitGrid();
            }
        }
    }

    //初始化地图
    public void InitMap()
    {
        bigLevelID = 0;
        levelID = 0;
        RecoverTowerPoint();
        roundInfoList.Clear();
        bgSR.sprite = null;
        roadSR.sprite = null;
    }

#if Tool
    //生成LevelInfo对象用来保存文件
    private LevelInfo CreateLevelInfoGo()
    {
        LevelInfo levelInfo = new LevelInfo
        {
            bigLevelID = this.bigLevelID,
            levelID = this.levelID,
        };
        levelInfo.gridPoints = new List<GridPoint.GridState>(); ;
        for (int x = 0; x < xCloumn; x++)
        {
            for (int y = 0; y < yRow; y++)
            {
                levelInfo.gridPoints.Add(gridPoints[x, y].gridState);
            }
        }
        levelInfo.monsterPath = new List<GridPoint.GridIndex>();
        for (int i = 0; i < monsterPath.Count; i++)
        {
            levelInfo.monsterPath.Add(monsterPath[i]);
        }
        levelInfo.roundInfo = new List<Round.RoundInfo>();
        for (int i = 0; i < roundInfoList.Count; i++)
        {
            levelInfo.roundInfo.Add(roundInfoList[i]);
        }
        Debug.Log("保存成功");
        return levelInfo;
    }

    //保存当前关卡的数据文件
    public void SaveLevelFileByJson()
    {
        LevelInfo levelInfoGo = CreateLevelInfoGo();
        string filePath = Application.streamingAssetsPath + "/Json/Level/" + "Level"
            + bigLevelID.ToString() + "_" + levelID.ToString() + ".json";
        string saveJsonStr = JsonMapper.ToJson(levelInfoGo);
        StreamWriter sw = new StreamWriter(filePath);
        sw.Write(saveJsonStr);
        sw.Close();
    }
    
#endif

    //读取关卡文件解析json转化为LevelInfo对象
    public LevelInfo LoadLevelInfoFile(string fileName)
    {
        LevelInfo levelInfo = new LevelInfo();
        string filePath = Application.streamingAssetsPath + "/Json/Level/" + fileName;
        if (File.Exists(filePath))
        {
            StreamReader sr = new StreamReader(filePath);
            string jsonStr = sr.ReadToEnd();
            sr.Close();
            levelInfo = JsonMapper.ToObject<LevelInfo>(jsonStr);
            return levelInfo;

        }
        Debug.Log("文件加载失败,加载路径是:" + filePath);
        return null;
    }

    //
    public void LoadLevelFile(LevelInfo levelInfo)
    {
        bigLevelID = levelInfo.bigLevelID;
        levelID = levelInfo.levelID;
        for (int x = 0; x < xCloumn; x++)
        {
            for (int y = 0; y < yRow; y++)
            {
                gridPoints[x, y].gridState = levelInfo.gridPoints[y + x * yRow];

                //更新格子的状态
                gridPoints[x, y].UpdateGrid();
            }
        }
        monsterPath.Clear();
        for (int x = 0; x < levelInfo.monsterPath.Count; x++)
        {
            monsterPath.Add(levelInfo.monsterPath[x]);
        }

        roundInfoList = new List<Round.RoundInfo>();
        for (int i = 0; i < levelInfo.roundInfo.Count; i++)
        {
            roundInfoList.Add(levelInfo.roundInfo[i]);
        }

        bgSR.sprite = Resources.Load<Sprite>("Pictures/NormalMordel/Game/"
            + bigLevelID.ToString() + "/" + "BG" + (levelID / 3).ToString());
        roadSR.sprite = Resources.Load<Sprite>("Pictures/NormalMordel/Game/"
            + bigLevelID.ToString() + "/" + "Road" + levelID.ToString());
    }
}
相关推荐
ttod_qzstudio17 小时前
Unity中使用EzySlice实现模型切割与UV控制完全指南
unity
南無忘码至尊17 小时前
Unity 实现与 Ollama API 交互的实时流式响应处理
unity·游戏引擎·交互
平行云21 小时前
如何实现UE程序大并发多集群的像素流部署
unity·ue5·图形渲染
向宇it2 天前
【unity小技巧】在 Unity 中将 2D 精灵添加到 3D 游戏中,并实现阴影投射效果,实现类《八分旅人》《饥荒》等等的2.5D游戏效果
游戏·3d·unity·编辑器·游戏引擎·材质
向宇it2 天前
Unity Universal Render Pipeline/Lit光照材质介绍
游戏·unity·c#·游戏引擎·材质
__water2 天前
RHA《Unity兼容AndroidStudio打Apk包》
android·unity·jdk·游戏引擎·sdk·打包·androidstudio
两水先木示3 天前
【Unity3D】微信小游戏适配安全区域或胶囊控件(圆圈按钮)水平高度一致方案
unity·微信小游戏·安全区域·ui适配·胶囊控件·safearea
枯萎穿心攻击3 天前
ECS由浅入深第三节:进阶?System 的行为与复杂交互模式
开发语言·unity·c#·游戏引擎
不绝1913 天前
怪物机制分析(有限状态机、编辑器可视化、巡逻机制)
网络·游戏·unity·游戏引擎
unicrom_深圳市由你创科技3 天前
Unity开发如何解决iOS闪退问题
unity·ios·蓝桥杯