18 Revīzijas 2fd49a2c83 ... c3c35caf32

Autors SHA1 Ziņojums Datums
  MirzkisD1Ex0 c3c35caf32 1 8 mēneši atpakaļ
  MirzkisD1Ex0 bee2a9eea1 1 8 mēneši atpakaļ
  MirzkisD1Ex0 1d74a21f3f 1 8 mēneši atpakaļ
  MirzkisD1Ex0 838189bcfb 1 8 mēneši atpakaļ
  MirzkisD1Ex0 daca7661c8 1 9 mēneši atpakaļ
  MirzkisD1Ex0 f09b12ea1b 1 10 mēneši atpakaļ
  MirzkisD1Ex0 42007d6a25 1 11 mēneši atpakaļ
  MirzkisD1Ex0 61a9f518a0 1 11 mēneši atpakaļ
  MirzkisD1Ex0 9bf36778c4 1 11 mēneši atpakaļ
  MirzkisD1Ex0 1e57257b99 Merge branch 'main' of https://github.com/MirzkisD1Ex0/ToneTuneToolkit 11 mēneši atpakaļ
  MirzkisD1Ex0 4b5ce27998 1 11 mēneši atpakaļ
  MirzkisD1Ex0 96b26b5226 1 11 mēneši atpakaļ
  MirzkisD1Ex0 cef6a07eab 1 11 mēneši atpakaļ
  MirzkisD1Ex0 3dcebcf904 1 1 gadu atpakaļ
  MirzkisD1Ex0 94b58e333d 1 1 gadu atpakaļ
  MirzkisD1Ex0 9843aa51fd 1 1 gadu atpakaļ
  MirzkisD1Ex0 6c2bed4e76 1 1 gadu atpakaļ
  MirzkisD1Ex0 dff97c3112 1 1 gadu atpakaļ
100 mainītis faili ar 6494 papildinājumiem un 1932 dzēšanām
  1. BIN
      Materials/Alpha Video Mask/01.png
  2. BIN
      Materials/Alpha Video Mask/02.png
  3. BIN
      Materials/Alpha Video Mask/03_整体场景结构.png
  4. 65 0
      Materials/Alpha Video Mask/AlphaVideoHandler.cs
  5. 56 0
      Materials/Alpha Video Mask/MaskedShader.shader
  6. 209 219
      Materials/Backend & Upload/BackendNetManager.cs
  7. 254 0
      Materials/Backend & Upload/Upload2OYManager.cs
  8. 97 0
      Materials/Backend & Upload/Upload2ZCManager.cs
  9. 0 247
      Materials/Backend & Upload/UploadManager.cs
  10. 45 0
      Materials/CanvasGroup_DG/StageManager.cs
  11. 79 0
      Materials/CanvasGroup_DG/Tools/CanvasGroupMaster.cs
  12. BIN
      Materials/Game/JigsawGame/JigsawGame.unitypackage
  13. BIN
      Materials/Game/JigsawGame/JigsawPuzzle20241104a_app.zip
  14. BIN
      Materials/Game/JigsawGame/SwitchJigsawGame.unitypackage
  15. BIN
      Materials/Game/Tetris/Tetris.unitypackage
  16. 5 0
      Materials/Keyboard/新建文本文档.txt
  17. 125 0
      Materials/LeapMotion/LeapMotionManager.cs
  18. 303 0
      Materials/MediaPipe/人体骨骼识别/WebCamSource.cs
  19. 2 0
      Materials/MediaPipe/人体骨骼识别/readme.txt
  20. 0 169
      Materials/OpenCV/面部识别模块/FaceDetecter.cs
  21. 187 0
      Materials/OpenCV/面部识别模块/FacialRecognitionHandler.cs
  22. BIN
      Materials/OpenCV/面部识别模块/OpenCVToolkit.unitypackage
  23. 0 0
      Materials/OpenCV/面部识别模块/StreamingAssets/haarcascade_frontalface_alt2.xml
  24. 236 0
      Materials/OpenCV/面部识别模块/WebCamTextureProcesser.cs
  25. 0 229
      Materials/OpenCV/面部识别模块/WebCamTextureToMat.cs
  26. 1 0
      Materials/OpenCV/面部识别模块/readme.txt
  27. 142 0
      Materials/ScrollView/20241125_左右滑动限位圆点/ScrollViewHandler.cs
  28. 44 20
      Materials/SequenceFrame/SequenceFrameHandler.cs
  29. 32 20
      Materials/SerialPortUtilityPro/SerialPortUtilityProConfiger.cs
  30. 42 35
      Materials/SerialPortUtilityPro/SerialPortUtilityProManager.cs
  31. 55 0
      Materials/SerialPortUtilityPro/SerialPortUtilityProResponder.cs
  32. 0 0
      Materials/SerialPortUtilityPro/readme.txt
  33. 55 0
      Materials/SerialPortUtilityPro/红外感应/SerialPortUtilityProResponder.cs
  34. BIN
      Materials/SerialPortUtilityPro/红外感应/微信截图_20241217163407.png
  35. 127 0
      Materials/SocketIO/SocketIOManager.cs
  36. 61 0
      Materials/UI活动检测/ClickListener.cs
  37. 2 6
      Materials/WebGL/SkipUnityLogo/WebGLSkipUnityLogo.cs
  38. 0 0
      Materials/WebGL/SkipUnityLogo/readme.txt
  39. 0 0
      Materials/WebGL/背景透明化/Plugins/TransparentBackground.jslib
  40. 3 0
      Materials/WebGL/背景透明化/Unity WebGL背景透明化.txt
  41. 0 99
      Materials/后置相机拍摄/WebCamManager.cs
  42. 55 0
      Materials/图片选择和加载/ImageLoader.cs
  43. 1 0
      Materials/打包后分辨率设置/123.txt
  44. 4 2
      ToneTuneToolkit/Assets/Examples/001_FileNameCapturer/Scripts/FNC.cs
  45. 2 2
      ToneTuneToolkit/Assets/Examples/003_TextLoader/Scripts/TL.cs
  46. 8 0
      ToneTuneToolkit/Assets/Examples/024Tips.meta
  47. 8 0
      ToneTuneToolkit/Assets/Examples/024Tips/Scenes.meta
  48. 204 390
      ToneTuneToolkit/Assets/Examples/024Tips/Scenes/Example.unity
  49. 7 0
      ToneTuneToolkit/Assets/Examples/024Tips/Scenes/Example.unity.meta
  50. 8 0
      ToneTuneToolkit/Assets/Examples/024Tips/Scripts.meta
  51. 18 0
      ToneTuneToolkit/Assets/Examples/024Tips/Scripts/Tips.cs
  52. 1 1
      ToneTuneToolkit/Assets/Examples/024Tips/Scripts/Tips.cs.meta
  53. 2724 0
      ToneTuneToolkit/Assets/StreamingAssets/ToneTuneToolkit/configs/sensitivewords.json
  54. 7 0
      ToneTuneToolkit/Assets/StreamingAssets/ToneTuneToolkit/configs/sensitivewords.json.meta
  55. 5 5
      ToneTuneToolkit/Assets/StreamingAssets/ToneTuneToolkit/configs/udpconfig.json
  56. 33 6
      ToneTuneToolkit/Assets/ToneTuneToolkit/README.md
  57. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/EventListener.cs
  58. 0 90
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/FileNameCapturer.cs
  59. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/PathChecker.cs
  60. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/SingletonMaster.cs
  61. 46 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/TTTDebug.cs
  62. 0 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/TTTDebug.cs.meta
  63. 0 45
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/TipTools.cs
  64. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/ToolkitManager.cs
  65. 14 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/DataConverter.cs
  66. 45 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/DataProcessor.cs
  67. 1 1
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/DataProcessor.cs.meta
  68. 54 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/ImageLoader.cs
  69. 11 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/ImageLoader.cs.meta
  70. 4 4
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/JsonManager.cs
  71. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/LitJsonManager.cs
  72. 191 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/SensitiveWordUtility.cs
  73. 11 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/SensitiveWordUtility.cs.meta
  74. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/TextLoader.cs
  75. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/TimestampCapturer.cs
  76. 4 4
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/CreateAssetBundles.cs
  77. 19 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/EditorStorage.cs
  78. 11 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/EditorStorage.cs.meta
  79. 113 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/RenameFolders.cs
  80. 11 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/RenameFolders.cs.meta
  81. 187 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/UpdateCopyrights.cs
  82. 11 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/UpdateCopyrights.cs.meta
  83. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Funny/BubbleSort.cs
  84. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/IO/FTPMaster.cs
  85. 100 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/IO/FileCapturer.cs
  86. 0 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/IO/FileCapturer.cs.meta
  87. 6 5
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/FullAngleScreenshotTool.cs
  88. 150 56
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/ScreenshotMaster.cs
  89. 1 1
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/ScreenshotMaster.cs.meta
  90. 0 114
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/ScreenshotMasterLite.cs
  91. 0 73
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/ScreenshotMasterMini.cs
  92. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/TextureProcessor.cs
  93. 42 41
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/WebCamHandler.cs
  94. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Mobile/ObjectRotateAndScale.cs
  95. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/MultimediaExhibitionHall/LED/LEDCommandCenter.cs
  96. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/MultimediaExhibitionHall/LED/LEDCommandHub.cs
  97. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/MultimediaExhibitionHall/LED/LEDHandler.cs
  98. 3 3
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/MultimediaExhibitionHall/LED/LEDNuclearShow.cs
  99. 8 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Networking.meta
  100. 97 0
      ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Networking/JsonUploadManager.cs

BIN
Materials/Alpha Video Mask/01.png


BIN
Materials/Alpha Video Mask/02.png


BIN
Materials/Alpha Video Mask/03_整体场景结构.png


+ 65 - 0
Materials/Alpha Video Mask/AlphaVideoHandler.cs

@@ -0,0 +1,65 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Video;
+
+public class AlphaVideoHandler : MonoBehaviour
+{
+  public static AlphaVideoHandler Instance;
+
+  private VideoPlayer videoPlayer;
+
+  // ==================================================
+
+  private void Awake() => Instance = this;
+  private void Start() => Init();
+  private void Update() => ShortcutKey();
+
+  // ==================================================
+
+  private void Init()
+  {
+    videoPlayer = GetComponent<VideoPlayer>();
+    StartCoroutine(nameof(InitAction));
+    return;
+  }
+
+  private IEnumerator InitAction()
+  {
+    yield return new WaitForSeconds(1f); // 为了视频加载完毕而延迟
+    videoPlayer.Play();
+    yield return new WaitForSeconds(0.2f); // 显示擦除的短暂空白
+    videoPlayer.Pause();
+    yield break;
+  }
+
+  // ==================================================
+
+  public void SwitchAlphaVideo(bool value)
+  {
+    if (value)
+    {
+      videoPlayer.Play();
+    }
+    else
+    {
+      videoPlayer.Pause();
+    }
+    return;
+  }
+
+  // ==================================================
+
+  private void ShortcutKey()
+  {
+    if (Input.GetKeyDown(KeyCode.Q))
+    {
+      SwitchAlphaVideo(true);
+    }
+    if (Input.GetKeyDown(KeyCode.W))
+    {
+      SwitchAlphaVideo(false);
+    }
+    return;
+  }
+}

+ 56 - 0
Materials/Alpha Video Mask/MaskedShader.shader

@@ -0,0 +1,56 @@
+Shader "Custom/MaskedShader" 
+{
+    Properties 
+    {
+        _MainTex ("Texture", 2D) = "white" {}
+        _AlphaVideo ("Alpha Video", 2D) = "white" {}
+    }
+    SubShader 
+    {
+        Tags { "RenderType"="Transparent" "Queue"="Transparent" }
+        LOD 100
+ 
+        Pass
+        {
+            Blend SrcAlpha OneMinusSrcAlpha
+ 
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+ 
+            #include "UnityCG.cginc"
+ 
+            struct appdata
+            {
+                float4 vertex : POSITION;
+                float2 uv : TEXCOORD0;
+            };
+ 
+            struct v2f
+            {
+                float2 uv : TEXCOORD0;
+                float4 vertex : SV_POSITION;
+            };
+ 
+            sampler2D _MainTex;
+            sampler2D _AlphaVideo;
+            float4 _MainTex_ST;
+ 
+            v2f vert (appdata v)
+            {
+                v2f o;
+                o.vertex = UnityObjectToClipPos(v.vertex);
+                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
+                return o;
+            }
+ 
+            fixed4 frag (v2f i) : SV_Target
+            {
+                fixed4 col = tex2D(_MainTex, i.uv);
+                fixed alpha = tex2D(_AlphaVideo, i.uv).r; // Assuming the alpha is in the red channel
+                return fixed4(col.rgb, alpha);
+            }
+            ENDCG
+        }
+    }
+}

+ 209 - 219
Materials/Backend & Upload/BackendNetManager.cs

@@ -6,265 +6,255 @@ using UnityEngine;
 using UnityEngine.Events;
 using UnityEngine.Networking;
 
-namespace LonginesYogaPhotoJoy
+/// <summary>
+/// 后端对接专用
+/// </summary>
+public class BackendNetManager : MonoBehaviour
 {
-  /// <summary>
-  /// 后端对接专用
-  /// </summary>
-  public class BackendNetManager : MonoBehaviour
-  {
-    public static BackendNetManager Instance;
+  public static BackendNetManager Instance;
 
-    // ==================================================
+  // ==================================================
 
-    private void Awake()
-    {
-      Instance = this;
-    }
+  private void Awake() => Instance = this;
+  private void Start() => Init();
 
-    private void Start()
-    {
-      Init();
-    }
+  // private void Update()
+  // {
+  //   if (Input.GetKeyUp(KeyCode.U))
+  //   {
+  //     string testPath = @"D:\2024-06-03-21-28-17.png";
+  //     UploadPhoto2Backend(ToneTuneToolkit.Media.TextureProcessor.ReadTexture(testPath));
+  //   }
+  // }
 
-    // private void Update()
-    // {
-    //   if (Input.GetKeyUp(KeyCode.U))
-    //   {
-    //     string testPath = @"D:\2024-06-03-21-28-17.png";
-    //     UploadPhoto2Backend(ToneTuneToolkit.Media.TextureProcessor.ReadTexture(testPath));
-    //   }
-    // }
+  // ==================================================
 
-    // ==================================================
+  private void Init()
+  {
+    GetStartupQR();
+    return;
+  }
 
-    private void Init()
-    {
-      GetStartupQR();
-      return;
-    }
+  // ==================================================
+  #region 获取启动QR码
+  public event UnityAction<string> OnGetStartupInfoComplete;
+  public StartupQRResponse QRData;
 
-    // ==================================================
-    #region 获取启动QR码
-    public event UnityAction<string> OnGetStartupInfoComplete;
-    public StartupQRResponse QRData;
+  private const string qrURL = "https://open.skyelook.com/api/longine_gz/startQr";
+  private const string deviceCode = "Test_001";
+  public void GetStartupQR()
+  {
+    StartCoroutine("GetStartupQRAction");
+    return;
+  }
+  private IEnumerator GetStartupQRAction()
+  {
+    WWWForm wwwForm = new WWWForm();
+    wwwForm.AddField("device_code", deviceCode);
 
-    private const string qrURL = "https://open.skyelook.com/api/longine_gz/startQr";
-    private const string deviceCode = "Test_001";
-    public void GetStartupQR()
-    {
-      StartCoroutine("GetStartupQRAction");
-      return;
-    }
-    private IEnumerator GetStartupQRAction()
+    using (UnityWebRequest www = UnityWebRequest.Post(qrURL, wwwForm)) // 获取二维码链接
     {
-      WWWForm wwwForm = new WWWForm();
-      wwwForm.AddField("device_code", deviceCode);
+      // www.SetRequestHeader("Content-Type", "multipart/form-data"); // 永远永远不要用这个
+      www.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+      DownloadHandler downloadHandler = new DownloadHandlerBuffer();
+      www.downloadHandler = downloadHandler;
+      yield return www.SendWebRequest();
 
-      using (UnityWebRequest www = UnityWebRequest.Post(qrURL, wwwForm)) // 获取二维码链接
+      if (www.result != UnityWebRequest.Result.Success)
+      {
+        Debug.Log($"{www.error}...<color=red>[ER]</color>");
+        yield break;
+      }
+      else
       {
-        // www.SetRequestHeader("Content-Type", "multipart/form-data"); // 永远永远不要用这个
-        www.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
-        DownloadHandler downloadHandler = new DownloadHandlerBuffer();
-        www.downloadHandler = downloadHandler;
-        yield return www.SendWebRequest();
+        Debug.Log($"QRCode Json :\n{www.downloadHandler.text}...<color=green>[OK]</color>");
+        QRData = JsonConvert.DeserializeObject<StartupQRResponse>(www.downloadHandler.text.ToString());
 
-        if (www.result != UnityWebRequest.Result.Success)
+        if (OnGetStartupInfoComplete != null)
         {
-          Debug.Log($"{www.error}...<color=red>[ER]</color>");
-          yield break;
-        }
-        else
-        {
-          Debug.Log($"QRCode Json :\n{www.downloadHandler.text}...<color=green>[OK]</color>");
-          QRData = JsonConvert.DeserializeObject<StartupQRResponse>(www.downloadHandler.text.ToString());
-
-          if (OnGetStartupInfoComplete != null)
-          {
-            OnGetStartupInfoComplete(QRData.data.start_code);
-          }
-          Debug.Log("QRCode Json...<color=green>[OK]</color>");
+          OnGetStartupInfoComplete(QRData.data.start_code);
         }
+        Debug.Log("QRCode Json...<color=green>[OK]</color>");
       }
-
-      // using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(QRData.data.qr_url)) // 获取二维码
-      // {
-      //   yield return www.SendWebRequest();
-      //   if (www.result != UnityWebRequest.Result.Success)
-      //   {
-      //     Debug.Log($"{www.error}...<color=red>[ER]</color>");
-      //     yield break;
-      //   }
-      //   else
-      //   {
-      //     if (OnGetStartupQRComplete != null)
-      //     {
-      //       OnGetStartupQRComplete(DownloadHandlerTexture.GetContent(www));
-      //     }
-      //     Debug.Log("QRCode Image...<color=green>[OK]</color>");
-      //     StartCoroutine("QueryUserStatus"); // 启动轮询
-      //   }
-      // }
-      yield break;
-    }
-
-    [Serializable]
-    public class StartupQRResponse
-    {
-      public int code;
-      public string message;
-      public StartupQRData data;
     }
-    [Serializable]
-    public class StartupQRData
-    {
-      public string start_code;
-      public string qr_url;
-    }
-    #endregion
-
-    // ==================================================
-    #region 轮询是否有玩家在玩
-    // public event UnityAction OnUserActive;
-    // public StatusResponse StatusData;
 
-    // private const string statusURL = "https://open.skyelook.com/api/longine_gz/startStatus";
-    // private IEnumerator QueryUserStatus()
+    // using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(QRData.data.qr_url)) // 获取二维码
     // {
-    //   WWWForm wwwForm = new WWWForm();
-    //   wwwForm.AddField("device_code", deviceCode);
-    //   wwwForm.AddField("start_code", QRData.data.start_code);
-
-    //   using (UnityWebRequest www = UnityWebRequest.Post(statusURL, wwwForm))
+    //   yield return www.SendWebRequest();
+    //   if (www.result != UnityWebRequest.Result.Success)
     //   {
-    //     www.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
-    //     www.downloadHandler = new DownloadHandlerBuffer();
-    //     yield return www.SendWebRequest();
-    //     if (www.result != UnityWebRequest.Result.Success)
-    //     {
-    //       Debug.Log($"{www.error}...<color=red>[ER]</color>");
-    //       yield break;
-    //     }
-    //     else
-    //     {
-    //       StatusData = JsonConvert.DeserializeObject<StatusResponse>(www.downloadHandler.text);
-    //       // Debug.Log($"{www.downloadHandler.text}...<color=green>[OK]</color>");
-    //       Debug.Log("Query...<color=green>[OK]</color>");
-    //     }
+    //     Debug.Log($"{www.error}...<color=red>[ER]</color>");
+    //     yield break;
     //   }
-
-    //   // 轮询 // 启动游戏
-    //   switch (StatusData.data.status)
+    //   else
     //   {
-    //     default: break;
-    //     case "0":
-    //       yield return new WaitForSeconds(2f);
-    //       StartCoroutine("QueryUserStatus");
-    //       break;
-    //     case "1":
-    //       if (OnUserActive != null)
-    //       {
-    //         OnUserActive();
-    //       }
-    //       StopCoroutine("QueryUserStatus");
-    //       break;
-    //     case "2": break;
-    //     case "3": break;
+    //     if (OnGetStartupQRComplete != null)
+    //     {
+    //       OnGetStartupQRComplete(DownloadHandlerTexture.GetContent(www));
+    //     }
+    //     Debug.Log("QRCode Image...<color=green>[OK]</color>");
+    //     StartCoroutine("QueryUserStatus"); // 启动轮询
     //   }
-    //   yield break;
     // }
+    yield break;
+  }
 
-    // [Serializable]
-    // public class StatusResponse
-    // {
-    //   public int code;
-    //   public string message;
-    //   public StatusResponseData data;
-    // }
-    // [Serializable]
-    // public class StatusResponseData
-    // {
-    //   public string status;
-    //   public string status_text;
-    // }
-    #endregion
+  [Serializable]
+  public class StartupQRResponse
+  {
+    public int code;
+    public string message;
+    public StartupQRData data;
+  }
+  [Serializable]
+  public class StartupQRData
+  {
+    public string start_code;
+    public string qr_url;
+  }
+  #endregion
 
-    // ==================================================
-    #region 上传图片
-    public UploadResponse UploadData;
+  // ==================================================
+  #region 轮询是否有玩家在玩
+  // public event UnityAction OnUserActive;
+  // public StatusResponse StatusData;
 
-    public event UnityAction<Texture2D> OnUpload;
+  // private const string statusURL = "https://open.skyelook.com/api/longine_gz/startStatus";
+  // private IEnumerator QueryUserStatus()
+  // {
+  //   WWWForm wwwForm = new WWWForm();
+  //   wwwForm.AddField("device_code", deviceCode);
+  //   wwwForm.AddField("start_code", QRData.data.start_code);
 
-    private const string uploadURL = "https://open.skyelook.com/api/longine_gz/uploadThumb";
+  //   using (UnityWebRequest www = UnityWebRequest.Post(statusURL, wwwForm))
+  //   {
+  //     www.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+  //     www.downloadHandler = new DownloadHandlerBuffer();
+  //     yield return www.SendWebRequest();
+  //     if (www.result != UnityWebRequest.Result.Success)
+  //     {
+  //       Debug.Log($"{www.error}...<color=red>[ER]</color>");
+  //       yield break;
+  //     }
+  //     else
+  //     {
+  //       StatusData = JsonConvert.DeserializeObject<StatusResponse>(www.downloadHandler.text);
+  //       // Debug.Log($"{www.downloadHandler.text}...<color=green>[OK]</color>");
+  //       Debug.Log("Query...<color=green>[OK]</color>");
+  //     }
+  //   }
 
-    public void UploadPhoto2Backend(Texture2D texture2D)
-    {
-      StartCoroutine(UploadPhoto2BackendAction(texture2D));
-      return;
-    }
+  //   // 轮询 // 启动游戏
+  //   switch (StatusData.data.status)
+  //   {
+  //     default: break;
+  //     case "0":
+  //       yield return new WaitForSeconds(2f);
+  //       StartCoroutine("QueryUserStatus");
+  //       break;
+  //     case "1":
+  //       if (OnUserActive != null)
+  //       {
+  //         OnUserActive();
+  //       }
+  //       StopCoroutine("QueryUserStatus");
+  //       break;
+  //     case "2": break;
+  //     case "3": break;
+  //   }
+  //   yield break;
+  // }
 
-    private IEnumerator UploadPhoto2BackendAction(Texture2D texture2D)
-    {
-      byte[] bytes = texture2D.EncodeToPNG(); // 图转比特流
-      string base64 = "data:image/png;base64," + Convert.ToBase64String(bytes);
+  // [Serializable]
+  // public class StatusResponse
+  // {
+  //   public int code;
+  //   public string message;
+  //   public StatusResponseData data;
+  // }
+  // [Serializable]
+  // public class StatusResponseData
+  // {
+  //   public string status;
+  //   public string status_text;
+  // }
+  #endregion
 
-      WWWForm wwwForm = new WWWForm();
-      wwwForm.AddField("device_code", deviceCode);
-      wwwForm.AddField("start_code", QRData.data.start_code);
-      wwwForm.AddField("file", base64);
-      // wwwForm.AddBinaryData("file", bytes);
+  // ==================================================
+  #region 上传图片
+  public UploadResponse UploadData;
 
-      using (UnityWebRequest www = UnityWebRequest.Post(uploadURL, wwwForm))
+  public event UnityAction<Texture2D> OnUpload;
+
+  private const string uploadURL = "https://open.skyelook.com/api/longine_gz/uploadThumb";
+
+  public void UploadPhoto2Backend(Texture2D texture2D)
+  {
+    StartCoroutine(UploadPhoto2BackendAction(texture2D));
+    return;
+  }
+
+  private IEnumerator UploadPhoto2BackendAction(Texture2D texture2D)
+  {
+    byte[] bytes = texture2D.EncodeToPNG(); // 图转比特流
+    string base64 = "data:image/png;base64," + Convert.ToBase64String(bytes);
+
+    WWWForm wwwForm = new WWWForm();
+    wwwForm.AddField("device_code", deviceCode);
+    wwwForm.AddField("start_code", QRData.data.start_code);
+    wwwForm.AddField("file", base64);
+    // wwwForm.AddBinaryData("file", bytes);
+
+    using (UnityWebRequest www = UnityWebRequest.Post(uploadURL, wwwForm))
+    {
+      www.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+      www.downloadHandler = new DownloadHandlerBuffer();
+      yield return www.SendWebRequest();
+      if (www.result != UnityWebRequest.Result.Success)
       {
-        www.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
-        www.downloadHandler = new DownloadHandlerBuffer();
-        yield return www.SendWebRequest();
-        if (www.result != UnityWebRequest.Result.Success)
-        {
-          Debug.Log($"{www.error}...<color=red>[ER]</color>");
-          yield break;
-        }
-        else
-        {
-          Debug.Log("Upload...<color=green>[OK]</color>");
-          Debug.Log(www.downloadHandler.text.ToString());
-          UploadData = JsonConvert.DeserializeObject<UploadResponse>(www.downloadHandler.text.ToString());
-        }
+        Debug.Log($"{www.error}...<color=red>[ER]</color>");
+        yield break;
+      }
+      else
+      {
+        Debug.Log("Upload...<color=green>[OK]</color>");
+        Debug.Log(www.downloadHandler.text.ToString());
+        UploadData = JsonConvert.DeserializeObject<UploadResponse>(www.downloadHandler.text.ToString());
       }
+    }
 
-      using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(UploadData.data.qr_url)) // 搞最终QR图 // DEBUG
+    using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(UploadData.data.qr_url)) // 搞最终QR图 // DEBUG
+    {
+      yield return www.SendWebRequest();
+      if (www.result != UnityWebRequest.Result.Success)
       {
-        yield return www.SendWebRequest();
-        if (www.result != UnityWebRequest.Result.Success)
-        {
-          Debug.Log($"{www.error}...<color=red>[ER]</color>");
-          yield break;
-        }
-        else
+        Debug.Log($"{www.error}...<color=red>[ER]</color>");
+        yield break;
+      }
+      else
+      {
+        if (OnUpload != null)
         {
-          if (OnUpload != null)
-          {
-            OnUpload(DownloadHandlerTexture.GetContent(www));
-          }
-          Debug.Log("Final QRCode...<color=green>[OK]</color>");
+          OnUpload(DownloadHandlerTexture.GetContent(www));
         }
+        Debug.Log("Final QRCode...<color=green>[OK]</color>");
       }
-      yield break;
     }
+    yield break;
+  }
 
-    [Serializable]
-    public class UploadResponse
-    {
-      public int code;
-      public string message;
-      public UploadResponseData data;
-    }
-    [Serializable]
-    public class UploadResponseData
-    {
-      public string file_url;
-      public string qr_url;
-    }
-    #endregion
+  [Serializable]
+  public class UploadResponse
+  {
+    public int code;
+    public string message;
+    public UploadResponseData data;
+  }
+  [Serializable]
+  public class UploadResponseData
+  {
+    public string file_url;
+    public string qr_url;
   }
+  #endregion
 }

+ 254 - 0
Materials/Backend & Upload/Upload2OYManager.cs

@@ -0,0 +1,254 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Events;
+using System.Text;
+using Newtonsoft.Json;
+using UnityEngine.Networking;
+using System.IO;
+using System;
+using ToneTuneToolkit.Common;
+
+/// <summary>
+/// 对乔哥法宝
+/// </summary>
+public class Upload2OYManager : SingletonMaster<Upload2OYManager>
+{
+  public static UnityAction<Texture2D> OnUploadFinishedBackTexture;
+  public static UnityAction<string> OnUploadFinishedBackString;
+
+  private int appID = 78;
+  private float retryWaitTime = 30f; // 重新上传尝试间隔
+
+  [Header("Token")]
+  [SerializeField] private TokenCallbackJson tokenJson = new TokenCallbackJson();
+  [Header("Cloud")]
+  [SerializeField] private CloudCallbackJson cloudCallbackJson = new CloudCallbackJson();
+  [Header("Server")]
+  [SerializeField] private ServerJson serverJson = new ServerJson();
+  [SerializeField] private ServerCallbackJson serverCallbackJson = new ServerCallbackJson();
+
+  private const string cloudTokenURL = @"https://h5.skyelook.com/api/qiniu/getAccessToken";
+  private const string qiniuURL = @"https://upload.qiniup.com";
+  private const string cloudURL = @"https://h5.skyelook.com/api/attachments";
+
+  // ==================================================
+  #region Step 00 // 完善文件信息
+
+  [Space]
+  [SerializeField] private string fileName;
+  [SerializeField] private string filePath;
+
+  public void UpdateFileInfo(string name, string path)
+  {
+    fileName = name;
+    filePath = path;
+    return;
+  }
+
+  #endregion
+  // ==================================================
+  #region Step 01 // 获取Token
+
+  public void UploadData2Net() => StartCoroutine(nameof(GetTokenFromCloud));
+  private IEnumerator GetTokenFromCloud()
+  {
+    using (UnityWebRequest unityWebRequest = UnityWebRequest.Get(cloudTokenURL))
+    {
+      yield return unityWebRequest.SendWebRequest();
+      if (unityWebRequest.result != UnityWebRequest.Result.Success)
+      {
+        Debug.Log($"[U2OYM] {unityWebRequest.error}");
+        StartCoroutine(nameof(RetryUpload));
+      }
+      else
+      {
+        tokenJson = JsonConvert.DeserializeObject<TokenCallbackJson>(unityWebRequest.downloadHandler.text);
+        Debug.Log($"[U2OYM] Get token sucessed: {tokenJson.data.token}");
+
+        StartCoroutine(nameof(PoseFile2Cloud)); // 下一步
+      }
+    }
+    yield break;
+  }
+
+  #endregion
+  // ==================================================
+  #region Step 02 // 上传文件到七牛云
+
+  private IEnumerator PoseFile2Cloud()
+  {
+    byte[] bytes = File.ReadAllBytes(filePath); // 文件转流
+
+    WWWForm wwwForm = new WWWForm();
+    wwwForm.AddField("token", tokenJson.data.token);
+    wwwForm.AddBinaryData("file", bytes, fileName);
+
+    using (UnityWebRequest unityWebRequest = UnityWebRequest.Post(qiniuURL, wwwForm))
+    {
+      yield return unityWebRequest.SendWebRequest();
+      if (unityWebRequest.result != UnityWebRequest.Result.Success)
+      {
+        Debug.Log($"[U2OYM] {unityWebRequest.error}");
+        StartCoroutine(nameof(RetryUpload));
+      }
+      else
+      {
+        cloudCallbackJson = JsonConvert.DeserializeObject<CloudCallbackJson>(unityWebRequest.downloadHandler.text);
+        Debug.Log($"[U2OYM] Upload sucessed: {cloudCallbackJson.data.file_url}");
+
+        StartCoroutine(SaveFile2Server()); // 下一步
+      }
+    }
+    yield break;
+  }
+
+  #endregion
+  // ==================================================
+  #region Step 03 // 七牛云返回数据传至服务器
+
+  private IEnumerator SaveFile2Server()
+  {
+    serverJson.file_url = cloudCallbackJson.data.file_url;
+    serverJson.app_id = appID;
+
+    string jsonString = JsonConvert.SerializeObject(serverJson);
+    byte[] bytes = Encoding.Default.GetBytes(jsonString);
+
+    using (UnityWebRequest unityWebRequest = new UnityWebRequest(cloudURL, "POST"))
+    {
+      unityWebRequest.SetRequestHeader("Content-Type", "application/json");
+      unityWebRequest.uploadHandler = new UploadHandlerRaw(bytes);
+      unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
+
+      yield return unityWebRequest.SendWebRequest();
+      if (unityWebRequest.result != UnityWebRequest.Result.Success)
+      {
+        Debug.Log($"[U2OYM] {unityWebRequest.error}");
+        StartCoroutine(nameof(RetryUpload));
+      }
+      else
+      {
+        serverCallbackJson = JsonConvert.DeserializeObject<ServerCallbackJson>(unityWebRequest.downloadHandler.text);
+        Debug.Log($"[U2OYM] {serverCallbackJson.data.view_url}");
+
+        // 返回链接
+        if (OnUploadFinishedBackString != null)
+        {
+          OnUploadFinishedBackString(serverCallbackJson.data.view_url);
+        }
+
+        // 组装
+        sunCodeURL = $"https://h5.skyelook.com/api/wechat/getQrcodeApp/{serverCallbackJson.data.code}/wx039a4c76d8788bb0/?env=trial"; // ?env=trial // 额外添加?
+        StartCoroutine(nameof(GetSunCode4Server)); // 下一步搞图
+      }
+    }
+    yield break;
+  }
+
+  #endregion
+  // ==================================================
+  #region Step 04 // 从服务器上获取码
+
+  [SerializeField] private string sunCodeURL;
+  [SerializeField] private Texture2D finalSunCode;
+  private IEnumerator GetSunCode4Server()
+  {
+    using (UnityWebRequest unityWebRequest = UnityWebRequestTexture.GetTexture(sunCodeURL)) // new UnityWebRequest(sunCodeURL, "GET"))
+    {
+      yield return unityWebRequest.SendWebRequest();
+      if (unityWebRequest.result != UnityWebRequest.Result.Success)
+      {
+        Debug.Log("[UM] " + unityWebRequest.error);
+      }
+      else
+      {
+        // td = new Texture2D(600, 600);
+        // td.LoadImage(unityWebRequest.tex);
+        finalSunCode = ((DownloadHandlerTexture)unityWebRequest.downloadHandler).texture;
+
+        // 返回图
+        if (OnUploadFinishedBackTexture != null)
+        {
+          OnUploadFinishedBackTexture(((DownloadHandlerTexture)unityWebRequest.downloadHandler).texture);
+        }
+      }
+    }
+    yield break;
+  }
+
+  #endregion
+  // ==================================================
+
+  /// <summary>
+  /// 传不上去硬传
+  /// </summary>
+  /// <returns></returns>
+  private IEnumerator RetryUpload()
+  {
+    yield return new WaitForSeconds(retryWaitTime);
+    PoseFile2Cloud();
+    yield break;
+  }
+
+  // ==================================================
+  // Json解析类
+
+  // 七牛云Token回执
+  [Serializable]
+  public class TokenCallbackJson
+  {
+    public int status;
+    public int code;
+    public TokenDataJson data;
+    public string message;
+  }
+  [Serializable]
+  public class TokenDataJson
+  {
+    public string token;
+  }
+
+  // 七牛云文件上传回执
+  [Serializable]
+  public class CloudCallbackJson
+  {
+    public int code;
+    public CloudCallbackDataJson data;
+    public int status;
+  }
+  [Serializable]
+  public class CloudCallbackDataJson
+  {
+    public string file_name;
+    public string file_url;
+  }
+
+  // 向服务器发送的json
+  [Serializable]
+  public class ServerJson
+  {
+    public string file_url;
+    public int app_id;
+    // public string options;
+  }
+  [Serializable]
+  public class ServerCallbackJson
+  {
+    public int status;
+    public int code;
+    public ServerCallbackDataJson data;
+  }
+
+  [Serializable]
+  public class ServerCallbackDataJson
+  {
+    public string file_url;
+    public int app_id;
+    public string code;
+    public string view_url;
+    public string updated_at;
+    public string created_at;
+    public int id;
+  }
+}

