UnityではじめるC#4日目

学習内容

  • 物理パズルの作成
  • PlayerPrefsの使い方

◇物理パズルの作成

やったこととしては、ゲームの要素となるボール、壁3種類(移動可能な壁、移動歩かな壁、ゲーム外検出壁)、ゴールをゲームオブジェクトとしてシーン上に配置して、Rigidbody2Dやコライダーなどのコンポーネントをアタッチ。
ゲームのリセット機能を付けたり、ボールやシーン上に配置する壁はPrefab化しておいて、新しいステージなどを作成する際やリセット時にオブジェクトを配置できるようにしたりしました。
また、ステージ選択画面を作ったりと、今までやってきたこと(Unity5の教科書も含めて)を総動員してゲームを形にしていきました。
なので、内容としては今までやってきたことの復習といった感じだったので、細かいことは割愛します。

◇PlayerPrefsの使い方

復習的な内容だったと話したばかりですが、どこまでクリアしたのかを記録できる機能をつけるために、PlayerPrefsの使い方を新しく学習したので、内容をまとめておこうと思います。

ステージセレクト時にいきなり先のステージに進むことが無いよう、クリアしたステージを確認し、直前のステージがクリアされていれば選択できるような機能を実装しました。ただ、そのためにはクリアしたステージの情報を記録する仕組みが必要です。
通常の変数はプログラムを終了すると情報が消えてしまうので、ファイルのように終了後も残すためには別の方法を使う必要がありますが、そのため仕組みとしてPlayerPrefsがUnityでは用意されています。
これを使うと、各OSの特定の場所にデータを保存してくれます。
使い方としてはPlayerPrefsクラスの関数を使って書き込みと読み出しを行うことが出来ます。
読み書きにはキーと呼ばれる値に識別するための名前と値となる数値や文字列をセットにします。具体的には以下のようにします。

PlayerPrefsクラスのメソッドには値を設定するSet○○○と、値を取得するGet○○○があって、それぞれfloat型用とint型用、string型用の三種類が用意されています。そのほかキーと値を削除するDeleteAll、DeleteKeyなどもあります。Set○○○で書き込んだ値はアプリの終了時にファイルに保存されますが、クラッシュすることもあるので、PlayerPrefsに用意されているSave関数を使えば確実に保存できます。
これらを使って、ゲームのクリア時にデータをセーブし、先のステージが解放されるような機能を実装しました。
ただ、いろいろ実験してみて理解を深めようとしてみたのですが、いまいち使い方が理解できていないので、もう少しリファレンス読んでみたり、ほかの技術ブログをのぞいてみたりしたいと思います。

・memo

Invoke関数:第一引数に指定した名前の関数を第2引数で指定した時間後に呼び出す関数。

そんなこんなで、完成したゲームがこちら。

これでUnityではじめるC#終了です!
3日程度で終了できましたが、学ぶことが多く、密度の濃い3日でした!
「脱出ゲーム」と「物理パズル」という2大定番ゲームの作り方を勉強できたのもかなり大きいです。
次はUnityの寺子屋を勉強しようと思います。
その傍らで、今月中を目標に自作アプリを作ってリリースしたいと思っているので、どんなアプリにするのか企画もしていきたいと思います。
それでは今日はこの辺で。

UnityではじめるC# 3日目

学習内容

chap5:脱出ゲームの作成

・タイトル画面の作成

UIの作成:ボタンなどUIの部品を配置する土台となるCanvasを作成し、UIを作りこむ。
Canvasはゲーム画面よりもはるかに大きいサイズで作成されるが、これでは単位の基準があてにならないので調整する。
調整はヒエラルキービューのRender ModeをScreenSpace-Cameraに変え、Render CameraにMain Cameraオブジェクトをドラッグ&ドロップ。
Canvas Scalarコンポーネントはキャンバス上のUIパーツのサイズを調整する。
UI Scale ModeをScale with Screen Sizeに変更した場合、Reference Resolutionに指定した改造お土(画面サイズ)を基準に調整する。
Screen Match Modeは実機の画面サイズとReference Resolutionの設定値が一致しない場合に用いる。
UIパーツはTransformコンポーネントの代わりにRectTransformコンポーネントを持っている。このコンポーネントでは、Anchorの種類によって、位置かサイズを自動調整できる。
UIパーツはボタンやテキストなど、必要なものは一通りそろっているので、それらを使って、UIをデザインしていく。
ボタンなどのUIパーツはイベントを持っており、そこにどのメソッドを呼び出すのかを指定することが出来る。
ボタンの場合にはOnClickイベントがある。クリックされたときにどんな関数が呼び出されるかなどを指定することが出来る。

