Unityのためにさらっと学ぶC#

mofmofはRailsをバックエンドに採用し、高速にMVPを組み立てて事業検証などを行うことを得意としている会社ですが、同時に新しいもの、技術が好きな会社でもあります。

今回HoloLens 2を触れる機会があったので、Unityを思い出しがてら触ってメモをしておきます。

https://www.microsoft.com/ja-jp/hololens/hardware

最初に触った時の感じはSwiftっぽい感じでした。 基本的にはコンポーネントを配置して、大きさなどのプロパティをGUIから触ったり、スクリプトプログラムを適用させてコードから値を変えたりして動きなどを実現していく感じです。

UnityではC#を用いて何かすることが多いらしいですが、一方でC#の学習にはUnityは適していないらしいです。(今は昔かもしれない)Unity上でのC#の位置づけはイベント処理を行う際のスクリプトとしてのものなので、C#におけるデバッグの

Console.WriteLine

などのコードはunity上のc#スクリプトではエラーになってしまいます。

C#を学ぶ場合はVisual Studioを使い、ASP.NET Coreなどでウェブサイトなどを作ってみるのが良いのかもしれませんが、今回の目的はUnityなので、そちらにはあまり力を入れすぎないようにします。

最初に感想ですが、見たらなんとなくわかるので、とっつきやすいと思います。

普段はWebの業務を行う都合上、RubyやJS(TS)をよく使用します。よく似ていると言われるJavaは業務で触ったことはありませんが、オブジェクト指向であることや、型みたいなのがあるので、その辺りの言語を触っている方にとっては癖はそんなにない気がしました。

変数・定数

変数は 変数の型 変数名という形で型を明示する方法と、var 変数名 のように暗黙の型推論を行わせる方法があります。

string freiend = "Sam";

string myName;
myName = "Tom";

var dogName = "ziru"

こんな感じで使用することができます。

定数は constをつかう。jsで使うのでなじみがある。

const string food = "pizza";

こんな感じでつかう。値を再代入できないので、

 const string drink;
 drink = "cola";

こういった書き方はエラーになる。

少し脱線しますが、C#には単純な1文字を表すchar型と、文字列を表すstring型が存在しています。

このとき、char型はシングルクォーテーションで囲むというルールで、stringである文字列型はダブルクォーテーションで囲むというルールになっています。

逆は許されないので注意。

ただ、一文字であっても、ダブルクォーテーションで囲んだものはstring型であるとみなされるので、

string name = "a";

とかはいける。ノリと勢いでやるとこの辺で転ぶので少し注意が必要かもしれない。

あと日本語の一文字はプログラム的には一文字ではない可能性もあるので、日本語の場合は一文字だったとしてもstring型にしておくのが無難。

string ,int, float, boolなどおなじみのものがプリミティブ型として存在している。 Unityの場合はSliderとかTextとかGameObjectとかがある。宣言しなくてもvar使えば暗黙推論してくれることも多いが、明示できるときはしといたほうが色々無難。

演算

演算子はほかの言語と大体一緒。 +-* / % += ++-- && ||とか。

文字列の連結の場合は普通に string +stringでもできるし、

string dog = "犬";
string cat = "猫";
String.format("{0}と{1}", dog, cat) #=> 犬と猫

のように書くこともできる。一方、

string sample = "#{dog}と猫"; #=>  "#{dog}と猫"

とかはそのまま出力される。

配列とlist

配列

int[] array = new int[3] { 0, 1, 23 }; のように宣言できる。

array[0] = 2;

のように既存の値の変更もできる。 特徴としては、初期化時に要素の個数が決定されて増減できないという点。 Webエンジニアの配列とC#の配列は結構異なっているので、ここもちょっと注意がいるかもしません。

どちらかというと後述するリストというもののほうがなじみある配列っぽい感じがします。

リスト

Rubyとかでなじみあるほうがリスト。 配列と異なり、

list.Add(100);
list.Remove(1);
list.RemoveAt(1);

