前回までにスクリプトファイルの作り方をやったので、実際にキャラクターを移動させるスクリプトを書いてみます。
前回は、CharacterControllerコンポーネントを取りつけ、スクリプトファイルを作成するところまでやりました。
まずは、スクリプト「Move」をシーンに配置している「masasi」にコンポーネントとして追加したいと思います。
追加の仕方は二通りあり、一つ目は
上画面のように、スクリプトファイルを選択しそのままシーンに配置している「masasi」にドラッグ&ドロップします。
もうひとつの方法は
上画面のように「Add Component」をクリックし
Scripts→Move
を選びMoveスクリプトを「masasi」に追加出来ます。(下画面)
検索窓にMoveを入力し検索すると早いです。
Moveスクリプトの追加が終わると、「masasi」のインスペクタにMoveスクリプトというコンポーネントが追加されたのがわかります。(下画面)
スクリプトもコンポーネント(構成要素)になります。
Moveスクリプトを「masasi」に設定する事が出来たので、Moveスクリプトの中身を記述していきます。
フィールドの宣言とコンポーネントの取得方法
クラス全体で扱う変数はフィールドとなりクラス内のメソッド等から参照出来ます。
フィールドにはpublicやprivateといったアクセス制限を付けます。
publicを付けたフィールドはスクリプト内で値の変更や参照も出来ますが、インスペクタ上で初期値を与えるという事も出来ます。
ただフィールドは原則privateに設定するので、インスペクタで値を設定したい時は[SerializeField]アトリビュートを付けます。
staticはクラス内で値を保持出来るフィールド(静的なメンバー)で、どのスクリプトからも参照出来ます。
ゲーム全体で保持したい値を入れておくのに便利かもしれません。
なんでもかんでもstaticを付けると他のスクリプトから簡単にアクセス出来てしまうので、普段はあまり使わない方がいいと思います。
staticなフィールドはクラスで保持しているフィールドなので、同じスクリプトを複数のゲームオブジェクトに設定した場合にある一つのstaticなフィールドの値を書き換えると他の同じスクリプトのstaticフィールドも変更されます。
privateはその名の通り、そのスクリプトでしか使用できません(外部スクリプトからアクセスするにはメソッドを介して行います)。
フィールド宣言は
1 2 3 4 | public int test1; private Vector3 test2; |
等と宣言します。
これらのフィールドはクラス内で宣言します。
メソッド内での宣言をするとそのメソッドで使用する変数宣言になります。
1 2 3 4 5 6 | void Start () { Vector3 position; float floatValue; } |
等と宣言します。
↑の例ではStartメソッド内で使用するVector3型のposition変数とfloat型(小数を扱う型)のfloatValue変数を宣言しています。
それでは今回の移動に関するスクリプトを組んでいきます。
まずは、Moveスクリプト全体で使用するフィールドを宣言します。
1 2 3 4 | private CharacterController characterController; private Vector3 velocity; |
characterControllerフィールドはCharacterController型の値を入れるフィールド宣言です。
コンポーネントもこのようにしてフィールドに入れる事が出来ます。
つまりcharacterControllerフィールドを使ってCharacterControllerの機能を使う事が出来ます。
velocityはVector3型のフィールドとして宣言します。
Vector3はx、y、zの軸のfloat値を保持する型なので、
1 2 3 | velocity = new Vector3(1f, 0f, 2f); |
のように扱う事が出来ます。
Unityではxは左右、yは上下、zは手前と奥の軸になります。
floatの値には値の後にfを付けていますが、付けなくても動作します。
これでフィールド宣言が出来ました。
次にフィールドに値をセットします。
Startメソッドはそのインスタンスが登場した時に1回実行されるので、この時にCharacterControllerの機能を取得します。
1 2 3 4 5 | void Start () { characterController = GetComponent <CharacterController> (); } |
GetComponentで、どのコンポーネントを取得するか指定出来ます。
今回はMoveスクリプトが設定されているインスタンス(masasi君)に、CharacterControllerを設定しているので、GetComponentのジェネリック(型指定)でCharacterControllerを指定し、コンポーネントの取得が出来ます。
他のインスタンスのコンポーネントや、同じ階層にないコンポーネントを取得する場合はまた別の方法が必要です。
を参照してください。
スクリプトはまったく関係ないゲームオブジェクトに取り付けて実行させる事も出来ますが、基本的にはそのスクリプトの処理を行う関係性の高いゲームオブジェクトに取り付けて使用します。
これは単純にキャラクターを動かすスクリプトだからキャラクターのゲームオブジェクトにスクリプトが設定されているということがわかりやすいのと、
GetComponentやtransform等のようにスクリプトでそのゲームオブジェクトのコンポーネント、そのゲームオブジェクトのTransformと簡単に指摘出来るからです。
1 2 3 4 5 6 | void Start () { characterController = GetComponent <CharacterController> (); transform.position = new Vector3(0f, 1f, 0f); } |
MoveスクリプトをMasasi君の子要素のゲームオブジェクトに設定していてMasasiのコンポーネントを取得しようと思ったら
1 2 3 4 5 6 | void Start() { characterController = GetComponentInParent<CharacterController>(); transform.parent.position = new Vector3(0f, 1f, 0f); } |
上のように親のコンポーネントを取得したり、親のTransformを見に行く必要が出てきます。
キーボードの押しで速度を計算し、CharacterControllerの機能で移動させる
次にキーボードが押されているかどうかの判定を、Updateメソッド内に記述し移動の処理をします。
1 2 3 4 5 6 7 8 | void Update () { if(characterController.isGrounded) { velocity = new Vector3 (Input.GetAxis ("Horizontal"), 0f, Input.GetAxis ("Vertical")); } characterController.Move(velocity); } |
んー・・・・急にわからなくなりましたねぇ・・・(;д;)
まずは上から見ていきます。
ifの条件文は条件が真ならば中身を実行し、偽ならば実行しません。
CharacterControlerのisGroundedで、そのキャラクターが地面に接地しているかどうかがわかります。
「masasi」君のコライダが地面についているかどうかなのでCharacterControllerの当たり判定の範囲が違う所にあると空中に浮いたり、地面に埋没して見えたりします。
1 2 3 | velocity = new Vector3 (Input.GetAxis ("Horizontal"), 0f, Input.GetAxis ("Vertical")); |
↑が地面に接地していた時に実行する内容です。
移動の為のキーが押されていたらキャラクターを移動させたいので、キーの取得をし、それをvelocityに代入しています。
x方向はInput.GetAxis(“Horizontal”)で左右方向の移動キーの値を取得、
y方向は上下方向なので0と初期化しています。
z方向はInput.GetAxis(“Vertical”)で前後方向の移動キーの値を取得しています。
Input.GetAxis(“Horizontal”)は横方向移動に指定されたキー
Input.GetAxis(“Vertical”)は前後方向移動に指定されたキー
の値を取得します。-1から1の間で0から段々増えたり減ったりします。
押しっぱなしの状態になると-1か1となります。
Input.GetAxis(“Horizontal”)はHorizontalに設定されたキーを押した時の値を取得していて、どのキーが押されたかの設定を変更する事が出来ます。
Edit→Project Setting→Input
を選択すると上画面のようにインスペクタでキーの設定が行えます。
値の設定が出来たので、最後にCharacterControllerの機能を利用して動かします。
1 2 3 | characterController.Move(velocity); |
CharacterControllerのMoveは、指定した速度で移動させる機能です。
ではスクリプトが出来たので、Unityの実行ボタンを押してみましょう。
おお!
見事に何も反応しません・・・・(゚д゚)
まぁ・・・当たり前なんですが、気づきましたでしょうか?
そうなんです。地面に接地せず「masasi」君は空中浮遊していますから!!残念!(・Д・)ノ
重力の計算とパソコンの処理速度で移動力が変わらない為の処理
空中に浮いていちゃ困るので「masasi」君に重力を働かせます。
1 2 3 | velocity.y += Physics.gravity.y; |
という一文を移動前に挿入しておきます。Physics.gravityで物体に重力を働かせる事が出来ます。
正しくは重力の値が設定されているプロパティ値をy方向に足しているだけですが・・。
重力値の変更をしたい場合は
上の画面のように
Edit→Project Setting→Physics
と選択し、
上画面のようにインペクタ上のgravityを調整します。
通常であれば変更する必要はありません。
これで重力が実装でき、キャラクターが地面に接地し↑↓←→キーでキャラクターが動くようになります。
ただ、キーをちょっと触っただけでものすごいスピードで移動します。
この原因は、Updateメソッドが呼ばれるごとにキーの処理が行われているからです。
Updateメソッドは1秒間に何十回も実行されます。
毎回その分移動するので、キーの値が1であって、30回Updateメソッドが実行されるのであれば、30m移動するという事になってしまいます・・・。
1秒で30m、人間ではありませんね・・・。
パソコンの処理速度によってUpdateメソッドの実行回数が変わってしまいます。
というわけで、Updateメソッドは実行するけどキーの値が1であった時に、1秒間で1m移動するように変更します。
その為にTime.deltatimeを使います。
Time.deltaTimeはUpdateの実行回数にかかわらず1秒間での動作距離を同じに出来ます。
Updateが10回の場合は0.1秒間に0.1m移動
Updateが20回の場合は0.1秒間に0.05m移動
という風に調整されます。
というわけで、移動距離を調整してくれるようにTime.deltaTimeをかけます。
1 2 3 | characterController.Move(velocity * Time.deltaTime); |
おお!動きました!1秒間に1m移動してますね?
やったー!と浮かれる前に重力の方も調整しとかなきゃいけません。
1 2 3 | velocity.y += Physics.gravity.y * Time.deltaTime; |
これで重力も1秒間に9.81下向きにかかる事になります。
移動する事は出来ましたが、1秒間に1mの移動というのは歩くのがちょっと遅いので、普通の大人の歩く平均速度1.25m/sに合わせてみます。(平均速度は不確か)
1 2 3 4 | [SerializeField] private float walkSpeed; |
というフィールドを追加します。
いい機会なので[SerializeField]アトリビュートを使いました。
[SerializeField]アトリビュートを使うと、Unityのインスペクタでフィールドの値を設定する事が出来ます。
上画面のように、Moveスクリプトの所にwalkSpeedというフィールドの値を設定する事が出来るようになります。
ここに1.25と入力しておきます。
1 2 3 | characterController.Move(velocity * walkSpeed * Time.deltaTime) |
設定したスピードをvelocityにかけます。これで1秒間に1.25m移動するキャラクターが作成できました。
キャラクター移動スクリプトのまとめ
これでUnityでのキャラクター移動のスクリプトは完成です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | using UnityEngine; using System.Collections; public class Move : MonoBehaviour { private CharacterController characterController; private Vector3 velocity; [SerializeField] private float walkSpeed; // Use this for initialization void Start () { characterController = GetComponent <CharacterController> (); } // Update is called once per frame void Update () { if(characterController.isGrounded) { velocity = new Vector3 (Input.GetAxis ("Horizontal"), 0f, Input.GetAxis ("Vertical")); } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * walkSpeed * Time.deltaTime); } } |
あとは、移動している時は歩行アニメーション、移動していない時は棒立ちのアニメーションへの変更をし、
移動する時に、移動する方向に向きを変えるという事をしないといけません。
次回はそういった部分が出来るようにアニメーションの設定をやろうと思います。
アニメーションの設定をやる前に「masasi」君のプレハブをHumanoidにしてなかった為、
再度シーンに配置する所からやり直さねばなりません・・・(;_;)
プレハブのArmy01をHumanoidにしたら、「masasi」君が横向きになって修正出来なくなってもうたんです。
3Dグラフィックの取り扱いの勉強しないとダメだなぁ・・・・ふぅ
いや・・・Unityの取り扱いか・・・((+_+))
というわけで次回は「masasi」君の再設置から始まります。