変数を宣言する際に変数型の前にconstを付けると定数化することが出来る。
定数の名前は変数名との見分けがつくように、慣用的に大文字のアルファベットを使う。

ボタンに関数を割り当てる
ボタンのOnClickイベントに関数を割り当てるには、割り当てたい関数を含むコンポーネントがアタッチされたオブジェクトなどをNoneの部分にドラッグ&ドロップし、Functionの選択から割り当てたい関数を選ぶことで割り当てることが出来る。
そうすることで、ボタンがクリックされた際に、OnClickイベントに割り当てた関数が呼び出され、実行される。

あとはひたすらゲームオブジェクトをボタンとして設置して、関数割り当てて、条件を満たしていればアイテムが手に入って…と、繰り返し作業してゲームが完成しました!

完成したゲームはこちら。

そんなこんなで脱出ゲームも完成しました!
この本を始めてからまだ3日目(1日目と2日目の内容は1日で終わっているので実質2日)でここまで来ているのでかなりさくさく進めてる実感があります!
この本が終わった後は、自作の寺子屋などにチャレンジするつもりですが、その後は1つ自力でゲームを作ってgoogleのプレイストアにリリースしてみようと考えています。

とりあえず、次の物理パズルゲームの作成を頑張ります。

それではこの辺で!

UnityではじめるC# 2日目

今日の学習内容

  • chap4:Unityプログラミングの基本

◇イベント関数:Start、Update関数などのこと。ほかにはOnMouseClickやOnMouseDrag、OnColisionEnter、OnColisionExitなどがある。
Update関数などの定期イベントはフレーム事に呼び出される。fpsが60くらいだとすると、Update関数もそのぐらいの頻度で呼び出される。

定期イベントにはFixedUpdate、Update、LateUpdateの三種類がある。

  • LateUpdate関数:すべてのゲームオブジェクトの更新が完了した後でやりたい処理がある場合に用いる。一般的には全キャラクターの移動が完了したあとで、それに合わせてカメラを移動する処理などがある。
  • FixedUpdate関数 : 固定間隔で呼び出される 定期イベント。3Dゲームなどで1フレームの処理がパソコンで処理しきれないほど重い場合には、描画しきれないフレームが間引かれ、fpsが減ってしまうことがある。これによって不都合が生じる場合に用いられる。

◇画像の表示

2DゲームではTextureTypeがSprite(2D and UI)になっている必要がある。
Generate Mip Mapsは3Dゲームのテクスチャ向けの設定で、有効にしているとスマートフォン上で画像がぼやけることがあるので、2Dゲームの場合にはオフにする。

SpriteRendererコンポーネント:Spriteタイプの画像を表示するコンポーネント。Spriteに指定された画像がゲームオブジェクトの外観として表示される。

◇インスペクタ―上の「プロパティ」

インスペクター上のTransformコンポーネントでPositionなどのプロパティを調整することで、オブジェクトの正確な位置などを調節できる。
ここでいう「プロパティ」とはC#のプロパティとは異なるものであるが、Unityのクラスが持つプロパティやメンバー変数はたいていインスペクタ―上に表示されるので、同じものと考えてもあながち間違えではない。
スクリプトコンポーネントをオブジェクトにアタッチすると、スクリプト上でpublicで宣言されているメンバ変数はインスペクタビューから値を設定できるようになることからも、ほぼ同じものといえるだろう。

◇コンポーネントの取得

GetComponent関数:ゲームオブジェクトが持っているコンポーネントを取得する関数。下記の表のような種類が存在する。

メンバー働き
gameObject変数
このコンポーネントがアタッチされているゲームオブジェクト
transform変数ゲームオブジェクトにアタッチされたTransform
GetComponent関数ゲームオブジェクトから指定した型のコンポーネントを取得
GetComponentChildren関数子オブジェクトのコンポーネントを取得
GetComponentParent関数親オブジェクトのコンポーネントを取得
GetComponents関数ゲームオブジェクトから指定した型のコンポーネントをすべて取得する(同じコンポーネントが複数アタッチされている場合に使用)

