ブルーノのC#プログラミング & unity勉強日記

プログラミング素人、ブルーノの自主勉強ノートです。他のプログラミングを勉強したい方の助けになれば幸いです。その他趣味の雑記もしていきたいです

Unityスクリプト / レンダラー編

f:id:covory10101101276:20180505092234j:plain

クスコ - ペルー

 

みなさんこんにちは🌚

昨日はディズニーシーに急に思い立って友人と遊びに行ってきたブルーノです。もうファストパスも発券できなかったし乗れた大きなアトラクションはタワーオブテラーくらいだったけどめちゃくちゃ楽しかったです。エンターテイメントの代表的な一角としてゲーム作りの上でキャラクターやアトラクションなど参考にできそうです🤓

さて今回は、あらゆるゲーム画面上に描画されるオブジェクトを我々が視認するのに欠かせないメッシュやマテリアルといったレンダラーについてまとめていきます。

 

メッシュとレンダラー

CreatePrimitive()メソッド等で生成されるオブジェクトも含み、我々がゲームシーン上で目にするオブジェクトにはほとんどの場合Mesh FilterMesh Rendererコンポーネントがついています。この二つはどうやらどちらも欠かすことはできないようです。

f:id:covory10101101276:20180505102315p:plain

メッシュフィルターがその頂点(ポリゴン)情報をもち、メッシュレンダラーがそれを画面上に描写するコンポーネントのようです。そしてこのメッシュレンダラーにマテリアルの情報を送ることでそれを元にポリゴン情報に絵をつけていくといった流れでしょうか。

スクリプトからこのメッシュを追加したり編集するにはUnityEngine.MeshFilterクラスからアクセスします。

そしてメッシュはmeshプロパティで取得または追加できます

meshプロパティ

public Mesh mesh { get; set; }

そして追加したメッシュ情報を描画するためのレンダラーはUnityEngine.MeshRendererクラスを用います。

f:id:covory10101101276:20180507142507p:plain

以前学習した[SerializeField]とコルーチンの機能を使っていますね。外部からは勝手にアクセスされないようprivate修飾子を使いつつもインスペクター上からメッシュを設定できるようにし、コルーチンを使うことでUpdateメソッドを使わずに3秒おきにメッシュ変数m1とm2のメッシュ情報を交互にメッシュレンダラー変数mf1に代入しています。

f:id:covory10101101276:20180507142516p:plain

SerializeFieldによりprivateでもこの欄から設定できます。試しにCubeとCapsuleを入れてみました。

f:id:covory10101101276:20180507142525p:plain

交互にオブジェクトのシルエットが変わりますね。でもこの新たに生成したオブジェクトにはマテリアルの情報がないため色や質がわからず、とりあえずピンクっぽい色で表示されています。

 

マテリアル

 マテリアル(Material)はその名の通りその物質の情報です。ザラザラした感じやツルツルした光沢、色やくすみなどの情報を持っています。先ほど自前で用意したゲームオブジェクトには形の情報があるのにこのマテリアルの情報がなかったためにどこからみても全ての部分が同じピンク色という不気味な物質になってしまっていたんですね。

まあ今までにも何回かこのマテリアルを編集したことはありますがインスペクタービューではこんな形でしたね。

f:id:covory10101101276:20180508230032p:plain

メッシュレンダラーのマテリアルの欄から指定することで使用でき、デフォルトでは真っ白になっています。ではこれをスクリプトから使用・追加するにはどうするのでしょうか

マテリアル関係のオブジェクトはUnityEngine.Materialクラスに属します。

UnityEngine.Materialクラス

public class Material : Object

スクリプトからマテリアルを生成する際には元のマテリアルを複製して使用するか、シェーダーを使うという2パターンがあるそうです。シェーダーについてはまだよくわからないので別の機会に勉強します。MeshRendererオブジェクトからmaterial(s)プロパティにアクセスした場合にマテリアルが初期化されていない場合はデフォルトマテリアルが自動で実装されるそうです。ここから色や質感を指定できるので個人的にはこの機能はポイントだと思います。

色の変更はcolorプロパティから取得もしくは変更します。

colorプロパティ

public Color color { get; set; }

 しかし色だけでは全ての質感を表現するのは難しいです。確かに本物そっくりに陰影をつけたりして見分けがつかない絵などもありますが角度が変わればわかってしまいますね。そのためマテリアルには表面にテクスチャを一緒に貼り付けることが多いです。

MaterialクラスのmainTextureプロパティからテクスチャを設定できます。

mainTextureプロパティ

public Texture mainTexture { get; set; }

 実際に使ってみます

f:id:covory10101101276:20180508232648p:plain

先ほどと似たコードのようですね。空のゲームオブジェクトを作成し、3秒ごとにランダムな色を持たせます。

f:id:covory10101101276:20180508232655p:plain

スクリプトから形状を指定すると

f:id:covory10101101276:20180508232702p:plain

色が変わりましたね!

f:id:covory10101101276:20180508232706p:plain

Default-Materialが生成されているのも確認できます。

メッシュの頂点生成などもあるのですが、スクリプトでこれを制御するのはちょっとコアすぎるのでやめておきます笑

今回はここまでにしておきます。

 

ついに始まったUnite Tokyo 2018!!初日の基調講演から行っているのですがもう全てが最高ですね!!🤩専門的なことは難しいですが実践しながら見せてくれる講演もあって違いが視覚的にわかりやすいし、私でもわかるレベルに噛み砕いてくれる講演も多々あって本当に初心者から上級者まで楽しめるものだと思います!絶対に行く価値がありますよ!