+ 97 - 0
Materials/Backend & Upload/Upload2ZCManager.cs

@@ -0,0 +1,97 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Networking;
+using ToneTuneToolkit.Common;
+using UnityEngine.Events;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+/// <summary>
+/// 对志城法宝
+/// </summary>
+public class Upload2ZCManager : SingletonMaster<Upload2ZCManager>
+{
+  private const string uploadURL = @"https://vw-aud.studiocapsule.cn/api/device/uploadWall";
+
+  public UnityAction<string> OnUploadFinished;
+
+  // ==================================================
+  #region 上传文件流
+
+  public void UploadData(byte[] fileBytes) => StartCoroutine(nameof(UploadDataAction), fileBytes);
+  private IEnumerator UploadDataAction(byte[] fileBytes)
+  {
+    WWWForm wwwForm = new WWWForm();
+    wwwForm.AddBinaryData("file", fileBytes);
+
+    using (UnityWebRequest www = UnityWebRequest.Post(uploadURL, wwwForm))
+    {
+
+      // www.SetRequestHeader("Content-Type", "multipart/form-data"); // wwwForm不要手动设置避免boundary消失
+      www.downloadHandler = new DownloadHandlerBuffer();
+      yield return www.SendWebRequest();
+
+      if (www.result != UnityWebRequest.Result.Success)
+      {
+        Debug.Log($"[U2ZCM] {www.error}");
+        yield break;
+      }
+
+      Debug.Log($"[U2ZCM] {www.downloadHandler.text}");
+      ResponData responData = JsonConvert.DeserializeObject<ResponData>(www.downloadHandler.text);
+
+      // // 解析方案A 动态类型
+      // dynamic data = responData.data;
+      // string qr = data.qr_url;
+
+      // 解析方案B
+      JObject data = JObject.FromObject(responData.data);
+      string qr_url = data["qr_url"].ToString();
+
+      // Debug.Log(qr_url);
+      DownloadQRCode(qr_url);
+    }
+    yield break;
+  }
+
+  #endregion
+  // ==================================================
+  #region 获取QR图片
+
+  public static UnityAction<Texture2D> OnDownloadQRCodeFinished;
+
+  [SerializeField] private Texture2D debug_peekQRCode;
+
+  public void DownloadQRCode(string url) => StartCoroutine(nameof(DownloadQRCodeAction), url);
+  private IEnumerator DownloadQRCodeAction(string url)
+  {
+    using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url))
+    {
+      yield return www.SendWebRequest();
+      if (www.result != UnityWebRequest.Result.Success)
+      {
+        Debug.Log($"[U2ZCM] {www.error}");
+        yield break;
+      }
+
+      debug_peekQRCode = DownloadHandlerTexture.GetContent(www); // DEBUG
+
+      if (OnDownloadQRCodeFinished != null)
+      {
+        OnDownloadQRCodeFinished(DownloadHandlerTexture.GetContent(www));
+      }
+    }
+    yield break;
+  }
+
+  #endregion
+  // ==================================================
+
+  public class ResponData
+  {
+    public int code;
+    public string message;
+    public object data;
+  }
+}

+ 0 - 247
Materials/Backend & Upload/UploadManager.cs

@@ -1,247 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
-using UnityEngine.Events;
-using System.Text;
-using Newtonsoft.Json;
-using UnityEngine.Networking;
-
-namespace OwnTheFloor
-{
-  public class UploadManager : MonoBehaviour
-  {
-    public static UploadManager Instance;
-
-    private event UnityAction<string, string> OnFinalCallbackUpdate; // sting形参
-
-    private int appID = 78;
-    private float retryWaitTime = 30f; // 重新上传尝试间隔
-
-    private Texture2D currentTexture2D;
-    private string currentFileName;
-
-    private TokenJson tokenJson = new TokenJson();
-    private CloudCallbackJson cloudCallbackJson = new CloudCallbackJson();
-    private ServerJson serverJson = new ServerJson();
-    private ServerCallbackJson serverCallbackJson = new ServerCallbackJson();
-
-    // ==================================================
-
-    private void Awake()
-    {
-      Instance = this;
-    }
-
-    // ==================================================
-
-    public void AddEventListener(UnityAction<string, string> unityAction)
-    {
-      OnFinalCallbackUpdate += unityAction;
-      return;
-    }
-
-    public void RemoveEventListener(UnityAction<string, string> unityAction)
-    {
-      OnFinalCallbackUpdate -= unityAction;
-      return;
-    }
-
-    private void EventNoticeAll()
-    {
-      if (OnFinalCallbackUpdate == null) // 如果没人订阅
-      {
-        return;
-      }
-      // OnFinalCallbackUpdate(serverCallbackJson.data.view_url); // 把viewurl丢出去
-      OnFinalCallbackUpdate(serverCallbackJson.data.view_url, serverCallbackJson.data.file_url); // 把fileurl丢出去
-      return;
-    }
-
-    // ==================================================
-
-    /// <summary>
-    /// 
-    /// </summary>
-    /// <param name="fileTexture"></param>
-    /// <param name="fileName"></param>
-    public void UploadData2Net(Texture2D fileTexture, string fileName)
-    {
-      currentTexture2D = fileTexture;
-      currentFileName = fileName;
-      StartCoroutine(GetToken4Cloud());
-      return;
-    }
-
-
-
-    /// <summary>
-    /// 获取Token
-    /// 第一步
-    /// </summary>
-    /// <returns></returns>
-    private IEnumerator GetToken4Cloud()
-    {
-      string url = @"https://h5.skyelook.com/api/qiniu/getAccessToken";
-
-      using (UnityWebRequest unityWebRequest = UnityWebRequest.Get(url))
-      {
-        yield return unityWebRequest.SendWebRequest();
-        if (unityWebRequest.result != UnityWebRequest.Result.Success)
-        {
-          Debug.Log(unityWebRequest.error + "...<color=red>[ER]</color>");
-          StartCoroutine(RetryUpload());
-        }
-        else
-        {
-          tokenJson = JsonConvert.DeserializeObject<TokenJson>(unityWebRequest.downloadHandler.text);
-          Debug.Log($"Get token sucessed: {tokenJson.data.token}...<color=green>[OK]</color>");
-
-          StartCoroutine(UploadData2Cloud());
-        }
-      }
-      yield break;
-    }
-
-    /// <summary>
-    /// 上传文件到七牛云
-    /// 第二步
-    /// </summary>
-    private IEnumerator UploadData2Cloud()
-    {
-      string url = @"https://upload.qiniup.com";
-      byte[] bytes = currentTexture2D.EncodeToPNG();
-
-      WWWForm wwwForm = new WWWForm();
-      wwwForm.AddField("token", tokenJson.data.token);
-      wwwForm.AddBinaryData("file", bytes, currentFileName);
-
-      using (UnityWebRequest unityWebRequest = UnityWebRequest.Post(url, wwwForm))
-      {
-        // unityWebRequest.SetRequestHeader("Content-Type", "multipart/form-data;charset=utf-8");
-        // unityWebRequest.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
-        // unityWebRequest.SetRequestHeader("Content-Type", "application/json");
-        yield return unityWebRequest.SendWebRequest();
-        if (unityWebRequest.result != UnityWebRequest.Result.Success)
-        {
-          Debug.Log(unityWebRequest.error + "...<color=red>[ER]</color>");
-          StartCoroutine(RetryUpload());
-        }
-        else
-        {
-          cloudCallbackJson = JsonConvert.DeserializeObject<CloudCallbackJson>(unityWebRequest.downloadHandler.text);
-          Debug.Log($"Upload sucessed: {cloudCallbackJson.data.file_url}...<color=green>[OK]</color>");
-
-          StartCoroutine(SaveFile2Server());
-        }
-      }
-      yield break;
-    }
-
-    /// <summary>
-    /// 七牛云返回数据传至服务器
-    /// 第三步
-    /// </summary>
-    /// <returns></returns>
-    private IEnumerator SaveFile2Server()
-    {
-      string url = "https://h5.skyelook.com/api/attachments";
-
-      serverJson.file_url = cloudCallbackJson.data.file_url;
-      serverJson.app_id = appID;
-      // serverJson.options = "google-gds-print";
-
-      string jsonString = JsonConvert.SerializeObject(serverJson);
-      byte[] bytes = Encoding.Default.GetBytes(jsonString);
-
-      Debug.Log(jsonString);
-
-      using (UnityWebRequest unityWebRequest = new UnityWebRequest(url, "POST"))
-      {
-        unityWebRequest.SetRequestHeader("Content-Type", "application/json");
-        unityWebRequest.uploadHandler = new UploadHandlerRaw(bytes);
-        unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
-
-        yield return unityWebRequest.SendWebRequest();
-        if (unityWebRequest.result != UnityWebRequest.Result.Success)
-        {
-          Debug.Log(unityWebRequest.error + "...<color=red>[ER]</color>");
-          StartCoroutine(RetryUpload());
-        }
-        else
-        {
-          serverCallbackJson = JsonConvert.DeserializeObject<ServerCallbackJson>(unityWebRequest.downloadHandler.text);
-          Debug.Log($"{unityWebRequest.downloadHandler.text}");
-          Debug.Log($"{serverCallbackJson.data.view_url}...<color=green>[OK]</color>");
-
-          EventNoticeAll(); // 钩子在此
-        }
-      }
-      yield break;
-    }
-
-    /// <summary>
-    /// 传不上去硬传
-    /// </summary>
-    /// <returns></returns>
-    private IEnumerator RetryUpload()
-    {
-      yield return new WaitForSeconds(retryWaitTime);
-      UploadData2Cloud();
-      yield break;
-    }
-
-    // ==================================================
-    // Json解析类
-    // 七牛云Token回执
-    public class TokenJson
-    {
-      public int status;
-      public int code;
-      public TokenDataJson data;
-      public string message;
-    }
-    public class TokenDataJson
-    {
-      public string token;
-    }
-
-    // 七牛云文件上传回执
-    public class CloudCallbackJson
-    {
-      public int code;
-      public CloudCallbackDataJson data;
-      public int status;
-    }
-    public class CloudCallbackDataJson
-    {
-      public string file_name;
-      public string file_url;
-    }
-
-    // 向服务器发送的json
-    public class ServerJson
-    {
-      public string file_url;
-      public int app_id;
-      // public string options;
-    }
-
-    // 服务器回执
-    public class ServerCallbackJson
-    {
-      public int status;
-      public int code;
-      public ServerCallbackDataJson data;
-    }
-    public class ServerCallbackDataJson
-    {
-      public string file_url;
-      public int app_id;
-      public string code;
-      public string view_url;
-      public string updated_at;
-      public string created_at;
-      public int id;
-    }
-  }
-}

+ 45 - 0
Materials/CanvasGroup_DG/StageManager.cs

@@ -0,0 +1,45 @@
+using System.Collections;
+using System.Collections.Generic;
+using ToneTuneToolkit.Common;
+using UnityEngine;
+
+/// <summary>
+/// 阶段管理工具
+/// </summary>
+public class StageManager : SingletonMaster<StageManager>
+{
+  [Header("Stages")]
+  [SerializeField] private List<CanvasGroup> cgStages = new List<CanvasGroup>();
+
+  // ==================================================
+
+  public void Reset()
+  {
+    SwitchStageTo(0);
+    return;
+  }
+
+  // ==================================================
+
+  public void SwitchStageTo(int stageIndex)
+  {
+    for (int i = 0; i < cgStages.Count; i++)
+    {
+      if (i == stageIndex)
+      {
+        CanvasGroupMaster.DoCanvasGroupFade(cgStages[i], true);
+      }
+      else
+      {
+        CanvasGroupMaster.DoCanvasGroupFade(cgStages[i], false);
+      }
+    }
+    return;
+  }
+
+  public void SingleSwitchStage(int stageIndex, bool inOrOut)
+  {
+    CanvasGroupMaster.DoCanvasGroupFade(cgStages[stageIndex], inOrOut);
+    return;
+  }
+}

+ 79 - 0
Materials/CanvasGroup_DG/Tools/CanvasGroupMaster.cs

@@ -0,0 +1,79 @@
+using System.Collections;
+using System.Collections.Generic;
+using ToneTuneToolkit.Common;
+using UnityEngine;
+using DG.Tweening;
+using System.Threading.Tasks;
+
+/// <summary>
+/// 切换阶段专用工具
+/// </summary>
+public class CanvasGroupMaster : SingletonMaster<CanvasGroupMaster>
+{
+  private const float ANIMTIME = 0.33f;
+
+  public static void DoCanvasGroupFade(CanvasGroup cg, bool isFadeIn)
+  {
+    if (isFadeIn)
+    {
+      cg.DOFade(1, ANIMTIME).OnComplete(() =>
+      {
+        cg.interactable = true;
+        cg.blocksRaycasts = true;
+      });
+    }
+    else
+    {
+      cg.interactable = false;
+      cg.DOFade(0, ANIMTIME).OnComplete(() =>
+      {
+        cg.blocksRaycasts = false;
+      });
+    }
+    return;
+  }
+
+  public static void DoCanvasGroupFade(CanvasGroup cg, bool isFadeIn, float time)
+  {
+    if (isFadeIn)
+    {
+      cg.DOFade(1, time).OnComplete(() =>
+      {
+        cg.interactable = true;
+        cg.blocksRaycasts = true;
+      });
+    }
+    else
+    {
+      cg.interactable = false;
+      cg.DOFade(0, time).OnComplete(() =>
+      {
+        cg.blocksRaycasts = false;
+      });
+    }
+    return;
+  }
+
+  public async static void DoCanvasGroupFade(CanvasGroup cg, bool isFadeIn, float time, float delayTime)
+  {
+    await Task.Delay((int)(1000 * delayTime));
+
+    if (isFadeIn)
+    {
+      cg.DOFade(1, time).OnComplete(() =>
+      {
+        cg.interactable = true;
+        cg.blocksRaycasts = true;
+      });
+    }
+    else
+    {
+      cg.interactable = false;
+      cg.DOFade(0, time).OnComplete(() =>
+      {
+        cg.blocksRaycasts = false;
+      });
+    }
+    return;
+  }
+}

BIN
Materials/Game/JigsawGame/JigsawGame.unitypackage


BIN
Materials/Game/JigsawGame/JigsawPuzzle20241104a_app.zip


BIN
Materials/Game/JigsawGame/SwitchJigsawGame.unitypackage


BIN
Materials/Game/Tetris/Tetris.unitypackage


+ 5 - 0
Materials/Keyboard/新建文本文档.txt

@@ -0,0 +1,5 @@
+  public void OpenKeyboard()
+  {
+    Process.Start("TabTip.exe");
+    return;
+  }

+ 125 - 0
Materials/LeapMotion/LeapMotionManager.cs

@@ -0,0 +1,125 @@
+using UnityEngine;
+using Leap;
+using Leap.Unity;
+using UnityEngine.Events;
+using System.Collections;
+
+public class LeapMotionManager : MonoBehaviour
+{
+  public static LeapMotionManager Instance;
+
+  public static UnityAction OnHandDetected;
+  public static UnityAction OnHandLost;
+  public static UnityAction OnSwipeRight;
+  public static UnityAction OnSwipeLeft;
+
+  // ==================================================
+
+  private void Awake() => Instance = this;
+  private void Update()
+  {
+    DetectHands();
+    DetectSwipe();
+  }
+
+  // ==================================================
+  #region 检测是否有手
+
+  private void DetectHands()
+  {
+    Frame frame = Hands.Provider.CurrentFrame;
+    if (frame.Hands.Count >= 1)
+    {
+      Debug.Log(frame.Hands.Count);
+      if (OnHandDetected != null)
+      {
+        OnHandDetected();
+      }
+    }
+    else
+    {
+      if (OnHandLost != null)
+      {
+        OnHandLost();
+      }
+    }
+    return;
+  }
+
+  #endregion
+  // ==================================================
+  #region 设置检测间隔
+
+  private float detectSpace = 1f; // 检测间隙
+  public void SetDetectSpace(float value)
+  {
+    detectSpace = value;
+    return;
+  }
+
+  #endregion
+  // ==================================================
+  #region 设置阈值
+
+  private float detectThreshold = 0.9f; // 挥手速度的阈值 // 越小越容易检测到
+  public void SetDetectThreshold(float value)
+  {
+    detectThreshold = value;
+    return;
+  }
+
+  #endregion
+  // ==================================================
+  #region 左右挥手检测
+
+  private bool allowNotice = true;
+
+  private void DetectSwipe()
+  {
+    Frame frame = Hands.Provider.CurrentFrame; // 获取当前帧
+    foreach (Hand hand in frame.Hands) // 检测每只手
+    {
+      Vector3 palmVelocity = hand.PalmVelocity; // 获取手的手掌速度
+
+      // 检测挥手动作
+      if (palmVelocity.x > detectThreshold || palmVelocity.x < -detectThreshold)
+      {
+        if (!allowNotice) // 是否允许发消息
+        {
+          return;
+        }
+        allowNotice = false; // 上锁
+
+        // 检测到挥手动作,执行相应的逻辑
+        // Debug.Log("检测到挥手动作,方向:" + (palmVelocity.x > 0 ? "向右" : "向左"));
+
+        if (palmVelocity.x > 0)
+        {
+          if (OnSwipeRight != null) // 右
+          {
+            OnSwipeRight();
+          }
+        }
+        else if (palmVelocity.x < 0) // 左
+        {
+          if (OnSwipeLeft != null)
+          {
+            OnSwipeLeft();
+          }
+        }
+
+        StartCoroutine(nameof(BeginCooldown)); // 解锁
+      }
+    }
+    return;
+  }
+
+  private IEnumerator BeginCooldown()
+  {
+    yield return new WaitForSeconds(detectSpace);
+    allowNotice = true;
+    yield break;
+  }
+
+  #endregion
+}

+ 303 - 0
Materials/MediaPipe/人体骨骼识别/WebCamSource.cs

@@ -0,0 +1,303 @@
+// Copyright (c) 2021 homuler
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+#if UNITY_ANDROID
+using UnityEngine.Android;
+#endif
+
+namespace Mediapipe.Unity
+{
+  public class WebCamSource : ImageSource
+  {
+    private readonly int _preferableDefaultWidth = 1280;
+
+    private const string _TAG = nameof(WebCamSource);
+
+    private readonly ResolutionStruct[] _defaultAvailableResolutions;
+
+    public WebCamSource(int preferableDefaultWidth, ResolutionStruct[] defaultAvailableResolutions)
+    {
+      _preferableDefaultWidth = preferableDefaultWidth;
+      _defaultAvailableResolutions = defaultAvailableResolutions;
+    }
+
+    private static readonly object _PermissionLock = new object();
+    private static bool _IsPermitted = false;
+
+    private WebCamTexture _webCamTexture;
+    private WebCamTexture webCamTexture
+    {
+      get => _webCamTexture;
+      set
+      {
+        if (_webCamTexture != null)
+        {
+          _webCamTexture.Stop();
+        }
+        _webCamTexture = value;
+      }
+    }
+
+    public override int textureWidth => !isPrepared ? 0 : webCamTexture.width;
+    public override int textureHeight => !isPrepared ? 0 : webCamTexture.height;
+
+    public override bool isVerticallyFlipped => isPrepared && webCamTexture.videoVerticallyMirrored;
+    public override bool isFrontFacing => isPrepared && (webCamDevice is WebCamDevice valueOfWebCamDevice) && valueOfWebCamDevice.isFrontFacing;
+    public override RotationAngle rotation => !isPrepared ? RotationAngle.Rotation0 : (RotationAngle)webCamTexture.videoRotationAngle;
+
+    private WebCamDevice? _webCamDevice;
+    private WebCamDevice? webCamDevice
+    {
+      get => _webCamDevice;
+      set
+      {
+        if (_webCamDevice is WebCamDevice valueOfWebCamDevice)
+        {
+          if (value is WebCamDevice valueOfValue && valueOfValue.name == valueOfWebCamDevice.name)
+          {
+            // not changed
+            return;
+          }
+        }
+        else if (value == null)
+        {
+          // not changed
+          return;
+        }
+        _webCamDevice = value;
+        resolution = GetDefaultResolution();
+      }
+    }
+    public override string sourceName => (webCamDevice is WebCamDevice valueOfWebCamDevice) ? valueOfWebCamDevice.name : null;
+
+    private WebCamDevice[] _availableSources;
+    private WebCamDevice[] availableSources
+    {
+      get
+      {
+        if (_availableSources == null)
+        {
+          _availableSources = WebCamTexture.devices;
+        }
+
+        return _availableSources;
+      }
+      set => _availableSources = value;
+    }
+
+    public override string[] sourceCandidateNames => availableSources?.Select(device => device.name).ToArray();
+
+#pragma warning disable IDE0025
+    public override ResolutionStruct[] availableResolutions
+    {
+      get
+      {
+#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
+        if (webCamDevice is WebCamDevice valueOfWebCamDevice) {
+          return valueOfWebCamDevice.availableResolutions.Select(resolution => new ResolutionStruct(resolution)).ToArray();
+        }
+#endif
+        return webCamDevice == null ? null : _defaultAvailableResolutions;
+      }
+    }
+#pragma warning restore IDE0025
+
+    public override bool isPrepared => webCamTexture != null;
+    public override bool isPlaying => webCamTexture != null && webCamTexture.isPlaying;
+
+    private IEnumerator Initialize()
+    {
+      yield return GetPermission();
+
+      if (!_IsPermitted)
+      {
+        yield break;
+      }
+
+      if (webCamDevice != null)
+      {
+        yield break;
+      }
+
+      availableSources = WebCamTexture.devices;
+
+      if (availableSources != null && availableSources.Length > 0)
+      {
+        // webCamDevice = availableSources[0];
+
+        // 施工区
+        for (int i = 0; i < availableSources.Length; i++)
+        {
+          Debug.LogWarning(availableSources[i].name);
+          if (availableSources[i].name == "MX Brio") // 
+          {
+            webCamDevice = availableSources[i];
+            break;
+          }
+        }
+        // 施工区
+
+      }
+    }
+
+    private IEnumerator GetPermission()
+    {
+      lock (_PermissionLock)
+      {
+        if (_IsPermitted)
+        {
+          yield break;
+        }
+
+#if UNITY_ANDROID
+        if (!Permission.HasUserAuthorizedPermission(Permission.Camera))
+        {
+          Permission.RequestUserPermission(Permission.Camera);
+          yield return new WaitForSeconds(0.1f);
+        }
+#elif UNITY_IOS
+        if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) {
+          yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
+        }
+#endif
+
+#if UNITY_ANDROID
+        if (!Permission.HasUserAuthorizedPermission(Permission.Camera))
+        {
+          Debug.LogWarning("Not permitted to use Camera");
+          yield break;
+        }
+#elif UNITY_IOS
+        if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) {
+          Debug.LogWarning("Not permitted to use WebCam");
+          yield break;
+        }
+#endif
+        _IsPermitted = true;
+
+        yield return new WaitForEndOfFrame();
+      }
+    }
+
+    public override void SelectSource(int sourceId)
+    {
+      if (sourceId < 0 || sourceId >= availableSources.Length)
+      {
+        throw new ArgumentException($"Invalid source ID: {sourceId}");
+      }
+
+      webCamDevice = availableSources[sourceId];
+    }
+
+    public override IEnumerator Play()
+    {
+      yield return Initialize();
+      if (!_IsPermitted)
+      {
+        throw new InvalidOperationException("Not permitted to access cameras");
+      }
+
+      InitializeWebCamTexture();
+      webCamTexture.Play();
+      yield return WaitForWebCamTexture();
+    }
+
+    public override IEnumerator Resume()
+    {
+      if (!isPrepared)
+      {
+        throw new InvalidOperationException("WebCamTexture is not prepared yet");
+      }
+      if (!webCamTexture.isPlaying)
+      {
+        webCamTexture.Play();
+      }
+      yield return WaitForWebCamTexture();
+    }
+
+    public override void Pause()
+    {
+      if (isPlaying)
+      {
+        webCamTexture.Pause();
+      }
+    }
+
+    public override void Stop()
+    {
+      if (webCamTexture != null)
+      {
+        webCamTexture.Stop();
+      }
+      webCamTexture = null;
+    }
+
+    public override Texture GetCurrentTexture() => webCamTexture;
+
+    private ResolutionStruct GetDefaultResolution()
+    {
+      var resolutions = availableResolutions;
+      return resolutions == null || resolutions.Length == 0 ? new ResolutionStruct() : resolutions.OrderBy(resolution => resolution, new ResolutionStructComparer(_preferableDefaultWidth)).First();
+    }
+
+    private void InitializeWebCamTexture()
+    {
+      Stop();
+      if (webCamDevice is WebCamDevice valueOfWebCamDevice)
+      {
+        webCamTexture = new WebCamTexture(valueOfWebCamDevice.name, resolution.width, resolution.height, (int)resolution.frameRate);
+        return;
+      }
+      throw new InvalidOperationException("Cannot initialize WebCamTexture because WebCamDevice is not selected");
+    }
+
+    private IEnumerator WaitForWebCamTexture()
+    {
+      const int timeoutFrame = 2000;
+      var count = 0;
+      Debug.Log("Waiting for WebCamTexture to start");
+      yield return new WaitUntil(() => count++ > timeoutFrame || webCamTexture.width > 16);
+
+      if (webCamTexture.width <= 16)
+      {
+        throw new TimeoutException("Failed to start WebCam");
+      }
+    }
+
+    private class ResolutionStructComparer : IComparer<ResolutionStruct>
+    {
+      private readonly int _preferableDefaultWidth;
+
+      public ResolutionStructComparer(int preferableDefaultWidth)
+      {
+        _preferableDefaultWidth = preferableDefaultWidth;
+      }
+
+      public int Compare(ResolutionStruct a, ResolutionStruct b)
+      {
+        var aDiff = Mathf.Abs(a.width - _preferableDefaultWidth);
+        var bDiff = Mathf.Abs(b.width - _preferableDefaultWidth);
+        if (aDiff != bDiff)
+        {
+          return aDiff - bDiff;
+        }
+        if (a.height != b.height)
+        {
+          // prefer smaller height
+          return a.height - b.height;
+        }
+        // prefer smaller frame rate
+        return (int)(a.frameRate - b.frameRate);
+      }
+    }
+  }
+}

+ 2 - 0
Materials/MediaPipe/人体骨骼识别/readme.txt

@@ -0,0 +1,2 @@
+相机名可改
+.\Assets\MediaPipeUnity\Samples\Common\Scripts\ImageSource\WebCamSource.cs

+ 0 - 169
Materials/OpenCV/面部识别模块/FaceDetecter.cs