◇Transformクラスの利用

Transformコンポーネント(C#から見るとTransformクラスのインスタンス)を取得し、Transformクラスのメンバー変数を使うことが良くあるが、Transformクラスは次の表のようなメンバー変数を持っている。

メンバー働き
localPotion変数ローカル空間(親オブジェクト基準)の位置
localRotation変数 ローカル空間(親オブジェクト基準)の回転
position変数ワールド空間の位置
rotation変数ワールド空間の回転
localScale変数親オブジェクトを基準にした相対的な拡大縮小率
LookAt関数対象をTransformの方向を向かせる?
TransformPoint関数ローカル空間からワールド空間へpositionを変換

ここでpositionはVector3型の変数であるが、Vector3はクラスではない。
Vector3は構造体と呼ばれ、classではなくstructで定義する。クラスに似ているが、intなどと同様に値として扱われる。変数から変数に代入された場合には、クラスであればインスタンスを参照する状態になるが、Vector3では別のものにメンバー変数がコピーされた状態になる。

◇Prefab→割愛

とりあえず4章を終わらせました。

4章は簡単なスクリプトを書いてみて、オブジェクトを動かしてみる感じでした。
内容的にもキホンのキの部分という感じなので、Unity5の教科書には載っていない部分だけピックアップして書き出してみています。
4章のようなオブジェクトをスクリプトコンポーネントで制御することは、すでにUnity5の教科書を通してやってきているので、スクリプトを読んで理解できたら再現はしない方針にしました。

chap5では脱出ゲームを作るようなので、こちらはしっかりスクリプトの意味を理解しつつ、実際に手を動かして取り組んでみようと思います。

ここまで読んでくださりありがとうございます。
それでは、この辺で。

UnityではしめるC# 1日目

どうも、みりんです。

今日からUnityではじめるC#に取り組んでいこうと思います。
すでに、Unity5の教科書に取り組み、一通りやり切ったのでこの本はサクサク進めていければと思っております。
進めている中で、大切だと感じだり、未知だったもの、復習もかねて残しておきたいことなどをここに書いているつもりです。
すでに一冊取り組んでいることもあり、記述内容が少しずつ絞られていく可能性があるので、内容が薄くなってしまったら申し訳ないですw

学習内容

  • chap1:Unityの基本操作→割愛
  • chap2:C#の基本
  • chap3:条件分岐と繰り返し処理

◇chap2:C#の基本

スクリプトの上の行→usingディレクティブ。Unityのクラスなどを使うために宣言。
スクリプト名のクラスの定義→後続の{}ブロックがスクリプト名のクラスの内容であることを意味している。
Start&Updateメソッド(関数)→2つの関数の役割については割愛。関数は必ずクラスの中に書く。
四則演算では、複数の演算子を使う場合、通常の四則演算と演算子の優先順位が異なるため注意が必要。
例えば以下の計算。

実行結果は「1+8×4は132」となる。*は+よりも計算の優先順位が高いため、8×4が計算され32となる。その後+の計算処理が行われるが、+は左の+から優先的に計算を行うため、前の文字列と1との間で文字列の連結が行われ、上記の実行結果となる。これは()を用いて計算の優先度を変えることで、防ぐことが出来る。
この辺りは、実際の数学と同じですね。

以下に演算子と優先順位を表としてまとめたものを載せておきます。
これは本のp34 表2-02と同じものです。本をお持ちの方はそちらを参照してみてください。

優先順位カテゴリ演算子
1基本式. () [] x++ x– new typeof checked unchecked
2単項式+ – ! ~ ++x –X (T)x
3乗法式* / %
4加法式+ –
5シフト<< >>
6関係式と型検査< > <= >= is as
7等値式== !=
8AND(論理)&
9XOR(論理)^
10OR(論理)|
11AND(条件)&&
12OR(条件)||
13条件?:
14代入= *= /= += -= <<= >>=&= ^= |=

C#の変数にはいくつかの型が存在する。代表的なのはint、float、double、string、boolなど。このように標準で用意されている型は組み込み型と呼ばれる。
型はクラスからつくることもできる。
変数の型と代入しようとしている値の型が異なる場合には代入することが出来ませんが、型変換(キャスト)と呼ばれる機能を使うことで、代入しようとしている値の型を変換して代入することが出来ます。キャストは下記のように記述します。

この場合、int型なので小数点以下は切り下げられてしまいます。この型変換はどんな型同士でもできるわけではなく、数値同士など関連性があるものだけで可能だそうです。

・クラス:変数や関数をグループ化して扱う「くくり」のようなもの。

Unityであればゲームオブジェクトそれぞれがクラスと関連付けられる。
例えば、プレイヤークラス、エネミークラスなど。こういった考えをオブジェクト指向と呼んでいます。
クラスを使って様々な実態のある物(Unityでいえばゲームオブジェクト)を作ります。例えばプレイヤークラスというものがあったら、実際にプレイヤーが操作する主人公となるキャラクターのことです。こういった、クラスを使って作成した実態(オブジェクト)はインスタンスと呼ばれます。
インスタンスはnew演算子を使って作ることが出来ます。
クラスを作るという事は、新しい型を作ることと同義なので、クラスを作るたびに新しい型が出来ていることになります。
あるクラスの中の関数や変数を利用したい場面があるかと思いますが、その時には.(ドット)演算子を使うことで、それらの関数、変数を呼び出して使うことが出来ます。

・クラスの継承

プレイヤークラス以外にも、NPCクラスなどの似通ったクラスを作る場合、それぞれに同じ機能が必要になる場合がありますが、そのような場合には継承を使います。継承を使うことで、元(親)となるクラスの変数や関数を引き継いだ新しいクラスを作ることが可能になります。共通する機能は親のクラスに記述し、新しく追加したい機能や個別に設定したい機能は子のクラスに記述することで、共通部分は記述せず、新しいクラスを定義することが出来ます。
Unityでは下記の1文が書かれていますが、これが継承です。

MonoBehaviourクラスを継承したLessonというクラスを作るという事を意味しています。Start関数やUpdate関数はMonoBehaviourクラスの機能で、これを継承しているため、新しく作ったスクリプト上で使用することが出来ています。
ただし、継承することのできる親クラスは1つまでなので、複数の親クラスから継承して新しいクラスを作ることはできないので注意です。

・インスタンスと変数の寿命

インスタンスはnewを使って作成し、変数に代入しますが、実際に変数にインスタンスが代入されているわけではありません。イメージとしては作成したインスタンスにアクセスできるチャンネル=変数と考えると良いかもしれません。インスタンスは変数によって参照されている(チャンネルが作られている)状態でないと、一定時間で自動的に消えてしまうので、消したくないインスタンスは常に変数から参照されている状態にしておく必要があります。

・変数のスコープ(寿命)

クラスのメンバー変数と関数内で宣言した変数にはスコープの違いが存在します。
メンバー変数はクラスのインスタンスが消滅するまで存在し続けますが、関数中の変数は所属している関数の処理が終わるまでしか存在し続けられません。
すべての変数がメンバー変数になってしまうと管理が大変になります。変数の寿命が決まっていれば、その変数が生きている間だけ管理すれば良く、異なるブロックであれば、同じ名前の変数を作ることもできるので、気軽に使い捨てることが出来ます。

◇chap3:条件分岐と繰り返し処理

・if文:割愛

・switch文:ifがtrueかfalseの2択なのに対し、switchでは条件を複数に分岐させることが可能。
switch文の変数や式の内容と、caseラベルの値が一致するかを比較し、一致した場合にはcase~break文までを実行する。
どのcaseとも一致しない場合は、default文の後の処理が実行される。
caseラベルには式や変数を使うことが出来ず、数値か文字列のみが使える。
そのため、等しいか否かしかチェックすることができない。
caseラベルを続けて記述すると、二つのうちどちらかのラベルと一致した場合に処理が実行される。

・条件演算子?:

?:を使うことで、演算子だけで条件分岐を作ることが出来る。
変数=条件式?trueの値:falseの値;
これを使うことでifよりも短い文で条件分岐を記述することが出来るが、わかりづらいので、無理に使う必要もないらしい。

・繰り返し文

for、while、do-while→割愛
continue文:繰り返し処理を1回スキップする。

・配列(変数):複数のデータを記録できる変数。普通の変数には値を1つしか代入することが出来ないが、配列には値を代入できる枠が複数あり、それぞれに別々の値を代入できる。イメージは棚やロッカー。
配列内の1つ1つの変数のことを要素、箱一つ一つに付けられている番号のことを添字と呼ぶ。
配列の宣言は以下のように行う。

これでarrayという要素数5個(箱の数が5個)の配列変数が宣言されていることになる。
配列はArrayという特殊なクラスのインスタンスで、宣言した配列変数はArray型インスタンスを参照している状態になっている。
そのため、下記のように1つのインスタンスを複数の配列変数で参照したり、宣言後の配列変数に別のインスタンスを参照させたりすることも可能。

配列変数も以下のように初期化させることが出来る。

複数の値を代入できるのは最初の宣言時のみで、宣言が済んだ配列変数に後から複数の値を記録することはできず、1つの要素ずつに代入していく必要がある。
要素に値を代入するには、変数名[添字]と書くことで、普通の変数と同じように使うことが出来る。ただし、添字は0からスタートすることに注意すること。

・クラス型の配列変数

クラス型の配列変数は次のように宣言する

クラス型の配列変数も{}を使うことで初期化することが出来る。
上記のスクリプトで

Person[] parr = new Person[5] → Person[] parr = {new Parson(), new Parson()}

と書き換えると要素数2の初期化された配列変数になる。
ただ、メンバ変数に後から代入するのが面倒なので、初期化時にそこまで済ませておきたい。

・コンストラクタ―

コンストラクタ―:クラスと同じ名前の特殊な関数でインスタンス作成時に初期値を設定する。関数の書き方と同じだが、呼び出す際にはnewを用いる。

上記のプログラム文にコンストラクタ―を追記すると下記の通り。

ただし、このままだとエラーが出てしまう。これは、クラス型の変数に=new Person()と代入していたが、このnew Person()も実はコンストラクタで、クラスの宣言時にデフォルトで用意されていたコンストラクタである。
これが引数を二つ持つ新しいコンストラクタを定義したことで使えなくなってしまうためである。
コンストラクタは作成する際に必ずクラス名で宣言しなければならないことを考えると、2つ以上のコンストラクタを作れないように見えるかもしれない。
しかし、コンストラクタは戻り値と引数の型が異なれば、同名のコンストラクタを作ることが可能であるので、新たに引数も返り値も持たないコンストラクタを宣言してやれば問題ない。
つまり、上記のスクリプトにpublic Person(){}を追記してやればよい。
これは関数の宣言時にも同じことができる。

コンストラクタを設定したことで、Start関数内の記述を下記のようにすることが出来る。

・多次元配列→割愛

・foreach文:配列変数と組み合わせやすいように改良されたfor文
下記のように使う。

foreachでは配列の要素が順番に変数に入るため、簡単に配列変数の全要素を処理することが出来る。上の場合、1,2,3,4,5とコンソールウィンドウに表示される。

・Array.Sort関数

Arrayクラスには配列変数の操作を助ける便利な関数を持っている。その中でも、Array.Sort関数は昇順に並べ替えることが出来る関数。引数に配列を渡すことで、中の要素を昇順に並べ替え、昇順になった配列を返してくれる。

・List<T>クラス

配列変数の要素数を変えることが出来るクラス。<T>の部分を型パラメータと呼び、Lsit<int>のように記録したい方を指定する。リストを操作するための下記のようなメンバ関数やプロパティが用意されている。

メンバー働き
Addメソッド末尾に要素を追加する。
Clearメソッドすべての要素を削除する。
Countプロパティ現在の要素の数を取得する。
Containsメソッドある要素がリスト内にあるか調べる。
Sortメソッドリストを並べ帰る(※静的メソッドでないので、変数名.Sort()の形で呼び出す。
Insertメソッド指定した位置に要素を挿入する。
Removeメソッド指定した位置の要素を削除する。

List<T>の使用例は以下の通り。

Add関数で要素を決定し、Countプロパティで要素数を調べている。
Add(4)の状態であれば、最終的にコンソールビューに4が表示され、Add(32)であれば、コンソールビューには32が表示される。

ということで、とりあえずchap3まで取り組みました。
一部、飛ばした部分もありましたが、C#の基本的なところは知らないことが多いので、とても勉強になりました。特にクラスやインスタンス、コンストラクタはあまり理解できていなかったところだったので、わかりやすく解説してあってよかったです。

プログラミングの基礎知識中心で退屈なところでしたし、未知の部分やクラスなどの考え方など、理解の難しい部分も多かった割にはサクサク進められたと思うので、この調子でこの先も進んでいきたいですね。

ここまで読んでくださり、ありがとうございました。
それでは、この辺で。