CharacterManager.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System.IO;
  5. using UnityEngine.SceneManagement;
  6. #if UNITY_EDITOR
  7. using UnityEditor;
  8. #endif
  9. /// <summary>
  10. /// This script manages all aspects of a character's appearance, storing the appearance data in a Dictionary using the character's UID as the key.
  11. /// Typically, you don't need to manually update the character's appearance as it is automatically handled by "CharacterEntity.cs".
  12. /// However, if you need to modify the character's appearance independently from "CharacterEntity.cs", you can use this script.
  13. /// Additionally, this script allows save/load a character's appearance from a byte file or a PNG file.
  14. /// </summary>
  15. namespace SoftKitty.MasterCharacterCreator
  16. {
  17. public class CharacterManager : MonoBehaviour
  18. {
  19. #region variables
  20. public static CharacterManager instance;
  21. public bool useRelativePathToGameInstallFolder = true;
  22. public string CharacterBlueprintRootPath = "MccBlueprints";
  23. public bool AllowOutfitsWhenCreate = true;
  24. public bool AllowChangeSexWhenCreate = true;
  25. public bool AllowOutfitsWhenCustomize = false;
  26. public bool AllowChangeNameWhenCustomize = false;
  27. public bool AllowChangeSexWhenCustomize = false;
  28. public bool AllowChangeRaceWhenCustomize = false;
  29. public bool BackCategoryVisible = true;
  30. public bool TailCategoryVisible = true;
  31. public bool RaceSettingVisible = true;
  32. public bool CacheMeshTexture = true;
  33. public List<EmotionSetting> EmotionSettings = new List<EmotionSetting>();
  34. public Dictionary<string, CharacterBoneControl> CharacterEntityDic = new Dictionary<string, CharacterBoneControl>();
  35. public static int PhotoMode = 0;
  36. public static Dictionary<string, EmotionSetting> CharacterEmotions = new Dictionary<string, EmotionSetting>();
  37. public static Dictionary<string, Texture> LoadedTextures = new Dictionary<string, Texture>();
  38. public static Dictionary<string, Mesh> LoadedMeshes = new Dictionary<string, Mesh>();
  39. public static Dictionary<string, Material> LoadedMaterials = new Dictionary<string, Material>();
  40. public static Dictionary<string, GameObject> LoadedObjects = new Dictionary<string, GameObject>();
  41. private static Dictionary<string, CharacterAppearance> CharacterAppearanceDic = new Dictionary<string, CharacterAppearance>();
  42. private static string CustomizeUid="";
  43. private static string ExitScene="";
  44. #endregion
  45. #region internal methods
  46. private void Awake()
  47. {
  48. instance = this;
  49. CharacterEmotions.Clear();
  50. foreach (var obj in EmotionSettings) {
  51. if (!CharacterEmotions.ContainsKey(obj.uid))
  52. {
  53. CharacterEmotions.Add(obj.uid,obj);
  54. }
  55. }
  56. SetPhotoMode(0);
  57. }
  58. public string BlueprintPath
  59. {
  60. get
  61. {
  62. if (useRelativePathToGameInstallFolder)
  63. {
  64. #if UNITY_EDITOR
  65. return Application.dataPath + "/" + CharacterBlueprintRootPath;
  66. #else
  67. return Application.dataPath + "/../" + CharacterBlueprintRootPath;
  68. #endif
  69. }
  70. else
  71. {
  72. return CharacterBlueprintRootPath;
  73. }
  74. }
  75. }
  76. public static void ClearLoadedAssets()
  77. {
  78. LoadedMeshes.Clear();
  79. LoadedTextures.Clear();
  80. LoadedMaterials.Clear();
  81. LoadedObjects.Clear();
  82. }
  83. public static Texture LoadTexture(string _path)
  84. {
  85. #if UNITY_EDITOR
  86. if (!Application.isPlaying)
  87. {
  88. return (Texture)AssetDatabase.LoadAssetAtPath("Assets/SoftKitty/MasterCharacterCreator/Resources/" + _path+".png", typeof(Texture));
  89. }
  90. #endif
  91. if (!instance.CacheMeshTexture) return Resources.Load<Texture>(_path);
  92. string _key = _path.Replace(" ", "").Replace("MasterCharacterCreator/Player/", "");
  93. if (LoadedTextures.ContainsKey(_key))
  94. {
  95. return LoadedTextures[_key];
  96. }
  97. else
  98. {
  99. LoadedTextures.Add(_key, Resources.Load<Texture>(_path));
  100. return LoadedTextures[_key];
  101. }
  102. }
  103. public static Mesh LoadMesh(string _path)
  104. {
  105. #if UNITY_EDITOR
  106. if (!Application.isPlaying)
  107. {
  108. return (Mesh)AssetDatabase.LoadAssetAtPath("Assets/SoftKitty/MasterCharacterCreator/Resources/"+ _path+".mesh", typeof(Mesh));
  109. }
  110. #endif
  111. if (!instance.CacheMeshTexture) return Resources.Load<Mesh>(_path);
  112. string _key = _path.Replace(" ", "").Replace("MasterCharacterCreator/Player/", "");
  113. if (LoadedMeshes.ContainsKey(_key))
  114. {
  115. return LoadedMeshes[_key];
  116. }
  117. else
  118. {
  119. LoadedMeshes.Add(_key, Resources.Load<Mesh>(_path));
  120. return LoadedMeshes[_key];
  121. }
  122. }
  123. public static Material LoadMaterial(string _path)
  124. {
  125. #if UNITY_EDITOR
  126. if (!Application.isPlaying)
  127. {
  128. return (Material)AssetDatabase.LoadAssetAtPath("Assets/SoftKitty/MasterCharacterCreator/Resources/" + _path+".mat", typeof(Material));
  129. }
  130. #endif
  131. if (!instance.CacheMeshTexture) return Resources.Load<Material>(_path);
  132. string _key = _path.Replace(" ", "").Replace("MasterCharacterCreator/Player/", "");
  133. if (LoadedMaterials.ContainsKey(_key))
  134. {
  135. return LoadedMaterials[_key];
  136. }
  137. else
  138. {
  139. LoadedMaterials.Add(_key, Resources.Load<Material>(_path));
  140. return LoadedMaterials[_key];
  141. }
  142. }
  143. public static GameObject LoadObject(string _path)
  144. {
  145. #if UNITY_EDITOR
  146. if (!Application.isPlaying)
  147. {
  148. return (GameObject)AssetDatabase.LoadAssetAtPath("Assets/SoftKitty/MasterCharacterCreator/Resources/" + _path+".prefab", typeof(GameObject));
  149. }
  150. #endif
  151. if (!instance.CacheMeshTexture ) return Resources.Load<GameObject>(_path);
  152. string _key = _path.Replace(" ", "").Replace("MasterCharacterCreator/Player/", "");
  153. if (LoadedObjects.ContainsKey(_key))
  154. {
  155. return LoadedObjects[_key];
  156. }
  157. else
  158. {
  159. LoadedObjects.Add(_key, Resources.Load<GameObject>(_path));
  160. return LoadedObjects[_key];
  161. }
  162. }
  163. private void OnDestroy()
  164. {
  165. SetPhotoMode(0);
  166. }
  167. private CharacterBoneControl NewPreviewCharacter(Sex _sex)
  168. {
  169. return Instantiate(Resources.Load<GameObject>("MasterCharacterCreator/Player/" + (_sex == Sex.Female ? "CharacterFemale" : "CharacterMale"))).GetComponent<CharacterBoneControl>();
  170. }
  171. public static void SetPhotoMode(int _mode)
  172. {
  173. PhotoMode = _mode;
  174. Shader.SetGlobalInt("PhotoMode", PhotoMode);
  175. }
  176. public static void StartCreation(CharacterEntity _entity, string _saveRootPath, SaveMethod _saveFormat, CharacterCusSetting _setting, Scene _exitScene)
  177. {
  178. CustomizeUid = _entity.uid;
  179. CharacterCusUI.InitialData = _entity.mCharacterAppearance.Copy();
  180. CharacterCusUI.SaveRootPath = _saveRootPath;
  181. CharacterCusUI.SaveFormat = _saveFormat;
  182. CharacterCusUI.Settings = _setting;
  183. ExitScene = _exitScene.name;
  184. TransitionUI.LastScene = _exitScene;
  185. TransitionUI.NextScene = "CharacterCustomization";
  186. SceneManager.LoadScene("Transition", LoadSceneMode.Additive);
  187. }
  188. public static void OnCharacterCustomized(CharacterAppearance _data)
  189. {
  190. if (CustomizeUid != "")
  191. {
  192. UpdateCharacterAppearance(CustomizeUid, _data);
  193. CustomizeUid = "";
  194. }
  195. TransitionUI.LastScene = SceneManager.GetActiveScene();
  196. TransitionUI.NextScene = ExitScene;
  197. SceneManager.LoadScene("Transition", LoadSceneMode.Additive);
  198. }
  199. public static bool isCharacterAppearanceExist(CharacterEntity _entity)
  200. {
  201. return CharacterAppearanceDic.ContainsKey(_entity.uid);
  202. }
  203. public static CharacterAppearance GetAppearanceData(CharacterEntity _entity)
  204. {
  205. if (CharacterAppearanceDic.ContainsKey(_entity.uid))
  206. {
  207. CharacterAppearance _data = CharacterAppearanceDic[_entity.uid].Copy();
  208. return _data;
  209. }
  210. else
  211. {
  212. return null;
  213. }
  214. }
  215. public static void LoadCharacterFromResources(CharacterBoneControl _character, string _resourcePath)
  216. {
  217. TextAsset bindata = Resources.Load(_resourcePath) as TextAsset;
  218. _character.MyData.Load(bindata.bytes);
  219. }
  220. public static void LoadCharacterFromFile(CharacterBoneControl _character, string _path)
  221. {
  222. byte[] _bytes = File.ReadAllBytes(_path);
  223. _character.MyData.Load(_bytes);
  224. }
  225. #endregion
  226. /// <summary>
  227. /// Updates the appearance data of a character identified by the provided UID. Use this function if you need to modify a character's appearance independently of CharacterEntity.cs"
  228. /// </summary>
  229. /// <param name="_uid"></param>
  230. /// <param name="_data"></param>
  231. public static void UpdateCharacterAppearance(string _uid, CharacterAppearance _data)
  232. {
  233. if (_uid != "")
  234. {
  235. if (CharacterAppearanceDic.ContainsKey(_uid))
  236. {
  237. CharacterAppearanceDic[_uid] = _data.Copy();
  238. }
  239. else
  240. {
  241. CharacterAppearanceDic.Add(_uid, _data.Copy());
  242. }
  243. }
  244. }
  245. /// <summary>
  246. /// Checks if the appearance data for a character with the specified UID exists in the internal Dictionary.
  247. /// </summary>
  248. /// <param name="_uid"></param>
  249. /// <returns></returns>
  250. public static bool isCharacterAppearanceExist(string _uid)
  251. {
  252. return CharacterAppearanceDic.ContainsKey(_uid);
  253. }
  254. /// <summary>
  255. /// Retrieves the appearance data of a character using the provided UID.
  256. /// </summary>
  257. /// <param name="_uid"></param>
  258. /// <returns></returns>
  259. public static CharacterAppearance GetAppearanceData(string _uid)
  260. {
  261. if (CharacterAppearanceDic.ContainsKey(_uid))
  262. {
  263. CharacterAppearance _data = CharacterAppearanceDic[_uid].Copy();
  264. return _data;
  265. }
  266. else
  267. {
  268. return null;
  269. }
  270. }
  271. /// <summary>
  272. /// Load appearance data from a byte file located in the resources folder.
  273. /// </summary>
  274. /// <param name="_resourcePath"></param>
  275. /// <param name="_data"></param>
  276. /// <returns></returns>
  277. public static CharacterAppearance LoadCharacterDataFromResources(string _resourcePath, CharacterAppearance _data)
  278. {
  279. TextAsset bindata = Resources.Load(_resourcePath) as TextAsset;
  280. CharacterAppearance _result;
  281. if (_data == null)
  282. {
  283. _result = new CharacterAppearance(bindata.bytes);
  284. }
  285. else{
  286. _result=_data.Copy();
  287. if (bindata.bytes[0] != 3 && bindata.bytes[1] != _result._CharacterData.Sex)
  288. {
  289. SimpleDynamicMsg.PopMsg("The gender of the blurprint is different from this character.");
  290. }
  291. else
  292. {
  293. _result.Load(bindata.bytes);
  294. }
  295. }
  296. return _result;
  297. }
  298. /// <summary>
  299. /// Load appearance data from a byte file stored on disk.
  300. /// </summary>
  301. /// <param name="_data"></param>
  302. /// <param name="_path"></param>
  303. /// <returns></returns>
  304. public static CharacterAppearance LoadCharacterDataFromFile(CharacterAppearance _data, string _path)
  305. {
  306. byte[] _bytes = File.ReadAllBytes(_path);
  307. CharacterAppearance _result;
  308. if (_data!=null)
  309. _result = _data.Copy();
  310. else
  311. _result= new CharacterAppearance(CharacterData.Create(_bytes[1]));
  312. if (_bytes[0] != 3 && _bytes[1] != _result._CharacterData.Sex)
  313. {
  314. SimpleDynamicMsg.PopMsg("The gender of the blurprint is different from this character.");
  315. }
  316. else
  317. {
  318. _result.Load(_bytes);
  319. }
  320. return _result;
  321. }
  322. /// <summary>
  323. /// Load appearance data from a PNG file on disk, with the appearance data hidden inside the image.
  324. /// </summary>
  325. /// <param name="_data"></param>
  326. /// <param name="_path"></param>
  327. /// <returns></returns>
  328. public static Texture2D LoadCharacterDataWithPhoto(ref CharacterAppearance _data, string _path)
  329. {
  330. byte[] _bytes = File.ReadAllBytes(_path);
  331. Texture2D _tex = new Texture2D(256, 256, TextureFormat.RGB24, false);
  332. ImageConversion.LoadImage(_tex, _bytes);
  333. int length = _bytes[_bytes.Length - 1]+ _bytes[_bytes.Length - 2];
  334. byte[] _dataBytes = new byte[length];
  335. System.Array.Copy(_bytes, _bytes.Length - 2 - length, _dataBytes, 0, length);
  336. if (_data == null) _data = new CharacterAppearance(CharacterData.Create(_dataBytes[1]));
  337. if (_dataBytes[0] != 3 && _dataBytes[1] != _data._CharacterData.Sex)
  338. {
  339. SimpleDynamicMsg.PopMsg("The gender of the blurprint is different from this character.");
  340. }
  341. else
  342. {
  343. _data.Load(_dataBytes);
  344. }
  345. return _tex;
  346. }
  347. /// <summary>
  348. /// Save appearance data to a byte file. Use BlurPrintType to specify if you want to save the entire appearance data or just specific parts.
  349. /// </summary>
  350. /// <param name="_character"></param>
  351. /// <param name="_path"></param>
  352. /// <param name="_type"></param>
  353. public static void SaveCharacterData(CharacterBoneControl _character, string _path, BlurPrintType _type= BlurPrintType.AllAppearance)
  354. {
  355. string filePath = Path.ChangeExtension(_path, ".bytes");
  356. string dir = Path.GetDirectoryName(filePath);
  357. if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
  358. if (File.Exists(filePath))File.Delete(filePath);
  359. File.WriteAllBytes(filePath, _character.MyData.ToBytes(_type));
  360. }
  361. /// <summary>
  362. /// Save appearance data to a PNG file. The data is embedded within the image file, with the option to limit saved data to certain parts using BlurPrintType.
  363. /// </summary>
  364. /// <param name="_character"></param>
  365. /// <param name="_photo"></param>
  366. /// <param name="_path"></param>
  367. /// <param name="_type"></param>
  368. public static void SaveCharacterDataWithPhoto(CharacterBoneControl _character, Texture2D _photo, string _path, BlurPrintType _type = BlurPrintType.AllAppearance)
  369. {
  370. string filePath = Path.ChangeExtension(_path, ".png");
  371. string dir = Path.GetDirectoryName(filePath);
  372. if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
  373. if (File.Exists(filePath)) File.Delete(filePath);
  374. byte[] _bytes = _character.MyData.ToBytes(_type);
  375. List<byte> _finalBytes = new List<byte>();
  376. byte[] photoBytes = _photo.EncodeToPNG();
  377. _finalBytes.AddRange(photoBytes);
  378. _finalBytes.AddRange(_bytes);
  379. int _length1 = Mathf.Min(_bytes.Length,200);
  380. int _length2 = Mathf.Max(_bytes.Length - _length1,0);
  381. _finalBytes.Add((byte)_length1);
  382. _finalBytes.Add((byte)_length2);
  383. File.WriteAllBytes(filePath, _finalBytes.ToArray());
  384. }
  385. /// <summary>
  386. /// [For developers] Save appearance data to a byte file in the resources folder. Use BlurPrintType if you intend to save only specific sections of the data.
  387. /// </summary>
  388. /// <param name="_character"></param>
  389. /// <param name="_resourcePath"></param>
  390. /// <param name="_type"></param>
  391. public static void SaveCharacterDataToResources(CharacterBoneControl _character, string _resourcePath, BlurPrintType _type = BlurPrintType.AllAppearance)
  392. {
  393. string filePath = _resourcePath+ ".bytes";
  394. string dir = Path.GetDirectoryName(filePath);
  395. if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
  396. if (File.Exists(filePath)) File.Delete(filePath);
  397. File.WriteAllBytes(filePath, _character.MyData.ToBytes(_type));
  398. }
  399. /// These functions allow you to manage preview characters, which is useful for displaying a character¡¯s current appearance within various UI interfaces
  400. /// (e.g., rendering the player¡¯s appearance to a RenderTexture for display in an equipment or customization interface)
  401. /// <summary>
  402. /// Creates a preview character at a specified position and angle. The key is used to identify this character for further operations
  403. /// </summary>
  404. /// <param name="_key"></param>
  405. /// <param name="_sex"></param>
  406. /// <param name="_position"></param>
  407. /// <param name="_angle"></param>
  408. /// <returns></returns>
  409. public CharacterBoneControl CreatePreviewCharacter(string _key, Sex _sex, Vector3 _position, float _angle)
  410. {
  411. if (GetPreviewCharacter(_key)!=null)return GetPreviewCharacter(_key);
  412. CharacterBoneControl _newChar = NewPreviewCharacter(_sex);
  413. _newChar.transform.position = _position;
  414. _newChar.transform.eulerAngles = new Vector3(0F, _angle,0F);
  415. _newChar.transform.localScale = Vector3.one;
  416. CharacterEntityDic.Add(_key, _newChar);
  417. return _newChar;
  418. }
  419. /// <summary>
  420. /// Create a preview character
  421. /// </summary>
  422. /// <param name="_key"></param>
  423. /// <param name="_sex"></param>
  424. /// <param name="_position"></param>
  425. /// <param name="_rotation"></param>
  426. /// <returns></returns>
  427. public CharacterBoneControl CreatePreviewCharacter(string _key, Sex _sex, Vector3 _position, Quaternion _rotation)
  428. {
  429. if (GetPreviewCharacter(_key) != null) return GetPreviewCharacter(_key);
  430. CharacterBoneControl _newChar = NewPreviewCharacter(_sex);
  431. _newChar.transform.position = _position;
  432. _newChar.transform.rotation = _rotation;
  433. _newChar.transform.localScale = Vector3.one;
  434. CharacterEntityDic.Add(_key, _newChar);
  435. return _newChar;
  436. }
  437. /// <summary>
  438. /// Create a preview character
  439. /// </summary>
  440. /// <param name="_key"></param>
  441. /// <param name="_sex"></param>
  442. /// <param name="_position"></param>
  443. /// <returns></returns>
  444. public CharacterBoneControl CreatePreviewCharacter(string _key, Sex _sex, Vector3 _position)
  445. {
  446. if (GetPreviewCharacter(_key) != null) return GetPreviewCharacter(_key);
  447. CharacterBoneControl _newChar = NewPreviewCharacter(_sex);
  448. _newChar.transform.position = _position;
  449. _newChar.transform.eulerAngles = Vector3.zero;
  450. _newChar.transform.localScale = Vector3.one;
  451. CharacterEntityDic.Add(_key, _newChar);
  452. return _newChar;
  453. }
  454. /// <summary>
  455. /// Create a preview character
  456. /// </summary>
  457. /// <param name="_key"></param>
  458. /// <param name="_sex"></param>
  459. /// <returns></returns>
  460. public CharacterBoneControl CreatePreviewCharacter(string _key, Sex _sex)
  461. {
  462. if (GetPreviewCharacter(_key) != null) return GetPreviewCharacter(_key);
  463. CharacterBoneControl _newChar = NewPreviewCharacter(_sex);
  464. _newChar.transform.position = Vector3.zero;
  465. _newChar.transform.eulerAngles = Vector3.zero;
  466. _newChar.transform.localScale = Vector3.one;
  467. CharacterEntityDic.Add(_key, _newChar);
  468. return _newChar;
  469. }
  470. /// <summary>
  471. /// Create a preview character
  472. /// </summary>
  473. /// <param name="_sex"></param>
  474. /// <returns></returns>
  475. public CharacterBoneControl CreatePreviewCharacter(Sex _sex)
  476. {
  477. CharacterBoneControl _newChar = NewPreviewCharacter(_sex);
  478. _newChar.transform.localScale = Vector3.one;
  479. return _newChar;
  480. }
  481. /// <summary>
  482. /// Retrieves a preview character using the provided key. Once retrieved, you can call CharacterBoneControl().Initialize(CharacterAppearance _data) to apply appearance data to the preview character.
  483. /// </summary>
  484. /// <param name="_key"></param>
  485. /// <returns></returns>
  486. public CharacterBoneControl GetPreviewCharacter(string _key)
  487. {
  488. if (CharacterEntityDic.ContainsKey(_key))
  489. {
  490. if (CharacterEntityDic[_key] != null)
  491. {
  492. return CharacterEntityDic[_key];
  493. }
  494. else
  495. {
  496. CharacterEntityDic.Remove(_key);
  497. }
  498. }
  499. return null;
  500. }
  501. /// <summary>
  502. /// Removes the preview character identified by the provided key
  503. /// </summary>
  504. /// <param name="_key"></param>
  505. public void RemovePreviewCharacter(string _key)
  506. {
  507. if (GetPreviewCharacter(_key) != null) Destroy(GetPreviewCharacter(_key).gameObject);
  508. if (CharacterEntityDic.ContainsKey(_key)) CharacterEntityDic.Remove(_key);
  509. }
  510. }
  511. }