明日最終日もいろんなこと聞いてみて回るかー🙃😙

Unityスクリプト / レンダリング編

f:id:covory10101101276:20180424192438j:plain

モハーの断崖 -アイルランド-

 

みなさんこんにちは🌚

早くゲームを作り始めたいのに他のサイトで情報を仕入れようとするたびに新しいワードや概念が出てきて自分の知識の無さにやきもきしながらも焦燥に駆られているブルーノです。さあ早く今回の分を勉強していきましょう!!

 

今回からはレンダリングについてです。レンダリングとは、ゲームの無機質なデータにあらゆる手法で彩りを持たせることを言います。今回やるのは主に照明やカメラワークについてです。

 

カメラ

カメラはUnityに最初に触れた時からずっと使い続けているコンポーネントですね。プロジェクトを新規に立ち上げた際にデフォルトで用意されているMain Cameraはいわゆるカメラコンポーネントを持ったゲームオブジェクトです。グランツーリスモなど視点が急に切り替わったりするゲームにみられるように、複数配置することができます。

コンポーネントからカメラをコントロールする場合はUnityEngine.Cameraクラスによって表されます。

UnityEngine.Cameraクラス

public sealed class Camera : Behaviour

 ゲーム空間でのオブジェクト同士の当たり判定はそれぞれ3次元座標内で交差しているかどうかで判断できますが、我々が覗いているゲーム画面は2次元です。クリックやタッチがオブジェクトに触れているかどうかを判定するにはスクリーン上の座標から光線を取得するScreenPointRay()メソッドを使用します。

ScreenPointRay()メソッド

public Ray ScreenPointToRay(Vector3 position);

positionパラメータにスクリーン上の座標を指定することでそこから画面奥方向へ向かう光線を取得できるので、PhysicsクラスRaycast()メソッドでそれが何かと衝突しているかを取得できます。ちなみにScreenPointToRay()メソッドの引数はVector3型ですが奥行きのz座標は使われません。

Raycast()メソッド

public static bool Raycast(Ray ray, out RaycastHit hitinfo);

 rayパラメータには先ほどのような光線の情報を入れ、hitinfoパラメータには光線に衝突したオブジェクトの情報を格納するRaycastHit構造体を指定します。引数の変数を変更するためref, outキーワードを使っています。構造体にいちいち値を代入しておかなくてもいいようにoutキーワードの方を使っていますね。

RaycastHit構造体

public static RaycastHit

RaycastHit構造体には衝突したゲームオブジェクトの情報が入っています。

では例を書いてみます。

f:id:covory10101101276:20180424203144p:plain

マウスがおかれている座標から光線を飛ばし(14行目)、それが衝突したオブジェクトの情報をhit構造体に格納し座標を加工して返す(17, 18行目)というものですね。短い中に色々工程が詰め込まれている。。。

f:id:covory10101101276:20180424203404p:plain

マウスがcubeに乗っている間だけ回転する!

 

照明

照明はゲームにおいてもドラマや映画などにおいても非常に重要ですよね。雰囲気から心情や状況の描写まで照明によってその印象は全く違うものになってきます。

UnityのLightコンポーネントには4種類の照明が用意されており、Typeの欄からその種類を変更できます。

f:id:covory10101101276:20180424204246p:plain

Spot :文字通り一箇所を円錐状に照らします。スポットライトですね

Directional :イメージとしては太陽の光のように、光源の座標に関係なくある角度から全てのオブジェクトを照らします。

Point :ある一点から周り全方位を照らします。蛍みたいです。

Area(baked Only) :四角形の範囲で照らす光ですが、演算処理の負荷が大きいらしく、リアルタイムでの描写はできないようになっています。そのため、ベイクしてもう周囲のオブジェクトへ反映のさせ方を決めておく必要があります。しかし柔らかくリアリティのある光が演出できるそうです。

 スクリプトからLightコンポーネントのこれらの種類を変更するにはLightTypeプロパティから取得もしくは変更します。

LightTypeプロパティ

public LightType type { get; set; }

 このtypeはエディター上で設定する時と同じ名前で大丈夫です。

ではプログラムを作成していきます

f:id:covory10101101276:20180504083709p:plain

Start()メソッド内で光を写す床と光源を持ったオブジェクトを用意し、位置を整えます。

そしてUpdate()メソッドでは時間に応じて光がDirectionalとSpotを交互に変えるようになっています。16, 27, 30行目でライトを変更するLighttypeプロパティが使われています。

f:id:covory10101101276:20180505100355p:plain

2秒ごとにスポットライトとディレクショナルライトが切り替わるようになりました。

このようにスクリプトからもエディター上でできる全ての変更を行うことができるんですね。今回はここまでにしておきます。

 

久々の更新です😇今も色々準備やら何やらで忙しいんですがこうして合間を縫って勉強しています。このGWも予定がいっぱいっぱい...Unite Tokyoもいくしお金がかつかつだな笑

Unityスクリプト / Mathematics編

f:id:covory10101101276:20180423211739j:plain

山の羊の群

 

みなさんこんにちは🌚

テニスの錦織がまた最近浮上してきてくれて嬉しいブルーノです。ノーシードのタフさを感じたと言っていましたがやはり一度落ちると調子を取り戻すのは大変のようですね。リスクとの兼ね合いも考えながら頑張って欲しいです🤯

 

さて今回は、今までのものとは少し違い、目に見えないけどゲームやアプリを作る上で欠かせなさそうな数学的要素の取り扱いについて学んでいきたいと思います。

 

