關於 web service, unity, blogger 等軟體工程筆記

C# Array operations in Unity

Edit icon 沒有留言
C# Array operations in Unity

Array

整理一下 C# Array(陣列)的筆記,紀錄對陣列常用的操作。

結論:若需要使用程式對 Array 進行大量的資料操作,請改用 List。

範例都以以下程式碼為基礎,在 Start 撰寫以下的展示程式碼。請注意引用到 UnityEditor,請留意發佈的編譯(Compile)問題。

using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.Linq;

public class Example : MonoBehaviour
{
[SerializeField]
string[] names = new string[0];
}

有一個字串的資料陣列,SerializeField,設計者可以透過 UnityEditor 介面編輯,輸入初始資料。(進階閱讀:不同的陣列編輯器

UnityEditor StringArray 編輯畫面

預設資料

可以改用以下宣告,設定預設值:

[SerializeField]
string[] names = new string[] { "Darjeeling", "Assam", "Orange Pekoe", "Rose Hip" };

或者是使用 Reset,一個一個元素(Array element)設定初始值:

void Reset()
{
names = new string[4];
names[0] = "Darjeeling";
names[1] = "Assam";
names[2] = "Orange Pekoe";
names[3] = "Rose Hip";
}

列舉元素

使用 index 列舉:

for (var i = 0; i < names.Length; i++)
{
Debug.Log(names[i]);
}

使用 C# foreach 列舉(個人傾向使用):

foreach (var name in names)
{
Debug.Log(name);
}

兩段程式碼的其輸出結果一樣:

Darjeeling
Assam
Orange Pekoe
Rose Hip

新增元素

這很為難,陣列是固定長度的資料容器(Data container),沒有提供類似的函數,需要迂迴來達成該功能。

原生方式,使用 System.Array.Resize 來增加一個長度,然後塞值。

Array.Resize(ref names, names.Length + 1);
names[names.Length - 1] = "Rukuriri";

透過暫存的容器,使用 System.Collections.Generic.List 這個常用且強大的容器來增加,最後輸出新增元素後的陣列。

var temp = new List<string>(names);
temp.Add("Rukuriri");
names = temp.ToArray();

Unity 專屬,引擎提供的 UnityEditor.ArrayUtility,包含一些對於陣列的操作。使用 ILSpy 去看實做方式,也使用暫存的容器 List 來達成。

ArrayUtility.Add(ref names, "Rukuriri");

新增一堆元素

假設要新增多個元素:

var newNames = new string[] { "Rukuriri", "St Gloriana" };

可以使用暫存的容器 List 來達成。

var temp = new List<string>(names);
temp.AddRange(newNames);
names = temp.ToArray();

或是 UnityEditor 的擴充:

ArrayUtility.AddRange(ref names, newNames);

移除指定位置的元素

使用 List 來達成:

var temp = new List<string>(names);
temp.RemoveAt(2);
names = temp.ToArray();

UnityEditor 的擴充:

ArrayUtility.RemoveAt(ref names, 2);

移除某個元素

使用 List 來達成:

var temp = new List<string>(names);
temp.Remove("Assam");
names = temp.ToArray();

UnityEditor 的擴充:

ArrayUtility.Remove(ref names, "Assam");

搜尋某個元素

搜尋是否有存在指定資料的元素,例如 Darjeeling 是否存在:

var exist = Array.IndexOf(names, "Darjeeling") >= 0;
var exist = Array.LastIndexOf(names, "Darjeeling") >= 0;

差異在搜尋順序,一個從 [0] 開始搜尋,另一個從陣列尾開始搜尋。

搜尋某個條件的元素

搜尋是否存在滿足條件的元素,例如名稱開頭為 Dar:

var exist = Array.FindIndex(names, (v) => { return v.StartsWith("Dar"); });
var exist = Array.FindLastIndex(names, (v) => { return v.StartsWith("Dar"); });

差異在搜尋順序,一個從 [0] 開始搜尋,另一個從陣列尾開始搜尋。

搜尋並列出符合某條件元素們(Elements)

搜尋名稱中,所有 D 開頭的名稱:

var searchNames = Array.FindAll(names, (v) => { return v.StartsWith("D"); });

注意這方式會產生另一個陣列來放結果。

轉換

將陣列元素全部轉換,例如將名稱轉換成小寫:

var lowercaseNames = Array.ConvertAll(names, (v) => { return v.ToLower(); });
// lowercaseNames: darjeeling, assam, orange pekoe, rose hip

注意這方式會產生另一個陣列來放結果。

排序 Sort

var data = new string[] { "a10", "a1", "a9", "a20", "a2" };
Array.Sort(data);
// data = a1, a10, a2, a20, a9

不得不提 UnityEditor.EditorUtility 的自然排列(Natural sort)實作,可以用以下程式碼進行自然排序:

var data = new string[] { "a10", "a1", "a9", "a20", "a2" };
Array.Sort(names, EditorUtility.NaturalCompare);
// data = a1, a2, a9, a10, a20

結果感覺好多了不是?

關於 Linq

Linq 是 C# Enumerable 的擴充,之前這一篇有再提其 Enumerable 概念,但 Linq 能做的事情實在是太多,因此這邊只列出兩個範例。

搜尋 D 開頭的名字:

foreach (var name in names.Where((v)=> { return v.StartsWith("D"); }))
{
Debug.Log(name);
}

// Output:
// Darjeeling

全部轉換成小寫:

foreach (var name in names.Select((v)=> { return v.ToLower(); }))
{
Debug.Log(name);
}

// Output:
// darjeeling
// assam
// orange pekoe
// rose hip

甚至這兩個做結合,先搜尋 D 字母開頭的名稱,在轉換成小寫:

foreach (var name in names.Where((v) => { return v.StartsWith("D"); }).Select((v) => { return v.ToLower(); }))
{
Debug.Log(name);
}

// Output:
// darjeeling

想法

很明顯的,拿個固定長度 Array 進行資料新增或是刪除,是相當不容易的一件事。若需要在遊戲中修改資料陣列長度,改用 List 吧。

public class Example : MonoBehaviour
{
[SerializeField]
List<string> names = new List<string>();
}

或是有初始值的宣告:

public class Example : MonoBehaviour
{
[SerializeField]
List<string> names = new List<string>(){ "Darjeeling", "Assam", "Orange Pekoe", "Rose Hip" };
}

沒有留言: