Project_WL/Assets/Script/Util/DSUtil.cs

1306 lines
43 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 PlayFab.ClientModels;
using UnityEngine.AI;
using CodeStage.AntiCheat.ObscuredTypes;
using static UnityEngine.GraphicsBuffer;
using static UnityEngine.UI.Image;
using Unity.Burst.CompilerServices;
#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 string Format(int key, params object[] args)
{
StringBuilder strBuilder = DSString;
strBuilder.AppendFormat(table_localtext.Ins.Get_Text(key), 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<bool>(false, "{0} 예외야!!", "나쁜"); )
public static T ErrorReturn<T>(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<T>(string _strTypeName, params object[] args)
{
return StringToEnum<T>(Format(_strTypeName, args));
}
public static T StringToEnum<T>(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<T>(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<T>(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<T>(Action<T> pLambda)
{
foreach (T eEnum in Enum.GetValues(typeof(T)))
pLambda(eEnum);
}
// Foreach : List
public static void ForeachToList<T>(List<T> list, Action<T> pLambda)
{
if (null == list)
return;
foreach (T tList in list)
pLambda(tList);
}
// Foreach : List
public static void ForeachToListOfBreak<T>(List<T> list, bool bBreakCondition, Func<T, bool> pLambda)
{
if (null == list)
return;
foreach (T tList in list)
{
if (bBreakCondition == pLambda(tList))
break;
}
}
// Foreach Dictionary
public static void ForeachToDic<TKey, TValue>(Dictionary<TKey, TValue> dic, Action<TKey, TValue> pLambda)
{
if (null == dic)
return;
foreach (KeyValuePair<TKey, TValue> kvp in dic)
pLambda(kvp.Key, kvp.Value);
}
//-------------------------------------------------------------------------
// FirstKey
// if (.NET 3.5) return dic.Keys.First();
public static Key FirstKey<Key, Value>(Dictionary<Key, Value> dic, Key defaultVal)
{
foreach (var pair in dic)
return pair.Key;
return defaultVal;
}
// dictionary 램덤키 뽑는다.
public static Key RandomKey<Key, Value>(Dictionary<Key, Value> 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<bool> list = new List<bool>();
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<T>
// 가중치에 따라 랜덤하게 원소 선택
public static T RandomW<T>(List<T> values, List<float> 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<float>(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<T>
// 가중치에 따라 랜덤하게 원소 선택 ( 무조건 하나는 선택되도록 )
// 가중치를 정규화해서 합이 1인 값으로 만들고, 의미없는 원소는 제거하여 RandomW처리
public static T RandomOneW<T>(List<T> values, List<float> 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<T> CopyValues = new List<T>();
List<float> CopyWeights = new List<float>();
// 가중치 정규화하면서 필요없는 원소는 제거
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<T>
// RandomW<T> 와 스팩은 동일하나, 모든 원소의 가중치가 동일한 경우로 제한함.
public static T RandomN<T>(List<T> 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<int> RandomIndex(int _iCount)
{
List<int> listResult = new List<int>();
List<int> listCount = new List<int>();
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<PrevValue, CurrentValue> - 이벤트 함수.
public static void PollingToEvent(float prev, Func<float> getter, Action<float, float> onChange)
{
// 값이 변했는지 검사. 다를 때만 호출
var current = getter();
if (prev != current)
onChange(prev, current);
}
/// <summary>
/// GUID
/// </summary>
/// <returns></returns>
public static string GetGUID()
{
System.Guid uid = System.Guid.NewGuid();
return uid.ToString();
}
/// <summary>
/// UUID(이걸 사용)
/// </summary>
/// <returns></returns>
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<Component>();
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<TKey, TValue>(Dictionary<TKey, TValue> dicContainer, TKey tKey)
{
if (false == dicContainer.ContainsKey(tKey))
dicContainer[tKey] = default(TValue);
}
// 클래스 Null체크
/*
* null for classes
* null (empty) for Nullable<T>
* zero/false/etc for other structs
*/
public static bool IsNull<T>(T value)
{
if (EqualityComparer<T>.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<FileInfo> pCallBack)
{
DirectoryInfo pDirInfo = new DirectoryInfo(strPath);
SearchFiles(pDirInfo, pCallBack);
SearchDirs(pDirInfo, pCallBack);
}
static void SearchDirs(DirectoryInfo pDirInfo, Action<FileInfo> pCallBack)
{
DirectoryInfo[] pDirs = pDirInfo.GetDirectories();
foreach (DirectoryInfo pDir in pDirs)
{
SearchFiles(pDir, pCallBack);
SearchDirs(pDir, pCallBack);
}
}
static void SearchFiles(DirectoryInfo pDirInfo, Action<FileInfo> 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
}
/// <summary>
/// 문자열에서 한글만 얻기
/// </summary>
/// <param name="_strText"></param>
/// <returns></returns>
public static string GetStringOnlyKorean(string _strText)
{
return Regex.Replace(_strText, @"[^가-힣]", "");
}
/// <summary>
/// 문자열에서 영어만 얻기
/// </summary>
/// <param name="_strText"></param>
/// <returns></returns>
public static string GetStringOnlyEnglish(string _strText)
{
return Regex.Replace(_strText, @"[^a-zA-Z]", "");
}
/// <summary>
/// 텍스트의 문자열이 영어 대소문자/숫자/한글인지 체크하는 정규식
/// </summary>
/// <param name="_strText">체크할 텍스트</param>
/// <returns>유효 여부</returns>
public static bool IsValidString(string _strText)
{
string strPattern = @"^[a-zA-Z0-9가-힣]*$";
return Regex.IsMatch(_strText, strPattern);
}
/// <summary>
/// int -> 세자리마다 콤마가 있는 숫자
/// </summary>
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);
}
/// <summary>
/// 00:00:00 형식
/// </summary>
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);
}
/// <summary>
/// float(1f 이하) -> 퍼센트
/// </summary>
public static string GetFloatToPercent(float _f)
{
return ((int)(_f * 100)).ToString();
}
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<T>(Action<T> action)
{
if (action == null)
return true;
return IsActionUnityObjectDestroyed(action.Target, action.Method);
}
public static bool IsActionUnityObjectDestroyed<T1, T2>(Action<T1, T2> 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<T>(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<T>();
}
public static T Get_Clone<T>(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<T>(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 string Get_GradeSpriteName(eItemGrade _Grade)
{
return Format("grade{0}", (int)_Grade);
}
public static int Get_RandomIndex(List<float> probs)
{
var total = 0f;
for (int i = 0; i < probs.Count; i++)
total += probs[i];
float randomPoint = Random.value * total;
for (int i = 0; i < probs.Count; i++)
{
if (randomPoint < probs[i])
return i;
else
randomPoint -= probs[i];
}
return probs.Count - 1;
}
public static int Get_RandomIndex(List<ObscuredFloat> probs)
{
var total = 0f;
for (int i = 0; i < probs.Count; i++)
total += probs[i];
float randomPoint = Random.value * total;
for (int i = 0; i < probs.Count; i++)
{
if (randomPoint < probs[i])
return i;
else
randomPoint -= probs[i];
}
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;
}
/// <summary>
/// PD 지시 #764 — "이동 불가 지형에 생성" 방지용 엄격 표집. 위 Get_RandomPos_onNavMesh 와 다른 점:
/// ① 스냅 반경(snapRadius)을 따로 받는다. 위 함수는 maxradius 를 스냅 반경으로 그대로 써서
/// 결과가 원점에서 최대 2×maxradius 까지 튄다(WL_Start 실측 570회 중 16회 초과).
/// ② NavMesh.CalculatePath 의 **반환값이 아니라 status == PathComplete** 를 본다.
/// CalculatePath 는 부분 경로(PathPartial)에서도 true 를 돌려주므로, 위 함수는 벽 너머·
/// 다른 섬의 점을 통과시킨다(WL_Start 실측: 표집 570점 중 295점 = 51% 가 도달 불가).
/// ③ 결과가 원점에서 maxradius 안인지 확인한다.
/// 실패하면 origin 을 돌려준다(위 함수와 동일). origin 이 도달 가능하면 결과도 도달 가능하다
/// (NavMesh 연결 성분 안에서 도달성은 대칭·추이적) — 그래서 호출부는 origin 의 도달성만 1회 확인하면 된다.
/// </summary>
public static Vector3 Get_RandomPos_onNavMesh_Reachable(Vector3 origin, float minradius, float maxradius, float snapRadius, int _trycount = 50)
{
minradius = Math.Max(0f, minradius);
if (snapRadius <= 0f) snapRadius = maxradius;
for (int i = 0; i < _trycount; i++)
{
NavMeshHit hit;
Vector3 randomDirection = Random.insideUnitSphere * maxradius + origin;
if (!NavMesh.SamplePosition(randomDirection, out hit, snapRadius, NavMesh.AllAreas)) continue;
float d = Vector3.Distance(origin, hit.position);
if (d < minradius || d > maxradius) continue;
NavMeshPath path = new NavMeshPath();
if (!NavMesh.CalculatePath(origin, hit.position, NavMesh.AllAreas, path)) continue;
if (path.status != NavMeshPathStatus.PathComplete) continue;
return hit.position;
}
return origin;
}
/// <summary>#764 — from 에서 to 까지 끊김 없는 경로가 있는지. CalculatePath 반환값이 아니라 status 를 본다.</summary>
public static bool IsReachable_onNavMesh(Vector3 from, Vector3 to)
{
NavMeshPath path = new NavMeshPath();
if (!NavMesh.CalculatePath(from, to, NavMesh.AllAreas, path)) return false;
return path.status == NavMeshPathStatus.PathComplete;
}
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;
}
static List<ItemData> list_CombineSameItem = new List<ItemData>();
public static List<ItemData> CombineSameItem(List<ItemData> lst)
{
list_CombineSameItem.Clear();
for (int i = 0; i < lst.Count; i++)
{
var item = list_CombineSameItem.Find(f => f.ItemType == lst[i].ItemType && f.Get_ID() == lst[i].Get_ID());
if (!CheckNull(item)) item.Amount += lst[i].Amount;
else list_CombineSameItem.Add(lst[i]);
}
return list_CombineSameItem;
}
public static void Fix_Rank(List<PlayerLeaderboardEntry> _lst)
{
// 같은 값 정리
for (int i = 0; i < _lst.Count; i++)
{
if (i < _lst.Count - 1)
{
if (_lst[i].StatValue == _lst[i + 1].StatValue)
_lst[i + 1].Position = _lst[i].Position;
}
}
}
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; }
static public List<int> Get_T_List(List<ObscuredInt> lst)
{
var temp = new List<int>();
for (int i = 0; i < lst.Count; i++)
temp.Add(lst[i]);
return temp;
}
static public List<double> Get_T_List(List<ObscuredDouble> lst)
{
var temp = new List<double>();
for (int i = 0; i < lst.Count; i++)
temp.Add(lst[i]);
return temp;
}
static public List<List<int>> Get_T_List(List<List<ObscuredInt>> lst)
{
var temp = new List<List<int>>();
for (int i = 0; i < lst.Count; i++)
temp.Add(Get_T_List(lst[i]));
return temp;
}
static public List<List<double>> Get_T_List(List<List<ObscuredDouble>> lst)
{
var temp = new List<List<double>>();
for (int i = 0; i < lst.Count; i++)
temp.Add(Get_T_List(lst[i]));
return temp;
}
static public Dictionary<string, int> Get_T_Dic(Dictionary<string, ObscuredInt> dic)
{
var temp = new Dictionary<string, int>();
if (!DSUtil.CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, item.Value);
}
return temp;
}
static public Dictionary<string, double> Get_T_Dic(Dictionary<string, ObscuredDouble> dic)
{
var temp = new Dictionary<string, double>();
if (!DSUtil.CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, item.Value);
}
return temp;
}
static public Dictionary<string, List<int>> Get_T_Dic(Dictionary<string, List<ObscuredInt>> dic)
{
var temp = new Dictionary<string, List<int>>();
if (!CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, Get_T_List(item.Value));
}
return temp;
}
static public Dictionary<string, List<double>> Get_T_Dic(Dictionary<string, List<ObscuredDouble>> dic)
{
var temp = new Dictionary<string, List<double>>();
if (!CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, Get_T_List(item.Value));
}
return temp;
}
static public Dictionary<string, List<List<int>>> Get_T_Dic(Dictionary<string, List<List<ObscuredInt>>> dic)
{
var temp = new Dictionary<string, List<List<int>>>();
if (!CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, Get_T_List(item.Value));
}
return temp;
}
static public Dictionary<string, Dictionary<string, List<int>>> Get_T_Dic(Dictionary<string, Dictionary<string, List<ObscuredInt>>> dic)
{
var temp = new Dictionary<string, Dictionary<string, List<int>>>();
if (!CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, Get_T_Dic(item.Value));
}
return temp;
}
static public List<ObscuredInt> Get_T_List(List<int> lst)
{
var temp = new List<ObscuredInt>();
if (!CheckNull(lst))
for (int i = 0; i < lst.Count; i++)
temp.Add(lst[i]);
temp.ForEach(f => f.RandomizeCryptoKey());
return temp;
}
static public List<ObscuredDouble> Get_T_List(List<double> lst)
{
var temp = new List<ObscuredDouble>();
if (!CheckNull(lst))
for (int i = 0; i < lst.Count; i++)
temp.Add(lst[i]);
temp.ForEach(f => f.RandomizeCryptoKey());
return temp;
}
static public List<List<ObscuredInt>> Get_T_List(List<List<int>> lst)
{
var temp = new List<List<ObscuredInt>>();
if (!CheckNull(lst))
for (int i = 0; i < lst.Count; i++)
temp.Add(Get_T_List(lst[i]));
return temp;
}
static public List<List<ObscuredDouble>> Get_T_List(List<List<double>> lst)
{
var temp = new List<List<ObscuredDouble>>();
if (!CheckNull(lst))
for (int i = 0; i < lst.Count; i++)
temp.Add(Get_T_List(lst[i]));
return temp;
}
static public Dictionary<string, ObscuredInt> Get_T_Dic(Dictionary<string, int> dic)
{
var temp = new Dictionary<string, ObscuredInt>();
if (!CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, item.Value);
}
return temp;
}
static public Dictionary<string, ObscuredDouble> Get_T_Dic(Dictionary<string, double> dic)
{
var temp = new Dictionary<string, ObscuredDouble>();
if (!CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, item.Value);
}
return temp;
}
static public Dictionary<string, List<ObscuredInt>> Get_T_Dic(Dictionary<string, List<int>> dic)
{
var temp = new Dictionary<string, List<ObscuredInt>>();
if (!CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, Get_T_List(item.Value));
}
return temp;
}
static public Dictionary<string, List<ObscuredDouble>> Get_T_Dic(Dictionary<string, List<double>> dic)
{
var temp = new Dictionary<string, List<ObscuredDouble>>();
if (!CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, Get_T_List(item.Value));
}
return temp;
}
static public Dictionary<string, List<List<ObscuredInt>>> Get_T_Dic(Dictionary<string, List<List<int>>> dic)
{
var temp = new Dictionary<string, List<List<ObscuredInt>>>();
if (!CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, Get_T_List(item.Value));
}
return temp;
}
static public Dictionary<string, Dictionary<string, List<ObscuredInt>>> Get_T_Dic(Dictionary<string, Dictionary<string, List<int>>> dic)
{
var temp = new Dictionary<string, Dictionary<string, List<ObscuredInt>>>();
if (!CheckNull(dic))
{
foreach (var item in dic)
temp.Add(item.Key, Get_T_Dic(item.Value));
}
return temp;
}
}
public static partial class DSUtil
{
private const string LogFilePath = "Assets/Editor/Logs/NoItemLogs.txt";
public static void ClearLogFile()
{
if (File.Exists(LogFilePath))
File.WriteAllText(LogFilePath, string.Empty);
}
public static void SaveLogToFile(LogEntry logEntry)
{
Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath)); // 디렉토리 생성
using (StreamWriter writer = new StreamWriter(LogFilePath, true))
{
writer.WriteLine($"{logEntry.Timestamp}\n{logEntry.ErrorMessage}\n{logEntry.CallStack}\n\n");
}
ToastUI.Ins.Set(174, $"{logEntry.ErrorMessage}\n{LogFilePath} 파일을 확인해 주세요!!!");
}
}
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;
}
}