乱数

乱数という言葉の存在や仕組みはポケモンのおかげで今やだいぶ普通の人にも浸透してきたのではないかと勝手に推測していますが、乱数とは実行するまで結果が確定できないランダムな値のことを指します。乱数を使うにはUnityEnigine.Randomクラスvalueプロパティから取得します。

UnityEnigine.Randomクラス

public sealed class Random

 

valueプロパティ

public static float value { get; }

このプロパティを使用することで、0.0 ~ 1.0までのランダムな値を取得します。乱数は呼び出すたびに新しいものが用意されます。実際に書いてみましょう

f:id:covory10101101276:20180424164429p:plain

Vector3の各座標を0 ~ 5の値で生成してはその座標にcubeを作るというのを100回繰り返すコードです。結果は以下の通りになります。

f:id:covory10101101276:20180424165527p:plain

なんか綺麗ですね。この結果は再生するたびに違うものになります

f:id:covory10101101276:20180424165609p:plain

先ほどはvalueプロパティの値に色々引いたりかけたりして5という範囲までの数字を得ましたが、それをしなくても同じくRandomクラスのRangeプロパティを使用することで最初から指定した範囲内で乱数を得ることもできます。

Rangeプロパティ

public static float Range(float min, float max);

public static int Range(int min, int max);

minとmaxパラメータにそれぞれの型の数字を入力することでその範囲内の乱数を得ることができます。ちなみにmaxの値は含まれません

 先ほど乱数はランダムな数値と言いましたが、実は完全にランダムではありません。シード値と呼ばれるタネをもとに作られます。基本的にシード値には現在時刻を利用しますが、Randomクラスのseedプロパティを利用すると自分でシード値を設定できます。つまり、シード値を保持しておけばそれを基に同じ乱数を生成することができるのです。

seedプロパティ

public static int seed { get; set; }

 

四元数

我々はUnityの中で角度を扱う場合、一般的にオイラー角を使用します。オイラー角は区間上にX, Y, Z軸のそれぞれの角度を持っていて、Vector3変数でそれを扱うことが多いです。しかしオイラー角ではジンバルロックという問題があり、正確に回転を表現できないことがあります。そこで4番目の数字を使うことでこれを回避しようということで、実際のUnity内部では四元数というものを用いています。まあ難しい解説は私にもわからないのでポイントだけ抑えると、「なぜUnityでは四元数で計算しているのに我々がUnityエディタ等で触れるのはオイラー角だけなのか」ということ。つまり四元数を我々に触れさせる前にオイラー角に直してくれているんですね。

まず四元数はQuaternion構造体を用いて表されます。

Quaternion構造体

public Quaternion(float x, float y, float z, float w);

 そして四元数オイラー角に直すには、Euler()メソッドを使います。

public static Quaternion Euler(Vector3 euler);

public static Quaternion Euler(float x, float y, float z);

 引数にVector3型の情報を入れることでQuatrnion構造体に変換してくれます。これで、Unityが扱う他の空間系のメソッドに使うことができます。

では当然逆のもの、つまりQuaternion構造体からオイラー角を出してくれるメソッドもあるはずですね。それがeulerAnglesプロパティになります。

eulerAnglesプロパティ

public Vector3 eulerAngles { get; set; }

四元数オイラー角間の計算にはコストがかかるため、Updateメソッドなどで連続して使用するのは避けたほうがいいらしいです。

他にも掘り出せばキリがないので、ゲームのときに頻出しそうなコンテンツだけを抜粋してみました。今回はこれで終わります。

 

人の気持ちというのはとても単純で複雑ですね。見方を変えれば全く理解できなかったものの気持ちというのも理解できるもんですね😙そしてそのきっかけは結局自分に回帰するのだと思います😎気の持ちようで周りは変わる

あーきのこスープ飲みたい

Unityスクリプト / 入力編(2/2)

f:id:covory10101101276:20180420220448j:plain

オカバンゴ・デルタ - ボツワナ

みなさんこんにちは🌚

 いきなり夏みたいな暑さで溶けそうなブルーノです。少し家を空けたら家の外より中の方が暑くなっていました🤑

さて今回は前回に引き続きUnityの入力について、少し特殊な入力方法について勉強していきます。

 

インプットマネージャー / Input Manager

Unityには、インプットマネージャーと呼ばれる機能があり、これを使うと複数の入力デバイスからの操作を1つのコマンドで設定することができます。簡単に説明すると、例えば「垂直方向」という名前をつけてその操作方法の中身を作っておけば、PSのコントローラーでもGCのコントローラーでも垂直方向キーにこの「垂直方向」インプットマネージャーを適応すれば微妙な調整や検証もいらずに直感的に操作方法を当てることができます。もう一つの特徴としては、単純にON・OFFで操作しないカーブや移動といった動作を滑らかに自然にしてくれる効果があります。百聞は一見に如かず ↓

f:id:covory10101101276:20180422161912p:plain

インプットマネージャーは[Edit]→[Project Setting]→[Input]から設定します

f:id:covory10101101276:20180422162014p:plain

色々メニューがありますが、[Horizontal]を覗いてみましょう。

AxisのSizeはこの入力の設定の数ですね。デフォルトでは18個入っているようです。足したい場合はこの値を増やしてリストを増やします。ちなみに個々の設定を仮想軸と言い、2方向の入力の仕方を設定します。(水平や垂直など。1方向でも可)

では上から順に各項目を説明します

Name: この仮想軸の名前を決めます。

