using UnityEngine; using System; using System.Text; using System.Collections.Generic; using System.Linq; using System.Diagnostics; using System.Security.Cryptography; using System.Runtime.Serialization.Formatters.Binary; using System.IO; using System.Text.RegularExpressions; using System.Reflection; using Random = UnityEngine.Random; using UnityEngine.AI; #if UNITY_EDITOR using UnityEditor; #endif public delegate void VoidDelegate(); public static partial class DSUtil { // -------------------------------------------------------------------- // 로그 관련 함수 // String객체 private static StringBuilder m_strBuilder = new StringBuilder(1024); public static StringBuilder DSString { get { m_strBuilder.Remove(0, m_strBuilder.Length); return m_strBuilder; } } // String조립 public static string Format(string strMessage, params object[] args) { StringBuilder strBuilder = DSString; strBuilder.AppendFormat(strMessage, args); return strBuilder.ToString(); } // 로그 : 에러 public static void LogError(string strMessage, params object[] args) { Verify(false, Format(strMessage, args)); } // 로그 : 워닝 public static void LogWarning(string strMessage, params object[] args) { #if UNITY_EDITOR UnityEngine.Debug.LogWarning(Format(strMessage, args)); #endif } // 로그 : 일반 public static void Log(string strMessage, params object[] args) { #if UNITY_EDITOR UnityEngine.Debug.Log(Format(strMessage, args)); #endif } // 서버로그 : 에러값 public static void LogErrorToServer(int iErrorCode, string strMessage = "") { string strErrorMessage = string.Empty; switch (iErrorCode) { // MAP_CONST[5] = "Http 프로토콜 에러"; // TimeOut 도 포함. case 5: //strErrorMessage = "Timed Out.. Please Retry.."; strErrorMessage = "시간이 초과되었습니다. 다시 시도해주세요."; break; default: break; } // 메시지가 있으면 덮어 씀. if (false == string.IsNullOrEmpty(strMessage)) { strErrorMessage = strMessage; } DSUtil.LogError(strErrorMessage); } // 예외 : try-Catch문에서 예외전달메시지 public static System.ArgumentException GetExceptionMsg(string strMessage, params object[] args) { return new System.ArgumentException(Format(strMessage, args)); } // 예외 : 에러로그와 리턴값을 하나로 묶음( EX : return ErrorReturn(false, "{0} 예외야!!", "나쁜"); ) public static T ErrorReturn(T tReturnValue, string strMessage, params object[] args) { LogError(strMessage, args); return tReturnValue; } // 정보 : 콜스택을 얻습니다. public static StackFrame[] GetCallStack() { StackTrace stackTrace = new StackTrace(); return stackTrace.GetFrames(); } // -------------------------------------------------------------------- // 형 변환 관련 // 형변환 : String을 Enum으로 // Enum.Parse 엄청느립니다. 가급적 사용금지!!! public static T FormatStringToEnum(string _strTypeName, params object[] args) { return StringToEnum(Format(_strTypeName, args)); } public static T StringToEnum(string _strTypeName) { _strTypeName = _strTypeName.Replace(" ", ""); if (true == string.IsNullOrEmpty(_strTypeName) || false == Enum.IsDefined(typeof(T), _strTypeName)) { LogWarning("[Not Defined " + typeof(T) + " ] " + _strTypeName); return default(T); } return (T)Enum.Parse(typeof(T), _strTypeName); } public static T CheerSoundStringToEnum(string _strTypeName) { _strTypeName = _strTypeName.Replace(" ", ""); if (true == string.IsNullOrEmpty(_strTypeName) || false == Enum.IsDefined(typeof(T), _strTypeName)) return default(T); return (T)Enum.Parse(typeof(T), _strTypeName); } // 형변환 : String을 Enum으로 // Enum.Parse 엄청느립니다. 가급적 사용금지!!! public static T StringToEnum(string strEnum, string strErrorLog) { strEnum = strEnum.Replace(" ", ""); if (true == string.IsNullOrEmpty(strEnum) || false == Enum.IsDefined(typeof(T), strEnum)) { LogError("{0}(Enum:{1})", strErrorLog, strEnum); } // 없는 Enum이면 크래시되도록 놔두자!! return (T)Enum.Parse(typeof(T), strEnum); } // 형변환 : Object을 String으로 public static string ObjectToString(System.Object _cObject) { if (_cObject == null) return null; var binaryFormatter = new BinaryFormatter(); var memoryStream = new MemoryStream(); binaryFormatter.Serialize(memoryStream, _cObject); return Convert.ToBase64String(memoryStream.GetBuffer()); } // 형변환 : String을 Object로 public static System.Object StringToObject(string _strValue) { if (true == string.IsNullOrEmpty(_strValue)) return null; var binaryFormatter = new BinaryFormatter(); var memoryStream = new MemoryStream(Convert.FromBase64String(_strValue)); System.Object obj = binaryFormatter.Deserialize(memoryStream); return obj; } // -------------------------------------------------------------------- // 컨테이너 관련 // Foreach : Enum public static void ForeachToEnum(Action pLambda) { foreach (T eEnum in Enum.GetValues(typeof(T))) pLambda(eEnum); } // Foreach : List public static void ForeachToList(List list, Action pLambda) { if (null == list) return; foreach (T tList in list) pLambda(tList); } // Foreach : List public static void ForeachToListOfBreak(List list, bool bBreakCondition, Func pLambda) { if (null == list) return; foreach (T tList in list) { if (bBreakCondition == pLambda(tList)) break; } } // Foreach Dictionary public static void ForeachToDic(Dictionary dic, Action pLambda) { if (null == dic) return; foreach (KeyValuePair kvp in dic) pLambda(kvp.Key, kvp.Value); } //------------------------------------------------------------------------- // FirstKey // if (.NET 3.5) return dic.Keys.First(); public static Key FirstKey(Dictionary dic, Key defaultVal) { foreach (var pair in dic) return pair.Key; return defaultVal; } // dictionary 램덤키 뽑는다. public static Key RandomKey(Dictionary dic, Key defaultVal) { if (null == dic) return defaultVal; if (0 == dic.Count) return defaultVal; System.Random rand = new System.Random(); var listKey = dic.ToList()[rand.Next(0, dic.Count)]; return listKey.Key; } //------------------------------------------------------------------------- // test_code public static void TestRandomTrue() { List list = new List(); var p = 0.3f; for (int i = 0; i < 1000; i++) list.Add(DSUtil.RandomTrue(p)); var countTrue = list.Count(e => e == true); var countTotal = list.Count(); var ratio = (float)countTrue / (float)countTotal; Log(" RandomTrue(" + p + "); " + "result ratio [" + ratio + "], " + "true count [" + countTrue + "], " + "total count [" + countTotal + "]"); } //------------------------------------------------------------------------- // RandomTrue // 확률 P% 이하로 true 리턴. (단, 100% == 1.0f) public static bool RandomTrue(float p) { // 유니티 랜덤함수 직접 사용[blueasa / 2014-10-30] return p > UnityEngine.Random.Range(0f, 1f); } public static bool RandomFalse(float p) { // 유니티 랜덤함수 직접 사용[blueasa / 2014-10-30] return RandomTrue(1.0f - p); } public static bool RandomTrue(double p) { // 유니티 랜덤함수 직접 사용[blueasa / 2014-10-30] return p > UnityEngine.Random.Range(0f, 1f); } //------------------------------------------------------------------------- // RandomW // 가중치에 따라 랜덤하게 원소 선택 public static T RandomW(List values, List weights) { if ((null == values) || (0 == values.Count)) return default(T); if ((null == weights) || (0 == weights.Count)) return RandomN(values); // ex: weights = { 0.1, 0.3, 0.6}; // assert(weight.Sum > 0); var subsums = new List(weights.Count); var sum = weights.Aggregate(0.0f, (acc, f) => { acc += f; subsums.Add(acc); return acc; }); // ex : subsums = {0.1, 0.4, 1.0}, sum = 1.0 var r = UnityEngine.Random.Range(0.0f, sum); // ex : r = 0.35 var index = subsums.FindIndex(f => (r < f)); // ex : index = 1 // 2017. 08. 29. 정인호. 크래쉬 리포트(ArgumentOutOfRangeException: Argument is out of range.)에 의해 로그 추가 if (values.Count <= index || index < 0) { string valuesvalue = ""; for (int iLoop = 0; iLoop < values.Count; ++iLoop) valuesvalue += values[iLoop] + ", "; string weightsvalue = ""; for (int iLoop = 0; iLoop < weights.Count; ++iLoop) weightsvalue += weights[iLoop] + ", "; Assert(false, Format("DSUtil RandomW Error : values : {0}, weights : {1}, index : {2}, ", valuesvalue, weightsvalue, index)); } return values[index]; } //------------------------------------------------------------------------- // RandomOneW // 가중치에 따라 랜덤하게 원소 선택 ( 무조건 하나는 선택되도록 ) // 가중치를 정규화해서 합이 1인 값으로 만들고, 의미없는 원소는 제거하여 RandomW처리 public static T RandomOneW(List values, List weights) { if ((null == values) || (0 == values.Count)) return default(T); if ((null == weights) || (0 == weights.Count)) return RandomN(values); // 전체 가중치 합 float fSum = 0.0f; foreach (float fValue in weights) fSum += fValue; // 예외처리 : 가중치 합이 0이면 선택할 원소가 없다... if (0.0f == fSum) return default(T); // 데이터 복사용 변수 List CopyValues = new List(); List CopyWeights = new List(); // 가중치 정규화하면서 필요없는 원소는 제거 for (int iLoop = 0; iLoop < weights.Count; ++iLoop) { if (0.0f == weights[iLoop]) continue; CopyValues.Add(values[iLoop]); CopyWeights.Add(weights[iLoop] / fSum); } return RandomW(CopyValues, CopyWeights); } //------------------------------------------------------------------------- // RandomN // RandomW 와 스팩은 동일하나, 모든 원소의 가중치가 동일한 경우로 제한함. public static T RandomN(List values) { if ((null == values) || (0 == values.Count)) return default(T); var index = (int)UnityEngine.Random.Range(0.0f, values.Count); if (index == values.Count) index = values.Count - 1; return values[index]; } //------------------------------------------------------------------------- // RandomRange // int일 경우, Max값도 나오도록 하기 위해 Max + 1을 함. public static int RandomRange(int _iMin, int _iMax) { if (_iMin == _iMax) return _iMin; return UnityEngine.Random.Range(_iMin, _iMax + 1); } public static float RandomRange(float _fMin, float _fMax) { if (_fMin == _fMax) return _fMin; return UnityEngine.Random.Range(_fMin, _fMax); } public static Vector3 RandomVector(float fMin, float fMax) { return RandomVector(fMin, fMax, fMin, fMax, fMin, fMax); } public static Vector3 RandomVector(float fMinX, float fMaxX, float fMinY, float fMaxY, float fMinZ, float fMaxZ) { return new Vector3(RandomRange(fMinX, fMaxX), RandomRange(fMinY, fMaxY), RandomRange(fMinZ, fMaxZ)); } public static Vector3 RandomVector_Exception_y(float minX, float maxX, float y, float minZ, float maxZ) { return new Vector3(RandomRange(minX, maxX), y, RandomRange(minZ, maxZ)); } //------------------------------------------------------------------------- // public static List RandomIndex(int _iCount) { List listResult = new List(); List listCount = new List(); for (int i = 0; i < _iCount; i++) listCount.Add(i); while (0 < listCount.Count) { int iRand = UnityEngine.Random.Range(0, listCount.Count); listResult.Add(listCount[iRand]); listCount.RemoveAt(iRand); } return listResult; } //======================================================================== // 폴링 함수 getter 를 이용해 이벤트 드리븐 함수 onChange 를 호출 // prev - 이전값 // getter - 폴링함수. // onChange := Action - 이벤트 함수. public static void PollingToEvent(float prev, Func getter, Action onChange) { // 값이 변했는지 검사. 다를 때만 호출 var current = getter(); if (prev != current) onChange(prev, current); } /// /// GUID /// /// public static string GetGUID() { System.Guid uid = System.Guid.NewGuid(); return uid.ToString(); } /// /// UUID(이걸 사용) /// /// public static string GetUUID() { return SystemInfo.deviceUniqueIdentifier; } // 프리팹에 Component가 Missing된 것이 있는지 체크 public static void CheckMissingComponent() { UnityEngine.Object[] pObjects = Resources.FindObjectsOfTypeAll(typeof(GameObject)); foreach (UnityEngine.Object pObject in pObjects) { GameObject pGameObject = pObject as GameObject; if (null == pGameObject) continue; Component[] pComponents = pGameObject.GetComponents(); foreach (Component pComponent in pComponents) { if (null == pComponent) DSUtil.LogError("MissingComponent!!(GameObject{0})", pObject.name); } } } // 유니티 에디터의 Pause를 Toggle합니다. public static void EditorPauseOfToggle(bool bToggle) { #if UNITY_EDITOR EditorApplication.isPaused = bToggle; #endif } public static void ContainsDictionary(Dictionary dicContainer, TKey tKey) { if (false == dicContainer.ContainsKey(tKey)) dicContainer[tKey] = default(TValue); } // 클래스 Null체크 /* * null for classes * null (empty) for Nullable * zero/false/etc for other structs */ public static bool IsNull(T value) { if (EqualityComparer.Default.Equals(value, default(T))) return true; return false; } /* *Assert 관련 */ public static void Assert(bool condition) { // if (Debug.isDebugBuild && !condition) throw new Exception(); if (!condition) throw new Exception(); } public static void Assert(bool condition, string log) { // if (Debug.isDebugBuild && !condition) { // Debug.Log (log); // throw new Exception (); // } if (!condition) { UnityEngine.Debug.LogError(log); throw new Exception(); } } public static void Assert(bool condition, string strMessage, params object[] args) { // if (Debug.isDebugBuild && !condition) { // Debug.Log (Format (strMessage, args)); // throw new Exception (); // } if (!condition) { UnityEngine.Debug.Log(Format(strMessage, args)); throw new Exception(); } } public static void Verify(bool condition) { if (!condition) { //EditorPauseOfToggle (true); } } public static void Verify(bool condition, string log) { if (!condition) { UnityEngine.Debug.LogError(log); //EditorPauseOfToggle (true); } } public static void Verify(bool condition, string strMessage, params object[] args) { if (!condition) { UnityEngine.Debug.LogError(Format(strMessage, args)); //EditorPauseOfToggle (true); } } public static void Search(string strPath, Action pCallBack) { DirectoryInfo pDirInfo = new DirectoryInfo(strPath); SearchFiles(pDirInfo, pCallBack); SearchDirs(pDirInfo, pCallBack); } static void SearchDirs(DirectoryInfo pDirInfo, Action pCallBack) { DirectoryInfo[] pDirs = pDirInfo.GetDirectories(); foreach (DirectoryInfo pDir in pDirs) { SearchFiles(pDir, pCallBack); SearchDirs(pDir, pCallBack); } } static void SearchFiles(DirectoryInfo pDirInfo, Action pCallBack) { FileInfo[] pFiles = pDirInfo.GetFiles(); foreach (FileInfo pFile in pFiles) { pCallBack(pFile); } } public static string Encrypt(string toEncrypt, string key = "12345678901234567890123456789012") /*key limit 32byte 12345678901234567890123456789012*/ { byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key); byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt); RijndaelManaged rDel = new RijndaelManaged(); rDel.Key = keyArray; rDel.Mode = CipherMode.ECB; rDel.Padding = PaddingMode.PKCS7; ICryptoTransform cTransform = rDel.CreateEncryptor(); byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); return Convert.ToBase64String(resultArray, 0, resultArray.Length); } public static string Decrypt(string toDecrypt, string key = "12345678901234567890123456789012") /*key limit 32byte 12345678901234567890123456789012*/ { byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key); byte[] toEncryptArray = Convert.FromBase64String(toDecrypt); RijndaelManaged rDel = new RijndaelManaged(); rDel.Key = keyArray; rDel.Mode = CipherMode.ECB; rDel.Padding = PaddingMode.PKCS7; ICryptoTransform cTransform = rDel.CreateDecryptor(); byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); return UTF8Encoding.UTF8.GetString(resultArray); } public static void Activate(GameObject _goTarget, bool _bActive) { if (null != _goTarget) { _goTarget.SetActive(_bActive); } } public static void ActivateGameObjects(GameObject[] _gos, int _inactiveIndex = -1) { for (int iLoop = 0; iLoop < _gos.Length; ++iLoop) { if (_gos[iLoop] != null) _gos[iLoop].SetActive(true); } if (_inactiveIndex > -1 && _gos[_inactiveIndex] != null) _gos[_inactiveIndex].SetActive(false); } public static void InActivateGameObjects(GameObject[] _gos, int _activeIndex = -1) { for (int iLoop = 0; iLoop < _gos.Length; ++iLoop) { if (_gos[iLoop] != null) _gos[iLoop].SetActive(false); } if (_activeIndex > -1 && _gos[_activeIndex] != null) _gos[_activeIndex].SetActive(true); } public static void ActivateCollider(BoxCollider _collider, bool _bActive) { if (null != _collider) { _collider.enabled = _bActive; } } private static Stopwatch m_cStopwatch = new Stopwatch(); public static void StartStopWatch() { m_cStopwatch.Reset(); m_cStopwatch.Start(); } public static TimeSpan StopStopWatch(bool debugtime) { m_cStopwatch.Stop(); if (debugtime) Log("{0}", m_cStopwatch.Elapsed); return m_cStopwatch.Elapsed; } public static int GetNumberLen(int iNum) { return iNum.ToString().Length; } public static void Quit() { #if UNITY_EDITOR EditorApplication.isPlaying = false; #else Application.Quit(); #endif } /// /// 문자열에서 한글만 얻기 /// /// /// public static string GetStringOnlyKorean(string _strText) { return Regex.Replace(_strText, @"[^가-힣]", ""); } /// /// 문자열에서 영어만 얻기 /// /// /// public static string GetStringOnlyEnglish(string _strText) { return Regex.Replace(_strText, @"[^a-zA-Z]", ""); } /// /// 텍스트의 문자열이 영어 대소문자/숫자/한글인지 체크하는 정규식 /// /// 체크할 텍스트 /// 유효 여부 public static bool IsValidString(string _strText) { string strPattern = @"^[a-zA-Z0-9가-힣]*$"; return Regex.IsMatch(_strText, strPattern); } /// /// int -> 세자리마다 콤마가 있는 숫자 /// public static string GetThousandCommaText(double data, bool _incluePercent = false) { if (_incluePercent) return Format("{0:#,##0}%", data); else return Format("{0:#,##0}", data); } public static string GetCommaText_N2(double data) { return Format("{0:#,##0.##}", data); } public static string GetCommaText_N4(double data) { return Format("{0:#,##0.####}", data); } /// /// 00:00:00 형식 /// public static string Get_TimeText_HMS(DateTime _dt) { return Format("{0:00}:{1:00}:{2:00}", _dt.Hour, _dt.Minute, _dt.Second); } public static string Get_TimeText_HMS(TimeSpan _ts) { return Format("{0:00}:{1:00}:{2:00}", _ts.Hours, _ts.Minutes, _ts.Seconds); } public static string Get_TimeText_MS(TimeSpan _ts) { return Format("{0:00}:{1:00}", _ts.Minutes, _ts.Seconds); } public static string Get_TimeText_HMS(bool _text, double _totalseconds) { if (_text) return Format("{0:00}시간 {1:00}분 {2:00}초", _totalseconds / 60 / 60, _totalseconds / 60 % 60, _totalseconds % 60); return Format("{0:00}:{1:00}:{2:00}", _totalseconds / 60 / 60, _totalseconds / 60 % 60, _totalseconds % 60); } public static string Get_TimeText_HM(double _totalminutes) { return Format("{0:00}:{1:00}", (int)(_totalminutes / 60), (int)(_totalminutes % 60)); } public static string Get_TimeText_MS(double _totalseconds) { return Format("{0:00}:{1:00}", (int)(_totalseconds / 60), (int)(_totalseconds % 60)); } public static string Get_TimeText_MSms(double _tick) { var min = (int)(_tick / 60); var sec = (int)(_tick % 60); float ms = ((float)_tick - (int)_tick) * 100f; return Format("{0:00}분 {1:00}초 {2:00}", min, sec, Mathf.Approximately(ms, 100f) ? 0 : ms); } public static bool IsUnityObjectDestroyed(object target) { if ((target is UnityEngine.Object) == false) return false; if ((target is UnityEngine.Object) && (target.Equals(null)) == true) return true; return false; } public static bool IsActionUnityObjectDestroyed(object target, MethodInfo method) { if (target == null && method == null) return true; if (IsUnityObjectDestroyed(target)) return true; return false; } public static bool IsActionUnityObjectDestroyed(Action action) { if (action == null) return true; return IsActionUnityObjectDestroyed(action.Target, action.Method); } public static bool IsActionUnityObjectDestroyed(Action action) { if (action == null) return true; return IsActionUnityObjectDestroyed(action.Target, action.Method); } public static bool IsActionUnityObjectDestroyed(Action action) { if (action == null) return true; return IsActionUnityObjectDestroyed(action.Target, action.Method); } public static void Set_Transform_withParent(Transform _tf, Transform _parent, Vector3? _pos = null, Vector3? _scale = null) { _tf.SetParent(_parent); _tf.localPosition = _pos == null ? Vector3.zero : (Vector3)_pos; _tf.localScale = _scale == null ? Vector3.one : (Vector3)_scale; _tf.localRotation = Quaternion.identity; } public static GameObject Get_Clone(string _path, Transform _parent = null, Vector3? _pos = null, Vector3? _scale = null) { var temp = GameObject.Instantiate(Resources.Load(_path)) as GameObject; Set_Transform_withParent(temp.transform, _parent, _pos, _scale); return temp; } public static GameObject Get_Clone(GameObject _obj, Transform _parent = null, Vector3? _pos = null, Vector3? _scale = null) { var temp = GameObject.Instantiate(_obj); Set_Transform_withParent(temp.transform, _parent, _pos, _scale); return temp; } public static T Get_Clone(GameObject _obj, Transform _parent = null, Vector3? _pos = null, Vector3? _scale = null) { var temp = GameObject.Instantiate(_obj); Set_Transform_withParent(temp.transform, _parent, _pos, _scale); return temp.GetComponent(); } public static T Get_Clone(string _path, Transform _parent = null, Vector3? _pos = null, Vector3? _scale = null) { var res = Resources.Load(_path); if (res == null) UnityEngine.Debug.Log(Format("no exist res : {0}", _path)); return Get_Clone(res as GameObject, _parent, _pos, _scale); } public static bool Check_ObjectInMainCamera(Transform _target) { var screenP = Camera.main.WorldToViewportPoint(_target.position); return screenP.z > 0 && screenP.x > 0 && screenP.x < 1 && screenP.y > 0 && screenP.y < 1; } //public static DateTime Get_NextWeekMonday() //{ // DateTime NextWeek; // switch (ServerInfo.ServerTime.DayOfWeek) // { // case DayOfWeek.Monday: NextWeek = ServerInfo.ServerTime.AddDays(7); break; // case DayOfWeek.Tuesday: NextWeek = ServerInfo.ServerTime.AddDays(6); break; // case DayOfWeek.Wednesday: NextWeek = ServerInfo.ServerTime.AddDays(5); break; // case DayOfWeek.Thursday: NextWeek = ServerInfo.ServerTime.AddDays(4); break; // case DayOfWeek.Friday: NextWeek = ServerInfo.ServerTime.AddDays(3); break; // case DayOfWeek.Saturday: NextWeek = ServerInfo.ServerTime.AddDays(2); break; // default: NextWeek = ServerInfo.ServerTime.AddDays(1); break; // } // return new DateTime(NextWeek.Year, NextWeek.Month, NextWeek.Day); //} public static void ChangeLayersRecursively(Transform trans, string name) { trans.gameObject.layer = LayerMask.NameToLayer(name); foreach (Transform child in trans) { ChangeLayersRecursively(child, name); } } public static Vector3 StringToVector3(string str) { string[] s = str.Split(','); return new Vector3(float.Parse(s[0]), float.Parse(s[1]), float.Parse(s[2])); } public static int Get_RandomIndex(List probs) where T : struct, IConvertible { float total = 0f; foreach (var p in probs) total += Convert.ToSingle(p); float randomPoint = Random.value * total; for (int i = 0; i < probs.Count; i++) { float val = Convert.ToSingle(probs[i]); if (randomPoint < val) return i; randomPoint -= val; } return probs.Count - 1; } public static Vector3 Get_RandomPos_onNavMesh(Vector3 origin, float minradius = 0f, float maxradius = 3f, int _trycount = 50) { // Set the parameters for the NavMesh.SamplePosition method NavMeshHit hit; Vector3 randomPoint = origin; int maxTries = _trycount; int currentTry = 0; minradius = Math.Max(0f, minradius); // Try to find a random location on the NavMesh while (currentTry < maxTries) { Vector3 randomDirection = Random.insideUnitSphere * maxradius; randomDirection += origin; NavMesh.SamplePosition(randomDirection, out hit, maxradius, NavMesh.AllAreas); // If a valid location is found, check if it is reachable and far enough if (hit.hit && Vector3.Distance(origin, hit.position) >= minradius) { NavMeshPath path = new NavMeshPath(); if (NavMesh.CalculatePath(origin, hit.position, NavMesh.AllAreas, path)) { // If a path can be found, return the random location randomPoint = hit.position; break; } } currentTry++; } // Return the random reachable location on the NavMesh return randomPoint; } public static bool IsValidPos_onNaveMesh(Vector3 pos, float maxDistance = 0f) { NavMeshHit hit; // maxDistance 안에서 pos에 가장 가까운 NavMesh 위치 찾기 if (NavMesh.SamplePosition(pos, out hit, maxDistance, NavMesh.AllAreas)) { // 유효한 위치가 있고 너무 멀리 떨어지지 않았으면 true return true; } return false; } public static bool CheckNull(UnityEngine.Object obj) { return ReferenceEquals(obj, null) || obj.Equals(null); } public static bool CheckNull(object obj) { return ReferenceEquals(obj, null); } public static float Get_SliderValue(float f) { return float.IsNaN(f) ? 0f : f; } public static bool WithInDistance(Vector3 a, Vector3 b, float dist) { return Get_Distance(a, b) <= dist * dist; } public static float Get_Distance(Vector3 a, Vector3 b) { return (a - b).sqrMagnitude; } } public class NumberFormatter { // 알파벳 시작값 (a = 97 in ASCII) private const char startChar = 'a'; public static string FormatNumber(double number) { if (number < 1000) return number.ToString("0.##"); // 1,000 미만은 그냥 숫자로 반환 int suffixIndex = 0; while (number >= 1000) { number /= 1000; suffixIndex++; } // 소수점 두 자리까지 표시하고, 알파벳 단위 추가 (suffixIndex에 따라 동적 알파벳 생성) char suffix = (char)(startChar + (suffixIndex - 1)); return $"{number:F2}{suffix}"; } } public struct ErrorString { public string error; public ErrorString(string str){error=str;} }; public class DSTime_HMS { public int m_Hour, m_Min, m_Sec; public DSTime_HMS(int _second) { m_Hour = _second / 3600; m_Min = _second % 3600 / 60; m_Sec = _second % 3600 % 60; } }