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);
}
}
}