Descriptive Name:仮想軸の正方向の説明です。ゲームランチャーでボタンを割り当てる際に表示されます。

Descrictive Negative:負方向の説明になります

Negative Button:負方向のボタンを設定できます

Positive Bottun:正方向のボタンを設定できます

Alt Negative Bottun:負方向の予備を設定できます

Alt Positive Bottun:正方向の予備を設定できます

Gravity:ボタンを話したときにどれくらいの速度で0に戻ろうとするかを設定します。大きいほどすぐ0に戻ります

Dead:0の範囲を設定します。つまり絶対値が0以上の値を入力しておけばそれ以下の時は反応しなくなります。

Sensitivity:反応の感度です。高ければすぐに反応し、低ければ鈍くなります。

Snap:反対方向の入力をされた時、値をすぐに0から始めるかを設定できます。

Invert:正負の方向を逆にします。

Type:仮想軸の操作を適応するデバイスの種類を設定します。

Axis:方向を指定します。例えば上のTypeでマウスの仮想軸を[X axis]に設定していた場合、カーソルを水平方向に移動した場合反応します。

Joy Num:コントローラーの番号を指定します。デフォルトでは全てのコントローラーを受け付けます。

そして設定した仮想軸を扱うにはGetAxis()メソッドを使用します 

GetAxis()メソッド

public static float GetAxis(string axisName)

 axisNameパラメータに使用する仮想軸の名前を記述します。例を書いてみます。

f:id:covory10101101276:20180422202359p:plain

とてもわかりやすい。だからGetAxisメソッドの中はstring型で戻り値はfloatなんですね。たまに使うことはありましたが疑問に思っていました。

f:id:covory10101101276:20180422202518p:plain

わかりにくいですが、キーを押した瞬間からは少しだけ加速し、離してから滑らかに停止します。

 

加速度

バイスの動きを感知する加速度センサーは今やどんなあらゆるケータイに搭載されており、ゲーム機にもその機能を取り入れたものはどんどん出ています。いまいちその使い方に成功したように思えるゲームを私はあまり知らないのですが、新しい機能はどんどん開拓してみたいですね。

accelerationプロパティは現在の加速度をVector3型で取得できるプロパティです。

f:id:covory10101101276:20180422210815p:plain

そのままオブジェクトのRotateに適用できるので設定は楽ですね。

f:id:covory10101101276:20180422210826p:plain

IPhoneを振るとその方向によってCubeも異なる方向へ回転し始めます。操気弾を使ってるみたいで気分が良いです。

 

ジャイロスコープ

加速度センサーではデバイスの動きを感知することはできますが、傾きは感知されていません。一般的にスマホなどの傾けて遊ぶゲームは加速度センサーよりもこちらのジャイロスコープの方が使われているのではないでしょうか。

ジャイロスコープから得られる情報はgyroプロパティから取得することができます。

gyroプロパティ

public static Gyroscope gyro { get; }

このプロパティはジャイロスコープからの情報をUnityEngine.Gyroscopeクラスオブジェクトを返します。

なお、デフォルトではジャイロスコープの機能はOFFになっているのでスクリプトから利用する場合はまずenabledプロパティで有効にします。

enabledプロパティ

public bool enabled { get; set; }

 このプロパティにtrueがセットされていればジャイロスコープの情報を得ることができます。そしていよいよ、現在のデバイスの姿勢をattitudeプロパティから取得します。

attitudeプロパティ

public Quaterion attitude { get; }

 実際に作ってみましょう

f:id:covory10101101276:20180423202254p:plain

25行目で一度Quaterion型の変数anglesに角度を格納し、その中の数値をGUIに表示します

f:id:covory10101101276:20180423202451p:plain

バイスを傾けるとそれに応じてCubeも傾きました。意思を持ってるみたいで面白いですね。今回はここまでにしておきます。

 

最近よく自分で料理を作ってみようとチャレンジしているのですが結局煮るか炒めるかになってしまうんですよね。なんかワンランク上のおしゃれな料理入門に最適な料理ってないですかねー🤔

Unityスクリプト / 入力編(1/2)

f:id:covory10101101276:20180420130851j:plain

キューケンホフ公園 - オランダ

みなさんこんにちは🌚

ミッチ・アルバムの作品を勉強中のブルーノです。人間が決められた原子の組成でこの記憶も感情もあるのなら私が今ここに存在している確率でもう一度この私でいつか蘇ることができるのではないかと思うんですがどうでしょう?

 

さて今回は、あらゆる機器からの入力を受け取る方法について書いていきます。

我々が機械に物理的に意思を伝える方法としてはマウスやキーボード、タッチなど様々なものがあり、自分が作るゲームやアプリを使うデバイスが持つ入力装置からの入力を受け取れるようにしなければなりません。Unityでどうやってそれを行うのかを見ていきます。前編はマウス・キーボード・タッチの超基本的な入力についてです。

 

マウス入力

まず、全ての入力はUnityEngine.Inputクラスに内包されています。

UnityEnigine.Inputクラス

public sealed class Input

マウスカーソルの座標はmousePositionプロパティです。

mousePositionプロパティ

public static Vector3 mousePosition { get; }

 ゲーム画面の左下隅を(0, 0)としてマウスカーソルの座標をピクセル単位で返します。Vector3なのでx, y, zは横・高さ・奥行きがありますが奥行きのzの値は常に0です。

例を見てみましょう

f:id:covory10101101276:20180420142431p:plain