@@ -1,169 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
-using UnityEngine.UI;
-
-// using OpenCVSharp;
-using OpenCVForUnity;
-using OpenCVForUnity.CoreModule;
-using OpenCVForUnity.ImgprocModule;
-using OpenCVForUnity.UnityUtils;
-using OpenCVForUnity.ObjdetectModule;
-
-namespace DiageoWhiskyBlending
-{
-  public class FaceDetecter : MonoBehaviour
-  {
-    private Mat gray; // 灰度图,方便识别
-    private Mat rotatedNewMat;
-    private MatOfRect faceRect; // 识别到的人脸的区域
-    private CascadeClassifier classifier; // 人脸识别分类器
-    private string cascadePath;
-
-    public float index = 0;
-
-    // ==================================================
-
-    private void Start()
-    {
-      // LogAllWebCam();
-      Init();
-    }
-
-    private void OnDestroy()
-    {
-      Dispose();
-    }
-
-    private void OnApplicationQuit()
-    {
-      Dispose();
-    }
-
-    // ==================================================
-
-    private void Init()
-    {
-      cascadePath = Application.streamingAssetsPath + "/haarcascade_frontalface_alt2.xml";
-      gray = new Mat(); // 初始化Mat
-      faceRect = new MatOfRect(); // 初始化识别到的人脸的区域
-      classifier = new CascadeClassifier(cascadePath); // 初始化人脸识别分类器
-
-      previewMat = new Mat();
-      previewTexture2D = new Texture2D(440, 440, TextureFormat.RGBA32, false);
-      return;
-    }
-
-    private void Dispose()
-    {
-      if (rotatedNewMat != null)
-      {
-        rotatedNewMat.Dispose();
-        rotatedNewMat = null;
-      }
-
-      if (previewMat != null)
-      {
-        previewMat.Dispose();
-        previewMat = null;
-      }
-      if (previewTexture2D != null)
-      {
-        Destroy(previewTexture2D);
-        previewTexture2D = null;
-      }
-      return;
-    }
-
-
-    public void DetectFace(Mat rgbaMat)
-    {
-      rotatedNewMat = MatRotate(rgbaMat.clone()); // 旋转原数据
-
-      Imgproc.cvtColor(rotatedNewMat, gray, Imgproc.COLOR_RGBA2GRAY); // 将获取到的摄像头画面转化为灰度图并赋值给gray
-
-      // mat/面部矩形向量组/识别精度越高越快越不准/面部识别次数2次以上算识别/?性能优化/最小检测尺寸/最大检测尺寸
-      classifier.detectMultiScale(gray, faceRect, 1.1d, 2, 2, new Size(20, 20), new Size()); // 检测gray中的人脸
-
-      OpenCVForUnity.CoreModule.Rect[] rects = faceRect.toArray();
-      if (rects.Length > 0)
-      {
-        for (int i = 0; i < rects.Length; i++)
-        {
-          Imgproc.rectangle(rotatedNewMat, new Point(rects[i].x, rects[i].y), new Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 255, 0, 255), 2);  //在原本的画面中画框,框出人脸额位置,其中rects[i].x和rects[i].y为框的左上角的顶点,rects[i].width、rects[i].height即为框的宽和高
-        }
-
-        index += Time.deltaTime;
-        if (index > .3f)
-        {
-          Debug.Log("连续监测超过1s...[OK]");
-          GameManager.Instance.EnterLogicScene00();
-        }
-      }
-      else
-      {
-        // if (index > 0)
-        // {
-        //   index -= Time.deltaTime / 2;
-        //   if (index < 0)
-        //   {
-        index = 0;
-        // }
-        // }
-      }
-
-      // 深复制识别、画框数据并显示
-      UpdatePreview(rotatedNewMat);
-      return;
-    }
-
-    // ==================================================
-    // 预览画面
-    public Image ImagePreviewImage;
-    private Mat previewMat;
-    private Texture2D previewTexture2D;
-    private void UpdatePreview(Mat value)
-    {
-      if (!ImagePreviewImage)
-      {
-        return;
-      }
-      previewMat = value.clone();
-      Utils.matToTexture2D(previewMat, previewTexture2D);
-      ImagePreviewImage.sprite = Sprite.Create(previewTexture2D, new UnityEngine.Rect(0, 0, 440, 440), Vector2.zero);
-      return;
-    }
-
-    // ==================================================
-
-    /// <summary>
-    /// Mat旋转方法
-    /// </summary>
-    /// <param name="orginalMat"></param>
-    /// <returns></returns>
-    private Mat MatRotate(Mat orginalMat)
-    {
-      Mat rotatedMat = new Mat(orginalMat.height(), orginalMat.width(), CvType.CV_8UC4, new Scalar(0, 0, 0, 255)); // DEBUG:宽高可能相反
-
-      // mat旋转
-      Point center = new Point(orginalMat.cols() / 2f, orginalMat.rows() / 2f);
-      Mat mat = Imgproc.getRotationMatrix2D(center, 90, 1);
-      Imgproc.warpAffine(orginalMat, rotatedMat, mat, orginalMat.size());
-
-      return rotatedMat;
-    }
-
-    /// <summary>
-    /// 预览有多少相机
-    /// </summary>
-    private void LogAllWebCam()
-    {
-      Debug.Log($"Found {WebCamTexture.devices.Length} device...<color=green>[OK]</color>");
-      foreach (WebCamDevice webcamDevice in WebCamTexture.devices)
-      {
-        Debug.Log($"{webcamDevice.name}...<color=green>[OK]</color>");
-      }
-      return;
-    }
-  }
-}

+ 187 - 0
Materials/OpenCV/面部识别模块/FacialRecognitionHandler.cs

@@ -0,0 +1,187 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+// using OpenCVSharp;
+using OpenCVForUnity;
+using OpenCVForUnity.CoreModule;
+using OpenCVForUnity.ImgprocModule;
+using OpenCVForUnity.UnityUtils;
+using OpenCVForUnity.ObjdetectModule;
+
+/// <summary>
+/// 面部识别
+/// </summary>
+public class FacialRecognitionHandler : MonoBehaviour
+{
+  public static FacialRecognitionHandler Instance;
+
+  private Mat gray; // 灰度图,方便识别
+  private Mat rotatedNewMat;
+  private MatOfRect faceRect; // 识别到的人脸的区域
+  private CascadeClassifier classifier; // 人脸识别分类器
+  private string cascadePath = Application.streamingAssetsPath + "/haarcascade_frontalface_alt2.xml";
+
+  [SerializeField] private float timer = 0;
+
+  #region 配置
+  private int resultPreviewTexture2DWidth = 480;
+  private int resultPreviewTexture2DHeight = 270;
+  #endregion
+
+  // ==================================================
+
+  private void Awake() => Instance = this;
+  private void Start() => Init();
+  private void OnDestroy() => Dispose();
+
+  // ==================================================
+
+  private void Init()
+  {
+    // LogAllWebCam();
+    gray = new Mat(); // 初始化Mat
+    faceRect = new MatOfRect(); // 初始化识别到的人脸的区域
+    classifier = new CascadeClassifier(cascadePath); // 初始化人脸识别分类器
+
+    previewMat = new Mat();
+    resultPreviewTexture2D = new Texture2D(resultPreviewTexture2DWidth, resultPreviewTexture2DHeight, TextureFormat.RGBA32, false);
+    return;
+  }
+
+  // ==================================================
+  #region 资源释放
+
+  private void Dispose()
+  {
+    if (rotatedNewMat != null)
+    {
+      rotatedNewMat.Dispose();
+      rotatedNewMat = null;
+    }
+
+    if (previewMat != null)
+    {
+      previewMat.Dispose();
+      previewMat = null;
+    }
+    if (resultPreviewTexture2D != null)
+    {
+      Destroy(resultPreviewTexture2D);
+      resultPreviewTexture2D = null;
+    }
+    return;
+  }
+  #endregion
+
+  // ==================================================
+  #region 面部识别
+  public void DetectFace(Mat rgbaMat)
+  {
+    // rotatedNewMat = MatRotate(rgbaMat.clone()); // 旋转原数据
+    rotatedNewMat = rgbaMat.clone(); // 原数据
+
+    Imgproc.cvtColor(rotatedNewMat, gray, Imgproc.COLOR_RGBA2GRAY); // 将获取到的摄像头画面转化为灰度图并赋值给gray
+
+    // mat/面部矩形向量组/识别精度越高越快越不准/面部识别次数2次以上算识别/?性能优化/最小检测尺寸/最大检测尺寸
+    // classifier.detectMultiScale(gray, faceRect, 1.1d, 2, 2, new Size(20, 20), new Size()); // 检测gray中的人脸
+    classifier.detectMultiScale(gray, faceRect, 1.1d, 6, 2, new Size(20, 20), new Size());
+
+    OpenCVForUnity.CoreModule.Rect[] rects = faceRect.toArray();
+    if (rects.Length > 0)
+    {
+      for (int i = 0; i < rects.Length; i++)
+      {
+        Imgproc.rectangle(rotatedNewMat, new Point(rects[i].x, rects[i].y), new Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 255, 0, 255), 2);  //在原本的画面中画框,框出人脸额位置,其中rects[i].x和rects[i].y为框的左上角的顶点,rects[i].width、rects[i].height即为框的宽和高
+      }
+
+      timer += Time.deltaTime;
+      if (timer > .3f)
+      {
+        Debug.Log("检测到面部");
+        AVProVideoManager.Instance.StartPlayVideo();
+      }
+    }
+    else
+    {
+      // if (timer > 0) // 缓慢减少检测值
+      // {
+      //   timer -= Time.deltaTime / 2;
+      //   if (timer < 0)
+      //   {
+      //     timer = 0;
+      //   }
+      // }
+      timer = 0;
+    }
+
+    // 深复制识别、画框数据并显示
+    UpdateResultPreview(rotatedNewMat);
+    return;
+  }
+  #endregion
+
+  // ==================================================
+  #region 预览画面
+  private bool allowPreview = false;
+  public void SwitchPreview(bool value)
+  {
+    allowPreview = value;
+    return;
+  }
+
+  [SerializeField] private Image resultPreviewImage;
+  private Mat previewMat;
+  private Texture2D resultPreviewTexture2D;
+  private void UpdateResultPreview(Mat value)
+  {
+    if (!allowPreview)
+    {
+      return;
+    }
+
+    if (resultPreviewImage == null)
+    {
+      return;
+    }
+
+    previewMat = value.clone();
+    Utils.matToTexture2D(previewMat, resultPreviewTexture2D);
+    resultPreviewImage.sprite = Sprite.Create(resultPreviewTexture2D, new UnityEngine.Rect(0, 0, resultPreviewTexture2D.width, resultPreviewTexture2D.height), Vector2.zero);
+    return;
+  }
+  #endregion
+
+  // ==================================================
+
+  /// <summary>
+  /// Mat旋转方法
+  /// </summary>
+  /// <param name="orginalMat"></param>
+  /// <returns></returns>
+  private Mat MatRotate(Mat orginalMat)
+  {
+    Mat rotatedMat = new Mat(orginalMat.height(), orginalMat.width(), CvType.CV_8UC4, new Scalar(0, 0, 0, 255)); // DEBUG:宽高可能相反
+
+    // mat旋转
+    Point center = new Point(orginalMat.cols() / 2f, orginalMat.rows() / 2f);
+    Mat mat = Imgproc.getRotationMatrix2D(center, 90, 1);
+    Imgproc.warpAffine(orginalMat, rotatedMat, mat, orginalMat.size());
+
+    return rotatedMat;
+  }
+
+  /// <summary>
+  /// 预览有多少相机
+  /// </summary>
+  private void LogAllWebCam()
+  {
+    Debug.Log($"Found {WebCamTexture.devices.Length} devices");
+    foreach (WebCamDevice webcamDevice in WebCamTexture.devices)
+    {
+      Debug.Log($"{webcamDevice.name}...<color=green>[OK]</color>");
+    }
+    return;
+  }
+}

BIN
Materials/OpenCV/面部识别模块/OpenCVToolkit.unitypackage


+ 0 - 0
Materials/OpenCV/面部识别模块/haarcascade_frontalface_alt2.xml → Materials/OpenCV/面部识别模块/StreamingAssets/haarcascade_frontalface_alt2.xml


+ 236 - 0
Materials/OpenCV/面部识别模块/WebCamTextureProcesser.cs

@@ -0,0 +1,236 @@
+using OpenCVForUnity;
+using OpenCVForUnity.CoreModule;
+using OpenCVForUnity.ImgprocModule;
+using OpenCVForUnity.UnityUtils;
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+/// <summary>
+/// WebCam色彩图处理
+/// </summary>
+public class WebCamTextureProcesser : MonoBehaviour
+{
+  public static WebCamTextureProcesser Instance;
+
+  private WebCamDevice webCamDevice;
+  private WebCamTexture webCamTexture;
+  private Mat rgbaMat;
+
+  private Color32[] colors;
+  private bool isInitWaiting = false;
+  private bool hasInitDone = false; // 如果Init完毕
+
+  // ==================================================
+
+  private void Awake() => Instance = this;
+  private void Start() => Init();
+  private void Update() => ProcessWebCamTexture();
+  private void OnDestroy() => Dispose();
+
+  // ==================================================
+
+  private void Init()
+  {
+    if (isInitWaiting)
+    {
+      return;
+    }
+    StartCoroutine(nameof(InitAction));
+    return;
+  }
+  private IEnumerator InitAction()
+  {
+    if (hasInitDone) // 防止重复安装
+    {
+      Dispose();
+    }
+    isInitWaiting = true;
+
+    CreateCamera(); // 创建相机
+
+    while (true)
+    {
+      if (webCamTexture.didUpdateThisFrame)
+      {
+        isInitWaiting = false;
+        hasInitDone = true;
+
+        if (colors == null || colors.Length != webCamTexture.width * webCamTexture.height) // 确定color尺寸
+        {
+          colors = new Color32[webCamTexture.width * webCamTexture.height];
+        }
+        if (orginalPreviewTexture2D == null || orginalPreviewTexture2D.width != webCamTexture.width || orginalPreviewTexture2D.height != webCamTexture.height) // 确定texture2d尺寸
+        {
+          orginalPreviewTexture2D = new Texture2D(webCamTexture.width, webCamTexture.height, TextureFormat.RGBA32, false);
+        }
+
+        rgbaMat = new Mat(webCamTexture.height, webCamTexture.width, CvType.CV_8UC4, new Scalar(0, 0, 0, 255)); // 高、宽
+
+        // UpdateOriginalPreview(); // ?这里为什么会有预览
+        break;
+      }
+      else
+      {
+        yield return null;
+      }
+    }
+    yield break;
+  }
+
+  // ==================================================
+  #region 色彩画面处理
+  private void ProcessWebCamTexture()
+  {
+    if (hasInitDone && webCamTexture.isPlaying && webCamTexture.didUpdateThisFrame)
+    {
+      Utils.webCamTextureToMat(webCamTexture, rgbaMat, colors);
+
+      transform.GetComponent<FacialRecognitionHandler>().DetectFace(rgbaMat); // 传入mat 检测人脸 // 会导致原数据反转?
+
+      UpdateOriginalPreview();
+    }
+    return;
+  }
+  #endregion
+
+  // ==================================================
+  #region 释放资源
+  private void Dispose()
+  {
+    isInitWaiting = false;
+    hasInitDone = false;
+
+    if (webCamTexture != null)
+    {
+      webCamTexture.Stop();
+      Destroy(webCamTexture);
+      webCamTexture = null;
+    }
+    if (rgbaMat != null)
+    {
+      rgbaMat.Dispose();
+      rgbaMat = null;
+    }
+    if (orginalPreviewTexture2D != null)
+    {
+      Destroy(orginalPreviewTexture2D);
+      orginalPreviewTexture2D = null;
+    }
+    return;
+  }
+  #endregion
+
+  // ==================================================
+  #region 创建、配置WebCamera
+  [SerializeField] private string requestedDeviceName = "MX Brio"; // "GC21 Video" // "Logitech BRIO"
+  private int requestedWidth = 480; // 440
+  private int requestedHeight = 270; // 440
+  private int requestedFPS = 30;
+  private void CreateCamera()
+  {
+    foreach (WebCamDevice device in WebCamTexture.devices)
+    {
+      if (device.name == requestedDeviceName)
+      {
+        webCamDevice = device;
+        webCamTexture = new WebCamTexture(webCamDevice.name, requestedWidth, requestedHeight, requestedFPS);
+        webCamTexture.Play();
+        Debug.Log($"[WTP] Name:{webCamTexture.deviceName} / Width:{webCamTexture.width} / Height:{webCamTexture.height} / FPS:{webCamTexture.requestedFPS}");
+        Debug.Log($"[WTP] VideoRotationAngle:{webCamTexture.videoRotationAngle} / VideoVerticallyMirrored:{webCamTexture.videoVerticallyMirrored} / IsFrongFacing:{webCamDevice.isFrontFacing}");
+        return;
+      }
+    }
+    return;
+  }
+  #endregion
+
+  // ==================================================
+  #region 预览画面
+  private bool allowPreview = false;
+  public void SwitchPreview(bool value)
+  {
+    allowPreview = value;
+    return;
+  }
+
+  [SerializeField] private Image originalPreviewImage;
+  private Texture2D orginalPreviewTexture2D;
+  private void UpdateOriginalPreview()
+  {
+    if (!allowPreview)
+    {
+      return;
+    }
+
+    if (originalPreviewImage == null)
+    {
+      return;
+    }
+
+    Utils.matToTexture2D(rgbaMat, orginalPreviewTexture2D, colors); // Mat更新为texture2d
+    // texture2D = RotateTexutre(texture2D, false);
+    originalPreviewImage.sprite = Sprite.Create(orginalPreviewTexture2D, new UnityEngine.Rect(0, 0, orginalPreviewTexture2D.width, orginalPreviewTexture2D.height), Vector2.zero);
+    return;
+  }
+  #endregion
+
+  // ==================================================
+  // 按钮
+
+  public void OnPlayButtonClick()
+  {
+    if (hasInitDone)
+    {
+      webCamTexture.Play();
+    }
+    return;
+  }
+
+  public void OnPauseButtonClick()
+  {
+    if (hasInitDone)
+    {
+      webCamTexture.Pause();
+    }
+    return;
+  }
+
+  public void OnStopButtonClick()
+  {
+    if (hasInitDone)
+    {
+      webCamTexture.Stop();
+    }
+    return;
+  }
+
+  // ==================================================
+  //  Texture画面旋转 // 不必要的 // 旋转处理在FaceDetecter中进行
+  // private Texture2D RotateTexutre(Texture2D originalTexture, bool clockwise)
+  // {
+  //   Color32[] original = originalTexture.GetPixels32();
+  //   Color32[] rotated = new Color32[original.Length];
+  //   int w = originalTexture.width;
+  //   int h = originalTexture.height;
+
+  //   int iRotated, iOriginal;
+
+  //   for (int j = 0; j < h; ++j)
+  //   {
+  //     for (int i = 0; i < w; ++i)
+  //     {
+  //       iRotated = (i + 1) * h - j - 1;
+  //       iOriginal = clockwise ? original.Length - 1 - (j * w + i) : j * w + i;
+  //       rotated[iRotated] = original[iOriginal];
+  //     }
+  //   }
+
+  //   Texture2D newTexture = new Texture2D(originalTexture.height, originalTexture.width, TextureFormat.RGBA32, false);
+  //   newTexture.SetPixels32(rotated);
+  //   newTexture.Apply();
+  //   return newTexture;
+  // }
+}

+ 0 - 229
Materials/OpenCV/面部识别模块/WebCamTextureToMat.cs

@@ -1,229 +0,0 @@
-using OpenCVForUnity;
-using OpenCVForUnity.CoreModule;
-using OpenCVForUnity.ImgprocModule;
-using OpenCVForUnity.UnityUtils;
-
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
-using UnityEngine.UI;
-
-namespace DiageoWhiskyBlending
-{
-  public class WebCamTextureToMat : MonoBehaviour
-  {
-    private WebCamDevice webCamDevice;
-    private WebCamTexture webCamTexture;
-    private Mat rgbaMat;
-
-    private Color32[] colors;
-    private bool isInitWaiting = false;
-    private bool hasInitDone = false; // 如果Init完毕
-
-    // ==================================================
-
-    private void Start()
-    {
-      Init();
-    }
-
-    private void Update()
-    {
-      if (hasInitDone && webCamTexture.isPlaying && webCamTexture.didUpdateThisFrame)
-      {
-        Utils.webCamTextureToMat(webCamTexture, rgbaMat, colors);
-        transform.GetComponent<FaceDetecter>().DetectFace(rgbaMat); // 传入mat 检测人脸 // 会导致原数据反转?
-
-        // UpdatePreview();
-      }
-    }
-
-    private void OnDestroy()
-    {
-      Dispose();
-    }
-
-    private void OnApplicationQuit()
-    {
-      Dispose();
-    }
-
-    // ==================================================
-
-    private void Init()
-    {
-      if (isInitWaiting)
-      {
-        return;
-      }
-      StartCoroutine(InitAction());
-      return;
-    }
-    private IEnumerator InitAction()
-    {
-      if (hasInitDone) // 防止重复安装
-      {
-        Dispose();
-      }
-      isInitWaiting = true;
-
-      CreateCamera(); // 创建相机
-
-      while (true)
-      {
-        if (webCamTexture.didUpdateThisFrame)
-        {
-          Debug.Log($"Name:{webCamTexture.deviceName} / Width:{webCamTexture.width} / Height:{webCamTexture.height} / FPS:{webCamTexture.requestedFPS}...<color=green>[OK]</color>");
-          Debug.Log($"VideoRotationAngle:{webCamTexture.videoRotationAngle} / VideoVerticallyMirrored:{webCamTexture.videoVerticallyMirrored} / IsFrongFacing:{webCamDevice.isFrontFacing}...<color=green>[OK]</color>");
-
-          isInitWaiting = false;
-          hasInitDone = true;
-
-          if (colors == null || colors.Length != webCamTexture.width * webCamTexture.height) // 确定color尺寸
-          {
-            colors = new Color32[webCamTexture.width * webCamTexture.height];
-          }
-          if (orginalPreviewTexture2D == null || orginalPreviewTexture2D.width != webCamTexture.width || orginalPreviewTexture2D.height != webCamTexture.height) // 确定texture2d尺寸
-          {
-            orginalPreviewTexture2D = new Texture2D(webCamTexture.width, webCamTexture.height, TextureFormat.RGBA32, false);
-          }
-
-          rgbaMat = new Mat(webCamTexture.height, webCamTexture.width, CvType.CV_8UC4, new Scalar(0, 0, 0, 255)); // 高、宽
-
-          // UpdatePreview();
-          break;
-        }
-        else
-        {
-          yield return null;
-        }
-      }
-      yield break;
-    }
-
-    // ==================================================
-    // 释放资源
-    // DONE
-    private void Dispose()
-    {
-      isInitWaiting = false;
-      hasInitDone = false;
-
-      if (webCamTexture != null)
-      {
-        webCamTexture.Stop();
-        Destroy(webCamTexture);
-        webCamTexture = null;
-      }
-      if (rgbaMat != null)
-      {
-        rgbaMat.Dispose();
-        rgbaMat = null;
-      }
-      if (orginalPreviewTexture2D != null)
-      {
-        Destroy(orginalPreviewTexture2D);
-        orginalPreviewTexture2D = null;
-      }
-      return;
-    }
-
-    // ==================================================
-    // 按钮
-
-    public void OnPlayButtonClick()
-    {
-      if (hasInitDone)
-      {
-        webCamTexture.Play();
-      }
-      return;
-    }
-
-    public void OnPauseButtonClick()
-    {
-      if (hasInitDone)
-      {
-        webCamTexture.Pause();
-      }
-      return;
-    }
-
-    public void OnStopButtonClick()
-    {
-      if (hasInitDone)
-      {
-        webCamTexture.Stop();
-      }
-      return;
-    }
-
-    // ==================================================
-    // 相机配置
-    // DONE
-    private string requestedDeviceName = "Logitech BRIO";
-    // private string requestedDeviceName = "GC21 Video";
-    private int requestedWidth = 440;
-    private int requestedHeight = 440;
-    private int requestedFPS = 30;
-    private void CreateCamera()
-    {
-      foreach (WebCamDevice device in WebCamTexture.devices)
-      {
-        if (device.name == requestedDeviceName)
-        {
-          webCamDevice = device;
-          webCamTexture = new WebCamTexture(webCamDevice.name, requestedWidth, requestedHeight, requestedFPS);
-          webCamTexture.Play();
-        }
-      }
-      return;
-    }
-
-    // ==================================================
-    // 预览画面
-
-    public Image ImageOrginalPreview;
-    private Texture2D orginalPreviewTexture2D;
-    private void UpdatePreview()
-    {
-      if (!ImageOrginalPreview)
-      {
-        return;
-      }
-
-      Utils.matToTexture2D(rgbaMat, orginalPreviewTexture2D, colors); // Mat更新为texture2d
-
-      // texture2D = RotateTexutre(texture2D, false);
-      ImageOrginalPreview.sprite = Sprite.Create(orginalPreviewTexture2D, new UnityEngine.Rect(0, 0, orginalPreviewTexture2D.width, orginalPreviewTexture2D.height), Vector2.zero);
-      return;
-    }
-
-    // ==================================================
-    //  Texture画面旋转
-    // private Texture2D RotateTexutre(Texture2D originalTexture, bool clockwise)
-    // {
-    //   Color32[] original = originalTexture.GetPixels32();
-    //   Color32[] rotated = new Color32[original.Length];
-    //   int w = originalTexture.width;
-    //   int h = originalTexture.height;
-
-    //   int iRotated, iOriginal;
-
-    //   for (int j = 0; j < h; ++j)
-    //   {
-    //     for (int i = 0; i < w; ++i)
-    //     {
-    //       iRotated = (i + 1) * h - j - 1;
-    //       iOriginal = clockwise ? original.Length - 1 - (j * w + i) : j * w + i;
-    //       rotated[iRotated] = original[iOriginal];
-    //     }
-    //   }
-
-    //   Texture2D newTexture = new Texture2D(originalTexture.height, originalTexture.width, TextureFormat.RGBA32, false);
-    //   newTexture.SetPixels32(rotated);
-    //   newTexture.Apply();
-    //   return newTexture;
-    // }
-  }
-}

+ 1 - 0
Materials/OpenCV/面部识别模块/readme.txt

@@ -0,0 +1 @@
+unitypackage非最新版

+ 142 - 0
Materials/ScrollView/20241125_左右滑动限位圆点/ScrollViewHandler.cs

@@ -0,0 +1,142 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using DG.Tweening;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class ScrollViewHandler : MonoBehaviour
+{
+  private ScrollRect sr;
+
+  [SerializeField] private int srContentCount;
+  [SerializeField] private float unitPosition;
+
+  private const float ANIMTIME = .5f;
+
+  [SerializeField] private int currentIndex = 0;
+
+  // ==================================================
+
+  private void Start() => Init();
+
+  private void Update()
+  {
+    if (Input.GetKeyDown(KeyCode.Q))
+    {
+      sr.DOHorizontalNormalizedPos(unitPosition, ANIMTIME);
+    }
+    if (Input.GetKeyDown(KeyCode.W))
+    {
+      sr.DOHorizontalNormalizedPos(unitPosition, ANIMTIME);
+    }
+  }
+
+  // ==================================================
+
+  private void Init()
+  {
+    sr = GetComponent<ScrollRect>();
+    srContentCount = transform.GetChild(0).GetChild(0).childCount;
+    unitPosition = 1f / (srContentCount - 1);
+    return;
+  }
+
+  // ==================================================
+  #region 手势检测
+
+  private Vector2 lastPos; // 鼠标上次位置
+  private Vector2 currPos; // 鼠标当前位置
+  private Vector2 offset; // 两次位置的偏移值
+
+  public void BeginDrag()
+  {
+    lastPos = Input.mousePosition;
+    return;
+  }
+
+  public void EndDrag()
+  {
+    currPos = Input.mousePosition;
+    offset = currPos - lastPos;
+
+    if (Mathf.Abs(offset.x) > Mathf.Abs(offset.y)) // 水平移动
+    {
+      if (offset.x > 0)
+      {
+        // Debug.Log("向右");
+        currentIndex--;
+        Jump2Position(currentIndex);
+      }
+      else
+      {
+        // Debug.Log("向左");
+        currentIndex++;
+        Jump2Position(currentIndex);
+      }
+    }
+    // else // 垂直移动
+    // {
+    //   if (offset.y > 0)
+    //   {
+    //     Debug.Log("向上");
+    //   }
+    //   else
+    //   {
+    //     Debug.Log("向下");
+    //   }
+    // }
+    return;
+  }
+
+  #endregion
+  // ==================================================
+  #region 跳转控制
+
+  public void Jump2Position(int index)
+  {
+    if (index <= 0)
+    {
+      currentIndex = 0;
+      sr.DOHorizontalNormalizedPos(0, ANIMTIME);
+      ControllDot(0);
+      return;
+    }
+    if (index >= 6)
+    {
+      currentIndex = 6;
+      sr.DOHorizontalNormalizedPos(1, ANIMTIME);
+      ControllDot(6);
+      return;
+    }
+
+    currentIndex = index;
+    sr.DOHorizontalNormalizedPos(unitPosition * index, ANIMTIME);
+    ControllDot(index);
+    return;
+  }
+
+  #endregion
+  // ==================================================
+  #region dot控制
+
+  [SerializeField] private List<GameObject> dots;
+
+  public void ControllDot(int value)
+  {
+    for (int i = 0; i < dots.Count; i++)
+    {
+      if (i == value)
+      {
+        dots[i].transform.DOScale(new Vector3(1.5f, 1.5f, 1.5f), ANIMTIME);
+        dots[i].GetComponent<Image>().DOColor(Color.white, ANIMTIME);
+        continue;
+      }
+      dots[i].transform.DOScale(Vector3.one, 0.5f);
+      dots[i].GetComponent<Image>().DOColor(Color.gray, ANIMTIME);
+    }
+    return;
+  }
+
+  #endregion
+}

+ 44 - 20
Materials/SequenceFrame/SequenceFrameHandler.cs

@@ -6,11 +6,13 @@ using UnityEngine.UI;
 public class SequenceFrameHandler : MonoBehaviour
 {
   [SerializeField] private List<Sprite> frames;
-  private float fps = 12f;
+  private float fps = 15f;
   private Image image;
   private bool isAnimationPlaying = false;
 
-  [SerializeField] private bool allowPlayOnStart = false;
+  [SerializeField] private bool playOnStart = false;
+  public bool reverse = false;
+
   private bool allowLoop = true;
 
   // ==================================================
@@ -22,48 +24,70 @@ public class SequenceFrameHandler : MonoBehaviour
   private void Init()
   {
     image = GetComponent<Image>();
-    if (allowPlayOnStart)
+    if (playOnStart)
     {
-      SwitchAnimation(true);
+      Play();
     }
     return;
   }
 
   // ==================================================
 
-  public void SwitchAnimation(bool value)
+  public void Reset()
   {
-    if (value)
+    if (!reverse)
     {
-      if (isAnimationPlaying)
-      {
-        return;
-      }
-      isAnimationPlaying = true;
-      StartCoroutine(nameof(AnimationAction));
+      image.sprite = frames[0];
     }
     else
     {
-      if (!isAnimationPlaying)
-      {
-        return;
-      }
-      isAnimationPlaying = false;
-      image.sprite = frames[0];
-      StopCoroutine(nameof(AnimationAction));
+      image.sprite = frames[frames.Count - 1];
+    }
+    return;
+  }
+
+  public void Play()
+  {
+    if (isAnimationPlaying)
+    {
+      return;
     }
+    isAnimationPlaying = true;
+    StartCoroutine(nameof(AnimationAction));
+    return;
+  }
+
+  public void Stop()
+  {
+    if (!isAnimationPlaying)
+    {
+      return;
+    }
+    isAnimationPlaying = false;
+    StopCoroutine(nameof(AnimationAction));
     return;
   }
 
   private IEnumerator AnimationAction()
   {
-    while (allowLoop) // 注释则不循环
+    // while (allowLoop) // 注释则不循环
+    // {
+    if (!reverse)
     {
       for (int i = 0; i < frames.Count; i++)
       {
+        yield return new WaitForSeconds(1f / fps);
         image.sprite = frames[i];
+      }
+    }
+    else
+    {
+      for (int i = frames.Count - 1; i > 0; i--)
+      {
         yield return new WaitForSeconds(1f / fps);
+        image.sprite = frames[i];
       }
     }
+    // }
   }
 }

+ 32 - 20
Materials/SerialPortUtilityPro/SerialPortUtilityProConfiger.cs

@@ -9,6 +9,7 @@ using Newtonsoft.Json;
 /// <summary>
 /// 通常来说设置产品的VID/PID就足以识别硬件了
 /// 填入序列号将导致识别唯一
+/// 仅读取配置
 /// </summary>
 public class SerialPortUtilityProConfiger : MonoBehaviour
 {
@@ -19,21 +20,18 @@ public class SerialPortUtilityProConfiger : MonoBehaviour
   #endregion
 
   #region Value
-  public List<DeviceInfoData> DeviceInfoDatas;
+  [SerializeField] private List<DeviceInfoData> deviceInfoDatas;
   #endregion
 
   // ==================================================
 
-  private void Awake()
-  {
-    Instance = this;
-    Init();
-  }
+  private void Awake() => Init();
 
   // ==================================================
 
   private void Init()
   {
+    Instance = this;
     ReadConfig();
     return;
   }
@@ -48,11 +46,21 @@ public class SerialPortUtilityProConfiger : MonoBehaviour
     {
       DeviceInfoData tempDID = new DeviceInfoData();
       string[] infoSlice = DeviceInfos[i].Split('_');
-      tempDID.VendorID = infoSlice[0];
-      tempDID.ProductID = infoSlice[1];
-      tempDID.SerialNumber = infoSlice[2];
 
-      DeviceInfoDatas.Add(tempDID);
+      if (infoSlice.Length == 3)
+      {
+        tempDID.VendorID = infoSlice[0];
+        tempDID.ProductID = infoSlice[1];
+        tempDID.SerialNumber = infoSlice[2];
+      }
+      else
+      {
+        tempDID.VendorID = infoSlice[0];
+        tempDID.ProductID = infoSlice[1];
+        tempDID.SerialNumber = null;
+      }
+
+      deviceInfoDatas.Add(tempDID);
     }
     return;
   }
@@ -61,24 +69,28 @@ public class SerialPortUtilityProConfiger : MonoBehaviour
 
   public string GetDeviceVendorID(int index)
   {
-    return DeviceInfoDatas[index].VendorID;
+    return deviceInfoDatas[index].VendorID;
   }
 
   public string GetDeviceProductID(int index)
   {
-    return DeviceInfoDatas[index].ProductID;
+    return deviceInfoDatas[index].ProductID;
   }
 
   public string GetDeviceSerialNumber(int index)
   {
-    return DeviceInfoDatas[index].SerialNumber;
+    return deviceInfoDatas[index].SerialNumber;
   }
-}
 
-[Serializable]
-public class DeviceInfoData
-{
-  public string VendorID;
-  public string ProductID;
-  public string SerialNumber;
+  // ==================================================
+  #region Value
+
+  [Serializable]
+  public class DeviceInfoData
+  {
+    public string VendorID;
+    public string ProductID;
+    public string SerialNumber;
+  }
+  #endregion
 }

+ 42 - 35
Materials/SerialPortUtilityPro/SerialPortUtilityProManager.cs

@@ -4,6 +4,7 @@ using UnityEngine;
 using System;
 using System.Text;
 using SerialPortUtility;
