Character第二章のプレイヤーキャラクター周りを実装、解説していきます。第一章に比べて今回の動画は35分!心が折れそうだ......プレイヤーオブジェクトの設置Models>Characters内のPlayerオブジェクトをシーンに設置。Positionが原点にあることを確認。オブジェクトのTagをPlayerに設定しておく。AnimationContorollerを用いたプレイヤーのアニメーション遷移設定Animationフォルダを作成し、そこにAnimationControllerを作成、名前はPlayerACとする。これをPlayerオブジェクトにアタッチする。Animatorを開き、アニメーションの遷移を設定していく。私の環境(Unity2018.2.1f)では、動画(Unity4.6)にないEntryというStateが存在する。Entryからアニメーションが出発する、という意味なので動画で触れられている通りEntryからTransitionが伸びているアニメーションがデフォルトのアニメとなる。ProjectビューにあるPlayerの直下から3つのアニメーションモデルDeath,Idle,MoveをAnimatorへD&D、Idleをデフォルトのアニメとする。2つのParameter、Bool型のIsWalkingとTrigger型のDieを作成したら、IdleからMoveへTransitionをつなぎ、このTransitionをクリックして遷移条件となるConditionをIsWalking:Trueに設定する。MoveからIdleへ戻るTransitionも同様につなぎ、こちらのConditionはIsWalking:Falseとする。Deathアニメはどの状態からも遷移させたいのでAnyStateからTransitionをつなぎ、ConditionをDieに設定する。Playerオブジェクトの設定PlayerにRigidbodyを付与。Drag,AngularDragを”infinity”(無限)に変更。Dragは力をかけて動くときの空気抵抗、AngularDragは回転をかけて動くときの空気抵抗。公式リファレンスでは”無限の場合、オブジェクトは動きを止めます。”とのことだがよく意味が分からない。動かしたらわかるだろう。継続的な力が止まった時に、0:動き続ける~100:わりと強い力で止まる、無限:すぐ止まる、みたいな感じかな?また、今回のゲームではジャンプなどY軸方向の動きやローリングなどはさせないので、FreezePositionでY軸にチェックを入れ、FreezeRotationでX軸とZ軸にチェックを入れる。ColliderはCapsuleColliderを付与。Centerは(0.2,0.6,0)に設定。被弾した時のSEを再生するためAudioSourceも付与し、PlayerHurtをAudioClipとして設定。PlayOnAwakeはチェックを外しておく。Playerの移動に関するScriptを作成LayerMask.GetMaskってなに?調べてみると、文字列から(この場合Floorという名前のオブジェクトを指定して)レイヤーマスクを作成する関数のようだ。そもそもレイヤーマスクって何?Raycastの引数に指定すると、このレイヤーマスクにだけRayが衝突するようになる、ってことかいな?でもそれをInt型の変数FloorMaskに格納してるのが意味わからん。なんでInt?http://unitylab.wiki.fc2.com/wiki/%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC%E3%83%9E%E3%82%B9%E3%82%AFここを見る限り、Unityのオブジェクトは必ずどこかのLayerに所属しており、整数のLayer番号を持つ。今回の場合はFloorオブジェクトの所属するLayer番号を取得し、それをIntに収めている、という理解でいいのかな?とりあえず調べるのはこの辺にしておこう。のちのちRaycastが出てくるはずなので、たぶんその時にわかるんじゃないかな。縦横移動の操作には、GetAxisRawを用いる。GetAxisが―1~1の間の小数も含めた値をとるのに対して、こちらは-1,0,1の整数のみを返す。加速したり減速したり、といった挙動には向かず、きびきびした挙動をすることになる。GetAxisRawを記述するのは、UpdateメソッドでなくFixedUpdateメソッドが望ましい。アニメーションを制御するコードは、独立したメソッドとし、FixedUpdate内から呼び出すこととする。した方がコードがすっきりするからである。(これはリーダブルコードにも書いてあった)movement.set(h,0,v)のmovementは頭で宣言したVector3型の変数である。Vector3.Set(x,y,z)でVector3を(x,y,z)に書き換えるので、たとえば→方向への入力を受けると、movement(1,0,0)となる。このVector3変数をオブジェクトのpositionへ加算してやれば(※代入ではない)、右方向へ1動く、というわけだ。hとvが同時に入力されたとき、オブジェクトの動くベクトルは斜め方向になるわけだが、一辺が1の二等辺直角三角形の斜辺は√2≒1.4であるため、上下左右よりも移動量が大きくなってしまう。これを解消するために、movementをnormalizedして大きさを1に統一してあげる必要がある。移動速度を調整できるように冒頭で宣言したspeed関数も乗算する。Time.deltaTimeも乗算する。1秒間に移動する単位を平均化するためである。Updateは初期設定で50fps,1秒間に50回実行される。Update内から呼び出される(予定)ため、もちろん1秒間に50回実行されるわけだ。すると、1秒間キーを押しっぱなしにしていれば50単位分移動をすることになる。キーを押した瞬間、もうこの愛らしい彼は銀河カナタへおさらばだ。それを防ぐのがTime.deltaTime。「前回のUpdateが呼び出されてから今回のUpdateが呼び出されるまでに何秒かかったか」を返す。30fpsなら、約0.03を返すわけだ。1*{time.deltaTime(≒0.03)}+1*{time.deltaTime(≒0.03)}....この合計が1になったタイミングが1秒経過したタイミングになるので、1秒間に移動する単位を1単位に均すことができるということ。最後に、オブジェクトのPositionにmovementを加算してやることで、キー入力に応じてオブジェクトが移動するようになる。(この時点ではfixedupdateにmoveメソッドの呼び出しがないのでまだ動かないが。)rigidbody.positionが空間のワープを行うのに対し、ここで使われているrigidbody.MovePositionという関数は、(isKinematicがtrueの時に限り)座標間の限りなく高速な移動、という動きになるらしい。なんだといわれると僕にはよくわからないのだが、とにかく0→1のイメージと0,0.1,0.2....1.0のイメージとしてとらえておく。方向転換にはRayを利用する。カメラからスクリーン上のマウスポインタの位置に向かって伸びるRay、camRayを宣言。Rayが衝突した時の情報を格納するためにRaycastHit型の関数も宣言する。ここでやっとlayerMaskの意味が分かりかける。layerMaskに指定したオブジェクトのみが、Colliderとは別にRayとの衝突判定を持つ、という理解でよさそうだ。Rayの使い方については、下記が詳しく、わかりやすい。Physics.Raycast自体はRayが何かに衝突したらtrueを返すので、ifの条件にそのまま使える。衝突したら、プレイヤーの場所から衝突した場所の方向へのベクトルを取得、プレイヤーの向きをrigidbody.MoveRotation(vector3)でそのベクトルの方向へ変更する。Quaternionについては、下記が詳しい。(わざわざ自分で書くのがめんどくさくなってきた...)Playerのアニメーション遷移を実装このboolの宣言の仕方は初めて見た。意味は「walking変数は、h,vのどちらかに0でない値が入っている=動いているときにtrueである」ということを書いているのだと思うが、こういう書き方していいんだ。