OnGUIメソッドから現在の座標を入力してみました。GUI.Label関数の引数の文章は""を入れてstring型にしないと直接Input.mousepositionプロパティの値(float型)を入れることはできないんですね。いやstring型にしないといけないのはわかってるけどこんな簡単でいいのか😃

f:id:covory10101101276:20180420142435p:plain

このスクショをとるときの最後の地点が限りなく左下でした笑

 

マウスボタン

マウスのボタンが押されているかはGetMouseBottun()メソッドから得ます。

GetMouseBottun()メソッド

public static bool GetMouseBottun(int button);

buttonパラメータにはそれぞれ左ボタン0, 右ボタン1,真ん中のボタン2のいずれかが入ります。指定したボタンが押されていればtrue,そうでなければfalseを返します。

例を書いてみます。

f:id:covory10101101276:20180420180227p:plain

16行目でvar rに、マウスが押されていれば10を、そうでなければ1を代入し、次の17行目でY軸の回転量にそれを代入します。

f:id:covory10101101276:20180420180359p:plain

マウスを押した時と通常時で回るスピードが変わる!

ところでこれだと、Update()メソッドに記述すると押された時のフレーム全てにtrueを返してしまうので決定ボタンとかの時は変な反応になってしまいますね。そこで「押してない→押してる」や「押してる→押してない」の時だけ反応するメソッドもあります。それが以下の二つです

GetMouseBottunDown()メソッド

public static bool GetMouseBottunDown(int button);

GetMouseBottunUp()メソッド

public static bool GetMouseBottunUp(int button);

Downの方は押された瞬間のみtrueを返し、Upは話された瞬間のみtrueを返します。どちらも戻り値はtrueです。

 

キーボード入力

キーボードのキーが押されているかはGetKey()メソッドを使用します。 

GetKey()メソッド

public static bool GetKey(KeyCode Key)

public static bool GetKey(String name)

KeyCodeとは列挙型の変数で、中には全てのキーに対応した変数があります。そしてそれは文字通り標準的なキーボードのキー名を入れると出てきます。例えばスペースキーならSpace, AならAといった感じです。string型のnameパラメータはその文字を小文字で表します。今の例ならスペースは"space"、Aなら"a"といった感じです。

例を見てみましょう。

f:id:covory10101101276:20180420182043p:plain

さっきとほぼ同じですが、17行目がスラッシュ(Slash, / )キーを押された時、に変わっています。

 

f:id:covory10101101276:20180420182140p:plain

結果も同じですね。

当然マウスの時と同じように「押してない→押してる」や「押してる→押してない」の時だけ反応するメソッドも存在します。

GetKeyDown()メソッド

public static bool GetKeyDown(KeyCode Key);

public static bool GetKeyDown(string name);

GetMouseBottunUp()メソッド

public static bool GetKeyUp(KeyCode Key);

public static bool GetKeyDown(string name);

 

マルチタッチ

スマホのタッチはマウス同様座標を取得するポインティングデバイスという種類のうちの一つに入りますが、マウスとは違いスマホでは複数の入力を得ることができ、それをマルチタッチと呼びます。現在画面をタッチしているポイントの数を取得するにはtouchCountプロパティを使います。

touchCountプロパティ

Public static int touchCount { get; }

それぞれのタッチされているポイントの情報を扱いたい場合はGetTouch()メソッドから使います。

GetTouch()メソッド

public static Touch GetTouch(int index);

indexパラメータにはtouchCountプロパティの返した値、つまりタッチされているポイントの数だけ範囲を持つインデックスになります。3本指でタッチしている間はインデックスは最大3になり、最初のポインタのインデックスは0にあたります。

全てのタッチ情報を配列で得たい場合はtouchesプロパティで得られます。

touchesプロパティ

public static Touch[] touches { get; }

 得る情報はUnityEngine.Touch構造体で表されています。

UnityEngine.Touch構造体

public struct Touch

タッチ情報の座標はpositionプロパティから得ることができます。

public Vector2 position { get; }

これもまたマウスの時と同じように左下隅を原点として(横, 縦)の座標を返します。

では実際に書いてみます。

f:id:covory10101101276:20180420195050p:plain

for文のiをインデックスにもつtouch.positionの座標を全て表示します。

GUI.Label()メソッドだけだとインデックスの数を一つ送りにできなかったので別のint型のmで計算しました。

結果は以下のようになります

f:id:covory10101101276:20180420195322p:plain

5本指でスマホに触れながらスクショをとりました。タッチ入力はUnityRemoteというアプリからできるのでそれを使いました。

得られたタッチの開始された瞬間や離された瞬間、現在移動しているかどうかの情報もphaseプロパティを使うことで得ることができます。

phaseプロパティ

public Touchphase phase { get; }

TouchPhaseは列挙型の変数で、中にはそれぞれ

Beganメンバ → タッチされた瞬間

Endedメンバ → 離された瞬間

Movedメンバ → 動いている間

Statinaryメンバ → 停止している間

Canceldメンバ → 何らかの原因でタッチの状態を追跡できなかった場合

が入っています。実際に確かめてみます。

f:id:covory10101101276:20180420202210p:plain

f:id:covory10101101276:20180420202221p:plain

一方の指は動かしながら、もう一方の指は止まった状態でスクショしました。ちゃんと「Moved」と「Stationary」と表示されていますね。

 

タップ

タップ?タッチとどう違う?と一瞬思いますが

タッチ → タッチパネルに触れている

タップ → タッチパネルに短い間隔で押して離す

という違いがあるんですね。このタップの回数はtapCountプロパティで取得できます。

tapCountプロパティ

