找回密碼
 注冊帳號

掃一掃,訪問微社區

zhang273162308 Unity實用小工具或腳本—利用反射制作動態編輯欄(四

53
回復
2386
查看
打印 上一主題 下一主題
[ 復制鏈接 ]
排名
141
昨日變化

123

主題

591

帖子

6983

積分

Rank: 9Rank: 9Rank: 9

UID
3579
好友
109
蠻牛幣
5217
威望
0
注冊時間
2013-9-10
在線時間
1536 小時
最后登錄
2019-8-10

專欄作家活力之星游戲蠻牛QQ群會員蠻牛哥

馬上注冊,結交更多好友,享用更多功能,讓你輕松玩轉社區。

您需要 登錄 才可以下載或查看,沒有帳號?注冊帳號

x
本帖最后由 zhang273162308 于 2019-5-13 17:41 編輯

一、前言
    在上一篇中,已經可以處理基本的數據類型的動態編輯欄創建并且保存數據的功能。但是,對于自定義的類型,比如Unity自帶的Vector類型,還不能處理。另外,編輯欄都是單一的輸入框,針對不同的數據創建不同的編輯框,如Bool類型,應該是Toggle類型。本篇主要是對這兩個功能的實現。效果圖如圖所示:點擊創建會動態生成結構體里所有的編輯欄,相比之前的只有輸入框的編輯欄,這里增加了對Bool、Vector3變量類型的處理,Bool類型的會生成Toggle的,Vector3會生成帶X、Y和Z的三個短輸入框組合。并且,再Vector3的三個輸入框中輸入字符會自動刪除,只能輸入數字。再點擊保存的時候會將編輯欄中的數據保存到結構體中,右下角的GlobalCtr中的TestData結構體數據會響應編輯欄的變化。

二、實現
1、動態編輯欄的拓展,在原有的基礎上,我們將對原有的動態編輯欄預設新增Toggle、DropdownList和Vector3組合框,如下圖所示:每個編輯框都默認是失活的,針對不同的變量類型,顯示不同的輸入框。

動態編輯欄的初始化函數修改如下:通過Switch里對不同變量的類型進行不同的處理,注意Vector3是Unity自帶的類型,它的完整類型名稱為:“UnityEngine.Vector3”。針對Vector3這樣的自定義類型,需要額外的腳本對其進行處理,控制這個組合編輯框
[AppleScript] 純文本查看 復制代碼
public void Init(Type variableType, string name,string value,string[] enumValue=null,Type strcutType=null,bool isInteractable=true)
    {
        valueInputContent = GetComponentInChildren<InputField>(true);
        valueDropdown = GetComponentInChildren<Dropdown>(true);
        valueToggle = GetComponentInChildren<Toggle>(true);
        valueVectorInput = GetComponentInChildren<UI2D_SubObjEAVectorInput>(true);

        valueDropdown.interactable = isInteractable;
        valueToggle.interactable = isInteractable;
        valueInputContent.interactable = isInteractable;

        varibleName = name;
        textName.text = name;
      //  Debug.Log(variableType.FullName+"v:"+value+"stype:"+strcutType.ToString());
        switch (variableType.FullName)
        {
            case "System.String":
                valueInputContent.text = value;
                curValue = value;
                lastValue = curValue;
                valueInputContent.gameObject.SetActive(true);
                break;
            case "System.Int32":
                if (null == enumValue)
                {
                    valueInputContent.text = value;
                    valueInputContent.gameObject.SetActive(true);
                    valueInputContent.contentType = InputField.ContentType.IntegerNumber;
                }
                else
                {
                    valueDropdown.gameObject.SetActive(true);
                    List<string> tempStrList = new List<string>();
                    for (int i = 0; i < enumValue.Length; i++)
                    {
                        tempStrList.Add(enumValue);
                    }
                    valueDropdown.options.Clear();
                    valueDropdown.AddOptions(tempStrList);
                    valueDropdown.value = int.Parse(value);
                }
                break;
            case "System.Boolean":
                valueToggle.gameObject.SetActive(true);
                bool tempIsOn;
                bool.TryParse(value, out tempIsOn);
                valueToggle.isOn = tempIsOn;
                break;
            case "System.Single":
                valueInputContent.text = value;
                valueInputContent.gameObject.SetActive(true);
                valueInputContent.contentType = InputField.ContentType.DecimalNumber;
                break;

            case "UnityEngine.Vector3":
                valueVectorInput.Init(value);
                valueVectorInput.gameObject.SetActive(true);
                break;
        }
        isInitSucced = true;

    }

Vector3的編輯框控制腳本為:在初始化獲取值的時候需要進行額外的處理,Vector3實例轉換成字符類型一般為“(xx,xx,xx)"。不同的字段類型會決定輸入框的輸入字符的類型,上述代碼中比如”System.Int32“類型的變量,其輸入框的設置為”valueInputContent.contentType = InputField.ContentType.IntegerNumber“,這個設置保證了輸入框里只能輸入整數數字,并且是可以為負數的,而編號之類的輸入框一般都是不為負數的更符合要求,這是Unity自帶的功能,不能改變,其實還是有不足之處。

可以通過打印Vector.ToString()來看看。這里其實也可以將object類型作為參數,主要是我不想修改”UI2D_SubObjEditorAttr“腳本里的初始化參數了,比如你可以將Init函數定義成 ,將所有的參數的值都轉換成基類object

[AppleScript] 純文本查看 復制代碼
public void Init(Type variableType, string name,object value,string[] enumValue=null,Type strcutType=null,bool isInteractable=true)

{

...

}

2、動態生成
還需要用到之前定義的方法“GetVaule_ReflectMethod”,該方法處理了結構體的泛型和非泛型的字段或屬性,并且將得到的字段或屬性的
[AppleScript] 純文本查看 復制代碼
 /// <summary>
    /// 獲取結構體非泛型的所有屬性和字段,并返回由所有屬性的名字和值組成的列表,Out 參數返回結構體或類中的泛型屬性或字段
    /// (這里只處理類或結構體只有一個List這樣的泛型字段或屬性)的整個列表
    /// </summary>
    /// <typeparam name="S">結構體類型</typeparam>
    /// <typeparam name="L">結構體屬性或字段列表中裝載的數據類型</typeparam>
    /// <param name="obj">結構體實例</param>
    /// <param name="listGenericDatas">結構體屬性或字段列表</param>
    /// <returns></returns>
    public static List<NP_SingleReflectInfo> GetVaule_ReflectMethod<S, L>(S obj, out List<L> listGenericDatas)
    {
        List<NP_SingleReflectInfo> tempList = new List<NP_SingleReflectInfo>();
        listGenericDatas = new List<L>();
        try
        {
            //遍歷所有的屬性
            PropertyInfo[] tempPI = obj.GetType().GetProperties();
            foreach (var info in tempPI)
            {
                //裝載泛型屬性
                if (info.PropertyType.IsGenericType)
                {
                    object tempListObj = info.GetValue(obj, null);
                    listGenericDatas = (List<L>)tempListObj;
                }
                else
                {
                    string tempVarName = info.Name;
                    string tempVarVaule = info.GetValue(obj, null).ToString();
                    //    Debug.Log(info.PropertyType);
                    NP_SingleReflectInfo tempData = new NP_SingleReflectInfo();
                    tempData.Init(info.PropertyType, tempVarName, tempVarVaule);
                    //裝載信息到列表中
                    tempList.Add(tempData);
                }
            }
            //遍歷所有的字段
            FieldInfo[] tempFI = obj.GetType().GetFields();
            foreach (var itemInfo in tempFI)
            {
                //裝載泛型字段
                if (itemInfo.FieldType.IsGenericType)
                {
                    object tempListObj = itemInfo.GetValue(obj);
                    listGenericDatas = (List<L>)tempListObj;
                }
                else
                {
                    string tempVarName = itemInfo.Name;
                    string tempVarVaule = itemInfo.GetValue(obj).ToString();
                    //  Debug.Log(itemInfo.FieldType);
                    NP_SingleReflectInfo tempData = new NP_SingleReflectInfo();
                    tempData.Init(itemInfo.FieldType, tempVarName, tempVarVaule);
                    //裝載信息到列表中
                    tempList.Add(tempData);
                }
            }
        }
        catch (Exception e)
        {
            Debug.Log("獲取類型數據錯誤" + e.Message);
            return null;
        }

        return tempList;
    }




名字和值都以String類型返回。結構體“NP_SingleReflectInfo”是用來封裝字段或屬性的名字、值和類型的,其定義為:


[AppleScript] 純文本查看 復制代碼
/// <summary>
/// 反射獲得結構體、類中的單個變量或屬性的名字和值
/// </summary>
[Serializable]
public struct NP_SingleReflectInfo
{
    /// <summary>
    /// 變量的類型
    /// </summary>
    public Type VariableType;
    /// <summary>
    /// 變量的名字
    /// </summary>
    public string VariableName;
    /// <summary>
    /// 變量的值
    /// </summary>
    public string VariableValue;
    public void Init(Type type, string name, string value)
    {
        VariableType = type;
        VariableName = name;
        VariableValue = value;
    }
}



定義的結構體“TestData"為:

[AppleScript] 純文本查看 復制代碼
[Serializable]
public struct TestData
{
    public float Number;
    public int Type;
    public string Name;
    public bool IsOPen;
    public Vector3 Pos;
    public void Init(float no,int type,string name,bool isOpen,Vector3 pos)
    {
        Number = no;
        Type = type;
        Name = name;
        IsOPen = isOpen;
        Pos = pos;
    }
}


創建按鈕的點擊事件方法為:該方法處理的點擊創建按鈕后,動態的生成結構體TestData變量testdata里的所有字段的編輯框

[AppleScript] 純文本查看 復制代碼
    public void BtnCreate_OnClick()
    {
        List<NP_SingleReflectInfo> tempListReflectInfo = null;
        List<TestData> tempListGeneData;
        //反射創建編輯條信息
        tempListReflectInfo = GetVaule_ReflectMethod(testData, out tempListGeneData);
        if (null != tempListReflectInfo)
        {
            string[] tempStrDropdwonList = null;
            for (int i = 0; i < tempListReflectInfo.Count; i++)
            {
                bool isInteractable = true;
                UI2D_SubObjEditorAttr tempSubObjEA = Instantiate(prefabSubObjEA, subObjEAParent);
                tempSubObjEA.transform.localScale = Vector3.one;
                tempSubObjEA.Init(tempListReflectInfo.VariableType, tempListReflectInfo.VariableName, tempListReflectInfo.VariableValue, tempStrDropdwonList, testData.GetType(), isInteractable);
                listSubObjEA.Add(tempSubObjEA);
            }
        }
    }


3、反射賦值
在動態的創建了結構體的字段編輯框之后,修改編輯框里面的任何值,點擊保存就可以將修改后的值動態的賦值給生成動態編輯欄的變量”testData",保存按鈕的代碼為:


[AppleScript] 純文本查看 復制代碼
    public void BtnSave_OnClick()
    {
        for (int i = 0; i < listSubObjEA.Count; i++)
        {
            testData = (TestData)SetValue_ReflectMethod(testData, listSubObjEA.M_VaribleName, listSubObjEA.M_Vaule);
        }
    }




以及用到的反射賦值的方法“SetValue_ReflectMethod”為:這個方法對比之前的稍微進行了一些修改,將“paramName”的參數類型由


[AppleScript] 純文本查看 復制代碼
    /// <summary>
    /// 使用反射動態設置結構體的變量值
    /// </summary>
    /// <typeparam name="T">結構體類型</typeparam>
    /// <param name="obj">結構體實例</param>
    /// <param name="paramName">變量的名字</param>
    /// <param name="paramValue">變量的值</param>
    /// <returns></returns>
    public static object SetValue_ReflectMethod<T>(T obj, object paramName, object paramValue)
    {
        //先裝箱 變成引用類型的
        object tempObj = obj;
        if (obj != null)
        {
            try
            {
                Type tempType = obj.GetType();
                //設置字段
                FieldInfo tempFI = tempType.GetField(paramName.ToString());
                if (null != tempFI)
                {
                     object tempObjValue = Convert.ChangeType(paramValue, tempFI.FieldType);
                    tempFI.SetValue(tempObj, tempObjValue);
                }
                //設置屬性
                PropertyInfo tempPI = tempType.GetProperty(paramName.ToString());
                if (null != tempPI)
                {
                    tempPI.SetValue(tempObj, Convert.ChangeType(paramValue, tempPI.PropertyType), null);
                }
            }
            catch (Exception e)
            {
                Debug.Log("編輯錯誤" + e.Message);
                tempObj = null;
            }
        }
        return tempObj;
    }


原來的“String”類型改為基類”object“類型,因為在將Vector3這樣自定義的類型,而非系統基礎類型的變量轉換的時候不能將字符串進行轉換,在” object tempObjValue = Convert.ChangeType(paramValue, tempFI.FieldType);“的時候報錯,” Convert.ChangeType()”方法里在處理非基礎類型的時候,參數必須是:第一個是該變量值轉換成Object類型,第二個是變量的類型。

三、總結
1、處理了不同類型輸入框的不同,并且輸入框內容的控制
2、處理了自定義類型的輸入框動態生成,并且將其生成的輸入框內容反射賦值給原結構體實例
3、不足之處,還需要拓展針對更多的數據類型或自定義組合類型
4、不能動態的進行界面排布,生成的都是依次往下排
5、完整的工程下載地址
游客,如果您要查看本帖隱藏內容請回復

點評

qiu
報了個錯啊The namespace `global::' already contains a definition for `NP_SingleReflectInfo'  發表于 2019-5-29 15:15
怎么沒看到下載地址?  發表于 2019-5-10 09:32
回復

使用道具 舉報

7日久生情
2068/5000
排名
4092
昨日變化

0

主題

1350

帖子

2068

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
254705
好友
1
蠻牛幣
1883
威望
0
注冊時間
2017-11-16
在線時間
356 小時
最后登錄
2019-8-12
沙發
2019-5-10 08:03:16 只看該作者
666666666666666666666666666666
回復 支持 反對

使用道具 舉報

7日久生情
1860/5000
排名
1989
昨日變化

6

主題

563

帖子

1860

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
54335
好友
3
蠻牛幣
5433
威望
0
注冊時間
2014-11-9
在線時間
609 小時
最后登錄
2019-8-10
板凳
2019-5-10 09:00:21 只看該作者
回復

使用道具 舉報

7日久生情
1860/5000
排名
1989
昨日變化

6

主題

563

帖子

1860

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
54335
好友
3
蠻牛幣
5433
威望
0
注冊時間
2014-11-9
在線時間
609 小時
最后登錄
2019-8-10
地板
2019-5-10 09:02:19 只看該作者
多謝樓主分享
回復

使用道具 舉報

7日久生情
1711/5000
排名
832
昨日變化

2

主題

153

帖子

1711

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
5714
好友
0
蠻牛幣
3284
威望
0
注冊時間
2013-10-15
在線時間
374 小時
最后登錄
2019-8-12
5#
2019-5-10 09:02:47 只看該作者
關注中。。。
回復

使用道具 舉報

7日久生情
1860/5000
排名
1989
昨日變化

6

主題

563

帖子

1860

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
54335
好友
3
蠻牛幣
5433
威望
0
注冊時間
2014-11-9
在線時間
609 小時
最后登錄
2019-8-10
6#
2019-5-10 09:03:43 只看該作者
樓主回復了隱藏內容還是沒開放
回復 支持 反對

使用道具 舉報

4四處流浪
328/500
排名
11372
昨日變化

0

主題

42

帖子

328

積分

Rank: 4

UID
301677
好友
0
蠻牛幣
290
威望
0
注冊時間
2018-10-29
在線時間
198 小時
最后登錄
2019-7-15
7#
2019-5-10 09:08:40 只看該作者

多謝樓主分享
回復

使用道具 舉報

7日久生情
3822/5000
排名
1487
昨日變化

0

主題

2144

帖子

3822

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
219676
好友
1
蠻牛幣
3905
威望
0
注冊時間
2017-7-12
在線時間
824 小時
最后登錄
2019-8-12

活力之星

8#
2019-5-10 09:09:00 只看該作者
謝謝分享
回復

使用道具 舉報

6蠻牛粉絲
1159/1500
排名
3542
昨日變化

0

主題

319

帖子

1159

積分

Rank: 6Rank: 6Rank: 6

UID
180321
好友
8
蠻牛幣
887
威望
0
注冊時間
2016-11-21
在線時間
418 小時
最后登錄
2019-8-7
9#
2019-5-10 09:16:43 只看該作者
大佬6的很
回復

使用道具 舉報

排名
20607
昨日變化

0

主題

15

帖子

90

積分

Rank: 2Rank: 2

UID
218216
好友
0
蠻牛幣
71
威望
0
注冊時間
2017-4-18
在線時間
49 小時
最后登錄
2019-7-29
10#
2019-5-10 09:31:24 只看該作者
666666666666666666666666666666
回復 支持 反對

使用道具 舉報

6蠻牛粉絲
1374/1500
排名
2010
昨日變化

21

主題

224

帖子

1374

積分

Rank: 6Rank: 6Rank: 6

UID
44764
好友
0
蠻牛幣
553
威望
0
注冊時間
2014-9-13
在線時間
451 小時
最后登錄
2019-8-12
11#
2019-5-10 10:02:45 只看該作者
6666666666666666666666666666
回復 支持 反對

使用道具 舉報

2初來乍到
101/150
排名
31432
昨日變化

0

主題

54

帖子

101

積分

Rank: 2Rank: 2

UID
160425
好友
0
蠻牛幣
9
威望
0
注冊時間
2016-8-3
在線時間
37 小時
最后登錄
2019-7-30
12#
2019-5-10 10:10:29 只看該作者
Unity實用小工具或腳本—利用反射制作動態編輯欄
回復 支持 反對

使用道具 舉報

5熟悉之中
719/1000
排名
6127
昨日變化

0

主題

86

帖子

719

積分

Rank: 5Rank: 5

UID
24766
好友
0
蠻牛幣
149
威望
0
注冊時間
2014-5-12
在線時間
409 小時
最后登錄
2019-8-12
13#
2019-5-10 10:15:16 只看該作者
多謝樓主分享
回復

使用道具 舉報

5熟悉之中
719/1000
排名
6127
昨日變化

0

主題

86

帖子

719

積分

Rank: 5Rank: 5

UID
24766
好友
0
蠻牛幣
149
威望
0
注冊時間
2014-5-12
在線時間
409 小時
最后登錄
2019-8-12
14#
2019-5-10 10:17:49 只看該作者

多謝樓主分享
回復

使用道具 舉報

3偶爾光臨
195/300
排名
14744
昨日變化

0

主題

73

帖子

195

積分

Rank: 3Rank: 3Rank: 3

UID
236535
好友
0
蠻牛幣
258
威望
0
注冊時間
2017-8-8
在線時間
68 小時
最后登錄
2019-8-10
15#
2019-5-10 10:22:20 只看該作者
多謝樓主分享
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 注冊帳號

本版積分規則

女校游泳队彩金