[Unity learning] UI playback system based on JSON (1)

[Unity learning] UI playback system based on JSON (1)

foreword

In view of the fact that the game playback plug-ins (EZ Replay Manager, Ultimate Replay 2.0) in the Unity mall are not easy to realize the playback operation of the UI, I store the UI text information based on the JSON file format, use ScriptableObject to load the UI data, and perform UI playback. Playback is enough for industrial and military simulations to meet its requirements. If there are other implementation methods, be sure to ask the almighty netizen to tell me. I only record my implementation method here.

demo video

Unity UI playback system

Implementation

1. Create a ScriptableObject script:

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

[CreateAssetMenu(fileName = "ReplayUI", menuName = "MyScriptObject/ReplayUI_SO")]
public class ReplayUI_SO : ScriptableObject
{
    
    
    public List<ReplayTask> replayTaskTexts = new List<ReplayTask>();
}

[System.Serializable]
public class ReplayTask
{
    
    
    public string taskName;
    public List<ReplayRealtimeText> replayRealtimeTexts = new List<ReplayRealtimeText>();

    public ReplayTask(string taskName, List<ReplayRealtimeText> replayRealtimeTexts)
    {
    
    
        this.taskName = taskName;
        this.replayRealtimeTexts = replayRealtimeTexts;
    }
}


[System.Serializable]
public class ReplayRealtimeText
{
    
    
    public float time;
    public string contentText;

    public ReplayRealtimeText(float time, string contentText)
    {
    
    
        this.time = time;
        this.contentText = contentText;
    }
}

2. Bind each UI content that needs to be played back to a script, and the script property is bound to the component content that needs to be played back. (The following script refers to my previous blog )

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

public class TaskRealTimeInfo : MonoBehaviour
{
    
    
    public Text textPrefab;
    public RectTransform contentRoot;
    public MScrollRect scrollRect;
    public InputField taskName;
    public List<ReplayRealtimeText> replayRealtimeTexts;
    private float interval = 1.5f;
    private float time = 0, recordTime;
    private string testText;

    private void Update()
    {
    
    
        if (ReplayUIManager.Instance.isRecord)
        {
    
    
            time += Time.deltaTime;
            if (time >= interval)
            {
    
    
                testText = Time.timeSinceLevelLoad.ToString();
                AddText(testText);
                time = 0;
            }
        }
        else if (!ReplayUIManager.Instance.isRecord)
        {
    
    
            recordTime = Time.timeSinceLevelLoad;
        }
    }

    public void AddText(string str)
    {
    
    
        UIHelper.AddScrollText(textPrefab, contentRoot, str, scrollRect);

        if (ReplayUIManager.Instance.isRecord)
        {
    
    
            replayRealtimeTexts.Add(new ReplayRealtimeText(Time.timeSinceLevelLoad-recordTime, str));
        }
    }
}

3. Create UI playback management class:

using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class ReplayUIManager : Singleton<ReplayUIManager>
{
    
    
    public TaskRealTimeInfo taskRealTimeInfo;
    public Dropdown replayTaskNameDrop;
    public string replayTaskName;
    public bool isRecord = false;
    public bool isReplay = false;
    private List<ReplayRealtimeText> replayRealtimeTexts;
    private ReplayUI_SO replayUI_SO;
    private ReplayRealtimeText tempReplayRealtimeText;
    private ReplayTask replayTask;
    private float dataTime, sceneTime;
    private float replayStartTime = 0f;
    private bool isGetReplayData = false;
    public int dataCount = 0;
    private Dropdown.OptionData optionData;

    protected override void Awake()
    {
    
    
        base.Awake();
        replayUI_SO = Resources.Load<ReplayUI_SO>("Replay/ReplayUI");
        LoadTaskUIData();
        replayTaskNameDrop.ClearOptions();
        foreach (var data in replayUI_SO.replayTaskTexts)
        {
    
    
            optionData = new Dropdown.OptionData(data.taskName);
            replayTaskNameDrop.options.Add(optionData);
        }
    }

    private void Update()
    {
    
    
        if (isReplay)
        {
    
    
            //开始回放UI
            if (isGetReplayData)
                ReplayRealtimeText();
            //回放结束
            if (dataCount <= 0)
            {
    
    
                replayRealtimeTexts = null;
                isReplay = false;
                isGetReplayData = false;
                Debug.Log("回放结束!");
            }
        }
    }

    private void ClearHistoryData()
    {
    
    
        //清除原有数据
        GameObject obj;
        for (int i = 0; i < taskRealTimeInfo.contentRoot.childCount; i++)
        {
    
    
            obj = taskRealTimeInfo.contentRoot.GetChild(i).gameObject;
            Destroy(obj);
        }
        replayTask = null;
        taskRealTimeInfo.replayRealtimeTexts=new List<ReplayRealtimeText>();
    }

    public void RecordStart()
    {
    
    
        ClearHistoryData();
        foreach (var data in replayUI_SO.replayTaskTexts)
        {
    
    
            if (data.taskName.Equals(taskRealTimeInfo.taskName.text))
            {
    
    
                Debug.LogError("任务名称重复,不能重复录制同一任务!");
                return;
            }
        }
        if (!string.IsNullOrEmpty(taskRealTimeInfo.taskName.text))
        {
    
    
            isRecord = true;
            Debug.Log("开始录制!");
        }
        else
        {
    
    
            Debug.LogWarning("未命名任务名称!");
        }
    }

    public void RecordEnd()
    {
    
    
        foreach (var data in replayUI_SO.replayTaskTexts)
        {
    
    
            if (data.taskName.Equals(taskRealTimeInfo.taskName.text))
            {
    
    
                Debug.LogError("任务名称重复,不能重复录制同一任务!");
                return;
            }
        }
        if (!string.IsNullOrEmpty(taskRealTimeInfo.taskName.text))
        {
    
    
            isRecord = false;
            if (isRecord)
            {
    
    
                return;
            }
            replayTask = new ReplayTask(taskRealTimeInfo.taskName.text, taskRealTimeInfo.replayRealtimeTexts);
            replayUI_SO.replayTaskTexts.Add(replayTask);
            SaveTaskUIData();

            replayTaskNameDrop.ClearOptions();
            foreach (var data in replayUI_SO.replayTaskTexts)
            {
    
    
                optionData = new Dropdown.OptionData(data.taskName);
                replayTaskNameDrop.options.Add(optionData);
            }

            Debug.Log("录制结束!");
        }
        else
        {
    
    
            Debug.LogWarning("未命名任务名称!");
        }
    }

    public void ReplayStart()
    {
    
    
        StartCoroutine(Replay());
    }

    IEnumerator Replay()
    {
    
    
        ClearHistoryData();
        if (replayTaskNameDrop.options.Count == 0)
            yield break;
        replayTaskName = replayTaskNameDrop.options[replayTaskNameDrop.value].text;
        yield return new WaitForSeconds(0.1f);
        isReplay = true;
        //根据任务名称获取回放数据
        foreach (var data in replayUI_SO.replayTaskTexts)
        {
    
    
            if (data.taskName.Equals(replayTaskName))
            {
    
    
                replayRealtimeTexts = data.replayRealtimeTexts;
                dataCount = replayRealtimeTexts.Count;
                isGetReplayData = true;
                //初始化回放起始时间
                replayStartTime = Time.timeSinceLevelLoad;
                break;
            }
        }
       
        Debug.Log("开始回放!");
    }

    private void ReplayRealtimeText()
    {
    
    
        foreach (var data in replayRealtimeTexts)
        {
    
    
            if (data.Equals(tempReplayRealtimeText))
                continue;
            dataTime = (int)(data.time * 100) * 0.01f;
            sceneTime = (int)((Time.timeSinceLevelLoad - replayStartTime) * 100) * 0.01f;
            if (dataTime == sceneTime)
            {
    
    
                taskRealTimeInfo.AddText(data.contentText);
                tempReplayRealtimeText = data;
                dataCount--;
                break;
            }
        }
    }

    private void SaveTaskUIData()
    {
    
    
        Save(replayUI_SO, @Application.streamingAssetsPath + "/Replay/ReplayUIData.json");
    }

    private void Save(object data, string url)
    {
    
    
        var jsonData = JsonUtility.ToJson(data, true);
        StreamWriter streamWriter = null;
        try
        {
    
    
            if (!File.Exists(url))
            {
    
    
                File.Create(url).Dispose();
            }
            if (File.Exists(url))
            {
    
    
                streamWriter = new StreamWriter(url);
                streamWriter.WriteLine(jsonData);
                streamWriter.Close();
                streamWriter.Dispose();
            }
        }
        catch (System.Exception e)
        {
    
    
            Debug.LogError(e);
        }
    }

    public async void LoadTaskUIData()
    {
    
    
        await Load(replayUI_SO, @Application.streamingAssetsPath + "/Replay/ReplayUIData.json");
    }

    public async Task Load(Object data, string url)
    {
    
    
        if (!File.Exists(url))
        {
    
    
            File.Create(url).Dispose();
        }
        Encoding encoding = Encoding.GetEncoding("utf-8");
        StreamReader streamReader = new StreamReader(url, encoding);
        string str = await streamReader.ReadToEndAsync();
        JsonUtility.FromJsonOverwrite(str, data);
        streamReader.Close();
        streamReader.Dispose();
    }
}

4. The singleton parent class used in the previous step is as follows:

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

public class Singleton<T> : MonoBehaviour where T:Singleton<T>
{
    
    
    private static T instance;
    public static T Instance
    {
    
    
        get {
    
     return instance; }
    }

    protected virtual void Awake()
    {
    
    
        if (instance != null)
        {
    
    
            Destroy(gameObject);
        }
        else
        {
    
    
            instance = (T)this;
        }
    }

    public static bool IsInitialized
    {
    
    
        get {
    
     return instance != null; }
    }

    protected virtual void OnDestroy()
    {
    
    
        if (instance == this)
        {
    
    
            instance = null;
        }
    }
}

Engineering download

Unity UI playback system Demo
The next article JSON-based UI playback system (double speed) (2) will combine with the Ultimate Replay 2.0 plug-in I use to introduce how to perform double-speed playback.

Guess you like

Origin blog.csdn.net/dong89801033/article/details/127705625