public int tapCount { get; }

個々のタッチにはそれぞれIDが割り振られ、連続して押されたものなのかどうかを判断できます。そしてその連続でタップされた回数分だけこのプロパティに蓄積されるというわけです。実際に書いてみます

f:id:covory10101101276:20180420201045p:plain

f:id:covory10101101276:20180420201122p:plain

短い間隔でタップするとその指の回数はカウントされていきます。

では最後に、先ほど少し触れたタッチ情報のIDの識別の仕方を学びます。

タッチ情報を格納するTouch構造体はクラス(参照型)ではなく構造体(値型)なのでインスタンス化して識別はできません。そこで、Touch構造体にはfingerIDプロパティというIDが割り振られています。では確かめてみます

f:id:covory10101101276:20180420203049p:plain

f:id:covory10101101276:20180420203100p:plain

一度4本指で画面をタッチし、最初の2本を離したのち、また一本の指で画面に触れました。最初の2本を離してIDナンバー0と1は消えましたが、2と3は残ったままでした。そこで新たに触れるとまたナンバー0が生成されました。確かにそれぞれのタッチに識別番号が割り振られているようですね。

我々が普段何気なく行なっている操作も細分化するとこんなにいっぱい種類があってそれら全てに対応する物が用意されているんですねー🙂

今回はここまでにしておきます。

 

仕事の定義って何でしょう。私は社会的に需要があることと責任が伴うことが条件だと思っています。いや、責任が伴うから社会的に需要が生まれるのか?いずれにせよ、その条件を満たせば趣味も仕事にできるのではないのでしょうか。どうでもいいか笑

Unityスクリプト / ゲーム内の時間編

f:id:covory10101101276:20180418193133j:plain

インドネシアのどこか

 

みなさんこんにちは🌚

最近日本酒が臭く感じなくなってぐびぐび飲めるようになってきてしまったブルーノです。別に一人で飲む機会が多いわけではないので友達と飲むときしか影響はないのですが自分の体が恐ろしくなってきました笑

 

さて今回は、ゲーム内の時間の概念とその扱い方についてやっていきたいと思います。

 

ゲームの時間管理

ゲームの中の時間は我々の世界とは違い、フレーム単位で進んでいきます。じゃあそれは何秒なの?と言われるとこれまた設定次第なので一概に何秒とは言えませんが、とにかく我々の1秒と同じではありません。そのフレームの設定から逆算すれば1秒をゲーム内で測れますが、毎回時間を扱う際にそのゲームと我々の時間の比率を計算するのはめんどくさいですよね。そこでUnityEngine.Timeクラスtimeプロパティを使うことで、起動後から呼び出されるまでの時間を我々の時間で測ってくれます!

UnityEngine.Timeクラス

public sealed class Time

timeプロパティ

public static float time { get; }

逆にフレームを測りたい場合は同じくTimeクラスのframeCountプロパティを使用します

frameCountプロパティ

public static int frameCount { get; }

 ちょっとみてみましょう

f:id:covory10101101276:20180418195925p:plain

7行目でまず座標を作り、22行目でTimeクラスからそれぞれframeCountとtimeプロパティを使っています。結果は以下の通りです

f:id:covory10101101276:20180418200106p:plain

フレームと我々の時間の進みはやはり全然違うようですね。

ゲーム内の時間はフレーム単位で進むことはわかりましたが、では我々の時間基準でオブジェクトを動かしたい場合や進行させたいときは時間をただ刻み続けているtimeプロパティじゃあまり意味がありませんね。そこで使われるのがdeltatimeプロパティです。このdeltatimeプロパティは呼び出される直前のフレームから現在のフレームまでにかかった時間を我々の時間単位に直してfloat型で返してくれます。つまりめっちゃ早いです。なんでそれで時間が測れるんだよって思いますがつまり、1秒が60フレームのゲームなら1/60の値を返すので、1秒に動いてほしい値にかけることでその1/60の値になります。それを1秒間に60回かけるのでうまく1秒にその値が収まるという理屈だそうです。厳密には違うそうです笑

では例を見ていきましょう

f:id:covory10101101276:20180418201821p:plain

これをCubeにつけます

f:id:covory10101101276:20180418201842p:plain

およそ1秒で一周していますね。室伏みたいですね。このdeltatimeプロパティは今後ゲームにしろアプリにしろ必須になってきそうですね。

 

フレームレートの設定

UnityはUpdateメソッドが毎回呼び出され更新することでゲームが動いてるように見えます。このUpdateメソッドが1秒間に呼び出される回数のことをフレームレートと言います。この回数はモニタのスペックに影響され、その画面が1秒に描画できる回数分がデフォルトでは設定されます。この画面の1秒に描画できる回数を垂直同期周波数(カッコイイ)というのですが、この設定を変更することもできます。エディタ画面上部の[Edit]から[Project Setting]の[Quality]を開きます

f:id:covory10101101276:20180419181801p:plain

インスペクターにQuality Settingが表示されます。

f:id:covory10101101276:20180419181840p:plain

このメニューの下側「Other」の欄のVSync Countから変更できます。

f:id:covory10101101276:20180419181947p:plain

デフォルトの「Every V Blank」は垂直同期周波数のことを表します。「Don't Sync」は垂直同期しないことを表し、「Every Second Sync」はEvery V Blankの半分の回数になります。

基本的に垂直同期しないという設定は使いませんがEvery Second Syncのように更新回数を減らせばモバイル向けアプリの動作を軽くしたりできます。

 

処理の分割 - コルーチン