のような、要素の追加や削除の関数が用意されている。 呼び出し方はこんな感じ。

List<int> list = new List<int>() { 2, 34, 4 };

使い分けとしては、データがあらかじめ決定されているかどうか。例えば一週間は7日というのは今のところ揺るがない事実なので、配列を使うほうがよい。

対して、変更があるような場合は、リストを使うほうがよい。

繰り返しになるが、配列は初期化時に要素の数を指定する必要があり、pushなどもないので、初期化後に要素を追加できないから。

関数

void 関数名() { } のような形で宣言できる。

一点注意点として、ここのvoidというのはTypescriptでいうところの戻り値の型を定義するような部分なので、jsのようなfunction, rubyでいうところのdefのような関数宣言ではないことに注意。

例えば名前をさん付けで返すような関数を定義したい場合は、


 string returnHonorificsName(string name)
        {
            return name + "さん";
        }

        string sample = returnHonorificsName("永井");

        Debug.Log(sample); #=> 永井さん

となる。

デバッグ方法

ちょこちょこ出てきてはいるので気づいてはいるかもしれないですが、UnityでC#を使う場合はそのままだとConsole.Write(Line)は使えないので

Debug.Log('some')

のようにデバッグを行う。

実行すると呼ばれたときに、Unity側のconsoleに出力される。

出力されたコンソールの内容をダブルクリックすると該当ソースまで飛ぶことができる。便利。

適当なスクリプトが正しく動くか試してみたい場合などは、空のオブジェクトを作成し、そこにスクリプトを張り付けて実行するとよい。

余談として

コメントアウトは//

複数行は/* ~~ */

Class

クラスもほかの言語を経験していたら似ているのでわかりやすい。

public class PlayerModel
    {
        public string name; //外から参照できる。ふつうはやらない
        int hp;
        int strength;

        //プロパティを作る。
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public PlayerModel(string name, int hp = 30, int strength = 100)
        {
            this.name = name;
            this.hp = hp;
            this.strength = strength;
        }

        public void sayName()
        {
            Debug.Log(name);
        }
    }

まず

  public string name; //外から参照できる。ふつうはやらない
    int hp;
    int strength;

この辺。 publicと書いておくと

PlayerModel player = new Player(”勇者”) としたときなどにplayer.nameとして外から使用できる。影響範囲とか慣習とか諸々あるのだと思うが、基本的にやっちゃだめらしい。

じゃあどうするのがいいかというと、Rubyなどにも存在するゲッターとセッターをつかう。

下記の部分。


   //プロパティを作る。
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

Debug.Log(player.Name);といった感じで呼び出せる。

つづいてはここ。Rubyのinitializeみたいなもの。生成されたときに呼び出される。


   public PlayerModel(string name, int hp = 30, int strength = 100)
    {
        this.name = name;
        this.hp = hp;
        this.strength = strength;
    }

引数をとることができる。

初期生成時に任意の値を入れたいときには引数をここで受け取れるようにしたい。

デフォルト値も設定することができ、初期値が与えられなかった場合にその値が使われることになる。

プロパティ相当の変数はthisをつけることで引数と区別して呼び出すことができる。

最後に関数。


public void sayName()
    {
        Debug.Log(name);
    }

インスタンスメソッドよろしく関数の定義も勿論できる。

Railsを使ってると割とこの辺り勝手にいい感じにしてくれているのであんまり自分で定義することもなかったりする。スピードは確かにでるので恩恵は受けているのだが、Railsのブラックボックス感も同時に感じるよね。

実際にUnityで使うときは別のコンポーネントを変数などで受け取り、それぞれをトリガーにして操作することになるので、C#の知識が雑でもなんか作れる。


 Transform tf = GetComponent<Transform>();
 tf.position = new Vector3(1.0f, 0, 1);

とはいえ、ちょっと知っておくと変なところで躓かないので暇があればちょいちょい動かしていきたいと思います。