UnityのスクリプトをC#、JavaScript、Booのどれで書こうかを悩んでいるなら、C#を使いましょう。おわり。
……という、結論に達するまでにJavaScriptで頑張ってみて、いろいろ悩んだり、悩んだが結局わからなかったことのメモ。
コードは Unity3.5.2 で確認しているつもり。
やたらと語尾が「〜〜っぽい」になってるのは、よくわからないまま書いているせいです。わからないことメモなので。
私の Unity の JavaScript に対する認識は「.NETのライブラリが呼べて、C#と大体同じ結果になるコードが書ける謎言語」なので、C#でできてることを、どうやって書いたらいいのかが主な要点。というか、このエントリの大半は、C# プログラミングガイドを見ながら書いた。
参考サイト
このエントリを読むより、下記のサイトを見たほうがタメになると思うので、最初に参考サイトを紹介しておきます。
その他、いろいろぐぐって出てきたサイトは割愛。というか、メモを取ってなかったので、もうどこを参考にしたんだかよくわからん。
配列
// 一次元配列の生成 var array1 : int[] = new int[5]; // 初期値指定ありで一次元配列 var array2: int[] = [ 1, 2, 3, 4, 5, 6 ]; // サイズの揃った二次元配列 var multiDimensionalArray1 : int[,] = new int[2, 3]; multiDimensionalArray1[1, 2] = 3; // 初期値ありで二次元配列を書こうとすると、配列の配列になる var jaggedArray1 = [ [ 1, 2, 3 ], [ 4, 5 ] ]; print(jaggedArray1[0][1]); ///=> 5
ここまではわかるんだけど、
// 配列の配列の型定義をどうしたらいいのかがわからない var jaggedArray2 : int[][] = [ [ 1, 2, 3 ], [ 4, 5 ] ]; // コンパイルエラー // new の仕方もわからない var jaggedArray3 = new int[6][]; // コンパイルエラー // ※これはなぜか配列にすらならず、intの変数になる???? var jaggedArray4 = new int[6][5]; jaggedArray4 = 1; print(jaggedArray4); ///=> 1
最後のやつは一体どういう理屈なんだ……
ちなみに、UnityのInspector上で見ることができるのは、一次元配列だけ。他のものもInspectorに対応させたい場合は自力で頑張れ。
ちなみにちなみに、最後のやつ(jaggedArray4)は、Inspector上でもintの変数として見れますw
プロパティ(getter/setter)
class Huga { var mA = 100; function get a() {return mA;} function set a(value : int) {mA = value;} static function Sample() { var huga = new Huga(); Debug.Log(huga.a); ///=> 100 huga.a = 200; Debug.Log(huga.a); ///=> 200 } }
setter の引数名を value 以外にするとコンパイルエラーになるお約束っぽい。
JavaScript で書いていると、明示的に class 宣言をしないと、自動的に MonoBehaviour を継承したクラスが作られるのだが、プロパティは class 宣言を書かないとコンパイルが通らない罠がある。
#pragma strict /// file : HogeJS.js //class HogeJS extends MonoBehaviour { // ※この行と(つづく) var mA = 10; function get a() {return mA;} // この行でコンパイルエラー function set a(value : int) {mA = value;} //} // (つづき)この行の行頭のコメントを外すと、コンパイルが通る
これでだいぶハマった……。
static 変数のプロパティは書けないっぽい。
演算子オーバーロード
class Hoge { var mA = 0; var mB = 0; function Hoge(a : int, b : int) { mA = a; mB = b; } static function op_Addition(a : Hoge, b : Hoge) { return new Hoge(a.mA + b.mA, a.mB + b.mB); } } function Start() { var a = new Hoge(10, 200); var b = new Hoge(30, 20); var c = a + b; Debug.Log("a:"+a.mA+","+a.mB + " b:"+b.mA+","+b.mB + " c:"+c.mA+","+c.mB); ///=> a:10,200 b:30,20 c:40,220 }
op_Addition が + のオーバーロード。
.NET の Operator Overloads のページに使えそうなリストがある。
+= などの Assignment 系はUnityではコンパイルが通らないっぽい。+ だけ定義すれば、+= も使えているもよう。
indexer
インデクサは定義できないっぽい……? getter/setterが使えて、演算子オーバーロードがあるのに、インデクサが無いというガッカリ感。気づいてないだけでありそうな気がするんだけども……
Generics
var list = new System.Collections.Generic.List.<int>();
List
JavaScript で Generics 対応のクラスや関数を宣言する方法はわからなかった……。Objectとダウンキャストで頑張ればなんとかなるけども……。
interface
classは単一継承で、interfeceは多重継承。
interface IHogeA { function GetA() : int; } interface IHogeB { function GetB() : float; } class CHoge { var c = 3000; } class Hoge extends CHoge implements IHogeA, IHogeB { function GetA() {return 10.5f;} function GetB() {return 200.5f;} } function Start() { var hoge = new Hoge(); var hogeA : IHogeA = hoge; var hogeB : IHogeB = hoge; var hogeC : CHoge = hoge; print(hogeA.GetA()); ///=> 10 print(hogeB.GetB()); ///=> 200.5 print(hogeC.c); ///=> 3000 }
普通ですね。オーバーライドしている GetA() で float を return しても、IHogeA.GetA() : int の定義通りに、intが帰っていることに注意?
delegate
C#でdelegateをつかって書くもの。関数ポインタっぽい処理とか、匿名関数とか。
function Hoge(f0 : function(int, int) : int, f1 : function(int, int) : int) { print(f0(2,3) + f1(4,5)); } function Start() { var func : function(int, int) : int; func = function(a : int, b : int) { return a * b; }; print(func(3,8)); ///=> 24 Hoge(func, func); ///=> 26 }
コロンが多めに登場するものの、いたって普通ですね。
C#みたいに、デリゲートの型(?)に名前を付ける方法はわからなかった。
// C# delegate int Del(int x); // これに該当するJavaScriptでの書き方がわからん void Start () { Del d = delegate(int x) {return x * 5;}; print(d(4)); ///=> 20 }
foreach / yield
C# のコードをそれっぽく書き換えると普通に動いてくれる。
例としてC#のソースをUnityのJavaScript書き換えてみた。
class DaysOfTheWeek implements System.Collections.IEnumerable { var days = [ "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" ]; function GetEnumerator() { for (var i = 0; i < days.Length; i++) { yield days[i]; } } } function Start() { var week = new DaysOfTheWeek(); for (var day in week) { Debug.Log(day); } }
普通ですね。
属性(?)
Serializable とかで使うやつ。
// C# [System.Serializable] public class Hoge { int a; }
Unity だと MenuItem をよく使う印象。
@MenuItem("Test/TestPrint") static function TestPrint() {Debug.Log("Menu Test");}
[ ] を @ にするだけ。
ちなみに、UnityのiPhoneビルドだと BinaryFormatterが未対応らしいので、Serializableの出番がない。つらい。何で代用したらいいんだろう……
例外処理
function Start() { var array = [0,1,2]; try { print(array[5]); // 配列の範囲外アクセス throw System.Exception(); // 既存の例外や自作の例外も、もちろん投げれる } catch (e : System.IndexOutOfRangeException) { // 例外クラス指定catch // printしてもみ消す print(e); } catch (e) { // 全 catch // printして投げ直し print(e); throw e; } finally { print("finally"); } }
普通ですね。
派生クラスでの基底クラス呼び出し
C# だと base なのかな? JavaScriptではsuper。
class Hoge { function Hoge() {Debug.Log("Hoge::Hoge");} function A() {Debug.Log("HogeA::A");} function B() {Debug.Log("HogeA::B");} } class HugaHoge extends Hoge { function B() { Debug.Log("HugaHoge::B"); super(); // 基底クラスHogeの B() を呼び出す super.A(); // 他のメソッドも呼べる super.B(); } } function Start() { var x : Hoge = new HugaHoge(); x.B(); }
出力結果
普通ですね。
その他箇条書き
- デフォルト引数は無い → オーバーロードで代用
- enum が使える
- 変数のスコープはクラスや関数ごと? for や if ではスコープは作られない(?)。
- struct は無い。classでなんとかする。
- const は無い(?)。変更しないように頑張る(?!)
結論とか、まとめとか特になく終わり。もうUnityのJavaScriptを書くことはないと思われるので、似たようなネタはもうやりませんのであしからず。わからなかったことの答えを偶然見つけたり、教えてもらったりしたら書くかもしれんが。