RPGゲーなどの場合、次のマップの読み込みやエフェクトの描画、入力の受付など、様々な処理が同時に行われてますが、もし1フレームに次のマップの読み込み全てを行わせていたらとてもじゃないですがPS4オープンワールドなんかは時間がかかってしまいます。マップの読み込みだけでなく、派手なエフェクトの描画なども処理が遅れれば処理落ちし、それがひどいとストレスがすごいですよね。そこでUnityでは、C#がもともと持っているコルーチンという機能を使ってその作業を何フレームにも分割させられるそうです。コルーチンは関数の途中で処理を中断し、その関数から抜け、再度呼び出された時に中断した部分から処理を行える機能です。

コルーチンはC#の「IEnumeratorインターフェイス」で表されます。インターフェイス懐かしいけど難しかった😓これとyield return文を使うことで書くことができます。

そしてIEnumeratorインターフェイスを呼び出すにはStartCoroutine()メソッドを使います。

StartCoroutine()メソッド

public Coroutine StartCoroutine(IEnumerator routine);

 routineの場所にIEnumeratorインターフェイスを記述します。

手順としては

IEnumeratorインターフェイスを記述

その中の中断するポイントでyield return文を書く

StartCoroutine()メソッドで呼び出す

となります。実際にちょっと書いてみます

f:id:covory10101101276:20180419200013p:plain

13行目〜30行目にIEnumeratorインターフェイスのRotateRoutineを記述し、その中にyield return null文を入れています。nullは「空っぽ」のことで、特に何もアクションをせずにここで処理を中断するのですが他にも色々なアクションをここで起こすことができます。例えばここに

StartCoroutine() →別のコルーチンに入る

WaitForSeconds() →一定時間待機する。

といったメソッドを入れることができます。とても便利!

さてこのスクリプトではfor文の中で「Vector3変数の r 文だけ回転するという処理を90回繰り返し」for文を抜け、向きを90度変えてcount変数に+1するというのを繰り返すものになっており、count変数が4になった場合初めて文が終わる仕組みになっています。コルーチンは関数の状態を保持してくれるのでこの中でcount変数を宣言しても再び呼び出したときにリセットされないし、rの中身もしっかり保持されていますね。

結果は以下のようになります

f:id:covory10101101276:20180419200603p:plain

4回向きが変わったところで止まりました。Startメソッドの中で書いたものがずっと動き続けたり中身がずっと保持されてるなんて現実世界の考え方にすごく近くなっていますね。これだけわかればますます直感的にゲームをみんなが作れるようになるんじゃないか....?

ほんとはコルーチンはそれ単体で記事を書く必要がありそうなくらい便利で奥深そうですが今回はここまでにしておきます。

 

今年2018年のUnityカンファレンスイベントの早割チケットを実は購入しました!ちっともスキルも伴ってない実戦経験皆無の素人ですがたくさん刺激をもらっておこうと思います😄それまでは勉強だ😤

 

 

Unityスクリプト / コンポーネント編

f:id:covory10101101276:20180410215406j:plain

ケルン大聖堂

みなさんこんにちは🌚

ゴッドイーターが面白くてアニメゲームアプリと全部やってしまっているブルーノです。ディストピア世界って好きなんですよねー。3が楽しみです!

 

さて今回はスクリプトからのコンポーネントの操作についての基礎を学んでいきたいと思います。コンポーネントといえば、シーン上から追加するには画面上部の[Component]もしくはゲームオブジェクトのインスペクター上の[Add Component]ボタンから追加することができ、目に見えるものからその性質まで色々な機能を持たせることができましたね。

f:id:covory10101101276:20180411073906p:plain

f:id:covory10101101276:20180411073914p:plain

 

コンポーネントの追加

全てのコンポーネントUnityEngine.Componentクラスから派生しています。スクリプトを書く際に派生させているMonobehaviourクラスもまたこのComponentクラスから派生しており、スクリプトコンポーネント(部品)として扱われることを表しています。さて、そのコンポーネントの追加ですが、スクリプトでこれを行うにはAddComponent()メソッドを使用します。

AddComponent()メソッド

public Component AddComponent(Type componentType)

public T AddComponent<T>() where T : Component

一つめのcomponentTypeにはコンポーネントの型を表すTypeオブジェクトを指定します。

二つめは前にやったジェネリックを使って型を指定しています。

dreameaters5239.hatenablog.com

dreameaters5239.hatenablog.com

 ジェネリックメソッドは中身の型をメソッド名の<>で表すことで中の同じ名前の部分が全て型パラメータの型になる便利なメソッドでしたね。言葉で今回の例を表すならば「<T>のコンポーネントを追加する」のTの部分に<スクリプトの名前>だったり<摩擦係数の名前>だったりを入れるといったとこでしょうか。 

例の上の文は、斜線の部分にコンポーネント名を入力します。また、componentTypeを入力する際は「typeof」キーワードをつけて戻り値はComponent型となります。例をちょっとみてみましょう

f:id:covory10101101276:20180418102916p:plain

f:id:covory10101101276:20180418102921p:plain

今回はスクリプトを二つ用意し、2つめのスクリプトには常にy軸を回転させることしか書いていません。一つめのスクリプトの9行目でまずCubeを生成し、次の10行目でBruno_Script02を追加しています。

f:id:covory10101101276:20180418104419p:plain

くるくる回っていますね。

