iT邦幫忙

2024 iThome 鐵人賽

DAY 10
0
Software Development

Unity黑科技揭秘:30個專業遊戲開發者必知的開發技巧系列 第 10

Unity 效率代碼 - 5個超好用的Utility Code Snippet !

  • 分享至 

  • xImage
  •  

1. 跳過Splash Screen

/* ---------------------------------------------------------------- */
/*                    Skip Unity Splash Screen                      */
/*                      Create by psygames                          */
/*            https://github.com/psygames/UnitySkipSplash           */
/* ---------------------------------------------------------------- */

#if !UNITY_EDITOR
using UnityEngine;
using UnityEngine.Rendering;

public class SkipSplash
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
    private static void BeforeSplashScreen()
    {
#if UNITY_WEBGL
        Application.focusChanged += Application_focusChanged;
#else
        System.Threading.Tasks.Task.Run(AsyncSkip);
#endif
    }

#if UNITY_WEBGL
    private static void Application_focusChanged(bool obj)
    {
        Application.focusChanged -= Application_focusChanged;
        SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
    }

#else
    private static void AsyncSkip()
    {
        SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
    }
#endif

}
#endif

2. 快速記錄存檔Config

寫入:

// 寫入JSON文件
PlayerData player = new PlayerData
{
    playerName = "Player1",
    playerScore = 100
};

string filePath = Path.Combine(Application.persistentDataPath, "playerData.json");
string jsonData = JsonConvert.SerializeObject(player, Formatting.Indented);
File.WriteAllText(filePath, jsonData);

讀取:

if (File.Exists(filePath))
{
    string loadedJsonData = File.ReadAllText(filePath);
    PlayerData loadedPlayer = JsonConvert.DeserializeObject<PlayerData>(loadedJsonData);

    Debug.Log("Loaded Player Name: " + loadedPlayer.playerName);
    Debug.Log("Loaded Player Score: " + loadedPlayer.playerScore);
}
else
{
    Debug.LogError("JSON file not found: " + filePath);
}

3. Web Request

Get:

IEnumerator GetRequest(){
        using (UnityWebRequest webRequest = UnityWebRequest.Get(baseUrl + "endpoint"))
        {
            yield return webRequest.SendWebRequest();

            if (webRequest.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError("Error: " + webRequest.error);
            }
            else
            {
                Debug.Log("GET Response: " + webRequest.downloadHandler.text);
            }
        }
}

Post:

IEnumerator PostFormDataRequest(){
        WWWForm form = new WWWForm();
        form.AddField("param1", "value1");
        form.AddField("param2", "value2");

        using (UnityWebRequest webRequest = UnityWebRequest.Post(baseUrl + "endpoint", form))
        {
            webRequest.SetRequestHeader("Authorization", "Bearer yourAccessToken");
            yield return webRequest.SendWebRequest();

            if (webRequest.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError("Error: " + webRequest.error);
            }
            else
            {
                Debug.Log("POST Form Data Response: " + webRequest.downloadHandler.text);
            }
        }
}

Post(Json):

 IEnumerator PostJsonRequest(){
        string jsonData = "{\"key1\":\"value1\",\"key2\":\"value2\"}";

        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);

        using (UnityWebRequest webRequest = new UnityWebRequest(baseUrl + "endpoint", "POST"))
        {
            webRequest.uploadHandler = new UploadHandlerRaw(bodyRaw);
            webRequest.SetRequestHeader("Content-Type", "application/json");

            // Add token header
            webRequest.SetRequestHeader("Authorization", "Bearer yourAccessToken");

            yield return webRequest.SendWebRequest();

            if (webRequest.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError("Error: " + webRequest.error);
            }
            else
            {
                Debug.Log("POST JSON Response: " + webRequest.downloadHandler.text);
            }
        }
}

使用方法:

 private const string baseUrl = "https://api.example.com/";

    private void Start()
    {
        StartCoroutine(GetRequest());
        StartCoroutine(PostFormDataRequest());
        StartCoroutine(PostJsonRequest());
    }

4. Instance Coroutine - 在不是MonoBehaviour的地方呼叫Couroutine

代碼:

using System.Collections;
using UnityEngine;

public class CoroutineManager : MonoBehaviour
{
    private static CoroutineManager instance;
    private static CoroutineManager Instance
    {
        get
        {
            if (instance == null)
            {
                GameObject coroutineManagerObject = new GameObject("CoroutineManager");
                instance = coroutineManagerObject.AddComponent<CoroutineManager>();
                DontDestroyOnLoad(coroutineManagerObject);
            }
            return instance;
        }
    }

    private void Awake()
    {
        if (instance != null && instance != this)
        {
            Destroy(gameObject);
            return;
        }
        instance = this;
        DontDestroyOnLoad(gameObject);
    }

    public static Coroutine StartStaticCoroutine(IEnumerator coroutine)
    {
        return Instance.StartCoroutine(coroutine);
    }
}

使用方法:

using System.Collections;
using UnityEngine;

public class CoroutineExample
{
    private void Initialize()
    {
        CoroutineManager.StartStaticCoroutine(MyCoroutine());
    }

    private IEnumerator MyCoroutine()
    {
        Debug.Log("Coroutine started");
        yield return new WaitForSeconds(2f);
        Debug.Log("Coroutine finished");
    }
}

5. Tab Navigator - 按下Tab後切換Input Field

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class TabNavigator : MonoBehaviour
{
    EventSystem system;

    public void Start()
    {
        system = EventSystem.current;
    }

    private void Update()
    {
        if (system.currentSelectedGameObject == null) return;
        
        var next = system.currentSelectedGameObject?.GetComponent<Selectable>().FindSelectableOnDown();
        if (!Input.GetKeyDown(KeyCode.Tab)) return;
        if (next == null) return;
            
        var inputField = next.GetComponent<InputField>();
        if (inputField != null)
            inputField.OnPointerClick(new PointerEventData(system));

        system.SetSelectedGameObject(next.gameObject, new BaseEventData(system));
    }
}

6. Singleton 單例模式用法

using UnityEngine;

public class SingletonExample : MonoBehaviour
{
    private static SingletonExample instance;

    public static SingletonExample Instance
    {
        get
        {
            if (instance == null)
            {
                instance = FindObjectOfType<SingletonExample>();

                if (instance == null)
                {
                    GameObject singletonGO = new GameObject("SingletonExample");
                    instance = singletonGO.AddComponent<SingletonExample>();
                }
            }
            return instance;
        }
    }

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    // 在這裡可以添加單例的功能代碼
}

7. 快速清除Child物件

public static void CleanChildObj(this GameObject parent)
{
  foreach (Transform child in parent.transform) {
                Object.Destroy(child.gameObject);
  }
}

8. Console To Screen

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ConsoleToScreen : MonoBehaviour
{
    const int maxLines = 50;
    const int maxLineLength = 120;
    private string _logStr = "";

    private readonly List<string> _lines = new List<string>();

    public int fontSize = 15;

    void OnEnable() { Application.logMessageReceived += Log; }
    void OnDisable() { Application.logMessageReceived -= Log; }

    public void Log(string logString, string stackTrace, LogType type)
    {
        foreach (var line in logString.Split('\n'))
        {
            if (line.Length <= maxLineLength)
            {
                _lines.Add(line);
                continue;
            }
            var lineCount = line.Length / maxLineLength + 1;
            for (int i = 0; i < lineCount; i++)
            {
                if ((i + 1) * maxLineLength <= line.Length)
                {
                    _lines.Add(line.Substring(i * maxLineLength, maxLineLength));
                }
                else
                {
                    _lines.Add(line.Substring(i * maxLineLength, line.Length - i * maxLineLength));
                }
            }
        }
        if (_lines.Count > maxLines)
        {
            _lines.RemoveRange(0, _lines.Count - maxLines);
        }
        _logStr = string.Join("\n", _lines);
    }

    void OnGUI()
    {
        GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity,
           new Vector3(Screen.width / 1200.0f, Screen.height / 800.0f, 1.0f));
        GUI.Label(new Rect(10, 10, 800, 370), _logStr, new GUIStyle() { fontSize = Math.Max(10, fontSize) });
    }
}

9. 切換主線程

using UnityEngine;
using System.Collections;
using System;
using System.Threading.Tasks;

public class UnityMainThreadDispatcher : MonoBehaviour
{
    private static readonly Queue<Action> _executionQueue = new Queue<Action>();
    private static UnityMainThreadDispatcher _instance = null;

    public static bool Exists()
    {
        return _instance != null;
    }

    public static UnityMainThreadDispatcher Instance()
    {
        if (!Exists())
        {
            CreateInstance();
        }
        return _instance;
    }

    private static void CreateInstance()
    {
        GameObject dispatcherObject = new GameObject("MainThreadDispatcher");
        _instance = dispatcherObject.AddComponent<UnityMainThreadDispatcher>();
        DontDestroyOnLoad(dispatcherObject);
    }

    public void Update()
    {
        lock (_executionQueue)
        {
            while (_executionQueue.Count > 0)
            {
                _executionQueue.Dequeue().Invoke();
            }
        }
    }

    public void Enqueue(IEnumerator action)
    {
        lock (_executionQueue)
        {
            _executionQueue.Enqueue(() =>
            {
                StartCoroutine(action);
            });
        }
    }

    public void Enqueue(Action action)
    {
        Enqueue(ActionWrapper(action));
    }

    public Task EnqueueAsync(Action action)
    {
        var tcs = new TaskCompletionSource<bool>();

        void WrappedAction()
        {
            try
            {
                action();
                tcs.TrySetResult(true);
            }
            catch (Exception ex)
            {
                tcs.TrySetException(ex);
            }
        }

        Enqueue(ActionWrapper(WrappedAction));
        return tcs.Task;
    }

    private IEnumerator ActionWrapper(Action a)
    {
        a();
        yield return null;
    }

    void Awake()
    {
        if (_instance == null)
        {
            _instance = this;
            DontDestroyOnLoad(this.gameObject);
        }
    }

    void OnDestroy()
    {
        _instance = null;
    }
}

10.跳出Window提示視窗

public static class MsgBox
{
    [DllImport("User32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
    public static extern int MessageBox(IntPtr handle, String message, String title, int type);

    public static void Pop(string title, string content)
    {
        MessageBox(IntPtr.Zero, content, title,  0);
    }

上一篇
Unity Editor - 客製化你的Unity編輯器!
下一篇
Unity Exe - 把Unity Build出來的文件壓縮成一個Exe檔案!
系列文
Unity黑科技揭秘:30個專業遊戲開發者必知的開發技巧25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言