+using UnityEngine.Events;
 
 public class SerialPortUtilityProManager : MonoBehaviour
 {
@@ -11,19 +12,14 @@ public class SerialPortUtilityProManager : MonoBehaviour
 
   private SerialPortUtilityPro serialPortUtilityPro;
 
-  // ==============================
+  public static UnityAction<string> OnReceiveMessage;
 
-  private void Awake()
-  {
-    Instance = this;
-  }
+  // ==================================================
 
-  private void Start()
-  {
-    Init();
-  }
+  private void Awake() => Instance = this;
+  private void Start() => Init();
 
-  // ==============================
+  // ==================================================
 
   private void Init()
   {
@@ -31,10 +27,10 @@ public class SerialPortUtilityProManager : MonoBehaviour
     return;
   }
 
-  // ==============================
+  // ==================================================
 
   /// <summary>
-  /// 
+  /// 加载端口信息
   /// </summary>
   /// <param name="index">设备序号</param>
   public void LoadPortInfo(int portInfoIndex)
@@ -45,10 +41,9 @@ public class SerialPortUtilityProManager : MonoBehaviour
     return;
   }
 
-  /// <summary>
-  /// 串口开关
-  /// </summary>
-  /// <param name="value"></param>
+  // ==================================================
+  #region 串口开关
+
   public void SwitchSerialPortUtilityPro(bool value)
   {
     if (value)
@@ -62,8 +57,9 @@ public class SerialPortUtilityProManager : MonoBehaviour
     return;
   }
 
-  // ==============================
-  // 发包
+  #endregion
+  // ==================================================
+  #region 发包
 
   /// <summary>
   /// 发送信号给设备
@@ -76,49 +72,60 @@ public class SerialPortUtilityProManager : MonoBehaviour
     // serialPortUtilityPro.Write(data);
 
     serialPortUtilityPro.Write(Encoding.ASCII.GetBytes(value)); // 插件
-    Debug.Log("SerialPort Send: " + value);
+    Debug.Log("[SPUP M] Send: " + value);
     return;
   }
 
+  #endregion
   // ==============================
-  // 收包
+  #region 收包
 
   /// <summary>
   /// 读原流
-  /// 配合SerialPortUtilityPro使用
+  /// 配合SerialPortUtilityPro事件使用
+  /// 需选择Read Data Structure
   /// </summary>
   /// <param name="streaming"></param>
   public void ReadStreaming(object streaming)
   {
-    Debug.Log("Arduino Recive: " + streaming);
+    Debug.Log("[SPUP M] Read: " + streaming);
     string stringRawData = streaming.ToString();
-    InMessageProcessing(stringRawData);
+    // stringRawData = InMessageProcessing(stringRawData);
+    if (OnReceiveMessage != null)
+    {
+      OnReceiveMessage(stringRawData);
+    }
     return;
   }
 
   /// <summary>
   /// 读二进制流
-  /// 配合SerialPortUtilityPro使用
+  /// 配合SerialPortUtilityPro事件使用
+  /// 需选择Read Data Structure
   /// </summary>
   /// <param name="byteData"></param>
   public void ReadBinaryStreaming(object byteData)
   {
     Debug.Log(byteData);
     string stringRawData = BitConverter.ToString((byte[])byteData); // 比特流翻译
-    Debug.Log("Arduino Recive: " + stringRawData.Replace('-', ' '));
-    InMessageProcessing(stringRawData);
+    stringRawData = InMessageProcessing(stringRawData);
+    Debug.Log("[SPUP M] Read: " + stringRawData);
+    if (OnReceiveMessage != null)
+    {
+      OnReceiveMessage(stringRawData);
+    }
     return;
   }
 
-  private void InMessageProcessing(string value)
+  /// <summary>
+  /// 额外处理收到的消息
+  /// </summary>
+  /// <param name="value"></param>
+  private string InMessageProcessing(string value)
   {
-    int resultValue;
-    bool canTrans = int.TryParse(value, out resultValue);
-
-    if (!canTrans) // 转换失败
-    {
-      return;
-    }
-    return;
+    value = value.Replace('-', ' ');
+    return value;
   }
+
+  #endregion
 }

+ 55 - 0
Materials/SerialPortUtilityPro/SerialPortUtilityProResponder.cs

@@ -0,0 +1,55 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace ToneTuneToolkit.SerialPort
+{
+  public class SerialPortUtilityProResponder : MonoBehaviour
+  {
+    public static SerialPortUtilityProResponder Instance;
+
+    // ==============================
+
+    private void Awake() => Instance = this;
+    private void Start() => Init();
+    private void OnDestroy() => Uninit();
+
+    // ==============================
+
+    private void Init()
+    {
+      SerialPortUtilityProManager.OnReceiveMessage += MessageProcessor;
+      return;
+    }
+
+    private void Uninit()
+    {
+      SerialPortUtilityProManager.OnReceiveMessage -= MessageProcessor;
+      return;
+    }
+
+    // ==============================
+
+    // AA 00 09 00 00 BB
+    // AA 00 09 00 04 BB
+
+    /// <summary>
+    /// 消息翻译器
+    /// </summary>
+    /// <param name="value"></param>
+    private void MessageProcessor(string value)
+    {
+      string[] parts = value.Split(" ");
+      for (int i = 0; i < parts.Length; i++)
+      {
+        if (parts[i] == "04")
+        {
+          // GameManager.Instance.EnterStage03SerialPort();
+          // GameManager.Instance.SetShootingGoal(true);
+          Debug.Log("asdas");
+        }
+      }
+      return;
+    }
+  }
+}

+ 0 - 0
Materials/SerialPortUtilityPro/新建文本文档.txt → Materials/SerialPortUtilityPro/readme.txt


+ 55 - 0
Materials/SerialPortUtilityPro/红外感应/SerialPortUtilityProResponder.cs

@@ -0,0 +1,55 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace ToneTuneToolkit.SerialPort
+{
+  public class SerialPortUtilityProResponder : MonoBehaviour
+  {
+    public static SerialPortUtilityProResponder Instance;
+
+    // ==============================
+
+    private void Awake() => Instance = this;
+    private void Start() => Init();
+    private void OnDestroy() => Uninit();
+
+    // ==============================
+
+    private void Init()
+    {
+      SerialPortUtilityProManager.OnReceiveMessage += MessageProcessor;
+      return;
+    }
+
+    private void Uninit()
+    {
+      SerialPortUtilityProManager.OnReceiveMessage -= MessageProcessor;
+      return;
+    }
+
+    // ==============================
+
+    // AA 00 09 00 00 BB
+    // AA 00 09 00 04 BB
+
+    /// <summary>
+    /// 消息翻译器
+    /// </summary>
+    /// <param name="value"></param>
+    private void MessageProcessor(string value)
+    {
+      string[] parts = value.Split(" ");
+      for (int i = 0; i < parts.Length; i++)
+      {
+        if (parts[i] == "04")
+        {
+          // GameManager.Instance.EnterStage03SerialPort();
+          // GameManager.Instance.SetShootingGoal(true);
+          Debug.Log("asdas");
+        }
+      }
+      return;
+    }
+  }
+}

BIN
Materials/SerialPortUtilityPro/红外感应/微信截图_20241217163407.png


+ 127 - 0
Materials/SocketIO/SocketIOManager.cs

@@ -0,0 +1,127 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using System;
+using Best.SocketIO;
+using Best.HTTP.JSON;
+using Best.HTTP.JSON.LitJson;
+using ToneTuneToolkit.Data;
+
+public class SocketIOManager : MonoBehaviour
+{
+  public static SocketIOManager Instance;
+
+  #region 路径
+  private string socketioConfigPath = $"{Application.streamingAssetsPath}/configs/socketioconfig.json";
+  #endregion
+
+  #region 配置
+  private string targetIP;
+  private string targetPort;
+  #endregion
+
+  private SocketManager manager;
+
+  // ==================================================
+
+  private void Awake() => Instance = this;
+  private void Start() => Init();
+  private void OnDestroy() => Uninit();
+
+  // ==================================================
+
+  private void Init()
+  {
+    targetIP = JsonManager.GetJson(socketioConfigPath, "target_ip");
+    targetPort = JsonManager.GetJson(socketioConfigPath, "target_port");
+
+    manager = new SocketManager(
+      new Uri($"https://{targetIP}"),
+      // new Uri($"https://{targetIP}:{targetPort}"),
+      new SocketOptions
+      {
+        // AdditionalQueryParams = new ObservableDictionary<string, string> { { "type", "machine" } }, // 识别用途
+        AutoConnect = false
+      });
+
+    manager.Socket.On(SocketIOEventTypes.Connect, OnConnected);
+    manager.Socket.On<BrandStartData>(@"brand-start", ReceiveData);
+    SwitchSocketIO(true);
+    return;
+  }
+
+  private void Uninit()
+  {
+    SwitchSocketIO(false);
+    return;
+  }
+
+  // ==================================================
+  // 绑定事件
+
+  private void OnConnected()
+  {
+    Debug.Log("[SocketIO Manager] Connected!");
+    return;
+  }
+
+  private void ReceiveData(BrandStartData value)
+  {
+    GameManager.Instance.SetInData(value);
+    // JsonData jd = JsonMapper.ToObject(receive);
+    // Debug.Log(jd["video_code"]);
+    // MessageProcessor.Instance.SendMessageOut(jd["video_code"].ToString());
+    return;
+  }
+
+  // ==================================================
+
+  /// <summary>
+  /// 发送消息
+  /// </summary>
+  /// <param name="eventName"></param>
+  /// <param name="message"></param>
+  public void MessageSend(string eventName, object message)
+  {
+    manager.Socket.Emit(eventName, message);
+    Debug.Log($"<color=white>[Socket IO]</color> Message [<color=white>{message}</color>].");
+    return;
+  }
+
+  // ==================================================
+
+  /// <summary>
+  /// 端口开关
+  /// </summary>
+  /// <param name="value"></param>
+  public void SwitchSocketIO(bool value)
+  {
+    if (value)
+    {
+      manager.Open();
+    }
+    else
+    {
+      manager.Close();
+    }
+    return;
+  }
+
+  // ==================================================
+
+  // 数据类
+  [Serializable]
+  public class BrandStartData
+  {
+    public string uuid;
+    public string clock;
+    public string start_code;
+  }
+
+  [Serializable]
+  public class BrandStopData
+  {
+    public string clock;
+    public string start_code;
+  }
+}

+ 61 - 0
Materials/UI活动检测/ClickListener.cs

@@ -0,0 +1,61 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.EventSystems;
+using UnityEngine.SceneManagement;
+
+public class ClickListener : MonoBehaviour
+{
+  public static ClickListener Instance;
+
+  // ==================================================
+
+  private void Awake() => Instance = this;
+  private void Update() => DetectOperation();
+
+  // ==================================================
+  #region 检测点击
+
+  private void DetectOperation()
+  {
+    if (Input.GetMouseButtonDown(0))
+    {
+      if (EventSystem.current.IsPointerOverGameObject())
+      {
+        SwitchResetSequence(false);
+        SwitchResetSequence(true);
+      }
+    }
+    return;
+  }
+
+  #endregion
+  // ==================================================
+  #region 开始暂停流程
+
+  public void SwitchResetSequence(bool value)
+  {
+    if (value)
+    {
+      StartCoroutine(nameof(ResetStateAction));
+    }
+    else
+    {
+      StopCoroutine(nameof(ResetStateAction));
+    }
+    return;
+  }
+
+  #endregion
+  // ==================================================
+  #region 自动重置流程
+
+  private IEnumerator ResetStateAction()
+  {
+    yield return new WaitForSeconds(60f);
+    SceneManager.LoadScene(0);
+    yield break;
+  }
+
+  #endregion
+}

+ 2 - 6
Materials/SkipLogo/SkipUnityLogo.cs → Materials/WebGL/SkipUnityLogo/WebGLSkipUnityLogo.cs

@@ -2,7 +2,7 @@
 using UnityEngine;
 using UnityEngine.Rendering;
 
-public class SkipUnityLogo
+public class WebGLSkipUnityLogo
 {
     [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
     private static void BeforeSplashScreen()
@@ -27,8 +27,4 @@ public class SkipUnityLogo
     }
 #endif
 }
-#endif
-
-// 百度和谷歌了一下解决方法,最终是通过用国外ip来重新激活license(仍然选择个人免费版),就解决问题了。
-// 就是说只要不是国内ip去激活,就不会显示 trial version 字样了。
-// 删除它的project settings文件夹。删除这个文件夹会导致一些配置的丢失,所以如果这样做的话需要考虑把配置迁移过来。
+#endif

+ 0 - 0
Materials/SkipLogo/readme.txt → Materials/WebGL/SkipUnityLogo/readme.txt


+ 0 - 0
Materials/WebGL/背景透明化/TransparentBackground.jslib → Materials/WebGL/背景透明化/Plugins/TransparentBackground.jslib


+ 3 - 0
Materials/WebGL/背景透明化/Unity WebGL背景透明化.txt

@@ -2,6 +2,9 @@ Unity WebGL背景透明化(画布透明),显示Html网页背景
 
 https://blog.csdn.net/boyZhenGui/article/details/105552137?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-2-105552137-blog-129862218.235%5Ev43%5Epc_blog_bottom_relevance_base8&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-2-105552137-blog-129862218.235%5Ev43%5Epc_blog_bottom_relevance_base8&utm_relevant_index=5
 
+
+https://blog.csdn.net/weixin_44347839/article/details/134923359
+
 .jslib放在Assets
 Camera - Flags - Soild Color
 index.html - backgroundColor: transparent

+ 0 - 99
Materials/后置相机拍摄/WebCamManager.cs

@@ -1,99 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
-using UnityEngine.UI;
-
-namespace LonginesYogaPhotoJoy
-{
-  public class WebCamManager : MonoBehaviour
-  {
-    public static WebCamManager Instance;
-
-    public RawImage WebCamRawImage;
-
-    private WebCamTexture webCamTexture;
-    // private string deviceName = "Logitech BRIO";
-    // private string deviceName = "Camera (NVIDIA Broadcast)";
-    private string deviceName = "EOS Webcam Utility";
-    // private string deviceName = "OBS Virtual Camera";
-
-    private bool isReady = false;
-
-    // ==================================================
-
-    private void Awake()
-    {
-      Instance = this;
-    }
-
-    private void Start()
-    {
-      InitWebcam();
-      // SwitchWebcam(true);
-    }
-
-    private void OnApplicationQuit()
-    {
-      SwitchWebcam(false);
-    }
-
-    // ==================================================
-
-    // private void Init()
-    // {
-    //   InitWebcam();
-    //   return;
-    // }
-
-    private void InitWebcam()
-    {
-      if (WebCamTexture.devices.Length <= 0)
-      {
-        Debug.Log("<color=red>[WM]</color> 无可用设备");
-        return;
-      }
-
-      WebCamDevice[] devices = WebCamTexture.devices;
-      for (int i = 0; i < devices.Length; i++)
-      {
-        Debug.Log($"[WM] 设备[{i}]:{devices[i].name}");
-        if (devices[i].name == deviceName)
-        {
-          webCamTexture = new WebCamTexture(devices[i].name, Screen.width, Screen.height, 30)
-          {
-            wrapMode = TextureWrapMode.Clamp
-          };
-
-          WebCamRawImage.texture = webCamTexture;
-          isReady = true;
-          break;
-        }
-      }
-      return;
-    }
-
-
-
-    public void SwitchWebcam(bool value)
-    {
-      if (webCamTexture == null || isReady == false)
-      {
-        return;
-      }
-
-      if (value == true)
-      {
-        webCamTexture.Play();
-      }
-      else
-      {
-        if (webCamTexture.isPlaying)
-        {
-          webCamTexture.Stop();
-        }
-      }
-      return;
-    }
-
-  }
-}

+ 55 - 0
Materials/图片选择和加载/ImageLoader.cs

@@ -0,0 +1,55 @@
+using UnityEngine;
+using NativeFileBrowser;
+using System.Collections.Generic;
+using System.Linq;
+using System.IO;
+using ToneTuneToolkit.Common;
+
+public class ImageLoader : SingletonMaster<ImageLoader>
+{
+  /// <summary>
+  /// 弹窗获取图片路径
+  /// </summary>
+  /// <returns>图片路径</returns>
+  public static string GetImagePath()
+  {
+    string title = "Select Image";
+    ExtensionFilter[] extensions = new ExtensionFilter[]
+    {
+      new ExtensionFilter("Image Files", "png", "jpg", "jpeg"),
+      new ExtensionFilter("JPG ", "jpg", "jpeg"),
+      new ExtensionFilter("PNG ", "png"),
+    };
+
+    // 标题、类型筛选器、是否允许选择多个文件
+    List<string> paths = StandaloneFileBrowser.OpenFilePanel(title, extensions, false).ToList();
+
+    if (paths.Count == 0)
+    {
+      return null;
+    }
+    // Debug.Log(paths[0]);
+    return paths[0];
+  }
+
+
+
+  /// <summary>
+  /// 获取图片
+  /// </summary>
+  /// <param name="path"></param>
+  /// <returns></returns>
+  public static Texture2D GetImageTexture(string path)
+  {
+    if (!File.Exists(path))
+    {
+      return null;
+    }
+
+    byte[] bytes = File.ReadAllBytes(path);
+    Texture2D texture2D = new Texture2D(2, 2);
+    texture2D.LoadImage(bytes);
+    texture2D.Apply();
+    return texture2D;
+  }
+}

+ 1 - 0
Materials/打包后分辨率设置/123.txt

@@ -0,0 +1 @@
+-screen-width 1754 -screen-height 1240 -screen-fullscreen 0

+ 4 - 2
ToneTuneToolkit/Assets/Examples/001_FileNameCapturer/Scripts/FNC.cs

@@ -1,5 +1,7 @@
 using UnityEngine;
 using ToneTuneToolkit.Common;
+using ToneTuneToolkit.IO;
+using System.Collections.Generic;
 
 namespace Examples
 {
@@ -10,11 +12,11 @@ namespace Examples
   {
     private void Start()
     {
-      string[] fileNames = FileNameCapturer.GetFileName2Array(ToolkitManager.ConfigsPath, ".json");
+      List<string> fileNames = FileCapturer.GetFileNames2List(ToolkitManager.ConfigsPath, ".json");
 
       foreach (string item in fileNames)
       {
-        TipTools.Notice(item);
+        TTTDebug.Log(item);
       }
     }
   }

+ 2 - 2
ToneTuneToolkit/Assets/Examples/003_TextLoader/Scripts/TL.cs

@@ -12,10 +12,10 @@ namespace Examples
     private void Start()
     {
       string txt = TextLoader.GetText(ToolkitManager.ConfigsPath + "sometext.txt", 1);
-      TipTools.Notice(txt);
+      TTTDebug.Log(txt);
 
       string json = JsonManager.GetJson(ToolkitManager.ConfigsPath + "udpconfig.json", "Local IP");
-      TipTools.Notice(json);
+      TTTDebug.Log(json);
     }
   }
 }

+ 8 - 0
ToneTuneToolkit/Assets/Examples/024Tips.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ffa1e168f31a6214d9434784683198bf
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
ToneTuneToolkit/Assets/Examples/024Tips/Scenes.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bfb5416d0e38f904aa1cbeca72b9a5fc
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 204 - 390
Materials/OpenCV/面部识别模块/New Scene.unity → ToneTuneToolkit/Assets/Examples/024Tips/Scenes/Example.unity

@@ -38,7 +38,6 @@ RenderSettings:
   m_ReflectionIntensity: 1
   m_CustomReflection: {fileID: 0}
   m_Sun: {fileID: 0}
-  m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
   m_UseRadianceAmbientProbe: 0
 --- !u!157 &3
 LightmapSettings:
@@ -104,7 +103,7 @@ NavMeshSettings:
   serializedVersion: 2
   m_ObjectHideFlags: 0
   m_BuildSettings:
-    serializedVersion: 2
+    serializedVersion: 3
     agentTypeID: 0
     agentRadius: 0.5
     agentHeight: 2
@@ -117,13 +116,13 @@ NavMeshSettings:
     cellSize: 0.16666667
     manualTileSize: 0
     tileSize: 256
-    accuratePlacement: 0
+    buildHeightMesh: 0
     maxJobWorkers: 0
     preserveTilesOutsideBounds: 0
     debug:
       m_Flags: 0
   m_NavMeshData: {fileID: 0}
---- !u!1 &474530307
+--- !u!1 &655137957
 GameObject:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
@@ -131,100 +130,46 @@ GameObject:
   m_PrefabAsset: {fileID: 0}
   serializedVersion: 6
   m_Component:
-  - component: {fileID: 474530311}
-  - component: {fileID: 474530310}
-  - component: {fileID: 474530309}
-  - component: {fileID: 474530308}
-  m_Layer: 5
-  m_Name: Canvas
+  - component: {fileID: 655137958}
+  - component: {fileID: 655137959}
+  m_Layer: 0
+  m_Name: Global Manager
   m_TagString: Untagged
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
   m_StaticEditorFlags: 0
   m_IsActive: 1
---- !u!114 &474530308
-MonoBehaviour:
+--- !u!4 &655137958
+Transform:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 474530307}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  m_IgnoreReversedGraphics: 1
-  m_BlockingObjects: 0
-  m_BlockingMask:
-    serializedVersion: 2
-    m_Bits: 4294967295
---- !u!114 &474530309
+  m_GameObject: {fileID: 655137957}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &655137959
 MonoBehaviour:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 474530307}
+  m_GameObject: {fileID: 655137957}
   m_Enabled: 1
   m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
+  m_Script: {fileID: 11500000, guid: 39c68a6f00b93774091a8e1f09718c22, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  m_UiScaleMode: 0
-  m_ReferencePixelsPerUnit: 100
-  m_ScaleFactor: 1
-  m_ReferenceResolution: {x: 800, y: 600}
-  m_ScreenMatchMode: 0
-  m_MatchWidthOrHeight: 0
-  m_PhysicalUnit: 3
-  m_FallbackScreenDPI: 96
-  m_DefaultSpriteDPI: 96
-  m_DynamicPixelsPerUnit: 1
-  m_PresetInfoIsWorld: 0
---- !u!223 &474530310
-Canvas:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 474530307}
-  m_Enabled: 1
-  serializedVersion: 3
-  m_RenderMode: 1
-  m_Camera: {fileID: 1880489732}
-  m_PlaneDistance: 100
-  m_PixelPerfect: 0
-  m_ReceivesEvents: 1
-  m_OverrideSorting: 0
-  m_OverridePixelPerfect: 0
-  m_SortingBucketNormalizedSize: 0
-  m_AdditionalShaderChannelsFlag: 0
-  m_SortingLayerID: 0
-  m_SortingOrder: 0
-  m_TargetDisplay: 0
---- !u!224 &474530311
-RectTransform:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 474530307}
-  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
-  m_LocalPosition: {x: 0, y: 0, z: 0}
-  m_LocalScale: {x: 0, y: 0, z: 0}
-  m_ConstrainProportionsScale: 0
-  m_Children:
-  - {fileID: 1399225159}
-  m_Father: {fileID: 0}
-  m_RootOrder: 2
-  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
-  m_AnchorMin: {x: 0, y: 0}
-  m_AnchorMax: {x: 0, y: 0}
-  m_AnchoredPosition: {x: 0, y: 0}
-  m_SizeDelta: {x: 0, y: 0}
-  m_Pivot: {x: 0, y: 0}
---- !u!1 &850486454
+  ValueA: 0
+  ValueB: 1.5
+  GO: {fileID: 0}
+--- !u!1 &848556291
 GameObject:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
@@ -232,46 +177,45 @@ GameObject:
   m_PrefabAsset: {fileID: 0}
   serializedVersion: 6
   m_Component:
-  - component: {fileID: 850486455}
-  - component: {fileID: 850486457}
-  - component: {fileID: 850486456}
-  m_Layer: 0
-  m_Name: Text (Legacy)
+  - component: {fileID: 848556292}
+  - component: {fileID: 848556294}
+  - component: {fileID: 848556293}
+  m_Layer: 5
+  m_Name: Image
   m_TagString: Untagged
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
   m_StaticEditorFlags: 0
   m_IsActive: 1
---- !u!224 &850486455
+--- !u!224 &848556292
 RectTransform:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 850486454}
-  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_GameObject: {fileID: 848556291}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
   m_LocalPosition: {x: 0, y: 0, z: 0}
   m_LocalScale: {x: 1, y: 1, z: 1}
   m_ConstrainProportionsScale: 0
   m_Children: []
-  m_Father: {fileID: 2054611989}
-  m_RootOrder: 0
+  m_Father: {fileID: 1705005025}
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
-  m_AnchorMin: {x: 0, y: 1}
-  m_AnchorMax: {x: 1, y: 1}
+  m_AnchorMin: {x: 0.5, y: 0.5}
+  m_AnchorMax: {x: 0.5, y: 0.5}
   m_AnchoredPosition: {x: 0, y: 0}
-  m_SizeDelta: {x: 0, y: 60}
-  m_Pivot: {x: 0.5, y: 0}
---- !u!114 &850486456
+  m_SizeDelta: {x: 200, y: 200}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &848556293
 MonoBehaviour:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 850486454}
+  m_GameObject: {fileID: 848556291}
   m_Enabled: 1
   m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
+  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
   m_Material: {fileID: 0}
@@ -282,29 +226,25 @@ MonoBehaviour:
   m_OnCullStateChanged:
     m_PersistentCalls:
       m_Calls: []
-  m_FontData:
-    m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
-    m_FontSize: 50
-    m_FontStyle: 0
-    m_BestFit: 0
-    m_MinSize: 5
-    m_MaxSize: 51
-    m_Alignment: 4
-    m_AlignByGeometry: 0
-    m_RichText: 1
-    m_HorizontalOverflow: 0
-    m_VerticalOverflow: 0
-    m_LineSpacing: 1
-  m_Text: Detect
---- !u!222 &850486457
+  m_Sprite: {fileID: 0}
+  m_Type: 0
+  m_PreserveAspect: 0
+  m_FillCenter: 1
+  m_FillMethod: 4
+  m_FillAmount: 1
+  m_FillClockwise: 1
+  m_FillOrigin: 0
+  m_UseSpriteMesh: 0
+  m_PixelsPerUnitMultiplier: 1
+--- !u!222 &848556294
 CanvasRenderer:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 850486454}
+  m_GameObject: {fileID: 848556291}
   m_CullTransparentMesh: 1
---- !u!1 &1076477677
+--- !u!1 &959247046
 GameObject:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
@@ -312,172 +252,101 @@ GameObject:
   m_PrefabAsset: {fileID: 0}
   serializedVersion: 6
   m_Component:
-  - component: {fileID: 1076477680}
-  - component: {fileID: 1076477679}
-  - component: {fileID: 1076477678}
-  m_Layer: 0
-  m_Name: EventSystem
+  - component: {fileID: 959247050}
+  - component: {fileID: 959247049}
+  - component: {fileID: 959247048}
+  - component: {fileID: 959247047}
+  m_Layer: 5
+  m_Name: Canvas
   m_TagString: Untagged
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
   m_StaticEditorFlags: 0
   m_IsActive: 1
---- !u!114 &1076477678
+--- !u!114 &959247047
 MonoBehaviour:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1076477677}
+  m_GameObject: {fileID: 959247046}
   m_Enabled: 1
   m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
+  m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  m_SendPointerHoverToParent: 1
-  m_HorizontalAxis: Horizontal
-  m_VerticalAxis: Vertical
-  m_SubmitButton: Submit
-  m_CancelButton: Cancel
-  m_InputActionsPerSecond: 10
-  m_RepeatDelay: 0.5
-  m_ForceModuleActive: 0
---- !u!114 &1076477679
+  m_IgnoreReversedGraphics: 1
+  m_BlockingObjects: 0
+  m_BlockingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+--- !u!114 &959247048
 MonoBehaviour:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1076477677}
+  m_GameObject: {fileID: 959247046}
   m_Enabled: 1
   m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
+  m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  m_FirstSelected: {fileID: 0}
-  m_sendNavigationEvents: 1
-  m_DragThreshold: 10
---- !u!4 &1076477680
-Transform:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1076477677}
-  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
-  m_LocalPosition: {x: 0, y: 0, z: 0}
-  m_LocalScale: {x: 1, y: 1, z: 1}
-  m_ConstrainProportionsScale: 0
-  m_Children: []
-  m_Father: {fileID: 0}
-  m_RootOrder: 0
-  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!1 &1359915752
-GameObject:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  serializedVersion: 6
-  m_Component:
-  - component: {fileID: 1359915760}
-  - component: {fileID: 1359915761}
-  - component: {fileID: 1359915758}
-  - component: {fileID: 1359915762}
-  - component: {fileID: 1359915763}
-  m_Layer: 0
-  m_Name: Quad
-  m_TagString: Untagged
-  m_Icon: {fileID: 0}
-  m_NavMeshLayer: 0
-  m_StaticEditorFlags: 0
-  m_IsActive: 1
---- !u!114 &1359915758
-MonoBehaviour:
+  m_UiScaleMode: 0
+  m_ReferencePixelsPerUnit: 100
+  m_ScaleFactor: 1
+  m_ReferenceResolution: {x: 800, y: 600}
+  m_ScreenMatchMode: 0
+  m_MatchWidthOrHeight: 0
+  m_PhysicalUnit: 3
+  m_FallbackScreenDPI: 96
+  m_DefaultSpriteDPI: 96
+  m_DynamicPixelsPerUnit: 1
+  m_PresetInfoIsWorld: 0
+--- !u!223 &959247049
+Canvas:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1359915752}
+  m_GameObject: {fileID: 959247046}
   m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 954b04b952bb3b149ade5e248c1b0839, type: 3}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  index: 0
-  PreviewImage: {fileID: 2054611990}
---- !u!224 &1359915760
+  serializedVersion: 3
+  m_RenderMode: 0
+  m_Camera: {fileID: 0}
+  m_PlaneDistance: 100
+  m_PixelPerfect: 0
+  m_ReceivesEvents: 1
+  m_OverrideSorting: 0
+  m_OverridePixelPerfect: 0
+  m_SortingBucketNormalizedSize: 0
+  m_VertexColorAlwaysGammaSpace: 0
+  m_AdditionalShaderChannelsFlag: 0
+  m_UpdateRectTransformForStandalone: 0
+  m_SortingLayerID: 0
+  m_SortingOrder: 0
+  m_TargetDisplay: 0
+--- !u!224 &959247050
 RectTransform:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1359915752}
-  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_GameObject: {fileID: 959247046}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
   m_LocalPosition: {x: 0, y: 0, z: 0}
-  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_LocalScale: {x: 0, y: 0, z: 0}
   m_ConstrainProportionsScale: 0
   m_Children:
-  - {fileID: 1770240637}
-  m_Father: {fileID: 1399225159}
-  m_RootOrder: 0
+  - {fileID: 1705005025}
+  m_Father: {fileID: 0}
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0}
   m_AnchorMax: {x: 0, y: 0}
   m_AnchoredPosition: {x: 0, y: 0}
   m_SizeDelta: {x: 0, y: 0}
-  m_Pivot: {x: 0.5, y: 0.5}
---- !u!114 &1359915761
-MonoBehaviour:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1359915752}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  m_Material: {fileID: 0}
-  m_Color: {r: 1, g: 1, b: 1, a: 1}
-  m_RaycastTarget: 1
-  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
-  m_Maskable: 1
-  m_OnCullStateChanged:
-    m_PersistentCalls:
-      m_Calls: []
-  m_Sprite: {fileID: 0}
-  m_Type: 0
-  m_PreserveAspect: 0
-  m_FillCenter: 1
-  m_FillMethod: 4
-  m_FillAmount: 1
-  m_FillClockwise: 1
-  m_FillOrigin: 0
-  m_UseSpriteMesh: 0
-  m_PixelsPerUnitMultiplier: 1
---- !u!222 &1359915762
-CanvasRenderer:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1359915752}
-  m_CullTransparentMesh: 1
---- !u!114 &1359915763
-MonoBehaviour:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1359915752}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: a6535bbeb939e004b8e5cbad7ec84dd6, type: 3}
-  m_Name: 
-  m_EditorClassIdentifier: 
---- !u!1 &1399225158
+  m_Pivot: {x: 0, y: 0}
+--- !u!1 &1705005024
 GameObject:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
@@ -485,109 +354,60 @@ GameObject:
   m_PrefabAsset: {fileID: 0}
   serializedVersion: 6
   m_Component:
-  - component: {fileID: 1399225159}
-  - component: {fileID: 1399225160}
+  - component: {fileID: 1705005025}
+  - component: {fileID: 1705005028}
+  - component: {fileID: 1705005027}
+  - component: {fileID: 1705005026}
   m_Layer: 5
-  m_Name: GameObject
+  m_Name: Image
   m_TagString: Untagged
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
   m_StaticEditorFlags: 0
   m_IsActive: 1
---- !u!224 &1399225159
+--- !u!224 &1705005025
 RectTransform:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1399225158}
+  m_GameObject: {fileID: 1705005024}
   m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
   m_LocalPosition: {x: 0, y: 0, z: 0}
   m_LocalScale: {x: 1, y: 1, z: 1}
   m_ConstrainProportionsScale: 0
   m_Children:
-  - {fileID: 1359915760}
-  - {fileID: 2054611989}
-  m_Father: {fileID: 474530311}
-  m_RootOrder: 0
+  - {fileID: 848556292}
+  m_Father: {fileID: 959247050}
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
-  m_AnchorMin: {x: 0, y: 0}
-  m_AnchorMax: {x: 1, y: 1}
+  m_AnchorMin: {x: 0.5, y: 0.5}
+  m_AnchorMax: {x: 0.5, y: 0.5}
   m_AnchoredPosition: {x: 0, y: 0}
-  m_SizeDelta: {x: -400, y: -400}
+  m_SizeDelta: {x: 100, y: 100}
   m_Pivot: {x: 0.5, y: 0.5}
---- !u!114 &1399225160
+--- !u!114 &1705005026
 MonoBehaviour:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1399225158}
+  m_GameObject: {fileID: 1705005024}
   m_Enabled: 1
   m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 8a8695521f0d02e499659fee002a26c2, type: 3}
+  m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  m_Padding:
-    m_Left: 0
-    m_Right: 0
-    m_Top: 0
-    m_Bottom: 0
-  m_ChildAlignment: 0
-  m_StartCorner: 0
-  m_StartAxis: 0
-  m_CellSize: {x: 440, y: 440}
-  m_Spacing: {x: 100, y: 100}
-  m_Constraint: 0
-  m_ConstraintCount: 2
---- !u!1 &1770240636
-GameObject:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  serializedVersion: 6
-  m_Component:
-  - component: {fileID: 1770240637}
-  - component: {fileID: 1770240639}
-  - component: {fileID: 1770240638}
-  m_Layer: 0
-  m_Name: Text (Legacy)
-  m_TagString: Untagged
-  m_Icon: {fileID: 0}
-  m_NavMeshLayer: 0
-  m_StaticEditorFlags: 0
-  m_IsActive: 1
---- !u!224 &1770240637
-RectTransform:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1770240636}
-  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
-  m_LocalPosition: {x: 0, y: 0, z: 0}
-  m_LocalScale: {x: 1, y: 1, z: 1}
-  m_ConstrainProportionsScale: 0
-  m_Children: []
-  m_Father: {fileID: 1359915760}
-  m_RootOrder: 0
-  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
-  m_AnchorMin: {x: 0, y: 1}
-  m_AnchorMax: {x: 1, y: 1}
-  m_AnchoredPosition: {x: 0, y: 0}
-  m_SizeDelta: {x: 0, y: 60}
-  m_Pivot: {x: 0.5, y: 0}
---- !u!114 &1770240638
+  m_ShowMaskGraphic: 0
+--- !u!114 &1705005027
 MonoBehaviour:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1770240636}
+  m_GameObject: {fileID: 1705005024}
   m_Enabled: 1
   m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
+  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
   m_Material: {fileID: 0}
@@ -598,29 +418,25 @@ MonoBehaviour:
   m_OnCullStateChanged:
     m_PersistentCalls:
       m_Calls: []
-  m_FontData:
-    m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
-    m_FontSize: 50
-    m_FontStyle: 0
-    m_BestFit: 0
-    m_MinSize: 5
-    m_MaxSize: 51
-    m_Alignment: 4
-    m_AlignByGeometry: 0
-    m_RichText: 1
-    m_HorizontalOverflow: 0
-    m_VerticalOverflow: 0
-    m_LineSpacing: 1
-  m_Text: Orginal
---- !u!222 &1770240639
+  m_Sprite: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0}
+  m_Type: 0
+  m_PreserveAspect: 0
+  m_FillCenter: 1
+  m_FillMethod: 4
+  m_FillAmount: 1
+  m_FillClockwise: 1
+  m_FillOrigin: 0
+  m_UseSpriteMesh: 0
+  m_PixelsPerUnitMultiplier: 1
+--- !u!222 &1705005028
 CanvasRenderer:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1770240636}
+  m_GameObject: {fileID: 1705005024}
   m_CullTransparentMesh: 1
---- !u!1 &1880489730
+--- !u!1 &1736974413
 GameObject:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
@@ -628,9 +444,8 @@ GameObject:
   m_PrefabAsset: {fileID: 0}
   serializedVersion: 6
   m_Component:
-  - component: {fileID: 1880489733}
-  - component: {fileID: 1880489732}
-  - component: {fileID: 1880489731}
+  - component: {fileID: 1736974416}
+  - component: {fileID: 1736974415}
   m_Layer: 0
   m_Name: Main Camera
   m_TagString: MainCamera
@@ -638,31 +453,31 @@ GameObject:
   m_NavMeshLayer: 0
   m_StaticEditorFlags: 0
   m_IsActive: 1
---- !u!81 &1880489731
-AudioListener:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1880489730}
-  m_Enabled: 1
---- !u!20 &1880489732
+--- !u!20 &1736974415
 Camera:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1880489730}
+  m_GameObject: {fileID: 1736974413}
   m_Enabled: 1
   serializedVersion: 2
   m_ClearFlags: 2
-  m_BackGroundColor: {r: 0.26415092, g: 0.26415092, b: 0.26415092, a: 0}
+  m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0}
   m_projectionMatrixMode: 1
   m_GateFitMode: 2
   m_FOVAxisMode: 0
+  m_Iso: 200
+  m_ShutterSpeed: 0.005
+  m_Aperture: 16
+  m_FocusDistance: 10
+  m_FocalLength: 50
+  m_BladeCount: 5
+  m_Curvature: {x: 2, y: 11}
+  m_BarrelClipping: 0.25
+  m_Anamorphism: 0
   m_SensorSize: {x: 36, y: 24}
   m_LensShift: {x: 0, y: 0}
-  m_FocalLength: 50
   m_NormalizedViewPortRect:
     serializedVersion: 2
     x: 0
@@ -689,22 +504,22 @@ Camera:
   m_OcclusionCulling: 1
   m_StereoConvergence: 10
   m_StereoSeparation: 0.022
---- !u!4 &1880489733
+--- !u!4 &1736974416
 Transform:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1880489730}
+  m_GameObject: {fileID: 1736974413}
+  serializedVersion: 2
   m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
   m_LocalPosition: {x: 0, y: 0, z: -10}
   m_LocalScale: {x: 1, y: 1, z: 1}
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 0}
-  m_RootOrder: 1
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!1 &2054611988
+--- !u!1 &1981348029
 GameObject:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
@@ -712,72 +527,71 @@ GameObject:
   m_PrefabAsset: {fileID: 0}
   serializedVersion: 6
   m_Component:
-  - component: {fileID: 2054611989}
-  - component: {fileID: 2054611991}
-  - component: {fileID: 2054611990}
-  m_Layer: 5
-  m_Name: NewMatImage
+  - component: {fileID: 1981348032}
+  - component: {fileID: 1981348031}
+  - component: {fileID: 1981348030}
+  m_Layer: 0
+  m_Name: EventSystem
   m_TagString: Untagged
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
   m_StaticEditorFlags: 0
   m_IsActive: 1
---- !u!224 &2054611989
-RectTransform:
+--- !u!114 &1981348030
+MonoBehaviour:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 2054611988}
-  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
-  m_LocalPosition: {x: 0, y: 0, z: 0}
-  m_LocalScale: {x: 1, y: 1, z: 1}
-  m_ConstrainProportionsScale: 0
-  m_Children:
-  - {fileID: 850486455}
-  m_Father: {fileID: 1399225159}
-  m_RootOrder: 1
-  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
-  m_AnchorMin: {x: 0, y: 0}
-  m_AnchorMax: {x: 0, y: 0}
-  m_AnchoredPosition: {x: 0, y: 0}
-  m_SizeDelta: {x: 0, y: 0}
-  m_Pivot: {x: 0.5, y: 0.5}
---- !u!114 &2054611990
+  m_GameObject: {fileID: 1981348029}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_SendPointerHoverToParent: 1
+  m_HorizontalAxis: Horizontal
+  m_VerticalAxis: Vertical
+  m_SubmitButton: Submit
+  m_CancelButton: Cancel
+  m_InputActionsPerSecond: 10
+  m_RepeatDelay: 0.5
+  m_ForceModuleActive: 0
+--- !u!114 &1981348031
 MonoBehaviour:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 2054611988}
+  m_GameObject: {fileID: 1981348029}
   m_Enabled: 1
   m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+  m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  m_Material: {fileID: 0}
-  m_Color: {r: 1, g: 1, b: 1, a: 1}
-  m_RaycastTarget: 1
-  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
-  m_Maskable: 1
-  m_OnCullStateChanged:
-    m_PersistentCalls:
-      m_Calls: []
-  m_Sprite: {fileID: 0}
-  m_Type: 0
-  m_PreserveAspect: 0
-  m_FillCenter: 1
-  m_FillMethod: 4
-  m_FillAmount: 1
-  m_FillClockwise: 1
-  m_FillOrigin: 0
-  m_UseSpriteMesh: 0
-  m_PixelsPerUnitMultiplier: 1
---- !u!222 &2054611991
-CanvasRenderer:
+  m_FirstSelected: {fileID: 0}
+  m_sendNavigationEvents: 1
+  m_DragThreshold: 10
+--- !u!4 &1981348032
+Transform:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 2054611988}
-  m_CullTransparentMesh: 1
+  m_GameObject: {fileID: 1981348029}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1660057539 &9223372036854775807
+SceneRoots:
+  m_ObjectHideFlags: 0
+  m_Roots:
+  - {fileID: 1736974416}
+  - {fileID: 655137958}
+  - {fileID: 959247050}
+  - {fileID: 1981348032}

+ 7 - 0
ToneTuneToolkit/Assets/Examples/024Tips/Scenes/Example.unity.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: a70edbb747fef394798ebea0e0f8b943
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
ToneTuneToolkit/Assets/Examples/024Tips/Scripts.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: dd6ab325c5dd4ee47984cd1be666142f
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 18 - 0
ToneTuneToolkit/Assets/Examples/024Tips/Scripts/Tips.cs

@@ -0,0 +1,18 @@
+using UnityEngine;
+
+namespace Examples
+{
+  /// <summary>
+  /// 
+  /// </summary>
+  public class Tips : MonoBehaviour
+  {
+
+    [SerializeField] private float ValueA = 0;
+
+    [Range(0, 10)] public float ValueB = 1.5f;
+
+    [Header("GG")] public GameObject GO;
+
+  }
+}

+ 1 - 1
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/ScreenshotMasterLite.cs.meta → ToneTuneToolkit/Assets/Examples/024Tips/Scripts/Tips.cs.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 63297da57baa4a44283af12894d2e248
+guid: 39c68a6f00b93774091a8e1f09718c22
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2

+ 2724 - 0
ToneTuneToolkit/Assets/StreamingAssets/ToneTuneToolkit/configs/sensitivewords.json

