2015年5月9日 星期六

Unity3D - Circular Scrolling List - Part 2 - 顯示內容

了解 ListBox 的移動概念後,接下來就要顯示內容拉~

以下分成幾個部分:

  • ListBank
  • 初始化內容
  • 更換內容

ListBank


  ListBank 的功能在於儲存清單的所有內容,提供給 ListBox 顯示。筆者在設計上讓 ListBank 提供:
  • public static instance。儲存整個 scene 中唯一一個 ListBank 的 object 的 reference,讓 ListBox 可以直接存取,免去在 ListBox 額外宣告 public member 來儲存 ListBank object 的 reference,而且要在 Inspector 為每個 ListBox 設定的麻煩。
  • 一個儲存資料內容的 array。可以是儲存角色名字的 string array 或是角色圖片的 sprite array,當然也可以是外部檔案的資訊清單,並在 Start() 中呼叫專屬的初始化函式等。
  • 兩個基本函式。getListContent 及 getListLength 來存取 array 內容。
程式碼如下:
using UnityEngine;
using System.Collections;

public class ListBank : MonoBehaviour
{
    public static ListBank Instance;

    public int numOfListBoxes;

    private int[] contents = {
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10
    };

    void Awake()
    {
        Instance = this;
    }

    public int getListContent( int index )
    {
    return contents[ index ];
    }

    public int getListLength()
    {
        return contents.Length;
    }
}
( 附註:在上一篇教學中,member numOfListBoxes 是要在 ListBox 中被設定,但是後來改成統一在 ListBank 中設定,而將 ListBox 中的 numOfListBoxes 改為 private,並在 Start() 中初始化 )

  然後在 scene 中,建立一個空的 gameObject,並將這個 script 貼上去。如此一來,每次 scene 開始時,Instance 變數所存的 reference 就會是這個 gameObject 所產生的 ListBank object。


初始化 ListBox 的顯示內容


  根據 ListBox 的 ID 來決定要取得什麼內容。直觀來說,要讓中間的 ListBox 顯示清單中第一個內容,往下依序顯示,而由中間往上的 ListBox,由下往上顯示最後一個、最後第二個...等內容。


第一步就是得到位在中間 ListBox 的 ID,利用 int 除以 int 小數會被捨去的特性,中間 ListBox 的 ID 等於 ( numOfListBoxes / 2 ),例如:5 個 ListBox 就會得到 2,7 個就會得到 3。第二步處理 ID 比中間 ID 大的 ListBox,利用跟中間 ID 的差來決定顯示清單第幾個內容。稍微麻煩的是處理 ID 比中間 ID 小的 ListBox,與中間 ID 差 1 的顯示清單中最後一個內容,差 2 的顯示清單中最後第二個內容,依此類推。
/* Initialize the content of ListBox.
 */
private int contentID;

void initialContent()
{
    if ( listBoxID == numOfListBox / 2 )
        contentID = 0;
    else if ( listBoxID < numOfListBox / 2 )
        contentID = ListBank.Instance.getListLength() -
            ( numOfListBox / 2 - listBoxID );
    else
        contentID = listBoxID - numOfListBox / 2;

    while ( contentID < 0 )
        contentID += ListBank.Instance.getListLength();
    contentID = contentID % ListBank.Instance.getListLength();

    updateContent( ListBank.Instance.getListContent( contentID ).ToString() );
}

void updateContent( string content )
{
    text.text = content;
}
在 Start() 呼叫 initialContent() 來初始化 ListBox 的內容,並透過 updateContent() 來更新目標 UI.text 的內容。需要注意的是程式碼中第 15 行到第 17 行是用來讓 contentID 為有效值,以免出現 exception 。當 contentID 為 -n 時,代表顯示最後第 n 個內容,這種情況會發生在可顯示內容比 ListBox 的數量的一半還要少的時候 ( 9 個 ListBox 顯示 3 個內容,或 7 個 ListBox 顯示 2 個內容 ),所以透過加上清單內容數量,得到正確的 contentID。

ObjectLabel


  只要為每個 ListBox 給與一個 UI.Text 作為 child object,並將 ListBox 的 member "content" 的 reference 指定到這個 text object,就可以用來顯示目前的內容。

隨著移動更換 ListBox 內容


  這部分就是 Circular Scrolling List 的精隨。沒錯!就像是鳳尾炸蝦的醬汁一樣。發生時機為 ListBox 需要出現在另一端的時候。情境如下:

  • 當 ListBox 出現在上方的時候,其內容為下方 ListBox 的上一個內容;
  • 當 ListBox 出現在下方的時候,其內容為上方 ListBox 的下一個內容。
所以每一個 ListBox 都必須要知道他上一個及下一個 ListBox 分別是誰。


如上圖所示,每個 ListBox 旁標記的是他的 ID,ListBox 間的線把 ListBox 串起來,同時也幫助標記每一個 ListBox 的上一個及下一個 ListBox 是誰。在此以 ListBox_0 做為觀察對象,特別以紅色標記。

  • 圖左:初始化的樣貌,可以看到 ListBox_0 顯示清單的最後第二個內容;
  • 圖中:ListBox_0 從下方出現,內容更新為上方 ListBox ( ID 4 ) 的下一個內容;
  • 圖右:ListBox_4 從上方出現,內容更新為下方 ListBox ( ID 0 ) 的上一個內容。
因此,ListBox 需要新增兩個 member - lastListBox 及 nextListBox 來記錄上一個及下一個 ListBox 是誰。並且要提供一個 get function 讓其他 ListBox 可以得到該 ListBox 目前的 contentID。
public ListBox lastListBox;
public ListBox nextListBox;

public int getCurrentContentID()
{
    return contentID;
}

void updateToLastContent()
{
    contentID = nextListBox.getCurrentContentID() - 1;
    contentID = ( contentID < 0 ) ? ListBank.Instance.getListLength() - 1 : contentID;

    updateContent( ListBank.Instance.getListContent( contentID ).ToString() );
}

void updateToNextContent()
{
    contentID = lastListBox.getCurrentContentID() + 1;
    contentID = ( contentID == ListBank.Instance.getListLength() ) ? 0 : contentID;

    updateContent( ListBank.Instance.getListContent( contentID ).ToString() );
}
在 checkBoundary() 中,當 ListBox 超出上界時,此時要出現在下方,因此呼叫 updateToNextContent(),反之,則呼叫 updateToLastContent()。當然要記得檢查 contentID 是否在有效的範圍內。


  最後的程式碼:Github

  Part 2 的教學到這邊結束,希望看完都會做鳳尾炸蝦((誤。有問題或是建議歡迎留言討論。

8 則留言:

  1. 回覆
    1. Sure! I would upload the project.

      刪除
    2. Hi! The project file is available here https://drive.google.com/file/d/0B4C0FIMFSOv6UVpXeEJSdERfT2s/view?usp=sharing.

      刪除
  2. 回覆
    1. 謝謝! 如果有問題,歡迎提出來

      刪除
    2. I try to do horizontal mode, but somethings bad. Do you have horizontal version?

      刪除
    3. It would take me some time to make horizontal version. If it's done, I would leave comment here.

      刪除