2.Player 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メソッドが望ましい。前者は描画ごとに実行されるため1秒間の実行回数がPCの処理能力に左右されるが、後者は1秒間の実行回数が固定されているためである。
移動、方向転換、アニメーションを制御するコードは、独立したメソッドとし、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秒間に移動する単位を平均化するためである。
前提として、UnityのFixed Updateは初期設定で50fps,1秒間に50回実行される。このMoveメソッドは、Fixed 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のイメージとしてとらえておく。
Playerの方向転換を実装
方向転換にはRayを利用する。まずはカメラからスクリーン上のマウスポインタの位置に向かって伸びるRay、camRayを宣言。Rayが衝突した時の情報を格納するためにRaycastHit型の関数も宣言する。
さあ、出てきたぞ、Physics.Raycast()は、Physics.Raycast(飛ばすRay,衝突した時の諸情報を格納するRaycastHit,Rayの長さ,Rayが衝突する対象を選択=layerMask *option)だ。ここでやっとlayerMaskの意味が分かりかける。layerMaskに指定したオブジェクトのみが、Colliderとは別にRayとの衝突判定を持つ、という理解でよさそうだ。
Rayの使い方については、下記が詳しく、わかりやすい。
Physics.Raycast自体はRayが何かに衝突したらtrueを返すので、ifの条件にそのまま使える。で、衝突したら、プレイヤーの場所から衝突した場所の方向へのベクトルを取得、プレイヤーの向きをrigidbody.MoveRotation(vector3)でそのベクトルの方向へ変更する。
Quaternionについては、下記が詳しい。(わざわざ自分で書くのがめんどくさくなってきた…)
Playerのアニメーション遷移を実装
このboolの宣言の仕方は初めて見た。意味は「walking変数は、h,vのどちらかに0でない値が入っている=動いているときにtrueである」ということを書いているのだと思うが、こういう書き方していいんだ。
https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/bool
また、
bool
として評価される式もbool
変数に代入できます。
これやね。そして、animator.SetBool(“trueにするパラメータの名前”,trueにする条件)でIsWalkingパラメータをtrueにすることでアニメーションのStateが遷移するわけだ。
実装確認
三つ、トラブルが起きました。
1.プレイヤーの移動が速すぎる
movementの値をnormalizedする行を書き忘れていたのが原因でした。ブログに書いてコードのほうに書き忘れてた凡ミスです。
2.プレイヤーが歩くアニメーションに移行しない
AnimatorのTransitionのInspectorにあるHas Exit Timeにチェックが入ったままだったことが原因です。これにより、アニメーションの再生が終わるまで次のアニメーションに移行しない状態になっていました。チェックを外してあげます。
3.プレイヤーの方向転換がされない
試しにfloormaskの指定を削除し
if(Physics.Raycast(camRay,out floorHit, camRayLength))
に書き換えてみたところうまく回転するようになりました。floorMaskがうまく実装できていないようです。Floorオブジェクトをよく見てみるとLayerがDefaultのままになっていました。これを8:Floorにしてやったところ、無事に回転するようになりました。
これでChapter.2は終了。次はChapter.3ではCameraをセットアップしていきます。
Chapter.3はコチラ
Chapter.1はこちら
「Unity公式チュートリアルSurvival Shooter日本語実践Chapter.2」への2件のフィードバック