@@ -0,0 +1,2724 @@
+[
+  "2 girls 1 cup",
+  "2g1c",
+  "4r5e",
+  "5h1t",
+  "5hit",
+  "5ht",
+  "@$$",
+  "a s s",
+  "a s shole",
+  "a55",
+  "a55hole",
+  "a_s_s",
+  "abbo",
+  "abeed",
+  "abuse",
+  "acrotomophilia",
+  "africoon",
+  "ahole",
+  "alabama hot pocket",
+  "alaskan pipeline",
+  "alligator bait",
+  "alligatorbait",
+  "amcik",
+  "anal",
+  "analannie",
+  "analprobe",
+  "analsex",
+  "andskota",
+  "anilingus",
+  "anus",
+  "apeshit",
+  "ar5e",
+  "arabush",
+  "arabushs",
+  "areola",
+  "areole",
+  "argie",
+  "armo",
+  "armos",
+  "arrse",
+  "arschloch",
+  "arse",
+  "arsehole",
+  "aryan",
+  "ash0le",
+  "ash0les",
+  "asholes",
+  "ass monkey",
+  "ass",
+  "ass-fucker",
+  "ass-hat",
+  "ass-pirate",
+  "assbag",
+  "assbagger",
+  "assbandit",
+  "assbang",
+  "assbanged",
+  "assbanger",
+  "assbangs",
+  "assbite",
+  "assblaster",
+  "assclown",
+  "asscock",
+  "asscowboy",
+  "asscracker",
+  "asses",
+  "assface",
+  "assfuck",
+  "assfucker",
+  "assfukka",
+  "assgoblin",
+  "assh0le",
+  "assh0lez",
+  "asshat",
+  "asshead",
+  "assho1e",
+  "asshole",
+  "assholes",
+  "assholz",
+  "asshopper",
+  "asshore",
+  "assjacker",
+  "assjockey",
+  "asskiss",
+  "asskisser",
+  "assklown",
+  "asslick",
+  "asslicker",
+  "asslover",
+  "assman",
+  "assmaster",
+  "assmonkey",
+  "assmunch",
+  "assmuncher",
+  "assnigger",
+  "asspacker",
+  "asspirate",
+  "asspuppies",
+  "assrammer",
+  "assranger",
+  "assshit",
+  "assshole",
+  "asssucker",
+  "asswad",
+  "asswhole",
+  "asswhore",
+  "asswipe",
+  "asswipes",
+  "auto erotic",
+  "autoerotic",
+  "ayir",
+  "azazel",
+  "azz",
+  "azzhole",
+  "b a s t a r d",
+  "b i t c h",
+  "b o o b",
+  "b!+ch",
+  "b!tch",
+  "b!tchin",
+  "b*tch",
+  "b00b",
+  "b00bies",
+  "b00biez",
+  "b00bs",
+  "b00bz",
+  "b17ch",
+  "b1tch",
+  "b7ch",
+  "babeland",
+  "babes",
+  "baby batter",
+  "baby juice",
+  "backdoorman",
+  "badfuck",
+  "ball gag",
+  "ball gravy",
+  "ball kicking",
+  "ball licking",
+  "ball sack",
+  "ball sucking",
+  "ballbag",
+  "balllicker",
+  "ballsack",
+  "bampot",
+  "bangbro",
+  "bangbros",
+  "bangbus",
+  "bareback",
+  "barely legal",
+  "barelylegal",
+  "barenaked",
+  "barface",
+  "barfface",
+  "bassterd",
+  "bassterds",
+  "bastard",
+  "bastardo",
+  "bastards",
+  "bastardz",
+  "basterds",
+  "basterdz",
+  "bastinado",
+  "bazongas",
+  "bazooms",
+  "bbw",
+  "bdsm",
+  "beaner",
+  "beaners",
+  "beaney",
+  "beaneys",
+  "beardedclam",
+  "beastality",
+  "beastial",
+  "beastiality",
+  "beastility",
+  "beatch",
+  "beatoff",
+  "beatyourmeat",
+  "beaver cleaver",
+  "beaver lips",
+  "beef curtains",
+  "beeyotch",
+  "bellend",
+  "beotch",
+  "bestial",
+  "bestiality",
+  "bi curious",
+  "bi+ch",
+  "bi7ch",
+  "biatch",
+  "bicurious",
+  "big black",
+  "big breasts",
+  "big knockers",
+  "big tits",
+  "bigass",
+  "bigbastard",
+  "bigbreasts",
+  "bigbutt",
+  "bigtits",
+  "bimbo",
+  "bimbos",
+  "bint",
+  "birdlock",
+  "bitch",
+  "bitchass",
+  "bitched",
+  "bitcher",
+  "bitchers",
+  "bitches",
+  "bitchez",
+  "bitchin",
+  "bitching",
+  "bitchslap",
+  "bitchtit",
+  "bitchy",
+  "biteme",
+  "bitties",
+  "black cock",
+  "blackcock",
+  "blackman",
+  "blacks",
+  "blonde action",
+  "blonde on blonde action",
+  "blonde on blonde",
+  "bloodclaat",
+  "blow j",
+  "blow job",
+  "blow your l",
+  "blow your load",
+  "blowjob",
+  "blowjobs",
+  "blue waffle",
+  "bluegum",
+  "bluegums",
+  "blumpkin",
+  "bo ob",
+  "bo obs",
+  "boang",
+  "boche",
+  "boches",
+  "boffing",
+  "bogan",
+  "bohunk",
+  "boink",
+  "boiolas",
+  "bollick",
+  "bollock",
+  "bollocks",
+  "bollok",
+  "bollox",
+  "bombers",
+  "bomd",
+  "bondage",
+  "boned",
+  "boner",
+  "boners",
+  "bong",
+  "boong",
+  "boonga",
+  "boongas",
+  "boongs",
+  "boonie",
+  "boonies",
+  "booobs",
+  "boooobs",
+  "booooobs",
+  "booooooobs",
+  "bootee",
+  "bootlip",
+  "bootlips",
+  "boozer",
+  "bosch",
+  "bosche",
+  "bosches",
+  "boschs",
+  "bosomy",
+  "bounty bar",
+  "bounty bars",
+  "bountybar",
+  "brea5t",
+  "breastjob",
+  "breastlover",
+  "breastman",
+  "brown shower",
+  "brown showers",
+  "brunette action",
+  "btch",
+  "buceta",
+  "buddhahead",
+  "buddhaheads",
+  "buffies",
+  "bugger",
+  "buggered",
+  "buggery",
+  "bukake",
+  "bukkake",
+  "bullcrap",
+  "bulldike",
+  "bulldyke",
+  "bullet vibe",
+  "bullshit",
+  "bullshits",
+  "bullshitted",
+  "bullturds",
+  "bumblefuck",
+  "bumfuck",
+  "bung hole",
+  "bung",
+  "bunga",
+  "bungas",
+  "bunghole",
+  "bunny fucker",
+  "burr head",
+  "burr heads",
+  "burrhead",
+  "burrheads",
+  "butchbabes",
+  "butchdike",
+  "butchdyke",
+  "butt plug",
+  "butt-pirate",
+  "buttbang",
+  "buttcheeks",
+  "buttface",
+  "buttfuck",
+  "buttfucker",
+  "buttfuckers",
+  "butthead",
+  "butthole",
+  "buttman",
+  "buttmuch",
+  "buttmunch",
+  "buttmuncher",
+  "buttpirate",
+  "buttplug",
+  "buttstain",
+  "buttwipe",
+  "byatch",
+  "c u n t",
+  "c-0-c-k",
+  "c-o-c-k",
+  "c-u-n-t",
+  "c.0.c.k",
+  "c.o.c.k.",
+  "c.u.n.t",
+  "c0ck",
+  "c0cks",
+  "c0cksucker",
+  "c0k",
+  "cabron",
+  "caca",
+  "cacker",
+  "cahone",
+  "camel jockey",
+  "camel jockeys",
+  "camel toe",
+  "cameljockey",
+  "cameltoe",
+  "camgirl",
+  "camslut",
+  "camwhore",
+  "carpet muncher",
+  "carpetmuncher",
+  "carruth",
+  "cawk",
+  "cawks",
+  "cazzo",
+  "chav",
+  "cheese eating surrender monkey",
+  "cheese eating surrender monkies",
+  "cheeseeating surrender monkey",
+  "cheeseeating surrender monkies",
+  "cheesehead",
+  "cheeseheads",
+  "cherrypopper",
+  "chickslick",
+  "china swede",
+  "china swedes",
+  "chinaman",
+  "chinamen",
+  "chinaswede",
+  "chinaswedes",
+  "chinc",
+  "chincs",
+  "ching chong",
+  "ching chongs",
+  "chinga",
+  "chingchong",
+  "chingchongs",
+  "chink",
+  "chinks",
+  "chinky",
+  "choad",
+  "chocolate rosebuds",
+  "chode",
+  "chodes",
+  "chonkies",
+  "chonky",
+  "chonkys",
+  "chraa",
+  "christ killer",
+  "christ killers",
+  "chug",
+  "chugs",
+  "chuj",
+  "chunger",
+  "chungers",
+  "chunkies",
+  "chunkys",
+  "cipa",
+  "circlejerk",
+  "cl1t",
+  "clamdigger",
+  "clamdiver",
+  "clamps",
+  "clansman",
+  "clansmen",
+  "clanswoman",
+  "clanswomen",
+  "cleveland steamer",
+  "clit",
+  "clitface",
+  "clitfuck",
+  "clitoris",
+  "clitorus",
+  "clits",
+  "clitty",
+  "clogwog",
+  "clover clamps",
+  "clusterfuck",
+  "cnts",
+  "cntz",
+  "cnut",
+  "cocain",
+  "cocaine",
+  "cock",
+  "cock-head",
+  "cock-sucker",
+  "cockbite",
+  "cockblock",
+  "cockblocker",
+  "cockburger",
+  "cockcowboy",
+  "cockface",
+  "cockfight",
+  "cockfucker",
+  "cockhead",
+  "cockholster",
+  "cockjockey",
+  "cockknob",
+  "cockknocker",
+  "cockknoker",
+  "cocklicker",
+  "cocklover",
+  "cockmaster",
+  "cockmongler",
+  "cockmongruel",
+  "cockmonkey",
+  "cockmunch",
+  "cockmuncher",
+  "cocknob",
+  "cocknose",
+  "cocknugget",
+  "cockqueen",
+  "cockrider",
+  "cocks",
+  "cockshit",
+  "cocksman",
+  "cocksmith",
+  "cocksmoker",
+  "cocksucer",
+  "cocksuck",
+  "cocksucked",
+  "cocksucker",
+  "cocksucking",
+  "cocksucks",
+  "cocksuka",
+  "cocksukka",
+  "cocktease",
+  "cocky",
+  "cohee",
+  "coital",
+  "coitus",
+  "cok",
+  "cokmuncher",
+  "coksucka",
+  "condom",
+  "coochie",
+  "coochy",
+  "coolie",
+  "coolies",
+  "cooly",
+  "coon ass",
+  "coon asses",
+  "coonass",
+  "coonasses",
+  "coondog",
+  "coons",
+  "cooter",
+  "coprolagnia",
+  "coprophilia",
+  "copulate",
+  "corksucker",
+  "cornhole",
+  "cra5h",
+  "crackcocain",
+  "crackpipe",
+  "crackwhore",
+  "crap",
+  "crapola",
+  "crapper",
+  "crappy",
+  "creampie",
+  "crotchjockey",
+  "crotchmonkey",
+  "crotchrot",
+  "cuck",
+  "cum face",
+  "cum licker",
+  "cum",
+  "cumbubble",
+  "cumdumpster",
+  "cumfest",
+  "cumguzzler",
+  "cuming",
+  "cumjockey",
+  "cumlickr",
+  "cumm",
+  "cummer",
+  "cummin",
+  "cumming",
+  "cumquat",
+  "cumqueen",
+  "cums",
+  "cumshot",
+  "cumshots",
+  "cumslut",
+  "cumstain",
+  "cumsucker",
+  "cumtart",
+  "cunilingus",
+  "cunillingus",
+  "cunn",
+  "cunnie",
+  "cunnilingus",
+  "cunntt",
+  "cunny",
+  "cunt",
+  "cunteyed",
+  "cuntface",
+  "cuntfuck",
+  "cuntfucker",
+  "cunthole",
+  "cunthunter",
+  "cuntlick",
+  "cuntlicker",
+  "cuntlicking",
+  "cuntrag",
+  "cunts",
+  "cuntslut",
+  "cuntsucker",
+  "cuntz",
+  "curry muncher",
+  "curry munchers",
+  "currymuncher",
+  "currymunchers",
+  "cushi",
+  "cushis",
+  "cyalis",
+  "cyberfuc",
+  "cyberfuck",
+  "cyberfucked",
+  "cyberfucker",
+  "cyberfuckers",
+  "cyberfucking",
+  "cybersex",
+  "cyberslimer",
+  "d0ng",
+  "d0uch3",
+  "d0uche",
+  "d1ck",
+  "d1ld0",
+  "d1ldo",
+  "d4mn",
+  "dago",
+  "dagos",
+  "dahmer",
+  "damm",
+  "dammit",
+  "damn",
+  "damnit",
+  "darkey",
+  "darkeys",
+  "darkie",
+  "darkies",
+  "darky",
+  "date rape",
+  "daterape",
+  "datnigga",
+  "dawgie style",
+  "dawgie-style",
+  "daygo",
+  "deapthroat",
+  "deep throat",
+  "deep throating",
+  "deepaction",
+  "deepthroat",
+  "deepthroating",
+  "defecate",
+  "deggo",
+  "dego",
+  "degos",
+  "dendrophilia",
+  "destroyyourpussy",
+  "deth",
+  "diaper daddy",
+  "diaper head",
+  "diaper heads",
+  "diaperdaddy",
+  "diaperhead",
+  "diaperheads",
+  "dick pic",
+  "dick",
+  "dick-ish",
+  "dickbag",
+  "dickbeater",
+  "dickbeaters",
+  "dickbrain",
+  "dickdipper",
+  "dickface",
+  "dickflipper",
+  "dickforbrains",
+  "dickfuck",
+  "dickhead",
+  "dickheads",
+  "dickhole",
+  "dickish",
+  "dickjuice",
+  "dickless",
+  "dicklick",
+  "dicklicker",
+  "dickman",
+  "dickmilk",
+  "dickmonger",
+  "dickpic",
+  "dickripper",
+  "dicks",
+  "dicksipper",
+  "dickslap",
+  "dickslicker",
+  "dicksucker",
+  "dickwad",
+  "dickweasel",
+  "dickweed",
+  "dickwhipper",
+  "dickwod",
+  "dickzipper",
+  "diddle",
+  "dike",
+  "dild0",
+  "dild0s",
+  "dildo",
+  "dildos",
+  "dilf",
+  "diligaf",
+  "dilld0",
+  "dilld0s",
+  "dillweed",
+  "dimwit",
+  "dingle",
+  "dingleberries",
+  "dingleberry",
+  "dink",
+  "dinks",
+  "dipship",
+  "dipshit",
+  "dipstick",
+  "dirsa",
+  "dirty pillows",
+  "dirty sanchez",
+  "dix",
+  "dixiedike",
+  "dixiedyke",
+  "dlck",
+  "dog style",
+  "dog-fucker",
+  "doggie style",
+  "doggie",
+  "doggie-style",
+  "doggiestyle",
+  "doggin",
+  "dogging",
+  "doggy style",
+  "doggy-style",
+  "doggystyle",
+  "dolcett",
+  "dominatricks",
+  "dominatrics",
+  "dominatrix",
+  "dommes",
+  "dong",
+  "donkey punch",
+  "donkeypunch",
+  "donkeyribber",
+  "doochbag",
+  "doodoo",
+  "doofus",
+  "dookie",
+  "doosh",
+  "dot head",
+  "dot heads",
+  "dothead",
+  "dotheads",
+  "double dong",
+  "double penetration",
+  "doubledong",
+  "doublepenetration",
+  "douch3",
+  "douche bag",
+  "douche",
+  "douche-fag",
+  "douchebag",
+  "douchebags",
+  "douchewaffle",
+  "douchey",
+  "dp action",
+  "dpaction",
+  "dragqueen",
+  "dragqween",
+  "dripdick",
+  "dry hump",
+  "dryhump",
+  "duche",
+  "dudette",
+  "dumass",
+  "dumb ass",
+  "dumbass",
+  "dumbasses",
+  "dumbbitch",
+  "dumbfuck",
+  "dumbshit",
+  "dumshit",
+  "dune coon",
+  "dune coons",
+  "dupa",
+  "dvda",
+  "dyefly",
+  "dyke",
+  "dykes",
+  "dziwka",
+  "earotics",
+  "easyslut",
+  "eat my ass",
+  "eat my",
+  "eatadick",
+  "eatballs",
+  "eathairpie",
+  "eatme",
+  "eatmyass",
+  "eatpussy",
+  "ecchi",
+  "ejackulate",
+  "ejakulate",
+  "ekrem",
+  "ekto",
+  "enculer",
+  "enema",
+  "erection",
+  "ero",
+  "erotic",
+  "erotism",
+  "esqua",
+  "essohbee",
+  "ethical slut",
+  "evl",
+  "excrement",
+  "exkwew",
+  "extacy",
+  "extasy",
+  "f u c k e r",
+  "f u c k e",
+  "f u c k",
+  "f u k",
+  "f*ck",
+  "f-u-c-k",
+  "f.u.c.k",
+  "f4nny",
+  "f_u_c_k",
+  "facefucker",
+  "fack",
+  "faeces",
+  "faen",
+  "fag",
+  "fag1t",
+  "fagbag",
+  "faget",
+  "fagfucker",
+  "fagg",
+  "fagg1t",
+  "fagged",
+  "fagging",
+  "faggit",
+  "faggitt",
+  "faggot",
+  "faggotcock",
+  "faggs",
+  "fagit",
+  "fagot",
+  "fagots",
+  "fags",
+  "fagt",
+  "fagtard",
+  "fagz",
+  "faig",
+  "faigs",
+  "faigt",
+  "fanculo",
+  "fannybandit",
+  "fannyflaps",
+  "fannyfucker",
+  "fanyy",
+  "fartknocker",
+  "fastfuck",
+  "fatah",
+  "fatfuck",
+  "fatfucker",
+  "fatso",
+  "fck",
+  "fckcum",
+  "fckd",
+  "fcuk",
+  "fcuker",
+  "fcuking",
+  "fecal",
+  "feck",
+  "fecker",
+  "feg",
+  "felatio",
+  "felch",
+  "felcher",
+  "felching",
+  "fellate",
+  "fellatio",
+  "feltch",
+  "feltcher",
+  "feltching",
+  "female squirting",
+  "femalesquirtin",
+  "femalesquirting",
+  "femdom",
+  "fetish",
+  "ficken",
+  "figging",
+  "fingerbang",
+  "fingerfood",
+  "fingerfuck",
+  "fingerfucked",
+  "fingerfucker",
+  "fingerfuckers",
+  "fingerfucking",
+  "fingerfucks",
+  "fingering",
+  "fisted",
+  "fister",
+  "fistfuck",
+  "fistfucked",
+  "fistfucker",
+  "fistfuckers",
+  "fistfucking",
+  "fistfuckings",
+  "fistfucks",
+  "fisting",
+  "fisty",
+  "fitt",
+  "flamer",
+  "flasher",
+  "flikker",
+  "flipping the bird",
+  "flogthelog",
+  "floo",
+  "floozy",
+  "flydie",
+  "flydye",
+  "foad",
+  "fok",
+  "fondle",
+  "foobar",
+  "fook",
+  "fooker",
+  "foot fetish",
+  "footaction",
+  "footfetish",
+  "footfuck",
+  "footfucker",
+  "footjob",
+  "footlicker",
+  "footstar",
+  "foreskin",
+  "forni",
+  "fornicate",
+  "fotze",
+  "foursome",
+  "fourtwenty",
+  "freakfuck",
+  "freakyfucker",
+  "freefuck",
+  "freex",
+  "frigg",
+  "frigga",
+  "frigger",
+  "frotting",
+  "fucck",
+  "fuck",
+  "fuck-tard",
+  "fucka",
+  "fuckable",
+  "fuckass",
+  "fuckbag",
+  "fuckbitch",
+  "fuckbook",
+  "fuckboy",
+  "fuckbrain",
+  "fuckbuddy",
+  "fuckbutt",
+  "fuckd",
+  "fucked",
+  "fuckedup",
+  "fucker",
+  "fuckers",
+  "fuckersucker",
+  "fuckface",
+  "fuckfest",
+  "fuckfreak",
+  "fuckfriend",
+  "fuckhead",
+  "fuckheads",
+  "fuckher",
+  "fuckhole",
+  "fuckin",
+  "fuckina",
+  "fucking",
+  "fuckingbitch",
+  "fuckings",
+  "fuckingshitmotherfucker",
+  "fuckinnuts",
+  "fuckinright",
+  "fuckit",
+  "fuckknob",
+  "fuckme",
+  "fuckmeat",
+  "fuckmehard",
+  "fuckmonkey",
+  "fuckn",
+  "fucknugget",
+  "fucknut",
+  "fucknuts",
+  "fucknutt",
+  "fucknutz",
+  "fuckoff",
+  "fuckpig",
+  "fuckpuppet",
+  "fuckr",
+  "fucks",
+  "fuckstick",
+  "fucktard",
+  "fucktards",
+  "fucktoy",
+  "fucktrophy",
+  "fuckup",
+  "fuckwad",
+  "fuckwhit",
+  "fuckwhore",
+  "fuckwit",
+  "fuckwitt",
+  "fuckyomama",
+  "fuckyou",
+  "fudge packer",
+  "fudgepacker",
+  "fugly",
+  "fuk",
+  "fukah",
+  "fuken",
+  "fuker",
+  "fukin",
+  "fuking",
+  "fukk",
+  "fukkah",
+  "fukken",
+  "fukker",
+  "fukkin",
+  "fukking",
+  "fuks",
+  "fuktard",
+  "fuktards",
+  "fukwhit",
+  "fukwit",
+  "funfuck",
+  "futanari",
+  "futanary",
+  "futkretzn",
+  "fuuck",
+  "fux",
+  "fux0r",
+  "fuxor",
+  "fvck",
+  "fvk",
+  "fxck",
+  "g-spot",
+  "g00k",
+  "gae",
+  "gai",
+  "gang bang",
+  "gangbang",
+  "gangbanged",
+  "gangbanger",
+  "gangbangs",
+  "ganja",
+  "gassyass",
+  "gator bait",
+  "gatorbait",
+  "gay sex",
+  "gayass",
+  "gaybob",
+  "gayboy",
+  "gaydo",
+  "gaygirl",
+  "gaylord",
+  "gaymuthafuckinwhore",
+  "gays",
+  "gaysex",
+  "gaytard",
+  "gaywad",
+  "gayz",
+  "geezer",
+  "geni",
+  "genital",
+  "genitals",
+  "getiton",
+  "gey",
+  "gfy",
+  "ghay",
+  "ghey",
+  "giant cock",
+  "gigolo",
+  "ginzo",
+  "ginzos",
+  "gipp",
+  "gippo",
+  "gippos",
+  "gipps",
+  "girl on top",
+  "girl on",
+  "girls gone wild",
+  "givehead",
+  "glans",
+  "glazeddonut",
+  "goatcx",
+  "goatse",
+  "god dammit",
+  "god damn",
+  "god damnit",
+  "god-dam",
+  "god-damned",
+  "godam",
+  "godammit",
+  "godamn",
+  "godamnit",
+  "goddam",
+  "goddamit",
+  "goddamm",
+  "goddammit",
+  "goddamn",
+  "goddamned",
+  "goddamnes",
+  "goddamnit",
+  "goddamnmuthafucker",
+  "godsdamn",
+  "gokkun",
+  "golden shower",
+  "goldenshower",
+  "golliwog",
+  "golliwogs",
+  "gonad",
+  "gonads",
+  "gonorrehea",
+  "gonzagas",
+  "goo girl",
+  "gooch",
+  "goodpoop",
+  "gook eye",
+  "gook eyes",
+  "gook",
+  "gookeye",
+  "gookeyes",
+  "gookies",
+  "gooks",
+  "gooky",
+  "gora",
+  "goras",
+  "goregasm",
+  "gotohell",
+  "goy",
+  "goyim",
+  "greaseball",
+  "greaseballs",
+  "groe",
+  "groid",
+  "groids",
+  "grope",
+  "grostulation",
+  "group sex",
+  "gspot",
+  "gstring",
+  "gtfo",
+  "gub",
+  "gubba",
+  "gubbas",
+  "gubs",
+  "guido",
+  "guiena",
+  "guineas",
+  "guizi",
+  "gummer",
+  "guro",
+  "gwailo",
+  "gwailos",
+  "gweilo",
+  "gweilos",
+  "gyopo",
+  "gyopos",
+  "gyp",
+  "gyped",
+  "gypo",
+  "gypos",
+  "gypp",
+  "gypped",
+  "gyppie",
+  "gyppies",
+  "gyppo",
+  "gyppos",
+  "gyppy",
+  "gyppys",
+  "gypsys",
+  "h e l l",
+  "h o m",
+  "h00r",
+  "h0ar",
+  "h0m0",
+  "h0mo",
+  "h0r",
+  "h0re",
+  "h4x0r",
+  "hadji",
+  "hadjis",
+  "hairyback",
+  "hairybacks",
+  "haji",
+  "hajis",
+  "hajji",
+  "hajjis",
+  "half breed",
+  "half caste",
+  "halfbreed",
+  "halfcaste",
+  "hamas",
+  "hamflap",
+  "hand job",
+  "handjob",
+  "haole",
+  "haoles",
+  "hapa",
+  "hard core",
+  "hardcore",
+  "hardcoresex",
+  "hardon",
+  "he11",
+  "headfuck",
+  "hebe",
+  "hebes",
+  "heeb",
+  "heebs",
+  "hells",
+  "helvete",
+  "hentai",
+  "heroin",
+  "herp",
+  "herpes",
+  "herpy",
+  "heshe",
+  "hijacking",
+  "hillbillies",
+  "hillbilly",
+  "hindoo",
+  "hiscock",
+  "hitler",
+  "hitlerism",
+  "hitlerist",
+  "hoare",
+  "hobag",
+  "hodgie",
+  "hoer",
+  "hoes",
+  "holestuffer",
+  "hom0",
+  "homo",
+  "homobangers",
+  "homodumbshit",
+  "homoey",
+  "honger",
+  "honkers",
+  "honkey",
+  "honkeys",
+  "honkie",
+  "honkies",
+  "honky",
+  "hooch",
+  "hooker",
+  "hookers",
+  "hoor",
+  "hoore",
+  "hootch",
+  "hooter",
+  "hooters",
+  "hore",
+  "hori",
+  "horis",
+  "hork",
+  "horndawg",
+  "horndog",
+  "horney",
+  "horniest",
+  "horny",
+  "horseshit",
+  "hosejob",
+  "hoser",
+  "hot carl",
+  "hot chick",
+  "hotcarl",
+  "hotdamn",
+  "hotpussy",
+  "hotsex",
+  "hottotrot",
+  "how to kill",
+  "how to murder",
+  "howtokill",
+  "howtomurdep",
+  "huevon",
+  "huge fat",
+  "hugefat",
+  "hui",
+  "hummer",
+  "humped",
+  "humper",
+  "humpher",
+  "humphim",
+  "humpin",
+  "humping",
+  "hussy",
+  "hustler",
+  "hymen",
+  "hymie",
+  "hymies",
+  "iblowu",
+  "ike",
+  "ikes",
+  "ikey",
+  "ikeymo",
+  "ikeymos",
+  "ikwe",
+  "illegals",
+  "incest",
+  "indon",
+  "indons",
+  "injun",
+  "injuns",
+  "insest",
+  "intercourse",
+  "intheass",
+  "inthebuff",
+  "israels",
+  "j3rk0ff",
+  "jack off",
+  "jack-off",
+  "jackass",
+  "jackhole",
+  "jackoff",
+  "jackshit",
+  "jacktheripper",
+  "jail bait",
+  "jailbait",
+  "jap",
+  "japcrap",
+  "japie",
+  "japies",
+  "japs",
+  "jebus",
+  "jelly donut",
+  "jerk off",
+  "jerk-off",
+  "jerk0ff",
+  "jerked",
+  "jerkoff",
+  "jerries",
+  "jerry",
+  "jewboy",
+  "jewed",
+  "jewess",
+  "jiga",
+  "jigaboo",
+  "jigaboos",
+  "jigarooni",
+  "jigaroonis",
+  "jigg",
+  "jigga",
+  "jiggabo",
+  "jiggaboo",
+  "jiggabos",
+  "jiggas",
+  "jigger",
+  "jiggerboo",
+  "jiggers",
+  "jiggs",
+  "jiggy",
+  "jigs",
+  "jihad",
+  "jijjiboo",
+  "jijjiboos",
+  "jimfish",
+  "jisim",
+  "jism",
+  "jiss",
+  "jiz",
+  "jizim",
+  "jizin",
+  "jizjuice",
+  "jizm",
+  "jizn",
+  "jizz",
+  "jizzd",
+  "jizzed",
+  "jizzim",
+  "jizzin",
+  "jizzn",
+  "jizzum",
+  "jugg",
+  "juggs",
+  "jungle bunnies",
+  "jungle bunny",
+  "junglebunny",
+  "junkie",
+  "junky",
+  "kacap",
+  "kacapas",
+  "kacaps",
+  "kaffer",
+  "kaffir",
+  "kaffre",
+  "kafir",
+  "kanake",
+  "kanker",
+  "katsap",
+  "katsaps",
+  "kawk",
+  "khokhol",
+  "khokhols",
+  "kigger",
+  "kike",
+  "kikes",
+  "kimchis",
+  "kinbaku",
+  "kink",
+  "kinkster",
+  "kinky",
+  "kinkyJesus",
+  "kissass",
+  "kiunt",
+  "kkk",
+  "klan",
+  "klansman",
+  "klansmen",
+  "klanswoman",
+  "klanswomen",
+  "klootzak",
+  "knobbing",
+  "knobead",
+  "knobed",
+  "knobend",
+  "knobhead",
+  "knobjocky",
+  "knobjokey",
+  "knobz",
+  "knockers",
+  "knulle",
+  "kock",
+  "kondum",
+  "kondums",
+  "kooch",
+  "kooches",
+  "koon",
+  "kootch",
+  "krap",
+  "krappy",
+  "kraut",
+  "krauts",
+  "kuffar",
+  "kuk",
+  "kuksuger",
+  "kum",
+  "kumbubble",
+  "kumbullbe",
+  "kumer",
+  "kummer",
+  "kumming",
+  "kums",
+  "kunilingus",
+  "kunnilingus",
+  "kunt",
+  "kunts",
+  "kuntz",
+  "kurac",
+  "kurwa",
+  "kushi",
+  "kushis",
+  "kusi",
+  "kwa",
+  "kwai lo",
+  "kwai los",
+  "kwif",
+  "kyke",
+  "kykes",
+  "kyopo",
+  "kyopos",
+  "kyrpa",
+  "l3i+ch",
+  "l3i\\+ch",
+  "l3itch",
+  "labia",
+  "lapdance",
+  "leather restraint",
+  "leather straight",
+  "leatherrestraint",
+  "lebos",
+  "lech",
+  "lemon party",
+  "lemonparty",
+  "leper",
+  "lesbain",
+  "lesbayn",
+  "lesbin",
+  "lesbo",
+  "lesbos",
+  "lez",
+  "lezbe",
+  "lezbefriends",
+  "lezbian",
+  "lezbians",
+  "lezbo",
+  "lezbos",
+  "lezz",
+  "lezzian",
+  "lezzie",
+  "lezzies",
+  "lezzo",
+  "lezzy",
+  "libido",
+  "licker",
+  "licking",
+  "lickme",
+  "lilniglet",
+  "limey",
+  "limpdick",
+  "limy",
+  "lingerie",
+  "lipshits",
+  "lipshitz",
+  "livesex",
+  "loadedgun",
+  "lolita",
+  "lovebone",
+  "lovegoo",
+  "lovegun",
+  "lovejuice",
+  "lovemuscle",
+  "lovepistol",
+  "loverocket",
+  "lowlife",
+  "lsd",
+  "lubejob",
+  "lubra",
+  "lucifer",
+  "luckycammeltoe",
+  "lugan",
+  "lugans",
+  "lusting",
+  "lusty",
+  "lynch",
+  "m-fucking",
+  "m0f0",
+  "m0fo",
+  "m45terbate",
+  "ma5terb8",
+  "ma5terbate",
+  "mabuno",
+  "mabunos",
+  "macaca",
+  "macacas",
+  "mafugly",
+  "magicwand",
+  "mahbuno",
+  "mahbunos",
+  "make me come",
+  "makemecome",
+  "makemecum",
+  "male squirting",
+  "mamhoon",
+  "mams",
+  "manhater",
+  "manpaste",
+  "maricon",
+  "maricón",
+  "marijuana",
+  "masochist",
+  "masokist",
+  "massa",
+  "massterbait",
+  "masstrbait",
+  "masstrbate",
+  "mastabate",
+  "mastabater",
+  "master-bate",
+  "masterb8",
+  "masterbaiter",
+  "masterbat",
+  "masterbat3",
+  "masterbate",
+  "masterbates",
+  "masterbating",
+  "masterbation",
+  "masterbations",
+  "masterblaster",
+  "mastrabator",
+  "masturbat",
+  "masturbate",
+  "masturbating",
+  "masturbation",
+  "mattressprincess",
+  "mau mau",
+  "mau maus",
+  "maumau",
+  "maumaus",
+  "mcfagget",
+  "meatbeatter",
+  "meatrack",
+  "menage",
+  "merd",
+  "mgger",
+  "mggor",
+  "mibun",
+  "mick",
+  "mickeyfinn",
+  "mideast",
+  "mierda",
+  "milf",
+  "mindfuck",
+  "minge",
+  "minger",
+  "mo-fo",
+  "mockey",
+  "mockie",
+  "mocky",
+  "mof0",
+  "mofo",
+  "moky",
+  "molest",
+  "molestation",
+  "molester",
+  "molestor",
+  "moneyshot",
+  "mong",
+  "monkleigh",
+  "moolie",
+  "moon cricket",
+  "moon crickets",
+  "mooncricket",
+  "mooncrickets",
+  "moron",
+  "moskal",
+  "moskals",
+  "moslem",
+  "mosshead",
+  "motha fucker",
+  "motha fuker",
+  "motha fukkah",
+  "motha fukker",
+  "mothafuck",
+  "mothafucka",
+  "mothafuckas",
+  "mothafuckaz",
+  "mothafucked",
+  "mothafucker",
+  "mothafuckers",
+  "mothafuckin",
+  "mothafucking",
+  "mothafuckings",
+  "mothafucks",
+  "mother fucker",
+  "mother fukah",
+  "mother fuker",
+  "mother fukkah",
+  "mother fukker",
+  "mother-fucker",
+  "motherfuck",
+  "motherfucka",
+  "motherfucked",
+  "motherfucker",
+  "motherfuckers",
+  "motherfuckin",
+  "motherfucking",
+  "motherfuckings",
+  "motherfuckka",
+  "motherfucks",
+  "motherfvcker",
+  "motherlovebone",
+  "mothrfucker",
+  "mouliewop",
+  "mound of venus",
+  "moundofvenus",
+  "mr hands",
+  "mrhands",
+  "mtherfucker",
+  "mthrfuck",
+  "mthrfucker",
+  "mthrfucking",
+  "mtrfck",
+  "mtrfuck",
+  "mtrfucker",
+  "muff diver",
+  "muff",
+  "muffdive",
+  "muffdiver",
+  "muffdiving",
+  "muffindiver",
+  "mufflikcer",
+  "muffpuff",
+  "muie",
+  "mulatto",
+  "mulkku",
+  "muncher",
+  "munging",
+  "munt",
+  "munter",
+  "muschi",
+  "mutha fucker",
+  "mutha fukah",
+  "mutha fuker",
+  "mutha fukkah",
+  "mutha fukker",
+  "muthafecker",
+  "muthafuckaz",
+  "muthafucker",
+  "muthafuckker",
+  "muther",
+  "mutherfucker",
+  "mutherfucking",
+  "muthrfucking",
+  "mzungu",
+  "mzungus",
+  "n1gga",
+  "n1gger",
+  "n1gr",
+  "nads",
+  "naked",
+  "nambla",
+  "nastt",
+  "nastybitch",
+  "nastyho",
+  "nastyslut",
+  "nastywhore",
+  "nawashi",
+  "nazi",
+  "nazis",
+  "nazism",
+  "necro",
+  "needthedick",
+  "negres",
+  "negress",
+  "negro",
+  "negroes",
+  "negroid",
+  "negros",
+  "neonazi",
+  "nepesaurio",
+  "nig nog",
+  "nig",
+  "niga",
+  "nigar",
+  "nigars",
+  "nigas",
+  "nigers",
+  "nigette",
+  "nigettes",
+  "nigg",
+  "nigg3r",
+  "nigg4h",
+  "nigga",
+  "niggah",
+  "niggahs",
+  "niggar",
+  "niggaracci",
+  "niggard",
+  "niggarded",
+  "niggarding",
+  "niggardliness",
+  "niggardlinesss",
+  "niggardly",
+  "niggards",
+  "niggars",
+  "niggas",
+  "niggaz",
+  "nigger",
+  "niggerhead",
+  "niggerhole",
+  "niggers",
+  "niggle",
+  "niggled",
+  "niggles",
+  "nigglings",
+  "niggor",
+  "niggress",
+  "niggresses",
+  "nigguh",
+  "nigguhs",
+  "niggur",
+  "niggurs",
+  "niglet",
+  "nignog",
+  "nigor",
+  "nigors",
+  "nigr",
+  "nigra",
+  "nigras",
+  "nigre",
+  "nigres",
+  "nigress",
+  "nigs",
+  "nigur",
+  "niiger",
+  "niigr",
+  "nimphomania",
+  "nimrod",
+  "ninny",
+  "nipple",
+  "nipplering",
+  "nipples",
+  "nips",
+  "nittit",
+  "nlgger",
+  "nlggor",
+  "nob jokey",
+  "nob",
+  "nobhead",
+  "nobjocky",
+  "nobjokey",
+  "nofuckingway",
+  "nog",
+  "nookey",
+  "nookie",
+  "nooky",
+  "noonan",
+  "nooner",
+  "nsfw images",
+  "nsfw",
+  "nudger",
+  "nudie",
+  "nudies",
+  "numbnuts",
+  "nut sack",
+  "nutbutter",
+  "nutfucker",
+  "nutsack",
+  "nutten",
+  "nympho",
+  "nymphomania",
+  "o c k",
+  "octopussy",
+  "omorashi",
+  "one cup two girls",
+  "one guy one jar",
+  "one guy",
+  "one jar",
+  "ontherag",
+  "orafis",
+  "orga",
+  "orgasim",
+  "orgasim;",
+  "orgasims",
+  "orgasm",
+  "orgasmic",
+  "orgasms",
+  "orgasum",
+  "orgies",
+  "orgy",
+  "oriface",
+  "orifiss",
+  "orospu",
+  "osama",
+  "ovum",
+  "ovums",
+  "p e n i s",
+  "p i s",
+  "p u s s y",
+  "p.u.s.s.y.",
+  "p0rn",
+  "packi",
+  "packie",
+  "packy",
+  "paddy",
+  "paedophile",
+  "paki",
+  "pakie",
+  "pakis",
+  "paky",
+  "palesimian",
+  "pancake face",
+  "pancake faces",
+  "panooch",
+  "pansies",
+  "pansy",
+  "panti",
+  "pantie",
+  "panties",
+  "panty",
+  "paska",
+  "payo",
+  "pcp",
+  "pearlnecklace",
+  "pecker",
+  "peckerhead",
+  "peckerwood",
+  "pedo",
+  "pedobear",
+  "pedophile",
+  "pedophilia",
+  "pedophiliac",
+  "peeenus",
+  "peeenusss",
+  "peehole",
+  "peenus",
+  "peepee",
+  "peepshow",
+  "peepshpw",
+  "pegging",
+  "peinus",
+  "pen1s",
+  "penas",
+  "pendejo",
+  "pendy",
+  "penetrate",
+  "penetration",
+  "peni5",
+  "penial",
+  "penile",
+  "penis",
+  "penis-breath",
+  "penises",
+  "penisfucker",
+  "penisland",
+  "penislick",
+  "penislicker",
+  "penispuffer",
+  "penthouse",
+  "penus",
+  "penuus",
+  "perse",
+  "perv",
+  "perversion",
+  "peyote",
+  "phalli",
+  "phallic",
+  "phone sex",
+  "phonesex",
+  "phuc",
+  "phuck",
+  "phuk",
+  "phuked",
+  "phuker",
+  "phuking",
+  "phukked",
+  "phukker",
+  "phukking",
+  "phuks",
+  "phungky",
+  "phuq",
+  "pi55",
+  "picaninny",
+  "piccaninny",
+  "picka",
+  "pickaninnies",
+  "pickaninny",
+  "piece of shit",
+  "pieceofshit",
+  "piefke",
+  "piefkes",
+  "pierdol",
+  "pigfucker",
+  "piker",
+  "pikey",
+  "piky",
+  "pillowbiter",
+  "pillu",
+  "pimmel",
+  "pimp",
+  "pimped",
+  "pimper",
+  "pimpis",
+  "pimpjuic",
+  "pimpjuice",
+  "pimpsimp",
+  "pindick",
+  "pinko",
+  "pis",
+  "pises",
+  "pisin",
+  "pising",
+  "pisof",
+  "piss pig",
+  "piss",
+  "piss-off",
+  "pissed",
+  "pisser",
+  "pissers",
+  "pisses",
+  "pissflap",
+  "pissflaps",
+  "pisshead",
+  "pissin",
+  "pissing",
+  "pissoff",
+  "pisspig",
+  "pizda",
+  "playboy",
+  "playgirl",
+  "pleasure chest",
+  "pleasurechest",
+  "pocha",
+  "pochas",
+  "pocho",
+  "pochos",
+  "pocketpool",
+  "pohm",
+  "pohms",
+  "polac",
+  "polack",
+  "polacks",
+  "polak",
+  "pole smoker",
+  "polesmoker",
+  "pollock",
+  "pollocks",
+  "pommie grant",
+  "pommie grants",
+  "pommy",
+  "ponyplay",
+  "poof",
+  "poon",
+  "poonani",
+  "poonany",
+  "poontang",
+  "poontsee",
+  "poop chute",
+  "poopchute",
+  "pooper",
+  "pooperscooper",
+  "pooping",
+  "poorwhitetrash",
+  "popimp",
+  "porch monkey",
+  "porch monkies",
+  "porchmonkey",
+  "porn",
+  "pornflick",
+  "pornking",
+  "porno",
+  "pornography",
+  "pornos",
+  "pornprincess",
+  "pound town",
+  "poundtown",
+  "pplicker",
+  "pr0n",
+  "pr1c",
+  "pr1ck",
+  "pr1k",
+  "prairie nigger",
+  "prairie niggers",
+  "preteen",
+  "pric",
+  "prickhead",
+  "pricks",
+  "prig",
+  "prince albert piercing",
+  "pron",
+  "prostitute",
+  "pthc",
+  "pu55i",
+  "pu55y",
+  "pube",
+  "pubes",
+  "pubic",
+  "pubiclice",
+  "pubis",
+  "pudboy",
+  "pudd",
+  "puddboy",
+  "pula",
+  "punani",
+  "punanny",
+  "punany",
+  "punkass",
+  "punky",
+  "punta",
+  "puntang",
+  "purinapricness",
+  "pusies",
+  "puss",
+  "pusse",
+  "pussee",
+  "pussi",
+  "pussie",
+  "pussies",
+  "pussy",
+  "pussycat",
+  "pussydestroyer",
+  "pussyeater",
+  "pussyfart",
+  "pussyfuck",
+  "pussyfucker",
+  "pussylicker",
+  "pussylicking",
+  "pussylips",
+  "pussylover",
+  "pussypalace",
+  "pussypounder",
+  "pussys",
+  "pusy",
+  "puta",
+  "puto",
+  "puuke",
+  "puuker",
+  "qahbeh",
+  "quashie",
+  "queaf",
+  "queef",
+  "queerhole",
+  "queero",
+  "queers",
+  "queerz",
+  "quickie",
+  "quicky",
+  "quiff",
+  "quim",
+  "qweers",
+  "qweerz",
+  "qweir",
+  "r-tard",
+  "r-tards",
+  "r5e",
+  "ra8s",
+  "raghead",
+  "ragheads",
+  "rape",
+  "raped",
+  "raper",
+  "raping",
+  "rapist",
+  "rautenberg",
+  "rearend",
+  "rearentry",
+  "recktum",
+  "rectal",
+  "rectum",
+  "rectus",
+  "redleg",
+  "redlegs",
+  "redlight",
+  "redskin",
+  "redskins",
+  "reefer",
+  "reestie",
+  "reetard",
+  "reich",
+  "renob",
+  "rentafuck",
+  "rere",
+  "retard",
+  "retarded",
+  "retards",
+  "retardz",
+  "reverse cowgirl",
+  "reversecowgirl",
+  "rimjaw",
+  "rimjob",
+  "rimming",
+  "ritard",
+  "rosebuds",
+  "rosy palm and her 5 sisters",
+  "rosy palm",
+  "rosypalm",
+  "rosypalmandher5sisters",
+  "rosypalmandherefivesisters",
+  "round eyes",
+  "roundeye",
+  "rtard",
+  "rtards",
+  "rumprammer",
+  "ruski",
+  "russki",
+  "russkie",
+  "rusty trombone",
+  "rustytrombone",
+  "s h i t",
+  "s hit",
+  "s&m",
+  "s-h-1-t",
+  "s-h-i-t",
+  "s-o-b",
+  "s.h.i.t.",
+  "s.o.b.",
+  "s0b",
+  "s_h_i_t",
+  "sadis",
+  "sadism",
+  "sadist",
+  "sadom",
+  "sambo",
+  "sambos",
+  "samckdaddy",
+  "sanchez",
+  "sand nigger",
+  "sand niggers",
+  "sandm",
+  "sandnigger",
+  "santorum",
+  "sausagequeen",
+  "scag",
+  "scallywag",
+  "scank",
+  "scantily",
+  "scat",
+  "schaffer",
+  "scheiss",
+  "schizo",
+  "schlampe",
+  "schlong",
+  "schmuck",
+  "schvartse",
+  "schvartsen",
+  "schwartze",
+  "schwartzen",
+  "scissoring",
+  "screwyou",
+  "scroat",
+  "scrog",
+  "scrote",
+  "scrotum",
+  "scrud",
+  "seduce",
+  "semen",
+  "seppo",
+  "seppos",
+  "septics",
+  "sex",
+  "sexcam",
+  "sexed",
+  "sexfarm",
+  "sexhound",
+  "sexhouse",
+  "sexi",
+  "sexing",
+  "sexkitten",
+  "sexo",
+  "sexpot",
+  "sexslave",
+  "sextogo",
+  "sextoy",
+  "sextoys",
+  "sexual",
+  "sexually",
+  "sexwhore",
+  "sexx",
+  "sexxi",
+  "sexxx",
+  "sexxxi",
+  "sexxxy",
+  "sexxy",
+  "sexy",
+  "sexymoma",
+  "sexyslim",
+  "sh!+",
+  "sh!t",
+  "sh1t",
+  "sh1ter",
+  "sh1ts",
+  "sh1tter",
+  "sh1tz",
+  "shag",
+  "shagger",
+  "shaggin",
+  "shagging",
+  "shamedame",
+  "sharmuta",
+  "sharmute",
+  "shat",
+  "shav",
+  "shaved beaver",
+  "shaved pussy",
+  "shavedbeaver",
+  "shavedpussy",
+  "shawtypimp",
+  "sheeney",
+  "shemale",
+  "shhit",
+  "shi+",
+  "shibari",
+  "shibary",
+  "shinola",
+  "shipal",
+  "shit ass",
+  "shit",
+  "shit-ass",
+  "shit-bag",
+  "shit-bagger",
+  "shit-brain",
+  "shit-breath",
+  "shit-cunt",
+  "shit-dick",
+  "shit-eating",
+  "shit-face",
+  "shit-faced",
+  "shit-fit",
+  "shit-head",
+  "shit-heel",
+  "shit-hole",
+  "shit-house",
+  "shit-load",
+  "shit-pot",
+  "shit-spitter",
+  "shit-stain",
+  "shitass",
+  "shitbag",
+  "shitbagger",
+  "shitblimp",
+  "shitbrain",
+  "shitbreath",
+  "shitcan",
+  "shitcunt",
+  "shitdick",
+  "shite",
+  "shiteater",
+  "shiteating",
+  "shited",
+  "shitey",
+  "shitface",
+  "shitfaced",
+  "shitfit",
+  "shitforbrains",
+  "shitfuck",
+  "shitfucker",
+  "shitfull",
+  "shithapens",
+  "shithappens",
+  "shithead",
+  "shitheel",
+  "shithole",
+  "shithouse",
+  "shiting",
+  "shitings",
+  "shitlist",
+  "shitload",
+  "shitola",
+  "shitoutofluck",
+  "shitpot",
+  "shits",
+  "shitspitter",
+  "shitstain",
+  "shitt",
+  "shitted",
+  "shitter",
+  "shitters",
+  "shittiest",
+  "shitting",
+  "shittings",
+  "shitty",
+  "shity",
+  "shitz",
+  "shiz",
+  "shiznit",
+  "shortfuck",
+  "shota",
+  "shylock",
+  "shylocks",
+  "shyt",
+  "shyte",
+  "shytty",
+  "shyty",
+  "simp",
+  "sissy",
+  "sixsixsix",
+  "sixtynine",
+  "sixtyniner",
+  "skag",
+  "skanck",
+  "skank",
+  "skankbitch",
+  "skankee",
+  "skankey",
+  "skankfuck",
+  "skanks",
+  "skankwhore",
+  "skanky",
+  "skankybitch",
+  "skankywhore",
+  "skeet",
+  "skinflute",
+  "skribz",
+  "skullfuck",
+  "skum",
+  "skumbag",
+  "skurwysyn",
+  "skwa",
+  "skwe",
+  "slag",
+  "slanteye",
+  "slanty",
+  "slapper",
+  "sleezeball",
+  "slideitin",
+  "slimeball",
+  "slimebucket",
+  "slopehead",
+  "slopeheads",
+  "sloper",
+  "slopers",
+  "slopey",
+  "slopeys",
+  "slopies",
+  "slopy",
+  "slut",
+  "slutbag",
+  "slutbucket",
+  "slutdumper",
+  "slutkiss",
+  "sluts",
+  "slutt",
+  "slutting",
+  "slutty",
+  "slutwear",
+  "slutwhore",
+  "slutz",
+  "smackthemonkey",
+  "smeg",
+  "smegma",
+  "smut",
+  "smutty",
+  "snatchpatch",
+  "sniggered",
+  "sniggering",
+  "sniggers",
+  "snowback",
+  "snowballing",
+  "snownigger",
+  "snuff",
+  "socksucker",
+  "sodom",
+  "sodomise",
+  "sodomite",
+  "sodomize",
+  "sodomy",
+  "son of a bitch",
+  "son of a whore",
+  "son-of-a-bitch",
+  "son-of-a-whore",
+  "sonofabitch",
+  "sonofbitch",
+  "sooties",
+  "souse",
+  "soused",
+  "soyboy",
+  "spac",
+  "spaghettibender",
+  "spaghettinigger",
+  "spank",
+  "spankthemonkey",
+  "spastic",
+  "spearchucker",
+  "spearchuckers",
+  "sperm",
+  "spermacide",
+  "spermbag",
+  "spermhearder",
+  "spermherder",
+  "sphencter",
+  "spic",
+  "spick",
+  "spicks",
+  "spics",
+  "spierdalaj",
+  "spig",
+  "spigotty",
+  "spik",
+  "spiks",
+  "splittail",
+  "splooge",
+  "spludge",
+  "spooge",
+  "spread legs",
+  "spreadeagle",
+  "spunk",
+  "spunky",
+  "sqeh",
+  "squa",
+  "squarehead",
+  "squareheads",
+  "squaw",
+  "squinty",
+  "squirting",
+  "stagg",
+  "stfu",
+  "stiffy",
+  "stoned",
+  "stoner",
+  "strap on",
+  "strapon",
+  "strappado",
+  "strip club",
+  "stripclub",
+  "stroking",
+  "stuinties",
+  "stupidfuck",
+  "stupidfucker",
+  "style doggy",
+  "suckdick",
+  "sucked",
+  "sucker",
+  "sucking",
+  "suckme",
+  "suckmyass",
+  "suckmydick",
+  "suckmytit",
+  "suckoff",
+  "suicide girl",
+  "suicide girls",
+  "suicidegirl",
+  "suicidegirls",
+  "suka",
+  "sultrywoman",
+  "sultrywomen",
+  "sumofabiatch",
+  "swallower",
+  "swalow",
+  "swamp guinea",
+  "swamp guineas",
+  "swastika",
+  "syphilis",
+  "t i t",
+  "t i ts",
+  "t1t",
+  "t1tt1e5",
+  "t1tties",
+  "tacohead",
+  "tacoheads",
+  "taff",
+  "take off your",
+  "tar babies",
+  "tar baby",
+  "tarbaby",
+  "tard",
+  "taste my",
+  "tastemy",
+  "tawdry",
+  "tea bagging",
+  "teabagging",
+  "teat",
+  "teets",
+  "teez",
+  "terd",
+  "teste",
+  "testee",
+  "testes",
+  "testical",
+  "testicle",
+  "testicles",
+  "testis",
+  "thicklip",
+  "thicklips",
+  "thirdeye",
+  "thirdleg",
+  "threesome",
+  "threeway",
+  "throating",
+  "thumbzilla",
+  "thundercunt",
+  "tied up",
+  "tig ol bitties",
+  "tig old bitties",
+  "tight white",
+  "timber nigger",
+  "timber niggers",
+  "timbernigger",
+  "tit",
+  "titbitnipply",
+  "titfuck",
+  "titfucker",
+  "titfuckin",
+  "titi",
+  "titjob",
+  "titlicker",
+  "titlover",
+  "tits",
+  "titt",
+  "tittie",
+  "tittie5",
+  "tittiefucker",
+  "titties",
+  "tittis",
+  "titty",
+  "tittyfuck",
+  "tittyfucker",
+  "tittys",
+  "tittywank",
+  "titwank",
+  "tity",
+  "to murder",
+  "tongethruster",
+  "tongue in a",
+  "tongueina",
+  "tonguethrust",
+  "tonguetramp",
+  "toots",
+  "topless",
+  "tortur",
+  "torture",
+  "tosser",
+  "towel head",
+  "towel heads",
+  "towelhead",
+  "trailertrash",
+  "trannie",
+  "tranny",
+  "transsexual",
+  "transvestite",
+  "tribadism",
+  "trisexual",
+  "trois",
+  "trots",
+  "tub girl",
+  "tubgirl",
+  "tuckahoe",
+  "tunneloflove",
+  "turd burgler",
+  "turnon",
+  "tush",
+  "tushy",
+  "tw4t",
+  "twat",
+  "twathead",
+  "twatlips",
+  "twats",
+  "twatty",
+  "twatwaffle",
+  "twink",
+  "twinkie",
+  "two girls one cup",
+  "twobitwhore",
+  "twunt",
+  "twunter",
+  "udge packer",
+  "ukrop",
+  "unclefucker",
+  "unfuckable",
+  "upskirt",
+  "uptheass",
+  "upthebutt",
+  "urethra play",
+  "urethraplay",
+  "urophilia",
+  "usama",
+  "ussys",
+  "uzi",
+  "v a g i n a",
+  "v14gra",
+  "v1gra",
+  "v4gra",
+  "va-j-j",
+  "va1jina",
+  "vag",
+  "vag1na",
+  "vagiina",
+  "vaj1na",
+  "vajina",
+  "valium",
+  "venus mound",
+  "vgra",
+  "vibr",
+  "vibrater",
+  "vibrator",
+  "vigra",
+  "violet wand",
+  "virginbreaker",
+  "vittu",
+  "vixen",
+  "vjayjay",
+  "vorarephilia",
+  "voyeurweb",
+  "voyuer",
+  "vullva",
+  "vulva",
+  "w00se",
+  "w0p",
+  "wab",
+  "wang",
+  "wank",
+  "wanker",
+  "wanking",
+  "wanky",
+  "waysted",
+  "wazoo",
+  "weenie",
+  "weewee",
+  "weiner",
+  "welcher",
+  "wench",
+  "wet dream",
+  "wetb",
+  "wetback",
+  "wetbacks",
+  "wetdream",
+  "wetspot",
+  "wh00r",
+  "wh0re",
+  "wh0reface",
+  "whacker",
+  "whash",
+  "whigger",
+  "whiggers",
+  "whiskeydick",
+  "whiskydick",
+  "whit",
+  "white power",
+  "white trash",
+  "whitenigger",
+  "whitepower",
+  "whitetrash",
+  "whitey",
+  "whiteys",
+  "whities",
+  "whoar",
+  "whop",
+  "whoralicious",
+  "whore",
+  "whorealicious",
+  "whorebag",
+  "whored",
+  "whoreface",
+  "whorefucker",
+  "whorehopper",
+  "whorehouse",
+  "whores",
+  "whoring",
+  "wichser",
+  "wigga",
+  "wiggas",
+  "wigger",
+  "wiggers",
+  "willie",
+  "willies",
+  "williewanker",
+  "willy",
+  "wog",
+  "wogs",
+  "woose",
+  "wop",
+  "worldsex",
+  "wrapping men",
+  "wrinkled starfish",
+  "wtf",
+  "wuss",
+  "wuzzie",
+  "x-rated",
+  "x-rated2g1c",
+  "xkwe",
+  "xrated",
+  "xtc",
+  "xx",
+  "xxx",
+  "xxxxxx",
+  "yank",
+  "yaoi",
+  "yarpie",
+  "yarpies",
+  "yed",
+  "yellow showers",
+  "yellowman",
+  "yellowshowers",
+  "yid",
+  "yids",
+  "yiffy",
+  "yobbo",
+  "yourboobs",
+  "yourpenis",
+  "yourtits",
+  "yury",
+  "zabourah",
+  "zigabo",
+  "zigabos",
+  "zipperhead",
+  "zipperheads",
+  "zoophile",
+  "zoophilia",
+  "🖕"
+]

