//tips
//継承について
Unityでスクリプトを描く際に、新たにスクリプトを作成する際には、過去に書いた類似スクリプトのものをコピーして流用することが多かったが、この手法は見直した方がよく、参照をうまく活かすためにオリジナルからの継承という形でスクリプト同士の関係を作っていくことが、コードを管理する点で優れていることがわかってきた。
シンプルでわかりやすい短いコードを描くためには参照を最大限に活用した方がよく、それを活かせるのが継承なのである。
いくつかC#の継承の例を記載する。
using System;
namespace SampleApplication1
{
class Person //基底クラス
{
string name;
int age;
public Person(string name, int age) //基底クラスのコンストラクタ
{
this.name = name;
this.age = age;
Console.WriteLine("基底クラス");
Console.WriteLine("名前: " + name + ", 年齢: " + age);
}
}
class Employee : Person // 派生クラス
{
int id;
string department;
public Employee(string name, int age, int id, string department) : base(name, age) //派生クラスのコンストラクタ
{
this.id = id;
this.department = department;
Console.WriteLine("派生クラス");
Console.WriteLine("社員番号: " + id + "部署: " + department);
}
public static void Main()
{
Employee empl_1 = new Employee("ichiro", 100, 123, "system"); //インスタンスを生成
}
}
}
実行結果:
基底クラス //基底クラスコンストラクタ
名前: ichiro, 年齢: 100 //基底クラスコンストラクタ
派生クラス //基底クラスコンストラクタ + 派生クラスのコンストラクタ
社員番号: 123 部署: system
実行結果から、派生クラスのインスタンスを生成した際に、基底クラスで実装された処理が実行されている事が分かる。基底クラスの処理から派生クラスの処理へと移ることがわかる。
派生クラスのコンストラクタを追加する場合は: base()で指定されている変数内容も含めて()内に記述し、{}では追加内容を記載する。
最終的にpublic static void Main()などで
クラス名 変数名 = new クラス名(コンストラクタの変数要素)
の形でインスタンス化している。
次は基底クラスのメソッドを上書きするオーバーライドを確認する。
using System;
namespace SampleApplication1
{
class SuperClass
{
private DateTime now;
public virtual String SampleMethod()
{
return "基底クラスのメソッド";
}
}
class SubClass : SuperClass
{
public override String SampleMethod()
{
return base.SampleMethod() + "をオーバーライド";
}
public static void Main()
{
SubClass test = new SubClass();
Console.WriteLine(test.SampleMethod());
}
}
}
実行結果:
基底クラスのメソッドをオーバーライド
継承した : SuperClassのメソッドSampleMethod()をSubClassで上書きしている。
上書きの際にはわかりやすいようにpublic override String SampleMethod()とoverrideを記載する。上書きされる基底クラスメソッドにはvirtualをつける。
SubClassのインスタンスとして生成した後にインスタンス代入変数+メソッド名test.SampleMethod()の形で表示している。
インタフェースと呼ばれるものもあり、基底クラスで、継承するクラスに一部の処理実装を強制させるもの仕組みもある。
インターフェースでメソッドの生成を強制し、そのメソッドを継承クラスに記載させているものが下記となる。
using System;
namespace SampleApplication1
{
interface IClock //インタフェースの名前はIから始まる
{
void getTime();
}
class Watch : IClock //継承
{
public void getTime() //インタフェースで宣言したメソッドを実装
{
Console.WriteLine("時刻0:00");
}
public static void Main()
{
Watch smartWatch = new Watch();
smartWatch.getTime();
}
}
}
インターフェイスでは、interface I+インターフェイス名という表示を行い、その中に実装させたい処理を書く。
処理内容は、インスタンスの生成ができないため、メソッド名の宣言だけとなる。interfaceの使用時継承先でのoverrideの記載は不要となる。
インターフェイスと似ているものに抽象クラスがあり、抽象クラスでは、継承先に強制したい処理以外の内容も記載することができる。
インターフェイスは宣言時classと記載しないことも覚えておく。
using System;
namespace SampleApplication1
{
abstract class Clock //抽象クラスの宣言
{
public void alerm() //抽象メソッド以外を宣言、処理の記述ができる
{
Console.WriteLine("15:00にアラーム");
}
abstract public void getTime(); //抽象メソッドの宣言
//抽象メソッドは処理を記述できない。
}
class Watch : Clock //継承
{
public override void getTime() //オーバーライドする必要がある
{
Console.WriteLine("時刻は0:00");
}
public static void Main()
{
Watch smartWatch = new Watch();
smartWatch.getTime();
smartWatch.alerm();
}
}
}
インターフェイス異なり、abstract classとして宣言し、Iも不要となる。強制しないpublic void alerm() と強制したい abstract public void getTime(); を記載できる。abstractをつけて目印をつけ、強制するメソッドの処理内容も記載できないことが特徴。
また、インターフェイスとは異なり、強制されるメソッドでもoverrideをつける必要がある。
ただ、手間が増えるだけの悪い継承の仕方もあるようでそちらも認識しておく。
https://qiita.com/tonluqclml/items/c0110098722763caa556
Unityで実際に継承を行おうとするとエラーの連鎖が起き、苦戦したので、まずは初歩的な部分から確認していくことにした。
まずは基底クラスを作成。
using UnityEngine;
using System.Collections;
public class Animal : MonoBehaviour
{
public string nakigoe = "ウホウホ";
}
これを継承するクラスを作成し空のオブジェクトにアタッチする。基底クラスはアセット内にはあるがシーンのオブジェクトには付加されていない。
using UnityEngine;
using System.Collections;
public class Gorilla : Animal
{
void Start()
{
Debug.Log(base.nakigoe);
}
}
これはきちんとウホウホと表示された。
次は基底クラスのメソッドを使う形に変更してみる。
using UnityEngine;
using System.Collections;
public class Animal : MonoBehaviour {
public void Start () {
Debug.Log("動物!");
}
}
再度空のオブジェクトに承継先のクラスを作成する。メソッドはbase.で引き継いでいる。
using UnityEngine;
using System.Collections;
public class Hiyoko : Animal
{
public void Start()
{
base.Start();
print("ぴよぴよ");
}
}
きちんと「動物!」「ぴよぴよ」が表示された。両スクリプトとも、void Startの前にpublicがついていることに注意が必要。
//パノラマ画像を円柱に投影する
横長のパノラマ画像をunityにインストールし、cylinderにアタッチ。その上で、shaderを先日作成した表裏を反転させるInwardShaderに変更する。
//マルチプレイヤーによるネットワーキング
マルチプレイヤーサービスとは、全てのアクティブなクライアント間でゲーム内の状態を共有するもので、新しいプレイヤーとオブジェクトの生成、セキュリティへの配慮、ローレベルのネットワーク接続、プロトコルの管理、及びデータ転送速度やパフォーマンスなどのサービス品質まで含んでいる。
ネットワーキングはいくつかの層に分かれており、下位層はデータ転送の細部を取り扱い、データ内容には関与しない。中間から上位層はネットワークアプリケーションに直接関与する層になっている。
ユーザーとの接触度が高い最上位層を最小限のカスタムスクリプトであることが理想で、特別な要件がある場合はAPIを用いて他のネットワーク層にアクセスすることで、セキュリティが高く安定したサービスを提供できる。
Unityでマルチプレイヤーサービスを組み込む際には、Phonton Unity Networkingが使え、アセットストアから無料で追加することができるよう。
https://doc.photonengine.com/ja-jp/pun/v2/getting-started/pun-intro
ネットワークの鍵になるのは、クライアントサーバー形式のネットワーク構造で、例えば、オンラインゲームの実体は、ネットワークに接続された時にはクライアントの状態だが、背後には他の全てのクライアントの状態や制御情報を通信するサーバーがある。
クライアントの動作がネットワーク経由で実行される場合、ユーザーの入力に対して、ネットワークによって接続されたクライアントコンピュータ上でプログラムを呼び出し、実行させるリモートプロシージャコールが行われる。
リアルタイムでクライアントとサーバーでの情報のやり取りが行われていることになる。
UnityのネットワークエンジンにはネットワークIDの管理・ネットワークの管理などのハイレベルな機能が組み込まれているようなのでまるチプレイヤーサービスを作成する際には使ってみると良いかもしれない。
次回はマルチプレイネットワーキングを利用したシーンを作成してみる。