using System.Linq; using UnityEngine; namespace Crosstales.Common.Util { /// Base for various helper functions. #if UNITY_EDITOR [UnityEditor.InitializeOnLoad] #endif public abstract class BaseHelper { #region Variables public static bool ApplicationIsPlaying = Application.isPlaying; protected static readonly System.Random _rnd = new System.Random(); private static string[] _args = null; private static int _androidAPILevel = 0; #endregion #region Properties /// The current culture of the application. /// Culture of the application. public static System.Globalization.CultureInfo BaseCulture { get { try { return new System.Globalization.CultureInfo(LanguageToISO639(Application.systemLanguage)); } catch { return System.Globalization.CultureInfo.CurrentCulture; } } } /// Checks if we are in Editor mode. /// True if in Editor mode. public static bool isEditorMode => isEditor && !ApplicationIsPlaying; /// Checks if the current build target uses IL2CPP. /// True if the current build target uses IL2CPP. public static bool isIL2CPP { get { #if UNITY_EDITOR UnityEditor.BuildTarget target = UnityEditor.EditorUserBuildSettings.activeBuildTarget; UnityEditor.BuildTargetGroup group = UnityEditor.BuildPipeline.GetBuildTargetGroup(target); #if UNITY_2023_1_OR_NEWER UnityEditor.Build.NamedBuildTarget nbt = UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(group); return UnityEditor.PlayerSettings.GetScriptingBackend(nbt) == UnityEditor.ScriptingImplementation.IL2CPP; #else return UnityEditor.PlayerSettings.GetScriptingBackend(group) == UnityEditor.ScriptingImplementation.IL2CPP; #endif #else #if ENABLE_IL2CPP return true; #else return false; #endif #endif } } /// Returns the current platform. /// The current platform. public static Crosstales.Common.Model.Enum.Platform CurrentPlatform { get { if (isWindowsPlatform) return Crosstales.Common.Model.Enum.Platform.Windows; if (isMacOSPlatform) return Crosstales.Common.Model.Enum.Platform.OSX; if (isLinuxPlatform) return Crosstales.Common.Model.Enum.Platform.Linux; if (isAndroidPlatform) return Crosstales.Common.Model.Enum.Platform.Android; if (isIOSBasedPlatform) return Crosstales.Common.Model.Enum.Platform.IOS; if (isWSABasedPlatform) return Crosstales.Common.Model.Enum.Platform.WSA; return isWebPlatform ? Crosstales.Common.Model.Enum.Platform.Web : Crosstales.Common.Model.Enum.Platform.Unsupported; } } /// Returns the Android API level of the current device (Android only)". /// The Android API level of the current device. public static int AndroidAPILevel { get { #if UNITY_ANDROID if (_androidAPILevel == 0) _androidAPILevel = int.Parse(SystemInfo.operatingSystem.Substring(SystemInfo.operatingSystem.IndexOf("-") + 1, 3)); #endif return _androidAPILevel; } } #region Platforms /// Checks if the current platform is Windows. /// True if the current platform is Windows. public static bool isWindowsPlatform { get { #if UNITY_STANDALONE_WIN return true; #else return false; #endif } } /// Checks if the current platform is OSX. /// True if the current platform is OSX. public static bool isMacOSPlatform { get { #if UNITY_STANDALONE_OSX return true; #else return false; #endif } } /// Checks if the current platform is Linux. /// True if the current platform is Linux. public static bool isLinuxPlatform { get { #if UNITY_STANDALONE_LINUX return true; #else return false; #endif } } /// Checks if the current platform is standalone (Windows, macOS or Linux). /// True if the current platform is standalone (Windows, macOS or Linux). public static bool isStandalonePlatform => isWindowsPlatform || isMacOSPlatform || isLinuxPlatform; /// Checks if the current platform is Android. /// True if the current platform is Android. public static bool isAndroidPlatform { get { #if UNITY_ANDROID return true; #else return false; #endif } } /// Checks if the current platform is iOS. /// True if the current platform is iOS. public static bool isIOSPlatform { get { #if UNITY_IOS return true; #else return false; #endif } } /// Checks if the current platform is tvOS. /// True if the current platform is tvOS. public static bool isTvOSPlatform { get { #if UNITY_TVOS return true; #else return false; #endif } } /// Checks if the current platform is WSA. /// True if the current platform is WSA. public static bool isWSAPlatform { get { #if UNITY_WSA || UNITY_XBOXONE return true; #else return false; #endif } } /// Checks if the current platform is XboxOne. /// True if the current platform is XboxOne. public static bool isXboxOnePlatform { get { #if UNITY_XBOXONE return true; #else return false; #endif } } /// Checks if the current platform is PS4. /// True if the current platform is PS4. public static bool isPS4Platform { get { #if UNITY_PS4 return true; #else return false; #endif } } /* /// Checks if the current platform is PS5. /// True if the current platform is PS5. public static bool isPS5Platform { get { #if UNITY_PS5 //TODO does that define exists? return true; #else return false; #endif } } */ /// Checks if the current platform is WebGL. /// True if the current platform is WebGL. public static bool isWebGLPlatform { get { #if UNITY_WEBGL return true; #else return false; #endif } } /// Checks if the current platform is Web (WebPlayer or WebGL). /// True if the current platform is Web (WebPlayer or WebGL). public static bool isWebPlatform => isWebGLPlatform; /// Checks if the current platform is Windows-based (Windows standalone, WSA or XboxOne). /// True if the current platform is Windows-based (Windows standalone, WSA or XboxOne). public static bool isWindowsBasedPlatform => isWindowsPlatform || isWSAPlatform || isXboxOnePlatform; /// Checks if the current platform is WSA-based (WSA or XboxOne). /// True if the current platform is WSA-based (WSA or XboxOne). public static bool isWSABasedPlatform => isWSAPlatform || isXboxOnePlatform; /// Checks if the current platform is Apple-based (macOS standalone, iOS or tvOS). /// True if the current platform is Apple-based (macOS standalone, iOS or tvOS). public static bool isAppleBasedPlatform => isMacOSPlatform || isIOSPlatform || isTvOSPlatform; /// Checks if the current platform is iOS-based (iOS or tvOS). /// True if the current platform is iOS-based (iOS or tvOS). public static bool isIOSBasedPlatform => isIOSPlatform || isTvOSPlatform; /// Checks if the current platform is mobile (Android and iOS). /// True if the current platform is mobile (Android and iOS). public static bool isMobilePlatform => isAndroidPlatform || isIOSBasedPlatform; /// Checks if we are inside the Editor. /// True if we are inside the Editor. public static bool isEditor => isWindowsEditor || isMacOSEditor || isLinuxEditor; /// Checks if we are inside the Windows Editor. /// True if we are inside the Windows Editor. public static bool isWindowsEditor { get { #if UNITY_EDITOR_WIN return true; #else return false; #endif } } /// Checks if we are inside the macOS Editor. /// True if we are inside the macOS Editor. public static bool isMacOSEditor { get { #if UNITY_EDITOR_OSX return true; #else return false; #endif } } /// Checks if we are inside the Linux Editor. /// True if we are inside the Linux Editor. public static bool isLinuxEditor { get { #if UNITY_EDITOR_LINUX return true; #else return false; #endif } } #endregion #endregion #region Static block #if UNITY_EDITOR static BaseHelper() { //Debug.Log("Static block"); initialize(); } #endif [RuntimeInitializeOnLoadMethod] private static void initialize() { //Debug.Log("initialize"); ApplicationIsPlaying = Application.isPlaying; /* if (!isEditorMode) { GameObject go = new GameObject("_HelperCT"); go.AddComponent(); GameObject.DontDestroyOnLoad(go); } */ } #endregion #region Public methods /// Creates a string of characters with a given length. /// Characters to generate the string (if more than one character is used, the generated string will be a randomized result of all characters) /// Length of the generated string /// Generated string public static string CreateString(string generateChars, int stringLength) { if (generateChars != null) { if (generateChars.Length > 1) { char[] chars = new char[stringLength]; for (int ii = 0; ii < stringLength; ii++) { chars[ii] = generateChars[_rnd.Next(0, generateChars.Length)]; } return new string(chars); } return generateChars.Length == 1 ? new string(generateChars[0], stringLength) : string.Empty; } return string.Empty; } /// Split the given text to lines and return it as list. /// Complete text fragment /// Ignore commente lines (optional, default: true) /// Number of skipped header lines (optional, default: 0) /// Number of skipped footer lines (optional, default: 0) /// Splitted lines as array public static System.Collections.Generic.List SplitStringToLines(string text, bool ignoreCommentedLines = true, int skipHeaderLines = 0, int skipFooterLines = 0) { System.Collections.Generic.List result = new System.Collections.Generic.List(100); if (string.IsNullOrEmpty(text)) { Debug.LogWarning( "Parameter 'text' is null or empty => 'SplitStringToLines()' will return an empty string list."); } else { string[] lines = Crosstales.Common.Util.BaseConstants.REGEX_LINEENDINGS.Split(text); for (int ii = 0; ii < lines.Length; ii++) { if (ii + 1 > skipHeaderLines && ii < lines.Length - skipFooterLines) { if (!string.IsNullOrEmpty(lines[ii])) { if (ignoreCommentedLines) { if (!lines[ii].CTStartsWith("#")) //valid and not disabled line? result.Add(lines[ii]); } else { result.Add(lines[ii]); } } } } } return result; } /// Format byte-value to Human-Readable-Form. /// Value in bytes /// Use SI-system (optional, default: false) /// Formatted byte-value in Human-Readable-Form. public static string FormatBytesToHRF(long bytes, bool useSI = false) { const string ci = "kMGTPE"; int index = 0; if (useSI) { if (-1000 < bytes && bytes < 1000) return bytes + " B"; while (bytes <= -999_950 || bytes >= 999_950) { bytes /= 1000; index++; } return $"{(bytes / 1000f):N2} {ci[index]}B"; } long absB = bytes == long.MinValue ? long.MaxValue : System.Math.Abs(bytes); if (absB < 1024) return bytes + " B"; long value = absB; for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { value >>= 10; index++; } value *= System.Math.Sign(bytes); return $"{(value / 1024f):N2} {ci[index]}iB"; } /// Format seconds to Human-Readable-Form. /// Value in seconds /// Formatted seconds in Human-Readable-Form. public static string FormatSecondsToHRF(double seconds) { bool wasMinus = seconds < 0; int totalSeconds = Mathf.Abs((int)seconds); int calcSeconds = totalSeconds % 60; if (seconds >= 86400) { int calcDays = totalSeconds / 86400; int calcHours = (totalSeconds -= calcDays * 86400) / 3600; int calcMinutes = (totalSeconds - calcHours * 3600) / 60; return $"{(wasMinus ? "-" : "")}{calcDays}d {calcHours}:{addLeadingZero(calcMinutes)}:{addLeadingZero(calcSeconds)}"; } if (seconds >= 3600) { int calcHours = totalSeconds / 3600; int calcMinutes = (totalSeconds - calcHours * 3600) / 60; return $"{(wasMinus ? "-" : "")}{calcHours}:{addLeadingZero(calcMinutes)}:{addLeadingZero(calcSeconds)}"; } else { int calcMinutes = totalSeconds / 60; return $"{(wasMinus ? "-" : "")}{calcMinutes}:{addLeadingZero(calcSeconds)}"; } } /// /// Generate nice HSV colors. /// Based on https://gist.github.com/rje/6206099 /// /// Hue /// Saturation /// Value /// Alpha (optional) /// True if the current platform is supported. public static Color HSVToRGB(float h, float s, float v, float a = 1f) { if (s < Crosstales.Common.Util.BaseConstants.FLOAT_TOLERANCE) return new Color(v, v, v, a); float _h = h / 60f; int sector = Mathf.FloorToInt(_h); float fact = _h - sector; float p = v * (1f - s); float q = v * (1f - s * fact); float t = v * (1f - s * (1f - fact)); switch (sector) { case 0: return new Color(v, t, p, a); case 1: return new Color(q, v, p, a); case 2: return new Color(p, v, t, a); case 3: return new Color(p, q, v, a); case 4: return new Color(t, p, v, a); default: return new Color(v, p, q, a); } } /// Generates a "Lorem Ipsum" based on various parameters. /// Length of the text /// Minimum number of sentences for the text (optional, default: 1) /// Maximal number of sentences for the text (optional, default: int.MaxValue) /// Minimum number of words per sentence (optional, default: 1) /// Maximal number of words per sentence (optional, default: 15) /// "Lorem Ipsum" based on the given parameters. public static string GenerateLoremIpsum(int length, int minSentences = 1, int maxSentences = int.MaxValue, int minWords = 1, int maxWords = 15) { string[] words = { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "sed", "diam", "nonummy", "nibh", "euismod", "tincidunt", "ut", "laoreet", "dolore", "magna", "aliquam", "erat" }; int numSentences = _rnd.Next(maxSentences - minSentences) + minSentences + 1; System.Text.StringBuilder result = new System.Text.StringBuilder(); for (int s = 0; s < numSentences && result.Length <= length; s++) { int numWords = _rnd.Next(maxWords - minWords) + minWords + 1; for (int w = 0; w < numWords && result.Length <= length; w++) { if (w > 0) result.Append(" "); result.Append( w == 0 ? words[_rnd.Next(words.Length)].CTToTitleCase() : words[_rnd.Next(words.Length)]); } result.Append(". "); } string text = result.ToString(); if (length > 0 && text.Length > length) text = text.Substring(0, length - 1) + "."; return text; } /// Converts a SystemLanguage to an ISO639-1 code. Returns "en" if the SystemLanguage could not be converted. /// SystemLanguage to convert. /// "ISO639-1 code for the given SystemLanguage. public static string LanguageToISO639(SystemLanguage language) { switch (language) { case SystemLanguage.Afrikaans: return "af"; case SystemLanguage.Arabic: return "ar"; case SystemLanguage.Basque: return "eu"; case SystemLanguage.Belarusian: return "be"; case SystemLanguage.Bulgarian: return "bg"; case SystemLanguage.Catalan: return "ca"; case SystemLanguage.ChineseSimplified: case SystemLanguage.ChineseTraditional: case SystemLanguage.Chinese: return "zh"; case SystemLanguage.Czech: return "cs"; case SystemLanguage.Danish: return "da"; case SystemLanguage.Dutch: return "nl"; case SystemLanguage.English: return "en"; case SystemLanguage.Estonian: return "et"; case SystemLanguage.Faroese: return "fo"; case SystemLanguage.Finnish: return "fi"; case SystemLanguage.French: return "fr"; case SystemLanguage.German: return "de"; case SystemLanguage.Greek: return "el"; case SystemLanguage.Hebrew: return "he"; case SystemLanguage.Hungarian: return "hu"; case SystemLanguage.Icelandic: return "is"; case SystemLanguage.Indonesian: return "id"; case SystemLanguage.Italian: return "it"; case SystemLanguage.Japanese: return "ja"; case SystemLanguage.Korean: return "ko"; case SystemLanguage.Latvian: return "lv"; case SystemLanguage.Lithuanian: return "lt"; case SystemLanguage.Norwegian: return "nb"; //return "no"; case SystemLanguage.Polish: return "pl"; case SystemLanguage.Portuguese: return "pt"; case SystemLanguage.Romanian: return "ro"; case SystemLanguage.Russian: return "ru"; //case SystemLanguage.SerboCroatian: // return "sh"; case SystemLanguage.Slovak: return "sk"; case SystemLanguage.Slovenian: return "sl"; case SystemLanguage.Spanish: return "es"; case SystemLanguage.Swedish: return "sv"; case SystemLanguage.Thai: return "th"; case SystemLanguage.Turkish: return "tr"; case SystemLanguage.Ukrainian: return "uk"; case SystemLanguage.Vietnamese: return "vi"; default: return "en"; } } /// Converts an ISO639-1 code to a SystemLanguage. Returns SystemLanguage.English if the code could not be converted. /// ISO639-1 code to convert. /// "SystemLanguage for the given ISO639-1 code. public static SystemLanguage ISO639ToLanguage(string isoCode) { if (!string.IsNullOrEmpty(isoCode) && isoCode.Length >= 2) { string code = isoCode.Substring(0, 2).ToLower(); switch (code) { case "af": return SystemLanguage.Afrikaans; case "ar": return SystemLanguage.Arabic; case "eu": return SystemLanguage.Basque; case "be": return SystemLanguage.Belarusian; case "bg": return SystemLanguage.Bulgarian; case "ca": return SystemLanguage.Catalan; case "zh": return SystemLanguage.Chinese; case "cs": return SystemLanguage.Czech; case "da": return SystemLanguage.Danish; case "nl": return SystemLanguage.Dutch; case "en": return SystemLanguage.English; case "et": return SystemLanguage.Estonian; case "fo": return SystemLanguage.Faroese; case "fi": return SystemLanguage.Finnish; case "fr": return SystemLanguage.French; case "de": return SystemLanguage.German; case "el": return SystemLanguage.Greek; case "he": return SystemLanguage.Hebrew; case "hu": return SystemLanguage.Hungarian; case "is": return SystemLanguage.Icelandic; case "id": return SystemLanguage.Indonesian; case "it": return SystemLanguage.Italian; case "ja": return SystemLanguage.Japanese; case "ko": return SystemLanguage.Korean; case "lv": return SystemLanguage.Latvian; case "lt": return SystemLanguage.Lithuanian; case "no": case "nb": return SystemLanguage.Norwegian; case "pl": return SystemLanguage.Polish; case "pt": return SystemLanguage.Portuguese; case "ro": return SystemLanguage.Romanian; case "ru": return SystemLanguage.Russian; //case "sh": // return SystemLanguage.SerboCroatian; case "sk": return SystemLanguage.Slovak; case "sl": return SystemLanguage.Slovenian; case "es": return SystemLanguage.Spanish; case "sv": return SystemLanguage.Swedish; case "th": return SystemLanguage.Thai; case "tr": return SystemLanguage.Turkish; case "uk": return SystemLanguage.Ukrainian; case "vi": return SystemLanguage.Vietnamese; default: return SystemLanguage.English; } } return SystemLanguage.English; } /// Invokes a public static method on a full qualified class. /// Full qualified name of the class /// Public static method of the class to execute /// Parameters for the method (optional) public static object InvokeMethod(string className, string methodName, params object[] parameters) { #if UNITY_WSA return null; #else if (string.IsNullOrEmpty(className)) { Debug.LogWarning("'className' is null or empty; can not execute."); return null; } if (string.IsNullOrEmpty(methodName)) { Debug.LogWarning("'methodName' is null or empty; can not execute."); return null; } foreach (System.Type type in System.AppDomain.CurrentDomain.GetAssemblies() .SelectMany(assembly => assembly.GetTypes())) { try { if (type.FullName?.Equals(className) == true) if (type.IsClass) return type.GetMethod(methodName, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)?.Invoke(null, parameters); } catch (System.Exception ex) { Debug.LogWarning($"Could not execute method call: {ex}"); } } return null; #endif } /// Returns an argument for a name from the url or command line. /// Name for the argument /// Argument for a name from the url or command line. public static string GetArgument(string name) { if (!string.IsNullOrEmpty(name)) { string[] args = GetArguments(); for (int ii = 0; ii < args.Length; ii++) { if (name.CTEquals(args[ii]) && args.Length > ii + 1) return args[ii + 1]; } } return null; } /// Returns all arguments from the url or command line. /// Arguments from the url or command line. public static string[] GetArguments() { if (_args == null) { #if UNITY_WEBGL && !UNITY_EDITOR //#if (UNITY_WEBGL || UNITY_ANDROID) && !UNITY_EDITOR // url with parameters syntax : http://example.com?arg1=value1&arg2=value2 string parameters = Application.absoluteURL.Substring(Application.absoluteURL.IndexOf("?") + 1); //Debug.Log("URL parameters: " + parameters); _args = parameters.Split(new char[] { '&', '=' }); #else #if !UNITY_WSA _args = System.Environment.GetCommandLineArgs(); #endif #endif } return _args; } /* /// Generates a string of all latin latin characters (ABC...xyz). /// "String of all latin latin characters. public static string GenerateLatinABC() { return GenerateLatinUppercaseABC() + GenerateLatinLowercaseABC(); } /// Generates a string of all latin latin characters in uppercase (ABC...XYZ). /// "String of all latin latin characters in uppercase. public static string GenerateLatinUppercaseABC() { System.Text.StringBuilder result = new System.Text.StringBuilder(); for (int ii = 65; ii <= 90; ii++) { result.Append((char)ii); } return result.ToString(); } /// Generates a string of all latin latin characters in lowercase (abc...xyz). /// "String of all latin latin characters in lowercase. public static string GenerateLatinLowercaseABC() { System.Text.StringBuilder result = new System.Text.StringBuilder(); for (int ii = 97; ii <= 122; ii++) { result.Append((char)ii); } return result.ToString(); } */ #endregion #region Private methods private static string addLeadingZero(int value) { return value < 10 ? "0" + value : value.ToString(); } #endregion // StringHelper /* public static byte[] GetBytesFromText(string text) { return new UnicodeEncoding().GetBytes(text); } public static string GetTextFromBytes(byte[] bytes) { return new UnicodeEncoding().GetString(bytes, 0, bytes.Length); } public static byte[] GetBytesFromBase64(string text) { return Convert.FromBase64String(text); } public static string GetBase64FromBytes(byte[] bytes) { return Convert.ToBase64String(bytes); } */ // MathHelper /* public static bool IsInRange(float actValue, float refValue, float range) { return (actValue >= refValue-range) && (actValue <= refValue+range); } public static bool IsInRange(int actValue, int refValue, int range) { return (actValue >= refValue-range) && (actValue <= refValue+range); } */ // Add Math3dHelper? // Color Helper /* public static Color HexToColor(string hex) { if (string.IsNullOrEmpty(hex)) throw new ArgumentNullException("hex"); byte r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); byte g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); byte b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); return new Color32(r, g, b, 255); } */ } } // © 2015-2023 crosstales LLC (https://www.crosstales.com)