+ 7 - 0
ToneTuneToolkit/Assets/StreamingAssets/ToneTuneToolkit/configs/sensitivewords.json.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: cf6311b85a9f3fe41a30f4c6528d0835
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 5 - 5
ToneTuneToolkit/Assets/StreamingAssets/ToneTuneToolkit/configs/udpconfig.json

@@ -1,7 +1,7 @@
 {
-  "local_ip": "192.168.1.100",
-  "local_port": "9999",
-  "target_ip": "192.168.1.100",
-  "target_port": "3456",
-  "recive_frequency": "0.04"
+  "local_ip": "192.168.1.1",
+  "local_port": 1000,
+  "target_ip": "192.168.1.201",
+  "target_port": 8216,
+  "recive_frequency": 0.04
 }

+ 33 - 6
ToneTuneToolkit/Assets/ToneTuneToolkit/README.md

@@ -1,8 +1,8 @@
 <font face="Source Han Sans TC" size=2 color=#FFFFFF>
 
 #### <center><font size=2>Make everything f<font color="#FF0000">or</font>king simple.</font></center>
-#### <center><font size=2>2024/10/11</font></center>
-# <center><font color="#54FF9F" size=6>**Tone Tune Toolkit v1.4.17**</font></center>
+#### <center><font size=2>2025/03/27</font></center>
+# <center><font color="#54FF9F" size=6>**Tone Tune Toolkit v1.5.0**</font></center>
 ## ToneTuneToolkit是什么?
 一个致力于帮助Unity六边形战士减轻开发负担的项目。</br>
 <s>但更多的时候是在帮助互动工程师偷懒。</s></br>
@@ -47,28 +47,44 @@
 26. 2024/06/18 添加了“LongTimeNoOperationDetector”,用于检测用户长时间无操作。
 27. 2024/07/18 添加了“UDPCommunicatorServer”,单端口非一次性play,用于作为server大量接收数据。
 28. 2024/10/11 更新了“ObjectDragRotate”,增加了旋转角度的限制,增加了一个角度校正的方法。
+29. 2024/12/18 添加了“RenameFolders”,一个用于在编辑器内批量化改变文件夹名的工具,直接更新选中的文件夹的文件夹名为新文件夹名或更新所有匹配原文件夹名的文件夹的文件夹名为新文件夹名,嗯。
+30. 2025/01/03 添加了“DataProcessor”,一个用于二级加工数据的工具,开新坑了,家人们。
+31. 2025/01/07 添加了“UpdateCopyrights”,一个用于批量添加版权信息的工具,在“Project”面板中选择“.cs”文件后可正常执行。
+32. 2025/01/10 添加了“ImageLoader”,用于运行时在弹窗内选择并加载图片,添加了第三方资源文件夹。
+33. 2025/01/13 添加了“JsonUploadManager”,用于上传json的工具。
+34. 2025/02/19 “QRCodeMaster”现在支持透明底二维码生成。
+35. 2025/03/27 “FileCapturer”被重制,拥有更高级的功能。
 
 </br>
 
 # <center>*SCRIPTS*</center>
 ### -> ToneTuneToolkit.Common/
-* DataConverter.cs      // 静态 // 数据转换 // 字符串与二进制之间转换 // 字符串与json之间转换
 * EventListener.cs      // 数值监听器 // 提供了一个泛型事件
-* FileNameCapturer.cs   // 静态 // 获取特定文件夹下特定格式的文件名
 * PathChecker.cs        // 静态 // 文件/文件夹检查 // 如果不存在则创建空的
 * SingletonMaster.cs    // 单例大师
+* ToolkitManager.cs     // 管理类 // 存放路径 // 多数功能的依赖
+* TTTDebug.cs           // 静态 // TTT工具箱专属Debug.Log
+
+### -> ToneTuneToolkit.Data/
+* DataConverter.cs      // 静态 // 数据转换 // 字符串与二进制之间转换 // 字符串与json之间转换
+* DataProcessor.cs      // 数据处理
+* ImageLoader.cs        // 图片选择和加载
+* JsonManager.cs        // newtonsoftjson管理器
+* LitJsonManager.cs     // litjson管理器
+* SensitiveWordUtility.cs // 关键词加载
 * TextLoader.cs         // 静态 // 文字加载 // 可以读取txt及json
 * TimestampCapturer.cs  // 静态 // 获取时间戳 // 本地获取静态方法 // 网络获取需单例
-* TipTools.cs           // 静态 // TTT工具箱专属Debug.Log
-* ToolkitManager.cs     // 管理类 // 存放路径 // 多数功能的依赖
 
 ### -> ToneTuneToolkit.Editor/
 * CreateAssetBundles.cs // AB包创建工具
+* RenameFolders.cs      // 批量化重命名文件夹工具
+* UpdateCopyrights.cs   // 更新版权工具
 
 ### -> ToneTuneToolkit.Funny/
 * BubbleSort.cs         // 静态 // 冒泡排序
 
 ### -> ToneTuneToolkit.IO/
+* FileCapturer.cs       // 静态 // 获取特定文件夹下特定格式的文件名
 * FTPMaster.cs          // FTP文件下载(暂无上传)器
 
 ### -> ToneTuneToolkit.Media/
@@ -85,6 +101,9 @@
 * LEDHandler.cs         // LED助手
 * LEDNuclearShow.cs     // 灯带压力测试 // DEBUG
 
+### -> ToneTuneToolkit.Networking/
+* JsonUploadManager.cs // Json上传
+
 ### -> ToneTuneToolkit.Object/
 * CorrectLookAtCamera.cs        // 使物体正对相机
 * NeonLight.cs                  // 随机霓虹灯
@@ -111,6 +130,7 @@
 ### -> ToneTuneToolkit.UI/
 * Parallax.cs         // 多层次视差
 * TextFlick.cs        // 文字通过透明度闪烁
+* UICurved.cs         // UI弯曲
 
 ### -> ToneTuneToolkit.Verification/
 * AntiVerifier.cs     // 反向验证器 // 二进制
@@ -230,6 +250,13 @@
 * Materials
 * Textures
 
+</br>
+
+# <center>*THIRDPARTY*</center>
+### -> 第三方脚本或资源
+* StandaloneFileBrowser // 运行时弹出窗口
+
+
 </br>
 
 # <center>*EXAMPLES*</center>

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/EventListener.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 namespace ToneTuneToolkit.Common
@@ -35,4 +35,4 @@ namespace ToneTuneToolkit.Common
       }
     }
   }
-}
+}

+ 0 - 90
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/FileNameCapturer.cs

@@ -1,90 +0,0 @@
-/// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
-/// </summary>
-
-using System.Collections.Generic;
-using System.IO;
-using UnityEngine;
-
-namespace ToneTuneToolkit.Common
-{
-  /// <summary>
-  /// 获取某个目录下指定类型的文件名
-  /// </summary>
-  public class FileNameCapturer
-  {
-    /// <summary>
-    /// 获取路径下全部指定类型的文件名
-    ///
-    /// string[] dd = Directory.GetFiles(url, "*.jpg");
-    /// </summary>
-    /// <param name="path">路径</param>
-    /// <param name="suffix">后缀名</param>
-    /// <param name="files">用以存储文件名的数组</param>
-    public static string[] GetFileName2Array(string path, string suffix)
-    {
-      if (!Directory.Exists(path)) // 如果路径不存在 // 返回 空
-      {
-        Debug.Log($"[FileNameCapturer] Path [<color=red>{path}</color>] dose not exist...[Er]");
-        return null;
-      }
-      DirectoryInfo directoryInfo = new DirectoryInfo(path); // 获取文件信息
-      FileInfo[] fileInfos = directoryInfo.GetFiles("*", SearchOption.AllDirectories);
-
-      // 统计有多少符合条件的文件
-      int arraySize = 0;
-      for (int i = 0; i < fileInfos.Length; i++)
-      {
-        if (fileInfos[i].Name.EndsWith(suffix))
-        {
-          arraySize++;
-          continue;
-        }
-      }
-
-      string[] filesArray = new string[arraySize]; // 新建数组
-
-      // 筛选符合条件的文件并储名进数组
-      int arrayIndex = 0;
-      for (int i = 0; i < fileInfos.Length; i++)
-      {
-        if (fileInfos[i].Name.EndsWith(suffix))
-        {
-          filesArray[arrayIndex++] = fileInfos[i].Name; // 把符合要求的文件名存储至数组中
-          continue;
-        }
-      }
-      return filesArray;
-    }
-
-
-    /// <summary>
-    /// List版本
-    /// </summary>
-    /// <param name="path">路径</param>
-    /// <param name="suffix">后缀名</param>
-    public static List<string> GetFileName2List(string path, string suffix)
-    {
-      if (!Directory.Exists(path))
-      {
-        Debug.Log($"[FileNameCapturer] Path [<color=red>{path}</color>] dose not exist...[Er]");
-        return null;
-      }
-      DirectoryInfo directoryInfo = new DirectoryInfo(path); // 获取文件信息
-      FileInfo[] fileInfos = directoryInfo.GetFiles("*", SearchOption.AllDirectories);
-
-      List<string> filesList = new List<string>();
-
-      // 筛选符合条件的文件并储名进数组
-      for (int i = 0; i < fileInfos.Length; i++)
-      {
-        if (fileInfos[i].Name.EndsWith(suffix))
-        {
-          filesList.Add(fileInfos[i].Name); // 把符合要求的文件名存储至数组中
-        }
-      }
-      return filesList;
-    }
-  }
-}

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/PathChecker.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using System.IO;
@@ -48,4 +48,4 @@ namespace ToneTuneToolkit.Common
       return false;
     }
   }
-}
+}

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/SingletonMaster.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2023 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using System.Collections;
@@ -40,4 +40,4 @@ namespace ToneTuneToolkit.Common
       }
     }
   }
-}
+}

+ 46 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/TTTDebug.cs

@@ -0,0 +1,46 @@
+/// <summary>
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
+/// </summary>
+
+using UnityEngine;
+
+namespace ToneTuneToolkit.Common
+{
+  /// <summary>
+  /// ToneTuneToolkit专属提示
+  /// </summary>
+  public static class TTTDebug
+  {
+    /// <summary>
+    /// 提示
+    /// </summary>
+    /// <param name="message"></param>
+    public static void Log(object message)
+    {
+
+      Debug.Log($"<color=#{ColorUtility.ToHtmlStringRGB(Color.white)}>[{nameof(ToneTuneToolkit)}] -> </color>{message}");
+      return;
+    }
+
+    /// <summary>
+    /// 警告
+    /// </summary>
+    /// <param name="message"></param>
+    public static void LogWarning(string message)
+    {
+      Debug.LogWarning($"<color=#{ColorUtility.ToHtmlStringRGB(Color.yellow)}>[TTT Warning] -> </color>{message}");
+      return;
+    }
+
+    /// <summary>
+    /// 错误
+    /// </summary>
+    /// <param name="message"></param>
+    public static void LogError(string message)
+    {
+      Debug.LogError($"<color=#{ColorUtility.ToHtmlStringRGB(Color.red)}>[TTT Error] -> </color>{message}");
+      return;
+    }
+  }
+}

+ 0 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/TipTools.cs.meta → ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/TTTDebug.cs.meta


+ 0 - 45
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/TipTools.cs

@@ -1,45 +0,0 @@
-/// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
-/// </summary>
-
-using UnityEngine;
-
-namespace ToneTuneToolkit.Common
-{
-  /// <summary>
-  /// ToneTuneToolkit专属提示
-  /// </summary>
-  public static class TipTools
-  {
-    /// <summary>
-    /// 提示
-    /// </summary>
-    /// <param name="text"></param>
-    public static void Notice(string text)
-    {
-      Debug.Log($"<color=#{ColorUtility.ToHtmlStringRGB(Color.white)}>[TTT Notice] -> </color>{text}");
-      return;
-    }
-
-    /// <summary>
-    /// 警告
-    /// </summary>
-    /// <param name="text"></param>
-    public static void Warning(string text)
-    {
-      Debug.Log($"<color=#{ColorUtility.ToHtmlStringRGB(Color.yellow)}>[TTT Warning] -> </color>{text}");
-      return;
-    }
-
-    /// <summary>
-    /// 错误
-    /// </summary>
-    /// <param name="text"></param>
-    public static void Error(string text)
-    {
-      Debug.Log($"<color=#{ColorUtility.ToHtmlStringRGB(Color.red)}>[TTT Error] -> </color>{text}");
-      return;
-    }
-  }
-}

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/ToolkitManager.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using UnityEngine;
@@ -29,4 +29,4 @@ namespace ToneTuneToolkit.Common
       PathChecker.FolderIntegrityCheck(AdditionalToolsPath);
     }
   }
-}
+}

+ 14 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/DataConverter.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using System;
@@ -8,6 +8,7 @@ using System.Text;
 using System.Text.RegularExpressions;
 using System.Collections.Generic;
 using Newtonsoft.Json;
+using UnityEngine;
 
 namespace ToneTuneToolkit.Data
 {
@@ -80,5 +81,15 @@ namespace ToneTuneToolkit.Data
       Dictionary<string, string> jsonDic = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString);
       return jsonDic;
     }
+
+    /// <summary>
+    /// 色彩转Hex
+    /// </summary>
+    /// <param name="value"></param>
+    /// <returns></returns>
+    public static string Color2Hex(Color value)
+    {
+      return $"#{ColorUtility.ToHtmlStringRGB(value)}";
+    }
   }
-}
+}

+ 45 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/DataProcessor.cs

@@ -0,0 +1,45 @@
+/// <summary>
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
+/// </summary>
+
+using UnityEngine;
+using System.Text.RegularExpressions;
+using System.Collections.Generic;
+using System;
+
+namespace ToneTuneToolkit.Data
+{
+  /// <summary>
+  /// 数据加工
+  /// </summary>
+  public class DataProcessor
+  {
+    /// <summary>
+    /// 使部分字体高亮
+    /// </summary>
+    /// <param name="originString"></param>
+    /// <param name="highlightString"></param>
+    /// <param name="highlightColor"></param>
+    /// <returns></returns>
+    public static string DoRichTextHighlight(string originString, string highlightSting, Color highlightColor)
+    {
+      string newString = null;
+
+      // // 方案A // 强匹配
+      // newString = originString;
+      // newString = newString.Replace(highlightString.ToString(), $"<color={DataConverter.Color2Hex(highlightColor)}>{highlightString}</color>");
+
+      // 方案B // 全字符匹配
+      newString = new Regex(@$"{string.Join('|', highlightSting.ToCharArray())}")
+         .Replace(originString, m => $"<color={DataConverter.Color2Hex(highlightColor)}>{m}</color>");
+
+      return newString;
+    }
+
+    public static string GetTime()
+    {
+      return DateTime.Now.ToString("yyyyMMdd_HHmmss_") + new System.Random().Next(0, 100);
+    }
+  }
+}

+ 1 - 1
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/ScreenshotMasterMini.cs.meta → ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/DataProcessor.cs.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 30e1959653d1ec44ba48f67c4825757c
+guid: 325e5b13988af384984086580f83e2fe
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2

+ 54 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/ImageLoader.cs

@@ -0,0 +1,54 @@
+using UnityEngine;
+using NativeFileBrowser;
+using System.Collections.Generic;
+using System.Linq;
+using System.IO;
+
+public class ImageLoader : MonoBehaviour
+{
+  /// <summary>
+  /// 弹窗获取图片路径
+  /// </summary>
+  /// <returns>图片路径</returns>
+  public static string GetImagePath()
+  {
+    string title = "Select Image";
+    ExtensionFilter[] extensions = new ExtensionFilter[]
+    {
+      new ExtensionFilter("Image Files", "png", "jpg", "jpeg"),
+      new ExtensionFilter("JPG ", "jpg", "jpeg"),
+      new ExtensionFilter("PNG ", "png"),
+    };
+
+    // 标题、类型筛选器、是否允许选择多个文件
+    List<string> paths = StandaloneFileBrowser.OpenFilePanel(title, extensions, false).ToList();
+
+    if (paths.Count == 0)
+    {
+      return null;
+    }
+    // Debug.Log(paths[0]);
+    return paths[0];
+  }
+
+
+
+  /// <summary>
+  /// 获取图片
+  /// </summary>
+  /// <param name="path"></param>
+  /// <returns></returns>
+  public static Texture2D GetImageTexture(string path)
+  {
+    if (!File.Exists(path))
+    {
+      return null;
+    }
+
+    byte[] bytes = File.ReadAllBytes(path);
+    Texture2D texture2D = new Texture2D(2, 2);
+    texture2D.LoadImage(bytes);
+    texture2D.Apply();
+    return texture2D;
+  }
+}

+ 11 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/ImageLoader.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 15d51a9ffcd7f964983c77d99f2fe750
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 4 - 4
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/JsonManager.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2023 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using System.Collections;
@@ -12,7 +12,7 @@ using Newtonsoft.Json;
 
 namespace ToneTuneToolkit.Data
 {
-  public class JsonManager : MonoBehaviour
+  public static class JsonManager
   {
     /// <summary>
     /// 配置文件获取器
@@ -58,4 +58,4 @@ namespace ToneTuneToolkit.Data
       return true;
     }
   }
-}
+}

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/LitJsonManager.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2024 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using System.Collections;
@@ -21,4 +21,4 @@ namespace ToneTuneToolkit.Data
       return jd[keyName];
     }
   }
-}
+}

+ 191 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/SensitiveWordUtility.cs

@@ -0,0 +1,191 @@
+/// <summary>
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
+/// </summary>
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using Newtonsoft.Json;
+using System.IO;
+using UnityEngine;
+
+namespace ToneTuneToolkit.Data
+{
+  public class SensitiveWordUtility : MonoBehaviour
+  {
+    private const string END_FLAG = "IsEnd";
+    private static Hashtable hashtable = new Hashtable();
+
+    private List<string> lexicon = new List<string>();
+
+    private string sensitiveWordsConfigPath = Application.streamingAssetsPath + "/configs/sensitivewords.json";
+
+    // ==================================================
+
+    private void Awake() => Init();
+
+    // ==================================================
+
+    private void Init()
+    {
+      lexicon = JsonConvert.DeserializeObject<List<string>>(File.ReadAllText(sensitiveWordsConfigPath, Encoding.UTF8));
+      InitLexicon(lexicon);
+      return;
+    }
+
+    // ==================================================
+
+    /// <summary>
+    /// 初始化词库
+    /// </summary>
+    public static void InitLexicon(List<string> lexicon)
+    {
+      hashtable = new Hashtable(lexicon.Count);
+      foreach (string word in lexicon)
+      {
+        Hashtable tempHashtable = hashtable;
+        for (int i = 0; i < word.Length; i++)
+        {
+          char c = word[i];
+          if (IsSymbol(c)) { continue; }
+          if (tempHashtable.ContainsKey(c))
+          {
+            tempHashtable = (Hashtable)tempHashtable[c];
+          }
+          else
+          {
+            var newHashtable = new Hashtable
+          {
+            { END_FLAG, 0 }
+          };
+            tempHashtable.Add(c, newHashtable);
+            tempHashtable = newHashtable;
+          }
+          if (i == word.Length - 1)
+          {
+            if (tempHashtable.ContainsKey(END_FLAG))
+            {
+              tempHashtable[END_FLAG] = 1;
+            }
+            else
+            {
+              tempHashtable.Add(END_FLAG, 1);
+            }
+          }
+        }
+      }
+      return;
+    }
+
+
+
+    public static bool CheckSensitiveWords(string text)
+    {
+      List<string> result = new List<string>();
+      for (int i = 0; i < text.Length; i++)
+      {
+        int length = SearchSensitiveWord(text, i);
+        if (length > 0)
+        {
+          result.Add(text.Substring(i, length));
+          i = i + length - 1;
+
+          return true;
+        }
+      }
+      return false;
+    }
+
+
+
+    /// <summary>
+    /// 查找所有敏感词,找到则返回敏感词长度
+    /// </summary>
+    /// <param name="text">需要过滤的字符串</param>
+    /// <param name="startIndex">查找的起始位置</param>
+    /// <returns></returns>
+    public static int SearchSensitiveWord(string text, int startIndex)
+    {
+      Hashtable newMap = hashtable;
+      bool flag = false;
+      int len = 0;
+      for (int i = startIndex; i < text.Length; i++)
+      {
+        char word = text[i];
+        if (IsSymbol(word))
+        {
+          len++;
+          continue;
+        }
+        Hashtable temp = (Hashtable)newMap[word];
+        if (temp != null)
+        {
+          if ((int)temp[END_FLAG] == 1) flag = true;
+          else newMap = temp;
+          len++;
+        }
+        else break;
+      }
+      if (!flag) len = 0;
+      return len;
+    }
+
+    /// <summary>
+    /// 找到内容字符串内所有敏感词
+    /// </summary>
+    /// <param name="text">需要处理的文本</param>
+    /// <returns></returns>
+    public static List<string> GetAllSensitiveWords(string text)
+    {
+      List<string> result = new List<string>();
+      for (int i = 0; i < text.Length; i++)
+      {
+        int length = SearchSensitiveWord(text, i);
+        if (length > 0)
+        {
+          result.Add(text.Substring(i, length));
+          i = i + length - 1;
+        }
+      }
+      return result;
+    }
+
+    /// <summary>
+    /// 替换 需要剔除的 敏感字
+    /// </summary>
+    /// <param name="text">需要处理的文本</param>
+    /// <returns></returns>
+    public static string ReplaceSensitiveWords(string text)
+    {
+      int i = 0;
+      StringBuilder builder = new StringBuilder(text);
+      while (i < text.Length)
+      {
+        int len = SearchSensitiveWord(text, i);
+        if (len > 0)
+        {
+          for (int j = 0; j < len; j++)
+          {
+            builder[i + j] = '*';
+          }
+          i += len;
+        }
+        else ++i;
+      }
+      return builder.ToString();
+    }
+
+    /// <summary>
+    /// 判断是否是一个符号
+    /// </summary>
+    /// <param name="c"></param>
+    /// <returns></returns>
+    private static bool IsSymbol(char c)
+    {
+      int ic = c;
+      // 0x2E80-0x9FFF 东亚文字范围
+      return !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) && (ic < 0x2E80 || ic > 0x9FFF);
+    }
+  }
+}

+ 11 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/SensitiveWordUtility.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cf1c39ff92e2029429890108614d88e5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/TextLoader.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using System.IO;
@@ -40,4 +40,4 @@ namespace ToneTuneToolkit.Data
       }
     }
   }
-}
+}

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Data/TimestampCapturer.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using System.Collections;
@@ -70,4 +70,4 @@ namespace ToneTuneToolkit.Data
       yield break;
     }
   }
-}
+}

+ 4 - 4
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/CreateAssetBundles.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using UnityEditor;
@@ -11,7 +11,7 @@ namespace ToneTuneToolkit.Editor
   public class CreateAssetBundles
   {
     [MenuItem("ToneTuneToolkit/Build AssetBundles")]
-    static void BuildAllAssetBundles()
+    private static void BuildAllAssetBundles()
     {
       string directory = "Assets/StreamingAssets/AssetBundles";
       if (Directory.Exists(directory) == false)
@@ -22,4 +22,4 @@ namespace ToneTuneToolkit.Editor
       BuildPipeline.BuildAssetBundles(directory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
     }
   }
-}
+}

+ 19 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/EditorStorage.cs

@@ -0,0 +1,19 @@
+/// <summary>
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
+/// </summary>
+
+namespace ToneTuneToolkit.Editor
+{
+  public class EditorStorage
+  {
+
+    public static class GUI
+    {
+      public const float Space = 30f;
+      public const float Width = 200f;
+      public const float Height = 300f;
+      public const float NarrowWidth = 100f;
+    }
+  }
+}

+ 11 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/EditorStorage.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e6f74eddb1946d84d92fd7de27d988b3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 113 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/RenameFolders.cs

@@ -0,0 +1,113 @@
+/// <summary>
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
+/// </summary>
+
+using System.IO;
+using UnityEditor;
+using UnityEngine;
+
+namespace ToneTuneToolkit.Editor
+{
+  public class RenameFolders : EditorWindow
+  {
+    private string oldFolderName = "Original Folder Name";
+    private string newFolderName = "New Folder Name";
+
+    // ==================================================
+
+    [MenuItem("ToneTuneToolkit/Rename Folders")]
+
+    private static void Init()
+    {
+      RenameFolders window = (RenameFolders)GetWindow(typeof(RenameFolders));
+      window.Show();
+      return;
+    }
+
+    private void OnGUI() => OnGUIRenameFolders();
+
+    // ==================================================c
+
+    private void OnGUIRenameFolders()
+    {
+      GUILayout.Label("Original & New Folder Name(s):");
+      oldFolderName = EditorGUILayout.TextField("Original Folder Name:", oldFolderName);
+      newFolderName = EditorGUILayout.TextField("New Folder Name:", newFolderName);
+
+      GUILayout.Space(EditorStorage.GUI.Space);
+
+      GUILayout.BeginHorizontal();
+      GUILayout.Label($"Rename Selected Folders to [{newFolderName}]");
+      if (GUILayout.Button("Rename", GUILayout.Width(EditorStorage.GUI.NarrowWidth)))
+      {
+        RenameSelectedFolders();
+      }
+      GUILayout.EndHorizontal();
+
+      GUILayout.BeginHorizontal();
+      GUILayout.Label($"Rename All [{oldFolderName}] to [{newFolderName}]");
+      if (GUILayout.Button("Rename", GUILayout.Width(EditorStorage.GUI.NarrowWidth)))
+      {
+        RenameAllMatchedFolders();
+      }
+      GUILayout.EndHorizontal();
+      return;
+    }
+
+    private void RenameSelectedFolders()
+    {
+      foreach (string guid in Selection.assetGUIDs)
+      {
+        string tempOld‌Directory‌Path = AssetDatabase.GUIDToAssetPath(guid);
+
+        // 如果不是文件夹,则跳过
+        if (!AssetDatabase.IsValidFolder(tempOld‌Directory‌Path))
+        {
+          continue;
+        }
+
+        string tempOldFolderName = Path.GetFileName(tempOld‌Directory‌Path);
+        string tempNew‌Directory‌Path = tempOld‌Directory‌Path.Replace(tempOldFolderName, newFolderName);
+
+        // Debug.Log(tempOldFolderPath);
+        // Debug.Log(tempNewFolderPath);
+
+        // 重命名文件夹
+        Directory.Move(tempOld‌Directory‌Path, tempNew‌Directory‌Path);
+        Directory.Move(tempOld‌Directory‌Path + ".meta", tempNew‌Directory‌Path + ".meta");
+      }
+      AssetDatabase.SaveAssets();
+      AssetDatabase.Refresh();
+      return;
+    }
+
+    private void RenameAllMatchedFolders()
+    {
+      RenameAllMatchedFoldersAction(new DirectoryInfo(Application.dataPath));
+      AssetDatabase.SaveAssets();
+      AssetDatabase.Refresh();
+      return;
+    }
+    private void RenameAllMatchedFoldersAction(DirectoryInfo root)
+    {
+      // 遍历所有子文件夹
+      foreach (DirectoryInfo directoryInfo in root.GetDirectories())
+      {
+        // 递归调用,遍历子文件夹中的文件夹
+        RenameAllMatchedFoldersAction(directoryInfo);
+      }
+
+      string tempDirectoryPath = root.FullName.Replace(@"\", @"/");
+      string tempFolderName = Path.GetFileName(tempDirectoryPath);
+
+      if (tempFolderName == oldFolderName)
+      {
+        string tempNew‌Directory‌Path = tempDirectoryPath.Replace(tempFolderName, newFolderName);
+        Directory.Move(tempDirectoryPath, tempNew‌Directory‌Path);
+        Directory.Move(tempDirectoryPath + ".meta", tempNew‌Directory‌Path + ".meta");
+      }
+      return;
+    }
+  }
+}

+ 11 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/RenameFolders.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3630494423fa414459d1c57a0c0c1aa1
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 187 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/UpdateCopyrights.cs

@@ -0,0 +1,187 @@
+/// <summary>
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
+/// </summary>
+
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using ToneTuneToolkit.Common;
+using UnityEditor;
+using UnityEngine;
+
+namespace ToneTuneToolkit.Editor
+{
+  public class UpdateCopyrights : EditorWindow
+  {
+    private string author = "MirzkisD1Ex0";
+    private string year = "2025";
+    private string codeVersion = "1.4.20";
+
+    private string displayString = "";
+    private Vector2 displayStringScrollPosition = Vector2.zero;
+
+    // ==================================================
+
+    [MenuItem(@"ToneTuneToolkit/Update Copyrights")]
+
+    private static void Init()
+    {
+      UpdateCopyrights window = (UpdateCopyrights)GetWindow(typeof(UpdateCopyrights));
+      window.Show();
+      return;
+    }
+
+    private void OnGUI() => OnGUIUpdateCopyright();
+
+    // ==================================================
+
+    private void OnGUIUpdateCopyright()
+    {
+      GUILayout.Label("Copyright Info");
+      author = EditorGUILayout.TextField("Author:", author);
+      year = EditorGUILayout.TextField("Year:", year);
+      codeVersion = EditorGUILayout.TextField("Code Version", codeVersion);
+
+      GUILayout.Space(EditorStorage.GUI.Space);
+
+      if (GUILayout.Button("Load All Selected File Info(s)"))
+      {
+        DsiplaySelectedFileInfos();
+      }
+      if (GUILayout.Button("Clear Info(s)", GUILayout.Width(EditorStorage.GUI.NarrowWidth)))
+      {
+        ClearFileInfos();
+      }
+
+      GUILayout.BeginVertical(GUILayout.Height(EditorStorage.GUI.Height));
+      displayStringScrollPosition = GUILayout.BeginScrollView(displayStringScrollPosition);
+      GUILayout.TextArea(displayString);
+      GUILayout.EndScrollView();
+      GUILayout.EndVertical();
+
+      GUILayout.Space(EditorStorage.GUI.Space);
+
+      if (GUILayout.Button("Add Copyright Info to Above File(s)"))
+      {
+        ChangeContent();
+      }
+      return;
+    }
+
+    // ==================================================
+
+    private List<string> scriptFilePaths = new List<string>();
+    private List<string> scriptFileNames = new List<string>();
+
+    private void ClearFileInfos()
+    {
+      scriptFilePaths.Clear();
+      scriptFileNames.Clear();
+      displayString = null;
+      return;
+    }
+
+
+
+    private void DsiplaySelectedFileInfos()
+    {
+      ClearFileInfos();
+
+      foreach (string guid in Selection.assetGUIDs)
+      {
+        string scriptFilePath = AssetDatabase.GUIDToAssetPath(guid);
+        if (string.IsNullOrEmpty(scriptFilePath) || !scriptFilePath.EndsWith(".cs"))
+        {
+          continue;
+        }
+        scriptFilePaths.Add(scriptFilePath);
+        scriptFileNames.Add(Path.GetFileName(scriptFilePath));
+      }
+
+      if (scriptFilePaths.Count <= 0)
+      {
+        TTTDebug.LogWarning($"{nameof(UpdateCopyrights)} Can't find any file and its path.");
+        return;
+      }
+
+      displayString = null;
+      for (int i = 0; i < scriptFilePaths.Count; i++)
+      {
+        displayString += scriptFileNames[i] + "\n" + scriptFilePaths[i];
+
+        if (i != scriptFilePaths.Count - 1) // 不是最后一个
+        {
+          displayString += "\n\n";
+          continue;
+        }
+      }
+      return;
+    }
+
+    /// <summary>
+    /// 改变内容
+    /// </summary>
+    private void ChangeContent()
+    {
+      if (scriptFilePaths.Count <= 0)
+      {
+        return;
+      }
+
+      Debug.Log(scriptFilePaths.Count);
+      List<string> fileContents = new List<string>();
+
+      foreach (string filePath in scriptFilePaths)
+      {
+        // string filePath = scriptFilePaths[0];
+        fileContents.Clear();
+        fileContents = File.ReadAllLines(filePath).ToList();
+
+        // 定位并删除所有Copyright
+        int startIndex = -1;
+        int endIndex = -1;
+
+        for (int i = 0; i < 4; i++) // fileContents.Count
+        {
+          if (fileContents[i].Contains("<summary>"))
+          {
+            startIndex = i;
+            break;
+          }
+        }
+
+        for (int i = 0; i < 4; i++)
+        {
+          if (fileContents[i].Contains("</summary>"))
+          {
+            endIndex = i;
+            break;
+          }
+        }
+
+        // 删除已有的版权信息
+        if (startIndex != -1 && endIndex != -1)
+        {
+          // 删除从开始位置到结束位置的所有行
+          fileContents.RemoveRange(startIndex, endIndex - startIndex + 1);
+        }
+
+        // 添加新的Copeyright
+        fileContents.Insert(0, $"/// <summary>");
+        fileContents.Insert(1, $"/// Copyright (c) {year} {author} All rights reserved.");
+        fileContents.Insert(2, $"/// Code Version {codeVersion}");
+        fileContents.Insert(3, $"/// </summary>");
+        if (fileContents[4] != null)
+        {
+          fileContents.Insert(4, $"");
+        }
+
+        File.WriteAllLines(filePath, fileContents);
+      }
+
+      ClearFileInfos();
+      return;
+    }
+  }
+}

+ 11 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Editor/UpdateCopyrights.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b4c3a47fedad0d847b3f81dd21b42f66
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Funny/BubbleSort.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 namespace ToneTuneToolkit.Funny
@@ -45,4 +45,4 @@ namespace ToneTuneToolkit.Funny
       return tempArray;
     }
   }
