今回はUnityのJavaScriptでクラスの作成と継承、インタフェースを作成する方法をやりたいと思います。
今までの記事でクラスを使った事もありましたが、あまり突っ込んで解説をしていなかったので、今回はサンプルを使って動作を確認していきます。
JavaScriptでスクリプトを組んでいるとなかなかクラスを作成しませんが、クラスを使うとUIのイベント受け取りをする時にEvent Triggerコンポーネントを追加してイベント処理関数を指定していた個所をインタフェースの実装という形で作成する事が出来ます。
通常のJavaScriptでクラス的なものを作ろうとすると結構面倒くさい事になりますが、
UnityのJavaScriptではJavaで作成するクラスと作成方法が同じです。
Javaをやった事がある方はほとんど同じだなと感じるはずです。
クラスを作成
ではさっそくクラスを作成していきます。
通常通りJavaScriptのファイルを作成します。
今回は名前をHumanという名前にします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | public class Human { // 名前 private var name : String; // 年齢 private var age : int; // 引数なしコンストラクタ function Human() { this.name = "名無し"; this.age = 0; } // 引数ありコンストラクタ function Human(name : String, age : int) { this.name = name; this.age = age; } // 名前のSetter function SetName(name : String) { this.name = name; } // 名前のGetter function GetName() : String { return this.name; } // 年齢のSetter function SetAge(age : int) { this.age = age; } // 年齢のGetter function GetAge() : int { return this.age; } // 攻撃関数(メソッド) function Attack() { Debug.Log("HumanAttack"); } // 人間関数(Humanだけが持っている関数) function HumanFunction() { Debug.Log("HumanFunction"); } } |
これ以降はしばらくクラスの作成の仕方なので、オブジェクト指向プログラムを学んだ方は飛ばして結構です。
オブジェクト指向プログラムを学んだ事がある方はわかると思いますが、クラスはファイル名と同じ名前にします。
Humanクラスが保持する値はprivateにしHumanクラスからしかアクセス出来ないようにします。
Human(人間)クラスはname(名前)とage(年齢)プロパティを持ちます。
クラス名と同じ名前の関数を作るとコンストラクタとなり、Humanクラスをインスタンス化した時に呼び出されます。
var human : Human = new Human(“masasi”, 18);
等とHumanクラスのインスタンス化をする時にコンストラクタが呼ばれます。
クラス内にHuman関数が引数なしと引数ありの二つありますが、インスタンスを作成する時の引数の型と数によって、自動的に呼び出す関数が決定されます。(オーバーロード機能)
privateで宣言した変数にはHumanクラスからしかアクセスが出来ない為SetterとGetter関数を用いて変数にアクセス出来るようにします。
Javaのクラス作成と違うのはメソッドの作成にfunctionという文字が必要な事と引数の並びや戻り値の指定がUnityのJavaScriptの関数と同じように作成するというところでしょうか。
これでHumanクラスが作成できました。
Humanクラスはひな形なのでインスタンス化して使用する必要があります。
クラスの継承を使う
次はクラスの継承を行う場合です。
Humanクラスを継承したSuperManクラスを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class SuperMan extends Human { function SuperMan() { super(); } function SuperMan(name : String, age : int) { super(name, age); } function Attack() { Debug.Log("SuperManAttack"); } function SuperManFunction() { Debug.Log("SuperManFunction"); } } |
Humanクラスを継承するには
class 子クラス extends Human
とします。
これもJavaと同じですね、SuperManクラスをインスタンス化すればHumanクラスの機能を使えます。
インタフェースを作成する
次はインタフェースの作り方です。
インタフェースはクラスに必ず持たせておきたい機能を宣言しておくもので、インタフェースを継承して作成したクラスは宣言した機能を実装(実際の処理を記述)する必要があります。
例えばキャラクターは必ず攻撃が出来るとしたら、インタフェースにAttackメソッドを宣言しておき、キャラクタークラスではAttackの機能を記述しなければいけないという事になります。
それではインタフェースを作成してみます。
1 2 3 4 5 | public interface InterfaceTest { function Attack(); } |
これもJavaと同じですね、interfaceという文字の後にファイル名と同じ名前を指定します。
Attack()の後は{}(中括弧)ではなく;(セミコロン)です。
インタフェースでは実装ではなく、これがなければダメだよと宣言するだけです。
つまりインタフェースを使ったクラス(そのクラスの継承先でもよい)ではAttackの中身が記述されていないといけません。
クラスの継承とインタフェースの実装を確認してみる
それではクラスとインタフェースが出来たのでこれらを使ってみます。
シーンになんでもいいのでゲームオブジェクトを設置し、そこにMakeInstanceという新しいスクリプトを作り設置します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | private var superMan : SuperMan; private var human1 : Human; private var human2 : SuperMan; function Start () { // SuperManインスタンスの作成 superMan = new SuperMan("スーパーマン", 20); // SuperManインスタンスをHuman型に入れる human1 = new SuperMan("人間1", 15); // Human型に入れたSuperManインスタンスのAttackを呼び出すとSuperMan型のAttack // が呼び出される。ただしAttackがHuman、SuperMan両方で記述されてる必要あり human1.Attack(); // SuperManだけで宣言されているSuperManFunctionはそのままでは呼び出せない // human1.SuperManFunction(); // SuperMan型にキャストしてSuperMan型のhuman2に入れる human2 = (human1) as SuperMan; // 通常通り呼び出せる human2.SuperManFunction(); } |
Unityの実行ボタンを押して確認してみてください。
human1.Attack()ではSuperManAttack
human2.SuperManFunction()ではSuperManFunction
という表示がされています。
SuperManのインスタンスを作成した後にSuperMan型の変数に代入した場合は、SuperManクラスで定義された機能が呼び出されます。
SuperManクラスで定義されていなければHumanクラスで探します。
SuperManインスタンスをHuman型の変数に代入した場合は
SuperMan、Humanクラス両方で定義されているメソッドであればSuperManクラスのメソッドが呼び出されます。
SuperManクラスでしか定義されていないSuperManFunctionはHuman型変数に代入したSuperManインスタンスからは呼び出せません。
SuperManクラスのSuperManFunctionを呼び出したければasを使用しキャストしてからなら呼び出せます。
ここら辺はJavaのクラスの継承と同じですね。
ではさきほど作ったインタフェースを使ってみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | public class Human implements InterfaceTest{ // 名前 private var name : String; // 年齢 private var age : int; // 引数なしコンストラクタ function Human() { this.name = "名無し"; this.age = 0; } // 引数ありコンストラクタ function Human(name : String, age : int) { this.name = name; this.age = age; } // 名前のSetter function SetName(name : String) { this.name = name; } // 名前のGetter function GetName() : String { return this.name; } // 年齢のSetter function SetAge(age : int) { this.age = age; } // 年齢のGetter function GetAge() : int { return this.age; } // 攻撃関数(メソッド) function Attack() { Debug.Log("HumanAttack"); } // 人間関数(Humanだけが持っている関数) function HumanFunction() { Debug.Log("HumanFunction"); } } |
クラス名の後にimplementsと書きその後インタフェース名を記述します。
InterfaceTestインタフェースではAttackメソッドが必ず定義されていなければいけません。
Humanクラスでは元々定義していたので、インタフェースのAttackメソッドは実装されている事になります。
でも、Attackメソッドを実装してなくてもコンパイルが通っているっぽいが・・・・謎
Warningは出てるみたいですけどね。
あとはJavaScriptだと抽象クラス、抽象メソッドが作れない・・・C#だと作れるみたいなんだけど、うーむ・・・
イベントのインタフェースの実装
ではインタフェースをもっと実践的に使ってみましょう。
今まではUIのイベント処理でEvent Triggerというコンポーネントを使いイベントの発生を管理し、自前の関数を呼び出して処理を実行していました。
↑の記事ではEvent Triggerを使っています。
しかし、Event Triggerを使わなくてもインタフェースの実装が出来るようになったので
(わたくしはやり方がわからなかったのでEvent Triggerを使っていた)
そちらを使ってボタンイベントを処理してみましょう。
上のようにUI→Canvasを作りその子要素にUI→Buttonを作成します。
Buttonに新しくPointerDownJSという新しいスクリプトを作成し設置します。
1 2 3 4 5 6 7 | public class PointerDownJS extends MonoBehaviour implements EventSystems.IPointerDownHandler { function OnPointerDown(eventData : EventSystems.PointerEventData) { Debug.Log("test"); } } |
JavaScriptで作成している時は気にする必要はありませんでしたが、MonoBehaviourクラスは必ず継承されていました。
JavaScriptでインタフェースの実装をする時は明示的にMonoBehaviourを継承しないとエラーが出る(コンポーネント追加が出来ない?)ので記述してください。
このクラスはIPointerDownHandlerインタフェースを実装します。
JavaScriptの場合はこのインタフェースがある親の階層EventSystemsから記述する必要があります。
import EventSystems;
という文言をクラス宣言の前に記述しておくと
EventSystems.IPointerDownHandler → IPointerDownHandler
に短縮して記述する事が出来ます。
C#は
using EventSystems;
です。
IPointerDownHandlerを使うとOnPointerDownメソッドを実装する必要があります。
ではUnityの実行ボタンを押して確認してみましょう。
ButtonにはEvent Triggerを取りつけていませんが、Buttonを押すたびにtestがConsoleに表示されるのが確認出来ます。
スクリプトでイベントを受け取れるようになるので、Event Triggerを取りつける必要もなくなり、イベント発生と受け取りを両方作る必要がないので少し楽になりますね。
UnityのJavaScriptを使っていてクラスを使いたい時に、
アニメーターのBehaviourを使う時はC#でしか作れず、JavaScriptのスクリプトの取得をする場合特殊な作業が必要みたいなので、C#で作っていった方が便利だなぁ・・・と思います。
今更って感じではありますがね・・・・(^_^;)
JavaScriptとC#でのクラス作成時の注意点
ここまででJavaScriptを使ってクラスを作成する方法を述べてきましたが、JavaScriptの場合は明示的にMonoBehaviourクラスを継承しなくてもデフォルトで継承されていました。
しかし、C#でファイルを作成するとデフォルトでMonoBehaviourクラスを継承してクラスが作成されています。
このままC#でアイテム情報を表すクラスを作成しようとItemDataクラスを作成して使おうと思ってもインスタンス化が出来ません。
それはMonoBehaviourクラスを継承して作成したクラスはインスタンス化が出来ない為です。
その為、C#でアイテムクラスを作成しようと思ったら
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class ItemData { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } } |
MonoBehaviourクラスを継承しないでクラスを作成するか
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class ItemData : Object { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } } |
Objectクラスを継承してクラスを作成するといいと思います。
JavaScriptの場合は臨機応変になっているみたいですね・・・・(^_^;)
C#でアイテムクラスを作成する時に困る人も出てくるんじゃないかと思ったので追記しました。