2019年12月3日 星期二

Unity內使用JSON的方式

JSON已是經典的資料格式之一了,而以前我在物聯網公司工作時,其中一項工作就是處理那一大堆的JSON資料。

那實在是整死人,因為那時的資料架構很多是沒有固定的,也就是裡面的欄位會任意增減,所以我都用原始的方式去一一檢驗裡面的架構再取得資料。

現在就輕鬆多了,資料架構都是事先固定的,故可用JSONObject一次性搞定,這邊就來記錄一下。

這邊使用的是LitJson,其他的Json也有一樣的做法。

首先是準備資料,我準備了一個JSON字串如下:
  1. [{"name":"這是一號清單","listID":1,"listData":[{"id":1,"content":"一號內容1"},{"id":2,"content":"一號內容2"},{"id":3,"content":"一號內容3"}]},{"name":"這是二號清單","listID":2,"listData":[{"id":1,"content":"二號內容1"},{"id":2,"content":"二號內容2"},{"id":3,"content":"二號內容3"},{"id":4,"content":"二號內容4"},{"id":5,"content":"二號內容5"}]}]

使用網路上的JSON Reader可看到排版後的資料內容:

這是一個兩層Array的架構,可以有數份清單,每份清單內有數個被編號的內容。

接著當要在程式內把這字串寫出來時,需要變換符號,主要是『"』這符號必須要變更,變更方式等在網路上都找得到,這裡就不贅述。
  1. [{\"name\":\"這是一號清單\", \"listID\":1, \"listData\":[{\"id\":1, \"content\":\"一號內容1\"},{\"id\":2,\"content\":\"一號內容2\"},{\"id\":3,\"content\":\"一號內容3\"}]},{\"name\":\"這是二號清單\",\"listID\":2,\"listData\":[{\"id\":1,\"content\":\"二號內容1\"},{\"id\":2,\"content\":\"二號內容2\"},{\"id\":3,\"content\":\"二號內容3\"},{\"id\":4,\"content\":\"二號內容4\"},{\"id\":5,\"content\":\"二號內容5\"}]}]

然後就來正式寫程式吧:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using LitJson;
  5.  
  6. public class DataController : MonoBehaviour
  7. {
  8. public class DataClass
  9. {
  10. public string name;
  11. public int listID;
  12. public ListDataClass[] listData;
  13. }
  14.  
  15. public class ListDataClass
  16. {
  17. public int id;
  18. public string content;
  19. }
  20.  
  21. private string jsonContent = "[{\"name\":\"這是一號清單\", \"listID\":1, \"listData\":[{\"id\":1, \"content\":\"一號內容1\"},{\"id\":2,\"content\":\"一號內容2\"},{\"id\":3,\"content\":\"一號內容3\"}]},{\"name\":\"這是二號清單\",\"listID\":2,\"listData\":[{\"id\":1,\"content\":\"二號內容1\"},{\"id\":2,\"content\":\"二號內容2\"},{\"id\":3,\"content\":\"二號內容3\"},{\"id\":4,\"content\":\"二號內容4\"},{\"id\":5,\"content\":\"二號內容5\"}]}]";
  22. private JsonData jsonData;
  23. private List dataClassList = new List();
  24. private DataClass dataClass = new DataClass();
  25. void Start()
  26. {
  27. jsonData = JsonMapper.ToObject(jsonContent);
  28. Debug.Log("這是直接版的【Name】:" + jsonData[0]["name"]);
  29. Debug.Log("這是直接版的【一號清單內的第3項之Content】:" + jsonData[0]["listData"][2]["content"]);
  30. Debug.Log("------------------------------------------------------------------");
  31. dataClassList = JsonMapper.ToObject>(jsonContent);
  32. for (int i = 0; i < dataClassList.Count; i++)
  33. {
  34. dataClass = dataClassList[i];
  35. //Debug.Log("【" + i + "=Name" + "】:" + dataClass.name);
  36. //Debug.Log("【" + i + "=List ID" + "】:" + dataClass.listID);
  37. Debug.Log(i);
  38. Debug.Log(" name:" + dataClass.name);
  39. Debug.Log(" listID:" + dataClass.listID);
  40. Debug.Log(" listData");
  41. for (int j = 0; j < dataClass.listData.Length; j++)
  42. {
  43. //Debug.Log("【" + i + "-" + j + "=ID" + "】:" + dataClass.listData[j].id);
  44. //Debug.Log("【" + i + "-" + j + "=Content" + "】:" + dataClass.listData[j].content);
  45. Debug.Log(" " + j);
  46. Debug.Log(" id:" + dataClass.listData[j].id);
  47. Debug.Log(" content:" + dataClass.listData[j].content);
  48. }
  49. }
  50. }
  51. void Update()
  52. {
  53. if (Input.GetKeyDown(KeyCode.Space))
  54. {
  55. dataClassList[0].name = "重新取名字";
  56. jsonContent = JsonMapper.ToJson(dataClassList);
  57. }
  58. }
  59. }

第8行和第15行的兩個Class,是依照JSON的兩層Array架構去宣告的,Class名稱可以任意,但是裡面的變數名稱變數類型一定要完全和JSON的資料一樣。

第28行到第30行是展示用原本的原始方式來取資料。

第33行開始是把JSON字串轉成JSONObject,並套到之前宣告的資料模型內,這樣便可以隨心所欲地提取資料了。

第61行是展示用這種資料模型方式來儲存修改的資料,之後便可依照各自需求把修改後的JSON字串發出去。

程式裡我有弄兩種方式顯示資料,這邊只顯示其中一種,執行程式後在Unity的Console可以看得到:

在使用JSON時最麻煩的是資料被包了又包、包了又包,動輒就好幾層;因此使用這種資料模型的方式時也得去宣告相對應的Class,反而實作只需一行便可取得資料了。

2019年12月2日 星期一

Unity內C#的Event使用方式

C語言有很多實作功能我在過往寫專案時都不會使用到,就像你有一台手機但大部分的附屬功能平常不需要用到一樣,因此自然就沒也特別去注意。但以後也許會有需要用到,還是先研究一下做個記錄比較好。

會用Event的情況通常是我在和其他SDK等做整合時,都已經有那些SDK準備好的Event可直接註冊使用,因此不需要了解那些Event是怎麼生成的;而這邊記錄的是完全無中生有,包含Event的生成、註冊和使用,這樣以後就可以在自己的程式自己來了。

以下是簡單的創造一個Event並來使用:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System;
  5.  
  6. public class ZZZ : MonoBehaviour
  7. {
  8. class TypeEventArgs : EventArgs
  9. {
  10. public float x = 0;
  11. public float y = 0;
  12. }
  13.  
  14. private event EventHandler eventHandler;
  15. private TypeEventArgs tempEventArgs = new TypeEventArgs();
  16.  
  17. void Start()
  18. {
  19. this.eventHandler += ReceiveData;
  20. }
  21.  
  22. void Update()
  23. {
  24. tempEventArgs.x += Time.deltaTime;
  25. tempEventArgs.y += 2 * Time.deltaTime;
  26.  
  27. eventHandler(this, tempEventArgs);
  28. }
  29.  
  30. public void ReceiveData(System.Object sender, EventArgs e)
  31. {
  32. Debug.Log(Mathf.FloorToInt((e as TypeEventArgs).x) + " = " + Mathf.FloorToInt((e as TypeEventArgs).y));
  33. }
  34. }

我做了一個Event,然後觸發此Event的情況是每個Frame時,會顯示一個累加時間和其兩倍的時間值。因此觸發條件便可因應各種需要,例如點擊一個按鈕時、用USB連接的Arduino傳過來資料時、或是有預料之中的錯誤發生時等。就可用Event的形式來加以對應。