-}
+}

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/IO/FTPMaster.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2023 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using UnityEngine;
@@ -68,4 +68,4 @@ namespace ToneTuneToolkit.IO
       return;
     }
   }
-}
+}

+ 100 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/IO/FileCapturer.cs

@@ -0,0 +1,100 @@
+/// <summary>
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
+/// </summary>
+
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UnityEngine;
+
+namespace ToneTuneToolkit.IO
+{
+  public class FileCapturer
+  {
+    // private string targetFolderPath = @"\\192.168.50.56\Screenshot\"; // 网络路径文件夹需要正确且需要设置无密码共享
+    // private const string fileSuffix = "*.jpg";
+
+    /// <summary>
+    /// 获取文件完整路径
+    /// </summary>
+    /// <param name="folderPath"></param>
+    /// <param name="fileSuffix"></param>
+    /// <returns></returns>
+    public static List<string> GetFileFullPaths2List(string folderPath, string fileSuffix)
+    {
+      if (!Directory.Exists(folderPath))
+      {
+        Debug.Log($"[File Capturer] Folder {folderPath} dose not exist");
+        return null;
+      }
+
+      return Directory.GetFiles(folderPath, fileSuffix).ToList(); // 完整路径
+    }
+
+    /// <summary>
+    /// 获取文件名
+    /// </summary>
+    /// <param name="folderPath"></param>
+    /// <param name="fileSuffix"></param>
+    /// <returns></returns>
+    public static List<string> GetFileNames2List(string folderPath, string fileSuffix)
+    {
+      if (!Directory.Exists(folderPath))
+      {
+        Debug.LogError($"[File Capturer] Folder {folderPath} dose not exist");
+        return null;
+      }
+
+      List<string> filePaths = Directory.GetFiles(folderPath, fileSuffix).ToList(); // 完整路径
+      List<string> fileNames = new List<string>(); // 文件名
+      foreach (string item in filePaths)
+      {
+        fileNames.Add(Path.GetFileName(item));
+      }
+
+      return fileNames;
+    }
+
+    public static byte[] GetFileBytes(string filePath)
+    {
+      if (!File.Exists(filePath))
+      {
+        Debug.LogError($"[File Capturer] File {filePath} dose not exist");
+        return null;
+      }
+
+      return File.ReadAllBytes(filePath);
+    }
+
+    #region Old
+    // /// <summary>
+    // /// 获取路径下全部指定类型的文件名
+    // /// </summary>
+    // /// <param name="folderPath">路径</param>
+    // /// <param name="fileSuffix">后缀名</param>
+    // public static List<string> GetFileName2List(string folderPath, string fileSuffix)
+    // {
+    //   if (!Directory.Exists(folderPath))
+    //   {
+    //     Debug.Log($"[File Capturer] Path {folderPath} dose not exist");
+    //     return null;
+    //   }
+    //   DirectoryInfo directoryInfo = new DirectoryInfo(folderPath); // 获取文件信息
+    //   FileInfo[] fileInfos = directoryInfo.GetFiles("*", SearchOption.AllDirectories);
+
+    //   List<string> filesList = new List<string>();
+
+    //   // 筛选符合条件的文件并储名进数组
+    //   for (int i = 0; i < fileInfos.Length; i++)
+    //   {
+    //     if (fileInfos[i].Name.EndsWith(fileSuffix))
+    //     {
+    //       filesList.Add(fileInfos[i].Name); // 把符合要求的文件名存储至数组中
+    //     }
+    //   }
+    //   return filesList;
+    // }
+    #endregion
+  }
+}

+ 0 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Common/FileNameCapturer.cs.meta → ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/IO/FileCapturer.cs.meta


+ 6 - 5
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/FullAngleScreenshotTool.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2023 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using System.Collections;
@@ -11,6 +11,7 @@ namespace ToneTuneToolkit.Media
 {
   /// <summary>
   /// 全角度截图工具
+  /// 等待修复
   /// </summary>
   [RequireComponent(typeof(ScreenshotMaster))]
   public class FullAngleScreenshotTool : MonoBehaviour
@@ -25,7 +26,7 @@ namespace ToneTuneToolkit.Media
 
     private void Start()
     {
-      ScreenshotCamera = ScreenshotMaster.Instance.ScreenshotCamera;
+      // ScreenshotCamera = ScreenshotMaster.Instance.ScreenshotCamera;
     }
 
     private void Update()
@@ -59,7 +60,7 @@ namespace ToneTuneToolkit.Media
         ScreenshotCamera.clearFlags = CameraClearFlags.SolidColor;
         ScreenshotCamera.clearFlags = CameraClearFlags.Nothing;
         yield return new WaitForEndOfFrame();
-        ScreenshotMaster.Instance.SaveRenderTexture(currentPath, string.Format("{0:d4}", i) + ".png");
+        // ScreenshotMaster.Instance.SaveRenderTexture(currentPath, string.Format("{0:d4}", i) + ".png");
       }
       Debug.Log($"[FullAngleScreenshotTool] {ShotTime} shots complete...[Done]");
 #if UNITY_EDITOR
@@ -69,4 +70,4 @@ namespace ToneTuneToolkit.Media
       yield break;
     }
   }
-}
+}

+ 150 - 56
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/ScreenshotMaster.cs

@@ -1,18 +1,18 @@
 /// <summary>
-/// Copyright (c) 2023 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
-using UnityEngine.Rendering;
-using UnityEngine.Experimental.Rendering;
-using UnityEngine.UI;
 using System;
 using System.IO;
+using UnityEngine.Events;
 using ToneTuneToolkit.Common;
 
+
+
 namespace ToneTuneToolkit.Media
 {
   /// <summary>
@@ -20,79 +20,173 @@ namespace ToneTuneToolkit.Media
   /// </summary>
   public class ScreenshotMaster : SingletonMaster<ScreenshotMaster>
   {
-    public Camera ScreenshotCamera;
+    public static UnityAction<Texture2D> OnScreenshotFinished;
+
+    [Header("DEBUG - Peek")]
+    [SerializeField] private Texture2D peekTexture;
+
+    // ==================================================
+
+    // private void Update()
+    // {
+    //   if (Input.GetKeyDown(KeyCode.Q))
+    //   {
+    //     SaveTest();
+    //   }
+    // }
 
-    [SerializeField]
-    private int textureHight = 1024, textureWidth = 1024; // 贴图尺寸
+    // public RectTransform Area;//用来取景的ui,设置为透明的
 
-    public RawImage PreviewImage; // 预览用UI
-    private RenderTexture _renderTexture;
+    // public void SaveTest()
+    // {
+    //   string fullPath = $"{Application.streamingAssetsPath}/IMAGE/{SpawnTimeStamp()}.png";
+    //   TakeScreenshot(Area, fullPath, CanvasType.ScreenSpaceOverlay);
+    // }
 
     // ==================================================
 
-    private void Awake()
+    /// <summary>
+    /// 传入用于标定范围的Image
+    /// 独立功能
+    /// </summary>
+    /// <param name="screenshotArea">标定范围</param>
+    /// <param name="fullFilePath">保存路径</param>
+    /// <param name="canvasType">截图类型</param>
+    public void TakeScreenshot(RectTransform screenshotArea, string fullFilePath, CanvasType canvasType)
+    {
+      StartCoroutine(TakeScreenshotAction(screenshotArea, fullFilePath, canvasType));
+      return;
+    }
+    private IEnumerator TakeScreenshotAction(RectTransform screenshotArea, string fullFilePath, CanvasType canvasType)
     {
-      _renderTexture = InitRenderTexture();
-      SettingCamera(ScreenshotCamera);
+      yield return new WaitForEndOfFrame(); // 等待渲染帧结束
+
+      int width = (int)screenshotArea.rect.width;
+      int height = (int)screenshotArea.rect.height;
 
-      if (PreviewImage)
+      Texture2D texture2D = new Texture2D(width, height, TextureFormat.RGBA64, false);
+
+      // 原点
+      float leftBottomX = 0;
+      float leftBottomY = 0;
+
+      switch (canvasType)
       {
-        PreviewImage.texture = _renderTexture;
+        default: break;
+        case CanvasType.ScreenSpaceOverlay:
+          leftBottomX = screenshotArea.transform.position.x + screenshotArea.rect.xMin;
+          leftBottomY = screenshotArea.transform.position.y + screenshotArea.rect.yMin;
+          break;
+        case CanvasType.ScreenSpaceCamera: // 如果是camera需要额外加上偏移值
+
+          // leftBottomX = Screen.width / 2;
+          // leftBottomY = Screen.height / 2;
+          // 相机画幅如果是1920x1080,设置透视、Size540可让UI缩放为111
+
+          // Debug.Log(Screen.width / 2 + "/" + Screen.height / 2);
+          break;
       }
+
+      texture2D.ReadPixels(new Rect(leftBottomX, leftBottomY, width, height), 0, 0);
+      texture2D.Apply();
+
+      // 保存至本地
+      byte[] bytes = texture2D.EncodeToPNG();
+      File.WriteAllBytes(fullFilePath, bytes);
+      Debug.Log($"[ScreenshotMasterLite] <color=green>{fullFilePath}</color>...[OK]");
+      // Destroy(texture2D);
+
+      peekTexture = texture2D;
+
+      if (OnScreenshotFinished != null)
+      {
+        OnScreenshotFinished(texture2D);
+      }
+      yield break;
     }
 
     // ==================================================
+    #region RenderTexture - 屏外渲染
 
     /// <summary>
-    /// 初始化RT
+    /// 屏外RT截图
+    /// Camera Size = Canvas高度的1/2
+    /// Canvas Render mode = Screen Space - Camera
     /// </summary>
-    /// <returns></returns>
-    private RenderTexture InitRenderTexture()
+    /// <param name="screenshotCamera"></param>
+    /// <param name="screenshotRT">新建的RT宽高色彩模式都要设置妥当</param>
+    public static Texture2D OffScreenshot(Camera screenshotCamera, RenderTexture screenshotRT, string fullFilePath)
     {
-      RenderTexture _tempRenderTexture = new RenderTexture(textureWidth, textureHight, 16)
-      {
-        name = "TempRenderTexutre",
-        dimension = TextureDimension.Tex2D,
-        antiAliasing = 1,
-        graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat
-      };
-      return _tempRenderTexture;
+      screenshotCamera.clearFlags = CameraClearFlags.SolidColor;
+      screenshotCamera.backgroundColor = Color.clear;
+      screenshotCamera.targetTexture = screenshotRT;
+      screenshotCamera.Render(); // 渲染到纹理
+
+      RenderTexture.active = screenshotRT;
+      Texture2D t2d = new Texture2D(screenshotRT.width, screenshotRT.height, TextureFormat.RGBA32, false);
+      t2d.ReadPixels(new Rect(0, 0, screenshotRT.width, screenshotRT.height), 0, 0);
+      t2d.Apply();
+
+      byte[] bytes = t2d.EncodeToPNG();
+      File.WriteAllBytes(fullFilePath, bytes);
+      Debug.Log(@$"[SM] <color=green>{fullFilePath}</color>");
+
+      RenderTexture.active = null;
+      screenshotRT.Release();
+      return t2d;
     }
 
-    /// <summary>
-    /// 设置相机
-    /// </summary>
-    /// <param name="tempCamera"></param>
-    private void SettingCamera(Camera tempCamera)
+    #endregion
+    // ==================================================
+    #region 实验性功能
+    public Texture2D InstantTakeScreenshot(Camera renderCamera, string fullFilePath)
     {
-      tempCamera.backgroundColor = Color.clear;
-      tempCamera.targetTexture = _renderTexture;
-      return;
+      // 创建一个RenderTexture
+      RenderTexture renderTexture = new RenderTexture(Screen.width, Screen.height, 24);
+      renderCamera.targetTexture = renderTexture;
+
+      // 手动渲染Camera
+      renderCamera.Render();
+
+      // 创建一个Texture2D来保存渲染结果
+      Texture2D texture2D = new Texture2D(Screen.width, Screen.height, TextureFormat.RGBA64, false); // TextureFormat.RGB24
+
+      // 从RenderTexture中读取像素数据
+      RenderTexture.active = renderTexture;
+      texture2D.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
+      texture2D.Apply();
+
+      // 保存至本地
+      byte[] bytes = texture2D.EncodeToPNG();
+      File.WriteAllBytes(fullFilePath, bytes);
+      Debug.Log($"[ScreenshotMasterLite] <color=green>{fullFilePath}</color>...[OK]");
+
+      peekTexture = texture2D;
+
+      // 清理
+      renderCamera.targetTexture = null;
+      RenderTexture.active = null;
+      Destroy(renderTexture);
+      // Destroy(texture2D);
+      return texture2D;
     }
 
-    public void SaveRenderTexture(string filePath, string fileName)
+    #endregion
+    // ==================================================
+    #region Tools
+
+    public static string SpawnTimeStamp()
     {
-      RenderTexture active = RenderTexture.active;
-      RenderTexture.active = _renderTexture;
-      Texture2D png = new Texture2D(_renderTexture.width, _renderTexture.height, TextureFormat.ARGB32, false);
-      png.ReadPixels(new Rect(0, 0, _renderTexture.width, _renderTexture.height), 0, 0);
-      png.Apply();
-      RenderTexture.active = active;
-      byte[] bytes = png.EncodeToPNG();
-      if (!Directory.Exists(filePath))
-      {
-        Directory.CreateDirectory(filePath);
-      }
-      FileStream fs = File.Open(filePath + fileName, FileMode.Create);
-      BinaryWriter writer = new BinaryWriter(fs);
-      writer.Write(bytes);
-      writer.Flush();
-      writer.Close();
-      fs.Close();
-      Destroy(png);
-      _renderTexture.Release();
-      Debug.Log($"[ScreenshotMaster] <color=green>{filePath}{fileName}</color>...[OK]");
-      return;
+      return $"{DateTime.Now:yyyy-MM-dd-HH-mm-ss}-{new System.Random().Next(0, 100)}";
     }
+
+    public enum CanvasType
+    {
+      ScreenSpaceOverlay = 0,
+      ScreenSpaceCamera = 1,
+      WorldSpace = 2
+    }
+
+    #endregion
   }
 }

+ 1 - 1
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/ScreenshotMaster.cs.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 45b6b5492f2e29648a2a581a782c723d
+guid: 63297da57baa4a44283af12894d2e248
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2

+ 0 - 114
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/ScreenshotMasterLite.cs

@@ -1,114 +0,0 @@
-/// <summary>
-/// Copyright (c) 2023 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
-/// </summary>
-
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
-using UnityEngine.Rendering;
-using UnityEngine.Experimental.Rendering;
-using UnityEngine.UI;
-using System;
-using System.IO;
-using ToneTuneToolkit.Common;
-
-namespace ToneTuneToolkit.Media
-{
-  /// <summary>
-  /// 截图大师Lite
-  /// </summary>
-  public class ScreenshotMasterLite : SingletonMaster<ScreenshotMasterLite>
-  {
-
-    private void Update()
-    {
-      if (Input.GetKeyDown(KeyCode.Q))
-      {
-        Save();
-      }
-    }
-
-
-    public RectTransform Area;//用来取景的ui,设置为透明的
-
-    public void Save()
-    {
-      string fullPath = $"{Application.streamingAssetsPath}/IMAGE/{DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss")}.png";
-      TakeScreenshot(Area, fullPath, CanvasType.ScreenSpaceOverlay);
-    }
-
-
-
-
-
-    /// <summary>
-    /// 传入用于标定范围的Image
-    /// 独立功能
-    /// </summary>
-    /// <param name="screenshotArea">标定范围</param>
-    /// <param name="fullFilePath">保存路径</param>
-    public void TakeScreenshot(RectTransform screenshotArea, string fullFilePath, CanvasType canvasType)
-    {
-      StartCoroutine(TakeScreenshotAction(screenshotArea, fullFilePath, canvasType));
-      return;
-    }
-
-    public Rect RRect;
-
-    // 新建overlayui确定截图范围
-    private IEnumerator TakeScreenshotAction(RectTransform screenshotArea, string fullFilePath, CanvasType canvasType)
-    {
-      yield return new WaitForEndOfFrame(); // 等待渲染帧结束
-
-      int width = (int)screenshotArea.rect.width;
-      int height = (int)screenshotArea.rect.height;
-
-      Texture2D texture2D = new Texture2D(width, height, TextureFormat.RGBA64, false);
-
-      // 原点
-      float leftBottomX = 0;
-      float leftBottomY = 0;
-
-      switch (canvasType)
-      {
-        default: break;
-        case CanvasType.ScreenSpaceOverlay:
-          leftBottomX = screenshotArea.transform.position.x + screenshotArea.rect.xMin;
-          leftBottomY = screenshotArea.transform.position.y + screenshotArea.rect.yMin;
-          break;
-        case CanvasType.ScreenSpaceCamera: // 如果是camera需要额外加上偏移值
-          leftBottomX = Screen.width / 2;
-          leftBottomY = Screen.height / 2;
-          Debug.Log(Screen.width / 2 + "/" + Screen.height / 2);
-          break;
-      }
-
-
-      Debug.Log($"{screenshotArea.transform.position.x}/{screenshotArea.transform.position.y}");
-
-      RRect = new Rect(leftBottomX, leftBottomY, width, height);
-
-      texture2D.ReadPixels(RRect, 0, 0);
-      texture2D.Apply();
-
-      // 保存至本地
-      byte[] bytes = texture2D.EncodeToPNG();
-      File.WriteAllBytes(fullFilePath, bytes);
-      Debug.Log($"[ScreenshotMasterLite] <color=green>{fullFilePath}</color>...[OK]");
-
-      // Destroy(texture2D);
-      yield break;
-    }
-
-    public enum CanvasType
-    {
-      ScreenSpaceOverlay = 0,
-      ScreenSpaceCamera = 1,
-      WorldSpace = 2
-    }
-
-    // DateTime dateTime = DateTime.Now;
-    // string fullPath = $"{Application.streamingAssetsPath}/{dateTime.Year}-{dateTime.Month}-{dateTime.Day}-{dateTime.Hour}-{dateTime.Minute}-{dateTime.Second}.png";
-  }
-}

+ 0 - 73
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/ScreenshotMasterMini.cs

@@ -1,73 +0,0 @@
-/// <summary>
-/// Copyright (c) 2024 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
-/// </summary>
-
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
-using System.IO;
-using UnityEngine.Events;
-
-namespace ToneTuneToolkit.Media
-{
-  /// <summary>
-  /// 截图大师Mini
-  /// </summary>
-  public class ScreenshotMasterMini : MonoBehaviour
-  {
-    public static ScreenshotMasterMini Instance;
-
-    public static UnityAction<Texture2D> OnScreenshotCompelete;
-
-    // ==================================================
-
-    private void Awake()
-    {
-      Instance = this;
-    }
-
-    // ==================================================
-
-    /// <summary>
-    /// 传入用于标定范围的Image
-    /// 独立功能
-    /// </summary>
-    /// <param name="screenshotArea">标定范围</param>
-    /// <param name="fullFilePath">保存路径</param>
-    public void TakeScreenshot(RectTransform screenshotArea, string fullFilePath)
-    {
-      StartCoroutine(TakeScreenshotAction(screenshotArea, fullFilePath));
-      return;
-    }
-
-    // 新建overlayui确定截图范围
-    private IEnumerator TakeScreenshotAction(RectTransform screenshotArea, string fullFilePath)
-    {
-      yield return new WaitForEndOfFrame(); // 等待渲染帧结束
-
-      int width = (int)screenshotArea.rect.width;
-      int height = (int)screenshotArea.rect.height;
-
-      Texture2D texture2D = new Texture2D(width, height, TextureFormat.RGBA32, false);
-
-      // 原点
-      float leftBottomX = screenshotArea.transform.position.x + screenshotArea.rect.xMin;
-      float leftBottomY = screenshotArea.transform.position.y + screenshotArea.rect.yMin;
-
-      texture2D.ReadPixels(new Rect(leftBottomX, leftBottomY, width, height), 0, 0);
-      texture2D.Apply();
-
-      // 保存至本地
-      byte[] bytes = texture2D.EncodeToPNG();
-      File.WriteAllBytes(fullFilePath, bytes);
-      Debug.Log($"[ScreenshotMasterLite] <color=green>{fullFilePath}</color>...<color=green>[OK]</color>");
-
-      if (OnScreenshotCompelete != null)
-      {
-        OnScreenshotCompelete(texture2D);
-      }
-      yield break;
-    }
-  }
-}

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/TextureProcessor.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2024 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using System.Collections;
@@ -160,4 +160,4 @@ namespace ToneTuneToolkit.Media
       return scaleTexutre2D;
     }
   }
-}
+}

+ 42 - 41
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Media/WebCamHandler.cs

@@ -1,55 +1,65 @@
+/// <summary>
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
+/// </summary>
+
 using System.Collections;
 using System.Collections.Generic;
+using ToneTuneToolkit.Common;
 using UnityEngine;
 using UnityEngine.UI;
 
-public class WebCamHandler : MonoBehaviour
+public class WebCamHandler : SingletonMaster<WebCamHandler>
 {
-  public static WebCamHandler Instance;
-
-  public RawImage previewRawImage;
+  [SerializeField] private RawImage previewRawImage;
 
-  private WebCamDevice _webCamDevice;
-  private WebCamTexture _webCamTexture;
-  private bool _isWebCamReady = false;
+  private WebCamDevice webCamDevice;
+  private WebCamTexture webCamTexture;
+  private bool isWebCamReady = false;
 
   // ==================================================
 
-  private void Awake()
-  {
-    Instance = this;
-  }
-
   private void Start()
   {
-    // InitWebcam();
+    InitWebcam();
     // StartWebcam();
   }
 
   // ==================================================
-  // 相机配置
+  #region 相机配置
 
-  // private const string _RequestedDeviceName = "Logitech BRIO";
-  private string _webCamName = "GC21 Video";
-  private int _webCamWidth = 1280;
-  private int _webCamHeight = 720;
-  private int _webCamFPS = 30;
+  private string _webCamName = "MX Brio";
+  private int _webCamWidth = 1920;
+  private int _webCamHeight = 1080;
+  private int _webCamFPS = 60;
 
-  private void InitWebcam()
+  public void SetWebcam(string name, int width, int height, int fps)
+  {
+    _webCamName = name;
+    _webCamWidth = width;
+    _webCamHeight = height;
+    _webCamFPS = fps;
+    return;
+  }
+
+  #endregion
+  // ==================================================
+
+  public void InitWebcam()
   {
     foreach (WebCamDevice device in WebCamTexture.devices)
     {
       // Debug.Log(device.name);
       if (device.name == _webCamName)
       {
-        _webCamDevice = device;
-        _webCamTexture = new WebCamTexture(_webCamDevice.name, _webCamWidth, _webCamHeight, _webCamFPS);
+        webCamDevice = device;
+        webCamTexture = new WebCamTexture(webCamDevice.name, _webCamWidth, _webCamHeight, _webCamFPS);
         // _webCamTexture.Play();
-        _isWebCamReady = true;
+        isWebCamReady = true;
 
         if (previewRawImage) // Preview
         {
-          previewRawImage.texture = _webCamTexture;
+          previewRawImage.texture = webCamTexture;
         }
         break;
       }
@@ -59,9 +69,9 @@ public class WebCamHandler : MonoBehaviour
 
   public WebCamTexture GetWebcamTexture()
   {
-    if (_isWebCamReady)
+    if (isWebCamReady)
     {
-      return _webCamTexture;
+      return webCamTexture;
     }
     else
     {
@@ -69,38 +79,29 @@ public class WebCamHandler : MonoBehaviour
     }
   }
 
-  public void SetWebcam(string name, int width, int height, int fps)
-  {
-    _webCamName = name;
-    _webCamWidth = width;
-    _webCamHeight = height;
-    _webCamFPS = fps;
-    return;
-  }
-
   public void StartWebcam()
   {
-    if (_isWebCamReady)
+    if (isWebCamReady)
     {
-      _webCamTexture.Play();
+      webCamTexture.Play();
     }
     return;
   }
 
   public void PauseWebcam()
   {
-    if (_isWebCamReady)
+    if (isWebCamReady)
     {
-      _webCamTexture.Pause();
+      webCamTexture.Pause();
     }
     return;
   }
 
   public void StopWebcam()
   {
-    if (_isWebCamReady)
+    if (isWebCamReady)
     {
-      _webCamTexture.Stop();
+      webCamTexture.Stop();
     }
     return;
   }

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Mobile/ObjectRotateAndScale.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using UnityEngine;
@@ -102,4 +102,4 @@ namespace ToneTuneToolkit.Mobile
       return false;
     }
   }
-}
+}

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/MultimediaExhibitionHall/LED/LEDCommandCenter.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using UnityEngine;
@@ -200,4 +200,4 @@ namespace ToneTuneToolkit.LED
     }
     #endregion
   }
-}
+}

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/MultimediaExhibitionHall/LED/LEDCommandHub.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 namespace ToneTuneToolkit.LED
@@ -47,4 +47,4 @@ namespace ToneTuneToolkit.LED
       public const string DimEffectSpeed = "&Speed=";
     }
   }
-}
+}

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/MultimediaExhibitionHall/LED/LEDHandler.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using UnityEngine;
@@ -73,4 +73,4 @@ namespace ToneTuneToolkit.LED
       return new Color(r, g, b, 1);
     }
   }
-}
+}

+ 3 - 3
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/MultimediaExhibitionHall/LED/LEDNuclearShow.cs

@@ -1,6 +1,6 @@
 /// <summary>
-/// Copyright (c) 2021 MirzkisD1Ex0 All rights reserved.
-/// Code Version 1.0
+/// Copyright (c) 2025 MirzkisD1Ex0 All rights reserved.
+/// Code Version 1.4.20
 /// </summary>
 
 using UnityEngine;
@@ -55,4 +55,4 @@ namespace ToneTuneToolkit.LED
       return;
     }
   }
-}
+}

+ 8 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Networking.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 779dd3aefd1a5c348bf51d19410cd345
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 97 - 0
ToneTuneToolkit/Assets/ToneTuneToolkit/Scripts/Networking/JsonUploadManager.cs

@@ -0,0 +1,97 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using Newtonsoft.Json;
+using ToneTuneToolkit.Common;
+using UnityEngine;
+using UnityEngine.Events;
+using UnityEngine.Networking;
+
+namespace ToneTuneToolkit.Networking
+{
+  /// <summary>
+  /// Json上传
+  /// </summary>
+  public class JsonUploadManager : SingletonMaster<JsonUploadManager>
+  {
+    private const string URL = "https://ai.digital-event.cn/api/ai/chat";
+
+    public static UnityAction<string> OnSentenceComplete;
+
+    // ==================================================
+
+    // private void Update()
+    // {
+    //   if (Input.GetKeyUp(KeyCode.Q))
+    //   {
+    //     UploadJson(new UploadJsonData() { message = "fox" });
+    //   }
+    // }
+
+    // ==================================================
+
+    [SerializeField] private UploadJsonData uploadJson = new UploadJsonData();
+    [SerializeField] private ResponJsonData responJson;
+
+    [Serializable]
+    public class UploadJsonData
+    {
+      public string message;
+    }
+    [Serializable]
+    public class ResponJsonData
+    {
+      public bool status;
+      public string message;
+      public string data;
+    }
+
+    // ==================================================
+
+    public void UploadJson(UploadJsonData value)
+    {
+      uploadJson = new UploadJsonData();
+      uploadJson = value;
+      StartCoroutine(nameof(UploadJsonAction));
+      return;
+    }
+
+    private IEnumerator UploadJsonAction()
+    {
+      string json = JsonConvert.SerializeObject(uploadJson);
+
+      using (UnityWebRequest www = new UnityWebRequest(URL, UnityWebRequest.kHttpVerbPOST))
+      {
+        // www.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+        www.SetRequestHeader("Content-Type", "application/json");
+        www.SetRequestHeader("Accept", "application/json");
+
+        byte[] bytes = Encoding.UTF8.GetBytes(json);
+        www.uploadHandler = new UploadHandlerRaw(bytes);
+
+        DownloadHandler downloadHandler = new DownloadHandlerBuffer();
+        www.downloadHandler = downloadHandler;
+
+        yield return www.SendWebRequest();
+
+        if (www.result != UnityWebRequest.Result.Success)
+        {
+          Debug.Log($"{www.error}...<color=red>[ER]</color>");
+          yield break;
+        }
+        else
+        {
+          // Debug.Log(www.downloadHandler.text);
+          responJson = new ResponJsonData();
+          responJson = JsonConvert.DeserializeObject<ResponJsonData>(www.downloadHandler.text.ToString());
+          if (OnSentenceComplete != null)
+          {
+            OnSentenceComplete(responJson.data);
+          }
+        }
+      }
+      yield break;
+    }
+  }
+}

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels