NpcSetposTool.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Text.RegularExpressions;
  5. using System.IO;
  6. using UnityEngine;
  7. using UnityEditor;
  8. using UnityEditor.SceneManagement;
  9. using OfficeOpenXml;
  10. using Debug = UnityEngine.Debug;
  11. public class NpcSetposTool : EditorWindow
  12. {
  13. private class NpcInfo
  14. {
  15. public int id = 0;
  16. public string name;
  17. public int avatarId = 0;
  18. public Vector3 position;
  19. public Vector3 rotation;
  20. public Transform npcRoot;
  21. public Transform model;
  22. public void CreateNpc()
  23. {
  24. if (!npcRoot)
  25. {
  26. name = ConfigMgr.GetInstance().getValue(avatarId, "Name", "AvatarCfg");
  27. GameObject npcGo = new GameObject(name + "(" + id + ")");
  28. npcRoot = npcGo.transform;
  29. npcRoot.position = position;
  30. npcRoot.rotation = Quaternion.Euler(rotation);
  31. }
  32. if (!model)
  33. {
  34. string prefab = ConfigMgr.GetInstance().getValue(avatarId, "AvatarPrefab", "AvatarCfg");
  35. GameObject go = AssetDatabase.LoadAssetAtPath<GameObject>(Path.Combine(Constants.ModelPath, prefab + ".prefab"));
  36. if (go)
  37. {
  38. GameObject modelGo = GameObject.Instantiate(go);
  39. model = modelGo.transform;
  40. model.parent = npcRoot;
  41. model.localScale = Vector3.one;
  42. model.localPosition = Vector3.zero;
  43. model.localRotation = Quaternion.identity;
  44. var colliders = modelGo.GetComponentsInChildren<Collider>();
  45. foreach(var collider in colliders)
  46. {
  47. GameObject.DestroyImmediate(collider);
  48. }
  49. }
  50. }
  51. }
  52. public void SaveNpc()
  53. {
  54. if (npcRoot)
  55. {
  56. position = npcRoot.position;
  57. rotation.y = npcRoot.rotation.eulerAngles.y;
  58. }
  59. }
  60. public void DeleteNpc()
  61. {
  62. if (npcRoot)
  63. {
  64. GameObject.DestroyImmediate(npcRoot.gameObject);
  65. }
  66. }
  67. }
  68. private const string c_MainCityScenePath = "Assets/Scenes/Scene_prontera_01/Scene_prontera_01.unity";
  69. private const string c_NPCExcelFilePathKey = "NPCExcelFilePathKey";
  70. private const string c_GlobalExcelFilePathKey = "GlobalExcelFilePathKey";
  71. private const string c_NPCDisableRadiusKey = "c_NPCDisableRadiusKey";
  72. private const string c_NPCCollisionRadiusKey = "c_NPCCollisionRadiusKey";
  73. private const string c_NPCTriggerRadiusKey = "c_NPCTriggerRadiusKey";
  74. private const string c_NPCExcelFileName = "MainCityNpcCfg.xlsx";
  75. private const string c_GlobalExcelFileName = "GlobalCfg.xlsx";
  76. private const string c_ExcelFileExtension = "xlsx";
  77. private const string c_NPCSetposSceneName = "NPC设置场景";
  78. private const string c_NPCRootGoName = "NPC";
  79. private bool m_NPCDisableRadiusShow = true;
  80. private bool m_NPCCollisionRadiusShow = true;
  81. private bool m_NPCTriggerRadiusShow = true;
  82. private float m_DiableRadiusDefault = 1;
  83. private float m_CollisionRadiusDefault = 1;
  84. private float m_TriggerRadiusDefault = 1;
  85. private float m_DiableRadius = 1;
  86. private float m_CollisionRadius = 1;
  87. private float m_TriggerRadius = 1;
  88. private Dictionary<int, NpcInfo> m_NpcInfos = new Dictionary<int, NpcInfo>();
  89. private void OnEnable() {
  90. SceneView.onSceneGUIDelegate += OnSceneGUI;
  91. var scene = EditorSceneManager.GetSceneByName(c_NPCSetposSceneName);
  92. EditorSceneManager.CloseScene(scene, true);
  93. m_NPCDisableRadiusShow = EditorPrefs.GetBool(c_NPCDisableRadiusKey, true);
  94. m_NPCCollisionRadiusShow = EditorPrefs.GetBool(c_NPCCollisionRadiusKey, true);
  95. m_NPCTriggerRadiusShow = EditorPrefs.GetBool(c_NPCTriggerRadiusKey, true);
  96. }
  97. private void OnDisable() {
  98. SceneView.onSceneGUIDelegate -= OnSceneGUI;
  99. var scene = EditorSceneManager.GetSceneByName(c_NPCSetposSceneName);
  100. EditorSceneManager.CloseScene(scene, true);
  101. }
  102. private void OnSceneGUI(SceneView sceneView)
  103. {
  104. if (m_NpcInfos.Count <= 0 ) return;
  105. Rect rect = sceneView.position;
  106. Handles.BeginGUI();
  107. GUILayout.BeginArea(new Rect(rect.width - 200, rect.height - 100, 200, 100), "设置");
  108. GUILayout.BeginVertical();
  109. DrawParamItem(ref m_NPCDisableRadiusShow, ref m_DiableRadius, c_NPCDisableRadiusKey, "是否显示不可摆摊区域");
  110. DrawParamItem(ref m_NPCCollisionRadiusShow, ref m_CollisionRadius, c_NPCCollisionRadiusKey, "是否显示碰撞区域");
  111. DrawParamItem(ref m_NPCTriggerRadiusShow, ref m_TriggerRadius, c_NPCTriggerRadiusKey, "是否显示触发事件区域");
  112. GUILayout.EndVertical();
  113. GUILayout.EndArea();
  114. Handles.EndGUI();
  115. foreach(var item in m_NpcInfos)
  116. {
  117. var npcinfo = item.Value;
  118. if (npcinfo != null)
  119. {
  120. Vector3 rotation = npcinfo.npcRoot.rotation.eulerAngles;
  121. if (!Mathf.Approximately(0f, rotation.x) || !Mathf.Approximately(0f, rotation.z))
  122. {
  123. rotation.x = 0f;
  124. rotation.z = 0f;
  125. npcinfo.npcRoot.rotation = Quaternion.Euler(rotation);
  126. }
  127. if (m_NPCDisableRadiusShow) DrawCylinder(npcinfo.npcRoot.position, m_DiableRadius, Color.red);
  128. if (m_NPCCollisionRadiusShow) DrawCylinder(npcinfo.npcRoot.position, m_CollisionRadius, Color.green);
  129. if (m_NPCTriggerRadiusShow) DrawCylinder(npcinfo.npcRoot.position, m_TriggerRadius, Color.blue);
  130. }
  131. }
  132. }
  133. private void DrawParamItem(ref bool show, ref float radius, string key, string content)
  134. {
  135. GUILayout.BeginHorizontal();
  136. bool showTemp = GUILayout.Toggle(show, content);
  137. if (showTemp != show)
  138. {
  139. show = showTemp;
  140. EditorPrefs.SetBool(key, show);
  141. }
  142. radius = EditorGUILayout.FloatField(radius);
  143. GUILayout.EndHorizontal();
  144. }
  145. private void DrawCylinder(Vector3 position, float radius, Color color)
  146. {
  147. Color defaultColor = Handles.color;
  148. Handles.color = color;
  149. Vector3 offset = new Vector3(0, 2, 0);
  150. Vector3 top = position + offset;
  151. Vector3 center = position + offset * 0.5f;
  152. Vector3 bottom = position;
  153. Vector3 point1 = top + Vector3.left * radius;
  154. Vector3 point2 = top + Vector3.right * radius;
  155. Vector3 point3 = top + Vector3.forward * radius;
  156. Vector3 point4 = top + Vector3.back * radius;
  157. Vector3 point5 = bottom + Vector3.left * radius;
  158. Vector3 point6 = bottom + Vector3.right * radius;
  159. Vector3 point7 = bottom + Vector3.forward * radius;
  160. Vector3 point8 = bottom + Vector3.back * radius;
  161. Handles.DrawWireDisc(top, Vector3.up, radius);
  162. Handles.DrawWireDisc(center, Vector3.up, radius);
  163. Handles.DrawWireDisc(bottom, Vector3.up, radius);
  164. Handles.DrawLine(point1, point5);
  165. Handles.DrawLine(point2, point6);
  166. Handles.DrawLine(point3, point7);
  167. Handles.DrawLine(point4, point8);
  168. Handles.color = defaultColor;
  169. }
  170. private void OnGUI() {
  171. if (EditorApplication.isPlaying)
  172. {
  173. GUILayout.FlexibleSpace();
  174. EditorGUILayout.HelpBox("不可在运行环境下编辑", MessageType.Warning);
  175. GUILayout.FlexibleSpace();
  176. return;
  177. }
  178. if (!IsMainCityScene())
  179. {
  180. GUILayout.FlexibleSpace();
  181. EditorGUILayout.HelpBox("需要先打开主城", MessageType.Info);
  182. if (GUILayout.Button("打开主城场景"))
  183. {
  184. OpenMainCityScene();
  185. }
  186. GUILayout.FlexibleSpace();
  187. return;
  188. }
  189. GUILayout.FlexibleSpace();
  190. if (GUILayout.Button("读取数据"))
  191. {
  192. bool success = ReadGlobalExcelData();
  193. if (success) success = ReadNPCExcelData();
  194. }
  195. EditorGUILayout.Space();
  196. EditorGUILayout.Space();
  197. if (GUILayout.Button("保存数据"))
  198. {
  199. if (m_NpcInfos.Count > 0)
  200. {
  201. bool success = SaveGlobalExcelData();
  202. if (success) success = SaveNPCExcelData();
  203. }
  204. }
  205. GUILayout.FlexibleSpace();
  206. }
  207. private bool IsMainCityScene()
  208. {
  209. return EditorSceneManager.sceneCount == 2
  210. && EditorSceneManager.GetSceneAt(0).path == c_MainCityScenePath
  211. && EditorSceneManager.GetActiveScene().name == c_NPCSetposSceneName;
  212. }
  213. private void OpenMainCityScene()
  214. {
  215. EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
  216. EditorSceneManager.OpenScene(c_MainCityScenePath, OpenSceneMode.Single);
  217. var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive);
  218. scene.name = c_NPCSetposSceneName;
  219. EditorSceneManager.SetActiveScene(scene);
  220. }
  221. private bool HasNpcDatas()
  222. {
  223. return false;
  224. }
  225. private bool IsVaildGlobalPath(string path)
  226. {
  227. return (!string.IsNullOrEmpty(path)
  228. && File.Exists(path)
  229. && Path.GetFileName(path) == c_GlobalExcelFileName);
  230. }
  231. private bool IsVaildNPCPath(string path)
  232. {
  233. return (!string.IsNullOrEmpty(path)
  234. && File.Exists(path)
  235. && Path.GetFileName(path) == c_NPCExcelFileName);
  236. }
  237. private bool ReadGlobalExcelData()
  238. {
  239. bool success = true;
  240. try
  241. {
  242. string filePath = EditorPrefs.GetString(c_GlobalExcelFilePathKey, string.Empty);
  243. if (!IsVaildGlobalPath(filePath))
  244. {
  245. string content = string.Concat("请选择", c_GlobalExcelFileName, "文件");
  246. string[] filters = new string[2]
  247. {
  248. "Excel File", c_ExcelFileExtension
  249. };
  250. filePath = EditorUtility.OpenFilePanelWithFilters(content, Application.dataPath, filters);
  251. if (!IsVaildGlobalPath(filePath))
  252. {
  253. EditorUtility.DisplayDialog("错误", string.Concat("未选择", c_GlobalExcelFileName, "文件"), "确定");
  254. return false;
  255. }
  256. EditorPrefs.SetString(c_GlobalExcelFilePathKey, filePath);
  257. }
  258. FileInfo fileInfo = new FileInfo(filePath);
  259. // using (var fs = new FileStream(filePath, FileMode.Open))
  260. {
  261. using(var excelPackage = new ExcelPackage(fileInfo))
  262. {
  263. var sheets = excelPackage.Workbook.Worksheets;
  264. var sheet = sheets[1];
  265. int rowIndex = 4;
  266. int colIndex = 1;
  267. while(!string.IsNullOrEmpty(sheet.GetValue<string>(rowIndex, colIndex)))
  268. {
  269. int id = sheet.GetValue<int>(rowIndex, 1);
  270. if (id == 34)
  271. {
  272. m_DiableRadius = m_DiableRadiusDefault = sheet.GetValue<float>(rowIndex, 5);
  273. }
  274. else if (id == 35)
  275. {
  276. m_CollisionRadius = m_CollisionRadiusDefault = sheet.GetValue<float>(rowIndex, 5);
  277. }
  278. else if (id == 36)
  279. {
  280. m_TriggerRadius = m_TriggerRadiusDefault = sheet.GetValue<float>(rowIndex, 5);
  281. }
  282. rowIndex += 1;
  283. }
  284. }
  285. }
  286. }
  287. catch (Exception e)
  288. {
  289. Debug.LogException(e);
  290. EditorUtility.DisplayDialog("错误", string.Concat("无法读取", c_GlobalExcelFileName, "文件数据"), "确定");
  291. success = false;
  292. }
  293. return success;
  294. }
  295. private bool ReadNPCExcelData()
  296. {
  297. bool success = true;
  298. try
  299. {
  300. string filePath = EditorPrefs.GetString(c_NPCExcelFilePathKey, string.Empty);
  301. if (!IsVaildNPCPath(filePath))
  302. {
  303. string content = string.Concat("请选择", c_NPCExcelFileName, "文件");
  304. string[] filters = new string[2]
  305. {
  306. "Excel File", c_ExcelFileExtension
  307. };
  308. filePath = EditorUtility.OpenFilePanelWithFilters(content, Application.dataPath, filters);
  309. if (!IsVaildNPCPath(filePath))
  310. {
  311. EditorUtility.DisplayDialog("错误", string.Concat("未选择", c_NPCExcelFileName, "文件"), "确定");
  312. return false;
  313. }
  314. EditorPrefs.SetString(c_NPCExcelFilePathKey, filePath);
  315. }
  316. foreach(var item in m_NpcInfos)
  317. {
  318. item.Value.DeleteNpc();
  319. }
  320. m_NpcInfos.Clear();
  321. EditorUtility.DisplayProgressBar("加载", "加载配置中", 0f);
  322. FileInfo fileInfo = new FileInfo(filePath);
  323. // using (var fs = new FileStream(filePath, FileMode.Open))
  324. {
  325. using(var excelPackage = new ExcelPackage(fileInfo))
  326. {
  327. var sheets = excelPackage.Workbook.Worksheets;
  328. var sheet = sheets[1];
  329. int rowIndex = 4;
  330. int colIndex = 1;
  331. while(!string.IsNullOrEmpty(sheet.GetValue<string>(rowIndex, colIndex)))
  332. {
  333. NpcInfo npcInfo = new NpcInfo();
  334. npcInfo.id = sheet.GetValue<int>(rowIndex, 1);
  335. string positionStr = sheet.GetValue<string>(rowIndex, 2);
  336. Vector3 position = Vector3.zero;
  337. if (!string.IsNullOrEmpty(positionStr))
  338. {
  339. var positionArr = positionStr.Split(';');
  340. if (positionArr.Length > 0) position.x = float.Parse(positionArr[0]);
  341. if (positionArr.Length > 1) position.z = float.Parse(positionArr[1]);
  342. if (positionArr.Length > 2) position.y = float.Parse(positionArr[2]);
  343. }
  344. npcInfo.position = position;
  345. float rotationY = sheet.GetValue<float>(rowIndex, 3);
  346. npcInfo.rotation = new Vector3(0, rotationY, 0);
  347. npcInfo.avatarId = sheet.GetValue<int>(rowIndex, 4);
  348. rowIndex += 1;
  349. EditorUtility.DisplayProgressBar("加载", "加载配置中", (float)rowIndex / sheet.Cells.Rows);
  350. npcInfo.CreateNpc();
  351. m_NpcInfos.Add(npcInfo.id, npcInfo);
  352. }
  353. }
  354. }
  355. EditorUtility.ClearProgressBar();
  356. }
  357. catch (Exception e)
  358. {
  359. Debug.LogException(e);
  360. EditorUtility.ClearProgressBar();
  361. EditorUtility.DisplayDialog("错误", string.Concat("无法读取", c_NPCExcelFileName, "文件数据"), "确定");
  362. success = false;
  363. }
  364. return success;
  365. }
  366. private bool SaveGlobalExcelData()
  367. {
  368. if (m_DiableRadius == m_DiableRadiusDefault
  369. && m_CollisionRadius == m_CollisionRadiusDefault
  370. && m_TriggerRadius == m_TriggerRadiusDefault)
  371. return true;
  372. bool success = true;
  373. try
  374. {
  375. string filePath = EditorPrefs.GetString(c_GlobalExcelFilePathKey, string.Empty);
  376. if (!IsVaildGlobalPath(filePath))
  377. {
  378. string content = string.Concat("请选择", c_GlobalExcelFileName, "文件");
  379. string[] filters = new string[2]
  380. {
  381. "Excel File", c_ExcelFileExtension
  382. };
  383. filePath = EditorUtility.OpenFilePanelWithFilters(content, Application.dataPath, filters);
  384. if (!IsVaildGlobalPath(filePath))
  385. {
  386. EditorUtility.DisplayDialog("错误", string.Concat("未选择", c_GlobalExcelFileName, "文件"), "确定");
  387. return false;
  388. }
  389. EditorPrefs.SetString(c_GlobalExcelFilePathKey, filePath);
  390. }
  391. FileInfo fileInfo = new FileInfo(filePath);
  392. // using (var fs = new FileStream(filePath, FileMode.Open))
  393. {
  394. using(var excelPackage = new ExcelPackage(fileInfo))
  395. {
  396. var sheets = excelPackage.Workbook.Worksheets;
  397. var sheet = sheets[1];
  398. int rowIndex = 4;
  399. int colIndex = 1;
  400. while(!string.IsNullOrEmpty(sheet.GetValue<string>(rowIndex, colIndex)))
  401. {
  402. int id = sheet.GetValue<int>(rowIndex, 1);
  403. if (id == 34)
  404. {
  405. sheet.SetValue(rowIndex, 5, m_DiableRadius);
  406. m_DiableRadiusDefault = m_DiableRadius;
  407. }
  408. else if (id == 35)
  409. {
  410. sheet.SetValue(rowIndex, 5, m_CollisionRadius);
  411. m_CollisionRadiusDefault = m_CollisionRadius;
  412. }
  413. else if (id == 36)
  414. {
  415. sheet.SetValue(rowIndex, 5, m_TriggerRadius);
  416. m_TriggerRadiusDefault = m_TriggerRadius;
  417. }
  418. rowIndex += 1;
  419. }
  420. excelPackage.SaveAs(fileInfo);
  421. }
  422. }
  423. }
  424. catch (Exception e)
  425. {
  426. Debug.LogException(e);
  427. EditorUtility.DisplayDialog("错误", string.Concat("有其它程序在使用该文件", c_GlobalExcelFileName, ", 请关闭"), "确定");
  428. success = false;
  429. }
  430. return success;
  431. }
  432. private bool SaveNPCExcelData()
  433. {
  434. bool success = true;
  435. try
  436. {
  437. string filePath = EditorPrefs.GetString(c_NPCExcelFilePathKey, string.Empty);
  438. if (!IsVaildNPCPath(filePath))
  439. {
  440. string content = string.Concat("请选择", c_NPCExcelFileName, "文件");
  441. string[] filters = new string[2]
  442. {
  443. "Excel File", c_ExcelFileExtension
  444. };
  445. filePath = EditorUtility.OpenFilePanelWithFilters(content, Application.dataPath, filters);
  446. if (!IsVaildNPCPath(filePath))
  447. {
  448. EditorUtility.DisplayDialog("错误", string.Concat("未选择", c_NPCExcelFileName, "文件"), "确定");
  449. return false;
  450. }
  451. EditorPrefs.SetString(c_NPCExcelFilePathKey, filePath);
  452. }
  453. FileInfo fileInfo = new FileInfo(filePath);
  454. // using (var fs = new FileStream(filePath, FileMode.Open))
  455. {
  456. using(var excelPackage = new ExcelPackage(fileInfo))
  457. {
  458. var sheets = excelPackage.Workbook.Worksheets;
  459. var sheet = sheets[1];
  460. int rowIndex = 4;
  461. int colIndex = 1;
  462. while(!string.IsNullOrEmpty(sheet.GetValue<string>(rowIndex, colIndex)))
  463. {
  464. int id = sheet.GetValue<int>(rowIndex, 1);
  465. if (m_NpcInfos.ContainsKey(id))
  466. {
  467. NpcInfo npcInfo = m_NpcInfos[id];
  468. npcInfo.SaveNpc();
  469. string positionStr = npcInfo.position.x + ";" + npcInfo.position.z + ";" + npcInfo.position.y;
  470. sheet.SetValue(rowIndex, 2, positionStr);
  471. sheet.SetValue(rowIndex, 3, npcInfo.rotation.y);
  472. rowIndex += 1;
  473. }
  474. }
  475. excelPackage.SaveAs(fileInfo);
  476. }
  477. }
  478. }
  479. catch (Exception e)
  480. {
  481. Debug.LogException(e);
  482. EditorUtility.DisplayDialog("错误", string.Concat("有其它程序在使用该文件", c_NPCExcelFileName, ", 请关闭"), "确定");
  483. success = false;
  484. }
  485. return success;
  486. }
  487. [MenuItem("RO_Tool/主城Npc放置工具")]
  488. private static void OpenNpcSetposTool()
  489. {
  490. var window = EditorWindow.GetWindow<NpcSetposTool>("主城Npc放置");
  491. }
  492. }