我的demo保卫萝卜中的技术要点
管理类:
GameManager(单例),GameController(单例);
一些其他的管理类(PlayerManager,AudioSourceManager,FactoryManager)作为GameManager的成员变量存在(这样也可以保证只有一个存在,并且初始化在GameManager之后)
使用到的多种设计模式
:复盘一下我用过的设计模式-CSDN博客
:: Scoll View 结合 GridLayoutGroup 组件可以实现组件的整齐排列
RectTransform
RectTransform 和 Transfrom 的区别
Inspector,Awake,OnEnable与Start之间微妙的关系
Inspector 早于 Awake 早于 OnEnable 早于 Start
地图编辑器 编辑类
MapTool类
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 类
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());
}
}