とりあえず完成しているゲームを見てみましょう。
Unityを開き、Demo
フォルダにあるDemo
シーンを開きます。
- レイアウト: 2 by 3
- One Column Layout
Directional Lightを作ります。 ハードシャドウの設定をしましょう。
GameObject → Create Other → Cube
名前を「Ground」としましょう。
適当に縦長になるようにグイーっと大きくしましょう
シーンを保存するのは大事です。
Player/Character/TeddyBear
をシーン上にドラッグ&ドロップしましょう。
位置を(0,0,0)の位置に移動しましょう。
Groundの中に埋もれちゃいましたね。モデルのYを0.5にしてみましょう。うまくGroundの上に立ちました。
カメラの位置をクマの後ろに来るように調整してみましょう。
Positionを(0, 3, -2)、Rotationを(30, 0, 0)にすると期待するカメラの位置になります。
プレイボタンを押して再生してみましょう。何も起こりません。
モデルに動きを与えてみましょう
Player/Animations/Run
をシーン上のモデルに向けてドラッグ&ドロップしましょう。
再生してみましょう。モデルが奥に走って行きました。
奥に走って行きましたがカメラが動いていないため、どんどんモデルが遠くに行ってしまいます。今回はモデルにカメラを追従させるようにしてみましょう。
ヒエラルキー
を見て、Main CameraをTeddyBearにドラッグ&ドロップしましょう。
再生してみましょう。カメラが追従するようになりました。
GameObject → Create Other → Cube
名前をBlockとします。Groundの幅に収まるように横に細長い障害物を作りましょう。
細かい値を言うと、Positionが(0, 0.7, 4)、Scaleが(4, 0.4, 0.5)になります。
再生してみましょう。すり抜けちゃいました。でも今はこれでいいんです。
モデルが障害物に当たったという判定を実装していきましょう。
モデルにCapsule Colliderをアタッチします。緑の枠がコライダーと言われる物があたったという判定を行えるエリアですが、モデルに対してズレてGroundに埋まってしまっています。
なのでCenterのYを少しずらし、Rudiusを変更して大きさをモデルに合わせましょう。
CenterのYは0.5、Rudiusは0.3に設定します。
次に、モデルにRigidbodyをアタッチします。これで当たり判定の準備が出来ました。再生してみましょう。
おや、再生直後に前に倒れてしまいました。
モデルが前に倒れた理由ですがこれはRigidbodyによって、重力が与えられ、Capsule Colliderの下側が丸いことによって傾いてしまったためです。
今回、モデル、詳しく言えばゲームオブジェクトを傾かせるような実装は行いません。なのでRigidbodyのFreeze RotationでX Y Zにチェックを入れてゲームを再生してみましょう。
前に進みましたね?障害物にぶつかりましたね?
これからこれからモデルと障害物に関係する実装を行っていくのですが、どちらも色が白いので見た目的にわかりにくいです。なので障害物に色を付けてみましょう。
Create -> Materialで新しくマテリアルを作成します。名前をBlockとしましょう。Main Colorを青っぽくしましょう。
そしてそのマテリアルをシーン上の障害物に向けてドラッグ&ドロップします。
これから「ぶつかったらゲームオーバー」というようにモデルが地面に倒れるというアニメーションを追加します。
説明はしませんでしたが、Runアニメーションをモデルに付けた時、Runアニメーションと同じ場所にTeddyBearというものができています。 これは複数のアニメーションを管理するためのアニメーターコントローラーと呼ばれるものです。略してアニメーターとも呼びます。 TeddyBearアニメーションコントローラーをダブルクリックしてAnimatorウィンドウを開きましょう
いま、真ん中辺りにオレンジ色のRunという「ステート」と呼ばれるものがあります。これがゲーム再生時直後に再生されているわけです。
AnimatorウィンドウにPlayer/Animations/Dying
をドラッグ&ドロップしましょう。
アニメーションを追加できました。これからこのDyingアニメーションを障害物にぶつかった時に再生させます。
アニメーターに「障害物にぶつかった」ということを伝えるため、パラメーターを作成します。
Animatorウィンドウの左下にParametersという部分があるのでその右側の**+**ボタンをクリックしてください。そうするとさらにメニューが開くので、Triggerを選択します。
名前をDeadにしましょう。
ステートからステートへ移動するにはトランジションを実装しなければいけません。
Any Stateで右クリックしてMake Transitionを選択し、「Dyingステート」をクリックしてトランジションをくっつけましょう。
Any Stateは様々なステートから遷移できる便利なものです。
Transitionの矢印をクリックして、インスペクターにあるConditionsを「Exit Time」から「Dead」に変更します。
こうすることで「Deadという条件がマッチしたらどのステートからでもDyingステートへ遷移する」というものが出来ました。
さてこれでアニメーター側の設定は出来ました。
スクリプトファイルを作成しましょう。
Create -> C# Script
名前をPlayerにしましょう。
TeddyBearゲームオブジェクトのインスペクターを選択してPlayerスクリプトをインスペクターのAdd Componentボタンへドラッグ&ドロップしましょう
Playerスクリプトの中身を書いていきます。
ですが今回は上にあるメニューのPlayerScript/Step 1
を選択してください。そうすると自動でスクリプトコードが記述されます。
using UnityEngine;
public class Player : MonoBehaviour
{
Animator animator;
void Start ()
{
animator = GetComponent<Animator> ();
}
void OnCollisionEnter (Collision collision)
{
if (collision.gameObject.name == "Block") {
animator.SetTrigger ("Dead");
}
}
}
この状態でゲームを再生させましょう。
障害物にぶつかると倒れましたか?なんだかモデルが地面に埋め込まれちゃってますが今回は気にせずに進みましょう。
Player/Animations/Vaut
をAnimatorウィンドウにドラッグ&ドロップします。
アニメーションパラメータでBoolを選択し、名前をVautにします。
Runステートの上で右クリックし、「Make Transition」でVautステートに繋げます。
そして、トランジションを「Exit Time」から「Vaut」に変更し条件がtrueであることを確認します。
最後にVautからRunへもトランジションを追加しましょう。
これでアニメーター側の設定は終わりました。
次はスクリプトで矢印キーの上(UpArrow)のキー入力を受け取って飛び越えるアニメーションを再生させます。
今回は上にあるメニューのPlayerScript/Step 2
を選択してください。そうすると自動でスクリプトコードが記述されます。
using UnityEngine;
public class Player : MonoBehaviour
{
Animator animator;
void Start ()
{
animator = GetComponent<Animator> ();
}
void Update ()
{
animator.SetBool ("Vaut", Input.GetKeyDown (KeyCode.UpArrow));
}
void OnCollisionEnter (Collision collision)
{
if (collision.gameObject.name == "Block") {
animator.SetTrigger ("Dead");
}
}
}
この状態で再生させましょう。矢印キーの上を押すと飛び越えるアニメーションが再生されます。ですが実際は飛び越えられずDead判定になってしまいます。
これは飛び越えるアニメーションをしていても当たり判定の範囲は飛び越える前と同じ範囲だからです。
これを修正する方法は2パターンあります。
- 飛び越えるアニメーションの再生中、当たり判定を行うCapsule Colliderの大きさをモデルに合わせて変更すること。
- **Trigger(トリガー)**というものを使い障害物はすり抜けるを事をデフォとすること
今回はトリガーを使ってこの問題を解決しましょう。
Blockのゲームオブジェクトを選択し、BoxColliderのIs Triggerにチェックを入れてください。
さて再生してみましょう。モデルが障害物をすり抜けましたか?
障害物のコライダーをトリガーにしたことによって動かなくなったスクリプトコードがあります。
それはOnCollisionEnter
です。OnCollisionEnterはトリガーではないコライダーの当たり判定を検出するものです。
ではトリガーであるコライダーの当たり判定をどうやってスクリプトから検出するのか、それはOnTriggerEnter
を使うようにすればいいのです。
スクリプトを書き換えましょう。今回は上にあるメニューのPlayerScript/Step 3
を選択してください。そうすると自動でスクリプトコードが記述されます。
using UnityEngine;
public class Player : MonoBehaviour
{
Animator animator;
void Start ()
{
animator = GetComponent<Animator> ();
}
void Update ()
{
animator.SetBool ("Vaut", Input.GetKeyDown (KeyCode.UpArrow));
}
void OnTriggerEnter (Collider other)
{
if (other.name == "Block") {
animator.SetTrigger ("Dead");
}
}
}
書き換えたらゲームを再生してみましょう。障害物に当たるとモデルが倒れるはずです。 これでトリガーによる当たり判定の検出が出来ました。ですがまだ障害物は飛び越えられません。つぎは障害物を飛び越えるようにしましょう。
飛び越える時、少し難しいかもしれませんが**「Vautアニメーションが再生中であれば障害物にあたっても無視する」**という条件をスクリプトで実装します。
スクリプトを書き換えましょう。今回は上にあるメニューのPlayerScript/Step 4
を選択してください。そうすると自動でスクリプトコードが記述されます。
using UnityEngine;
public class Player : MonoBehaviour
{
Animator animator;
void Start ()
{
animator = GetComponent<Animator> ();
}
void Update ()
{
animator.SetBool ("Vaut", Input.GetKeyDown (KeyCode.UpArrow));
}
void OnTriggerEnter (Collider other)
{
if (animator.GetCurrentAnimatorStateInfo (0).IsName ("Base Layer.Vaut")) {
return;
}
if (other.name == "Block") {
animator.SetTrigger ("Dead");
}
}
}
ゲームを再生してみましょう。飛び越えることが出来ましたか?やっとゲームらしくなってきましたね。
飛び越えるタイミングによってはモデルの一部と障害物がすり抜けてしまうかもしれませんが、今回はこれでよしということで次に進みます。
さて、次は障害物を飛び越えるのではなくて、くぐり抜けるようにしてみましょう。
既に作成したBlockを複製します。
シーン上でBlockを選択してEdit -> Duplicate
を選択します。
そうすると複製ができるので、Block同士が重ならないように少し横にずらしましょう。
片方のゲームオブジェクトの名前をHardleにします。
さらに新しくマテリアルを作成し、名前を同じHardleにしましょう。
色は少し赤っぽくします。
最後に障害物を空中に置いておわりです。
障害物の位置は(0, 1.4, 10)としました。
Player/Animations/Slide
をAnimatorウィンドウにドラッグ&ドロップします。
新しくBoolのアニメーターパラメーターを追加して、名前をSlideとします。
RunからSlideステートへトランジションを追加しましょう。そしてConditionsはSlide: trueにします。
最後にSlideからRunステートにトランジションを追加します。Conditionsは「Exit Time」のままで結構です。
これでアニメーターの作業は終了です。
2種類の障害物が出来たことによって2つの条件が生まれました。
- Vaut(飛び越える)アニメーション中にHardleを通過してはいけない
- Slide(くぐり抜ける)アニメーション中にBlockを通過してはいけない
スクリプトを書き換えましょう。今回は上にあるメニューのPlayerScript/Step 5
を選択してください。そうすると自動でスクリプトコードが記述されます。
using UnityEngine;
public class Player : MonoBehaviour
{
Animator animator;
void Start ()
{
animator = GetComponent<Animator> ();
}
void Update ()
{
animator.SetBool ("Vaut", Input.GetKeyDown (KeyCode.UpArrow));
animator.SetBool ("Slide", Input.GetKeyDown (KeyCode.DownArrow));
}
void OnTriggerEnter (Collider other)
{
if (animator.GetCurrentAnimatorStateInfo (0).IsName ("Base Layer.Vaut") && other.name == "Block") {
return;
}
if (animator.GetCurrentAnimatorStateInfo (0).IsName ("Base Layer.Slide") && other.name == "Hardle") {
return;
}
animator.SetTrigger ("Dead");
}
}
再生しているアニメーションとぶつかったゲームオブジェクト名を比較することによって通過できるかどうかを判断します。
ゲームを再生してみましょう。
これでひと通りの実装は終わりました。いまは障害物が2つしか無いですし、ステージも短いです。
ひと通り作成してみましょう。
(時間5分位とってつくってみましょう)
ステージが全部最初から見えてちゃつまらないですよね。なので霧を出して奥を見えなくしちゃいましょう。
Edit -> Render Settings
を選択して
- Fogをチェック
- Fog Densityを0.15
と設定しましょう。
青色ではなく霧と同じような色にしましょう。
Main Cameraを選択してCameraのBackgroundを選択します。
ここで終了です。お疲れ様でした。
もし時間があるなら、リトライボタンやゴールを作成してみましょう。