Skip to content

2nd term 7th week

Daisuke Suzuki edited this page Sep 2, 2021 · 2 revisions

今回のテーマ

  1. インターフェイス
  2. 実装

準備

  1. CSharp1-2-7.unitypackage をダウンロードして Unity のプロジェクトにインポートする

インターフェイス

抽象クラスは「一部のメンバー関数が抽象メソッドであるクラス」のことだが、「すべてのメンバー関数が抽象メソッドであるクラス」を特別に「インターフェイス」として定義することができる。インターフェイスは全てのメンバー関数が抽象クラスであるため、継承する時に複数のインターフェイスを継承することができる。(※)

(※)多重継承が禁止されている理由は、それを許すと各クラスの機能(メソッド内で行われている処理)が衝突する危険性があるからであった。インターフェイスはすべてのメンバー関数が抽象メソッド(中身のないメソッド)であるため、衝突する危険性がない。従って多重継承が許可されている。

一般的なルールとして、インターフェイスの名前は先頭に I(大文字)がプレフィックスとして追加されている。例えばポーズ機能を提供するインターフェイスの名前は IPause という名前にする。

インターフェイスの定義は以下のように書く。

interface IPause
{
    /// <summary>一時停止のための処理を実装する</summary>
    void Pause();
    /// <summary>再開のための処理を実装する</summary>
    void Resume();
}

メソッドは全て抽象メソッドと決まっているので、virtual や abstract などとは書かない。継承する側も override を指定しない。インターフェイスを継承してメンバーを実際に書くことを「実装 (implement)」という。メンバーを実装する時には override は指定しない。

参考資料

  • 独習 C# - 8.3.3 インターフェイス

使用例 1 - ゲームを一時停止する

Assets/_2-6 Interface/1 Pause/PauseExample シーンを実行し、ESC キーを押すと動きが一時停止する。もう一度押すと再開する。

一時停止させたい GameObject に対して、IPause インターフェイスを継承して Pause/Resume 関数を実装すると、それぞれ一時停止・再開する時に呼ばれる。つまり Pause/Resume 関数に「一時停止させるための処理」「再開させるための処理」を書くことで、ポーズ機能を実現している。

ESC キーを押された時の処理は PauseManager に書かれている。ESC キーを押すと、IPause インターフェイスを継承したコンポーネントを探し、実装された Pause()/Resume() を呼び出す。この時に多態性の仕組みが使われている。

課題 1 パドルを一時停止する(5点)

このシーンでは ESC キーを押すと、IPause インターフェイス経由で、ボール・回転するギミックが停止し、PAUSE というメッセージが表示される。しかし、パドルが操作できてしまう。IPause インターフェイスを使って、ボールやギミックと同様にパドルも一時停止するように機能を追加せよ。

ヒント

  • まず最初に PaddleController コンポーネントに IPause を継承させる
  • BallController, SpinController を参考にして PaddleController に一時停止・再開機能を追加する
  • パドルが一時停止する、とは「入力を受け付けない」または「入力を受け付けてもパドルを動かさない」ことである

使用例 2 - 提供されているインターフェイスを使う

Unity でも、C# (.NET Framework) でも多数のインターフェイスが提供されていて、それらを継承して実装することでいろいろな機能を使うことができる。Unity では特に UI 関連のインターフェイスが充実しているので、それらを使ってみる。

Assets/_2-6 Interface/2 CardGame Basic/CardDeck シーンを実行し、カードをドラッグできること、ボタンを押すと配置がリセットされることを確認し、次の説明に進んでください。

シーンの説明

以下の説明では、画面の各部分を以下のように テーブル・デッキ・カード (table, deck, card)と呼ぶ。

Unity の EventSystem(名前空間 UnityEngine.EventSystems)にあるインターフェイスを利用して、以下の課題を解決せよ。なお、UnityEngine.EventSystems 以下にあるインターフェイスの一覧は Unity のマニュアル に載っている。左パネルにあるツリー上の目次 (ToC, Table of Contents) から見ることができる。

課題 2(5点)

最初の状態では以下のようにカードをドラッグするとどこにでも置くことができる。

これを修正して、以下のように「デッキの上にカードをドロップしたらカードを並べる」機能を作れ。

ヒント

  • 最初に IPointerUpHandler インターフェイスを CardController に継承させる
  • カードの整列には LayoutGroup (Horizontal LayoutGroup) コンポーネントを使う
  • Horizontal LayoutGroup コンポーネントは既に UpperDeckPanel, LowerDeckPanel に追加されているので、ドラッグしてマウスボタンを離した時にそのカードをデッキの子オブジェクトにすればよい
  • 「マウスボタンを離した時」に呼ばれる関数を実装するために IPointerUpHandler インターフェイスを使う
  • 「マウスポインタがどのデッキの上にあるか」を取得するには CardController.GetCurrentDeck() 関数を使う
    • GetCurrentDeck() 関数は CardController コンポーネントの IPointerDownHandler.OnPointerDown() で使われているので参考にすること
  • オブジェクトの親子関係を設定するには、Transform.SetParent() 関数を使う
    • SetParent() 関数は CardController コンポーネントの IBeginDragHandler.OnBeginDrag() で使われているので参考にすること
  • Transform.SetParent() 関数の引数に null を渡すと、その GameObject はどの GameObject の子でもなくなり、シーンルートの直下に配置される

課題 3(5点)

現状のままでは、以下のように「後からドラッグしたカードが後ろに回ってしまう」という問題が起きる。

これを以下のように「ドラッグしているカードが常に前面に表示される」ように修正せよ。

ヒント

  • Unity の UI では、「Hierarchy ウインドウ内で下にある GameObject が手前に表示される」というルールがある
  • GameObject を親子関係で同じ階層のうち、一番下に移動させるには Transform.SetAsLastSibling() 関数を呼べばよい

課題 4(5点)

(この課題に取り掛かる前に、必ず課題3までを提出すること)

以下のように、「デッキ以外の場所でドロップしたら元のデッキに戻る」ように動作を変更せよ。