さて、先ほども言いましたが、AddComponent()メソッドで返される値はComponent型になって返ってきます。もちろんコンポーネントは皆Componet型を基として派生しているので動作上何も問題はありませんが、それを取得したり編集したりする場合その派生先、今回だったらBruno_Script02型として運用できた方が安全性も高まります。型キャストやas演算子を用いて2行で書くこともできますが、ジェネリックを使用すれば一行で収められます。例をみてみましょう。

f:id:covory10101101276:20180418110149p:plain

f:id:covory10101101276:20180418110200p:plain

Bruno_Script02にAnglesプロパティを追加し、回転量をBruno_Script01から設定しています。これができるのもBruno_Script02型として扱っているためその中のAnglesにアクセスできています。ちなみにさっきのメソッドで扱おうとしたら

f:id:covory10101101276:20180418110445p:plain

「Component型にはAnglesなんてものはないよ!」と怒られました。やはり基底クラスの中で探すようですね。。。

 

コンポーネントの取得

では次にオブジェクトに付いているコンポーネントの取得をしてみます。

コンポーネントの取得はGetComponent()メソッドで行います。

GetComponent()メソッド

public Component GetComponent(Type componentType)

public T GetComponent<T>() where T : Component

AddComponent()メソッドの時の「Add」部分が「Get」に置き換わっただけですね。使い方も同じです。例を書いてみます。

f:id:covory10101101276:20180418113958p:plain

今度はcubeにあるBruno_Script02の情報を取得し、その情報がなければAddComponentで追加するという流れです。

f:id:covory10101101276:20180418114256p:plain

結果は同じですね。

 

コンポーネントの削除

スクリプトからオブジェクトを削除するにはDestroy()メソッドを使用します。

使い方はゲームオブジェクト編の時と同じです。

 

dreameaters5239.hatenablog.com

 例を作成してみます。

f:id:covory10101101276:20180418153052p:plain

15行目にDestroy()メソッドを作り、5秒後に回転量が格納されているrot変数を削除します。

f:id:covory10101101276:20180418153203p:plain

5秒後に止まりました。

 

プロパティをエディタから編集

コンポーネントの状態はインスペクターから覗くことができ、変数をpublicにしておけば変数の値をインスペクター上に表示することもできます。しかし参照型のフィールドをpublicにしておくのはどこからでも容易にアクセスできる状態にしてしまうことに他なりません。そこで、privateな変数をインスペクター上に表示して編集できるようにすることができます。そこで使われるのがUnityEngine.SerializeField属性になります。

UnityEngine.SerializeField属性

public sealed class SerializeField : Attribute

また長い名前ですね。使い方の例を載せます。

f:id:covory10101101276:20180418183137p:plain

private変数の手前に[SerializeField]とつけています。

f:id:covory10101101276:20180418183553p:plain

インスペクターから値を入力することで回ります。

f:id:covory10101101276:20180418184256p:plain

これにより、privateな変数である_x, _y, _zがインスペクターに表示されました。紛らわしいのですが、このインスペクターの名前は_(アンダースコア)の後のものを大文字にしているのでこのインスペクターには9行目は表示されていません。アクセッサの仕様ということでしょうか。これで安全性を強化できますね。

 

必須コンポーネントの指定

では最後に、先ほどやった属性の別のものも重要そうだったのでまとめておきます。

今まで作ったスクリプトの中にも、別のスクリプトが必要なものがいくつもありました。そのスクリプトだけをつけても意味がない場合、もしくは他のコンポーネントが必要な場合、普通であればコンパイルエラーを起こしますよね。その時に何が足りないかがすぐわかると良いですよね。そこで今度はUnityEngine.RequireComponent属性を使うことでそのスクリプトが依存しているコンポーネントを明示することができます。

UnityEngine.RequireComponent属性

public sealed class RequireComponent : Attribute

実際に見てみましょう

f:id:covory10101101276:20180418190702p:plain

f:id:covory10101101276:20180418190712p:plain

Bruno_Script01のクラスよりも頭に[RequiredComponent(typeof(Bruno_Script02))]とありますね。このスクリプトがBruno_Script02に依存していることを表します。そしてBruno_Script02はDebug.Logで"I'm awakend"と言うだけです。

f:id:covory10101101276:20180418191030p:plain

実行するとメッセージが表示されました。さあ本番はここからです。エディタからBruno_Script02を除こうとすると...

f:id:covory10101101276:20180418191043p:plain

Bruno_Script01は02に依存しているため削除できません。と表示されました。こういったゲーム内だけでなく開発側からのサポートをスクリプトから行うこともできるんですね。今回はここまでにしておきます。

 

いやーどうしても毎回この勉強は記事が長くなりがち&ハテナの山です。最近は講義の方も忙しくなってきて、英語の勉強もしなければならなくなってきました。別に英語は嫌いじゃないんですがどうしても単調になりがちですね。根気のない私にはプログラミングの勉強の方が山あり谷ありで楽しいです😊

今日講義で聞いたのですが、英語の勉強は「将来役立つから」とか「学校で覚えなきゃいけないから」といった直接的な理由だと苦手な人は続かないそうです。そういったときは自分の好きなことに結びつけて考え、実際に活用してみると良いそうです。私だったら洋ゲーに多く触れる、とかスクリプトの理解を容易にする、とかですかね。実際英語が少しできるだけでだいぶ助かってます😀

まーでも勉強をやる理由は自分の世界を広げるために他ならないですよね。自分のしたいことができたとき、自分の中にどれだけあらゆることに知識があるか、見聞があるかでその中身の充実度が変わってきますから。それに大学入るまで気づかないなんて勿体無かった....°(ಗдಗ。)°.