Thursday, February 7, 2013

Send multiple parameter with single SendMessage in Unity3D | 在Unity3D中使用單個SendMessage送出多重參數

不久前買了一套第三方的Unity Asset,當我專案中的遊戲物件要獲取第三方的數據時,遇到了困難。 因為該套件以C#開發,個人比較習慣使用JavaScript開發,兩個不同語言的程式之間要互通參數本身就有困難,最簡單的解決方案就是透過SendMessage傳遞參數。 但是當你要傳送的參數很多時,真的只能一行一行把每個參數Send出去嗎?

在Unity Answers爬的結果是否定的,當你仔細看SendMessage的程式屬性時,傳遞參數那欄是object屬性,這代表著這裡可以是任何東西,可以是字元、向量、浮點數、布林、單一數據類型的陣列或者是混和數據的物件等等… 因此我再繼續爬下去,最後我實作了JavaScript的接收方,與JavaScript和C#的發送方(因為我個人用JavaScript開發,因此目前還不打算做C#的接受方)。

接收方 | Receiver
var unpkg1:String;
var unpkg2:boolean;
var unpkg3:Vector3;
var unpkg4:float;
var testMod:boolean=true;
var supportCS:boolean=false;

function Update()
{
 if(testMod)
  gameObject.transform.position=unpkg3;
}

function Unpackage(msgArr:Array)
{
 unpkg1=msgArr[0];
 unpkg2=msgArr[1];
 unpkg3=msgArr[2];
 unpkg4=msgArr[3];
 if(testMod)
 {
  Debug.Log(unpkg1);
  Debug.Log(unpkg2);
  Debug.Log(unpkg3);
  Debug.Log(unpkg4);
  Debug.Log(unpkg1+", and this's the "+unpkg2+". My address is "+unpkg3+", my phone number is "+unpkg4+". Remember to call me!!");
 }
}

function UnpackageCS(msgArrCS:Object)
{
 unpkgCS1=msgArrCS[0];
 unpkgCS2=msgArrCS[1];
 unpkgCS3=msgArrCS[2];
 unpkgCS4=msgArrCS[3];
 if(supportCS && testMod)
 {
  Debug.Log(unpkgCS1);
  Debug.Log(unpkgCS2);
  Debug.Log(unpkgCS3);
  Debug.Log(unpkgCS4);
  Debug.Log(unpkgCS1+", and this's the "+unpkgCS2+". My address is "+unpkgCS3+", my phone number is "+unpkgCS4+". Don't call me!!");
 }
}
原理很簡單,我先宣告我預定要接收的變數與數據類型(本範例分別使用:字元、布林、浮點數、三維向量,來當作示範),然後設置兩個函數分別用來接收JavaScript或C#傳來的參數(不一定要分成兩個函數,其實也可以在同個函數內解決,但為了方便辨識,我分成了兩個函數處理),傳來的參數可以用讀取陣列的方式解開參數內容。

Notes:
a. 函數不需要特別宣告傳值的數據類型也可以處裡,例:msgArr:Array,只打msgArr也是可以使用的;但是切記請勿以無法紀錄傳值的數據類型去宣告,例:Vector2,因為傳值內還有字元和布林,會出現錯誤。
b. 別忘了陣列總是從0開始。
c. 接收函數內部(例:Unpackage)不需要特別宣告變數(例:unpkg1),但是如果這個變數要在別的函數內使用時(例:在Update內使用unpkg1去改變gameObject位置),就需要事先宣告,不然別的函數會認不得。

發送方(JavaScript) | Sender(JavaScript)
var msgValue1:String="I'm a man";
var msgValue2:boolean=true;
var msgValue3:Vector3=new Vector3(2,4,6);
var msgValue4:float=1234.5678;

public var msgArr=new Array();

msgArr.push(msgValue1);
msgArr.push(msgValue2);
msgArr.push(msgValue3);
msgArr.push(msgValue4);

var otherObj:boolean=false;
var targetObj:GameObject;

function Start() 
{
 if(otherObj)
  targetObj.SendMessage("Unpackage",msgArr);
 else
  gameObject.SendMessage("Unpackage",msgArr);
}
在開頭宣告我要發送的變數,之後宣告一個新陣列(new Array()),在底下將變數加入到陣列內(Array.push),請記住變數在陣列中的順序依你加入的順序而定(上到下)。 然後SendMessage把此陣列給送出去就好了! JavaScript簡單多囉~

發送方(C#) | Sender(C#)
using UnityEngine;
using System.Collections;

public class Sender : MonoBehaviour
{
 public int valueLength=4;
 public string msgValue1="I'm a man, from C#";
 public bool msgValue2=false;
 public Vector3 msgValue3=new Vector3(1,3,5);
 public double msgValue4=8765.4321;
 public bool otherObj=false;
 public GameObject targetObj;
 
 void Start ()
 {
  object[] msgArrCS=new object[valueLength];
  msgArrCS[0]=msgValue1;
  msgArrCS[1]=msgValue2;
  msgArrCS[2]=msgValue3;
  msgArrCS[3]=msgValue4;
  
  if(otherObj)
   targetObj.SendMessage("UnpackageCS",msgArrCS);
  else
   SendMessage("UnpackageCS",msgArrCS);
 }
}
大致原理同JavaScript,不過C#中同一陣列只能單一數據類型,因此沒辦法用陣列傳值(如果是混和數據類型的話),所以改用Object傳值。 然後C#的陣列一定要有數組大小或者初始值,也就是範例中的valueLength位置,一定要放數值或者動態數值,沒辦法像JavaScript那樣,可以空陣列然後塞變數進去。

Notes:
a. 如果不想用Object存值的話,其實C#還是有使用陣列的解決方案 - 使用「List」,不過太複雜、太麻煩了… 我沒有實作出來,但我還是會在教學最後附上相關連結。

教學在這裡結束,至於以上程式碼我會放到我的GitHub上,GitHub的版本還會附上註解,有興趣的人可以去下載。

參考文章:
UnityAnswers - 
SendMessage , how can i send two parameter
Multiple paramaters with send message
Concatenating to a String[] Array Dynamically
array = new ArrayList() but in C#
Community - SendMessage with multiple arguments
msdn - List Class
不远道人的专栏《C#中Array与ArrayList用法及转换

No comments:

Post a Comment