using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using UnityEngine.SceneManagement; #if UNITY_EDITOR using UnityEditor; #endif /// /// 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. /// Typically, you don't need to manually update the character's appearance as it is automatically handled by "CharacterEntity.cs". /// However, if you need to modify the character's appearance independently from "CharacterEntity.cs", you can use this script. /// Additionally, this script allows save/load a character's appearance from a byte file or a PNG file. /// namespace SoftKitty.MasterCharacterCreator { public class CharacterManager : MonoBehaviour { #region variables public static CharacterManager instance; public bool useRelativePathToGameInstallFolder = true; public string CharacterBlueprintRootPath = "MccBlueprints"; public bool AllowOutfitsWhenCreate = true; public bool AllowChangeSexWhenCreate = true; public bool AllowOutfitsWhenCustomize = false; public bool AllowChangeNameWhenCustomize = false; public bool AllowChangeSexWhenCustomize = false; public bool AllowChangeRaceWhenCustomize = false; public bool BackCategoryVisible = true; public bool TailCategoryVisible = true; public bool RaceSettingVisible = true; public bool CacheMeshTexture = true; public List EmotionSettings = new List(); public Dictionary CharacterEntityDic = new Dictionary(); public static int PhotoMode = 0; public static Dictionary CharacterEmotions = new Dictionary(); public static Dictionary LoadedTextures = new Dictionary(); public static Dictionary LoadedMeshes = new Dictionary(); public static Dictionary LoadedMaterials = new Dictionary(); public static Dictionary LoadedObjects = new Dictionary(); private static Dictionary CharacterAppearanceDic = new Dictionary(); private static string CustomizeUid=""; private static string ExitScene=""; #endregion #region internal methods private void Awake() { instance = this; CharacterEmotions.Clear(); foreach (var obj in EmotionSettings) { if (!CharacterEmotions.ContainsKey(obj.uid)) { CharacterEmotions.Add(obj.uid,obj); } } SetPhotoMode(0); } public string BlueprintPath { get { if (useRelativePathToGameInstallFolder) { #if UNITY_EDITOR return Application.dataPath + "/" + CharacterBlueprintRootPath; #else return Application.dataPath + "/../" + CharacterBlueprintRootPath; #endif } else { return CharacterBlueprintRootPath; } } } public static void ClearLoadedAssets() { LoadedMeshes.Clear(); LoadedTextures.Clear(); LoadedMaterials.Clear(); LoadedObjects.Clear(); } public static Texture LoadTexture(string _path) { #if UNITY_EDITOR if (!Application.isPlaying) { return (Texture)AssetDatabase.LoadAssetAtPath("Assets/SoftKitty/MasterCharacterCreator/Resources/" + _path+".png", typeof(Texture)); } #endif if (!instance.CacheMeshTexture) return Resources.Load(_path); string _key = _path.Replace(" ", "").Replace("MasterCharacterCreator/Player/", ""); if (LoadedTextures.ContainsKey(_key)) { return LoadedTextures[_key]; } else { LoadedTextures.Add(_key, Resources.Load(_path)); return LoadedTextures[_key]; } } public static Mesh LoadMesh(string _path) { #if UNITY_EDITOR if (!Application.isPlaying) { return (Mesh)AssetDatabase.LoadAssetAtPath("Assets/SoftKitty/MasterCharacterCreator/Resources/"+ _path+".mesh", typeof(Mesh)); } #endif if (!instance.CacheMeshTexture) return Resources.Load(_path); string _key = _path.Replace(" ", "").Replace("MasterCharacterCreator/Player/", ""); if (LoadedMeshes.ContainsKey(_key)) { return LoadedMeshes[_key]; } else { LoadedMeshes.Add(_key, Resources.Load(_path)); return LoadedMeshes[_key]; } } public static Material LoadMaterial(string _path) { #if UNITY_EDITOR if (!Application.isPlaying) { return (Material)AssetDatabase.LoadAssetAtPath("Assets/SoftKitty/MasterCharacterCreator/Resources/" + _path+".mat", typeof(Material)); } #endif if (!instance.CacheMeshTexture) return Resources.Load(_path); string _key = _path.Replace(" ", "").Replace("MasterCharacterCreator/Player/", ""); if (LoadedMaterials.ContainsKey(_key)) { return LoadedMaterials[_key]; } else { LoadedMaterials.Add(_key, Resources.Load(_path)); return LoadedMaterials[_key]; } } public static GameObject LoadObject(string _path) { #if UNITY_EDITOR if (!Application.isPlaying) { return (GameObject)AssetDatabase.LoadAssetAtPath("Assets/SoftKitty/MasterCharacterCreator/Resources/" + _path+".prefab", typeof(GameObject)); } #endif if (!instance.CacheMeshTexture ) return Resources.Load(_path); string _key = _path.Replace(" ", "").Replace("MasterCharacterCreator/Player/", ""); if (LoadedObjects.ContainsKey(_key)) { return LoadedObjects[_key]; } else { LoadedObjects.Add(_key, Resources.Load(_path)); return LoadedObjects[_key]; } } private void OnDestroy() { SetPhotoMode(0); } private CharacterBoneControl NewPreviewCharacter(Sex _sex) { return Instantiate(Resources.Load("MasterCharacterCreator/Player/" + (_sex == Sex.Female ? "CharacterFemale" : "CharacterMale"))).GetComponent(); } public static void SetPhotoMode(int _mode) { PhotoMode = _mode; Shader.SetGlobalInt("PhotoMode", PhotoMode); } public static void StartCreation(CharacterEntity _entity, string _saveRootPath, SaveMethod _saveFormat, CharacterCusSetting _setting, Scene _exitScene) { CustomizeUid = _entity.uid; CharacterCusUI.InitialData = _entity.mCharacterAppearance.Copy(); CharacterCusUI.SaveRootPath = _saveRootPath; CharacterCusUI.SaveFormat = _saveFormat; CharacterCusUI.Settings = _setting; ExitScene = _exitScene.name; TransitionUI.LastScene = _exitScene; TransitionUI.NextScene = "CharacterCustomization"; SceneManager.LoadScene("Transition", LoadSceneMode.Additive); } public static void OnCharacterCustomized(CharacterAppearance _data) { if (CustomizeUid != "") { UpdateCharacterAppearance(CustomizeUid, _data); CustomizeUid = ""; } TransitionUI.LastScene = SceneManager.GetActiveScene(); TransitionUI.NextScene = ExitScene; SceneManager.LoadScene("Transition", LoadSceneMode.Additive); } public static bool isCharacterAppearanceExist(CharacterEntity _entity) { return CharacterAppearanceDic.ContainsKey(_entity.uid); } public static CharacterAppearance GetAppearanceData(CharacterEntity _entity) { if (CharacterAppearanceDic.ContainsKey(_entity.uid)) { CharacterAppearance _data = CharacterAppearanceDic[_entity.uid].Copy(); return _data; } else { return null; } } public static void LoadCharacterFromResources(CharacterBoneControl _character, string _resourcePath) { TextAsset bindata = Resources.Load(_resourcePath) as TextAsset; _character.MyData.Load(bindata.bytes); } public static void LoadCharacterFromFile(CharacterBoneControl _character, string _path) { byte[] _bytes = File.ReadAllBytes(_path); _character.MyData.Load(_bytes); } #endregion /// /// 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" /// /// /// public static void UpdateCharacterAppearance(string _uid, CharacterAppearance _data) { if (_uid != "") { if (CharacterAppearanceDic.ContainsKey(_uid)) { CharacterAppearanceDic[_uid] = _data.Copy(); } else { CharacterAppearanceDic.Add(_uid, _data.Copy()); } } } /// /// Checks if the appearance data for a character with the specified UID exists in the internal Dictionary. /// /// /// public static bool isCharacterAppearanceExist(string _uid) { return CharacterAppearanceDic.ContainsKey(_uid); } /// /// Retrieves the appearance data of a character using the provided UID. /// /// /// public static CharacterAppearance GetAppearanceData(string _uid) { if (CharacterAppearanceDic.ContainsKey(_uid)) { CharacterAppearance _data = CharacterAppearanceDic[_uid].Copy(); return _data; } else { return null; } } /// /// Load appearance data from a byte file located in the resources folder. /// /// /// /// public static CharacterAppearance LoadCharacterDataFromResources(string _resourcePath, CharacterAppearance _data) { TextAsset bindata = Resources.Load(_resourcePath) as TextAsset; CharacterAppearance _result; if (_data == null) { _result = new CharacterAppearance(bindata.bytes); } else{ _result=_data.Copy(); if (bindata.bytes[0] != 3 && bindata.bytes[1] != _result._CharacterData.Sex) { SimpleDynamicMsg.PopMsg("The gender of the blurprint is different from this character."); } else { _result.Load(bindata.bytes); } } return _result; } /// /// Load appearance data from a byte file stored on disk. /// /// /// /// public static CharacterAppearance LoadCharacterDataFromFile(CharacterAppearance _data, string _path) { byte[] _bytes = File.ReadAllBytes(_path); CharacterAppearance _result; if (_data!=null) _result = _data.Copy(); else _result= new CharacterAppearance(CharacterData.Create(_bytes[1])); if (_bytes[0] != 3 && _bytes[1] != _result._CharacterData.Sex) { SimpleDynamicMsg.PopMsg("The gender of the blurprint is different from this character."); } else { _result.Load(_bytes); } return _result; } /// /// Load appearance data from a PNG file on disk, with the appearance data hidden inside the image. /// /// /// /// public static Texture2D LoadCharacterDataWithPhoto(ref CharacterAppearance _data, string _path) { byte[] _bytes = File.ReadAllBytes(_path); Texture2D _tex = new Texture2D(256, 256, TextureFormat.RGB24, false); ImageConversion.LoadImage(_tex, _bytes); int length = _bytes[_bytes.Length - 1]+ _bytes[_bytes.Length - 2]; byte[] _dataBytes = new byte[length]; System.Array.Copy(_bytes, _bytes.Length - 2 - length, _dataBytes, 0, length); if (_data == null) _data = new CharacterAppearance(CharacterData.Create(_dataBytes[1])); if (_dataBytes[0] != 3 && _dataBytes[1] != _data._CharacterData.Sex) { SimpleDynamicMsg.PopMsg("The gender of the blurprint is different from this character."); } else { _data.Load(_dataBytes); } return _tex; } /// /// Save appearance data to a byte file. Use BlurPrintType to specify if you want to save the entire appearance data or just specific parts. /// /// /// /// public static void SaveCharacterData(CharacterBoneControl _character, string _path, BlurPrintType _type= BlurPrintType.AllAppearance) { string filePath = Path.ChangeExtension(_path, ".bytes"); string dir = Path.GetDirectoryName(filePath); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); if (File.Exists(filePath))File.Delete(filePath); File.WriteAllBytes(filePath, _character.MyData.ToBytes(_type)); } /// /// 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. /// /// /// /// /// public static void SaveCharacterDataWithPhoto(CharacterBoneControl _character, Texture2D _photo, string _path, BlurPrintType _type = BlurPrintType.AllAppearance) { string filePath = Path.ChangeExtension(_path, ".png"); string dir = Path.GetDirectoryName(filePath); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); if (File.Exists(filePath)) File.Delete(filePath); byte[] _bytes = _character.MyData.ToBytes(_type); List _finalBytes = new List(); byte[] photoBytes = _photo.EncodeToPNG(); _finalBytes.AddRange(photoBytes); _finalBytes.AddRange(_bytes); int _length1 = Mathf.Min(_bytes.Length,200); int _length2 = Mathf.Max(_bytes.Length - _length1,0); _finalBytes.Add((byte)_length1); _finalBytes.Add((byte)_length2); File.WriteAllBytes(filePath, _finalBytes.ToArray()); } /// /// [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. /// /// /// /// public static void SaveCharacterDataToResources(CharacterBoneControl _character, string _resourcePath, BlurPrintType _type = BlurPrintType.AllAppearance) { string filePath = _resourcePath+ ".bytes"; string dir = Path.GetDirectoryName(filePath); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); if (File.Exists(filePath)) File.Delete(filePath); File.WriteAllBytes(filePath, _character.MyData.ToBytes(_type)); } /// These functions allow you to manage preview characters, which is useful for displaying a character¡¯s current appearance within various UI interfaces /// (e.g., rendering the player¡¯s appearance to a RenderTexture for display in an equipment or customization interface) /// /// Creates a preview character at a specified position and angle. The key is used to identify this character for further operations /// /// /// /// /// /// public CharacterBoneControl CreatePreviewCharacter(string _key, Sex _sex, Vector3 _position, float _angle) { if (GetPreviewCharacter(_key)!=null)return GetPreviewCharacter(_key); CharacterBoneControl _newChar = NewPreviewCharacter(_sex); _newChar.transform.position = _position; _newChar.transform.eulerAngles = new Vector3(0F, _angle,0F); _newChar.transform.localScale = Vector3.one; CharacterEntityDic.Add(_key, _newChar); return _newChar; } /// /// Create a preview character /// /// /// /// /// /// public CharacterBoneControl CreatePreviewCharacter(string _key, Sex _sex, Vector3 _position, Quaternion _rotation) { if (GetPreviewCharacter(_key) != null) return GetPreviewCharacter(_key); CharacterBoneControl _newChar = NewPreviewCharacter(_sex); _newChar.transform.position = _position; _newChar.transform.rotation = _rotation; _newChar.transform.localScale = Vector3.one; CharacterEntityDic.Add(_key, _newChar); return _newChar; } /// /// Create a preview character /// /// /// /// /// public CharacterBoneControl CreatePreviewCharacter(string _key, Sex _sex, Vector3 _position) { if (GetPreviewCharacter(_key) != null) return GetPreviewCharacter(_key); CharacterBoneControl _newChar = NewPreviewCharacter(_sex); _newChar.transform.position = _position; _newChar.transform.eulerAngles = Vector3.zero; _newChar.transform.localScale = Vector3.one; CharacterEntityDic.Add(_key, _newChar); return _newChar; } /// /// Create a preview character /// /// /// /// public CharacterBoneControl CreatePreviewCharacter(string _key, Sex _sex) { if (GetPreviewCharacter(_key) != null) return GetPreviewCharacter(_key); CharacterBoneControl _newChar = NewPreviewCharacter(_sex); _newChar.transform.position = Vector3.zero; _newChar.transform.eulerAngles = Vector3.zero; _newChar.transform.localScale = Vector3.one; CharacterEntityDic.Add(_key, _newChar); return _newChar; } /// /// Create a preview character /// /// /// public CharacterBoneControl CreatePreviewCharacter(Sex _sex) { CharacterBoneControl _newChar = NewPreviewCharacter(_sex); _newChar.transform.localScale = Vector3.one; return _newChar; } /// /// 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. /// /// /// public CharacterBoneControl GetPreviewCharacter(string _key) { if (CharacterEntityDic.ContainsKey(_key)) { if (CharacterEntityDic[_key] != null) { return CharacterEntityDic[_key]; } else { CharacterEntityDic.Remove(_key); } } return null; } /// /// Removes the preview character identified by the provided key /// /// public void RemovePreviewCharacter(string _key) { if (GetPreviewCharacter(_key) != null) Destroy(GetPreviewCharacter(_key).gameObject); if (CharacterEntityDic.ContainsKey(_key)) CharacterEntityDic.Remove(_key); } } }