Unityでキャラクターの移動をプログラミングしてみる

前回まででスクリプトファイルの作り方をやったので、実際にMonoDevelopを使ってキャラクターを移動させるスクリプトを書いてみます。

前回はCharacterControllerコンポーネントを取りつけ、スクリプトファイルを作成するところまでやりました。

Unityでキャラクターを移動する為の準備と概要(スクリプトを書く前に)
Unityでキャラクターを移動する為に準備しておく事と、移動させる為に必要な事を考える

まずはスクリプト「Move」をシーンに配置している「masasi」にコンポーネントとして追加したいと思います。

追加の仕方は二通りあり、一つ目は

スクリプトファイルをキャラクターにドラッグ&ドロップ


上画面のようにスクリプトファイルを選択しそのままシーンに配置している「masasi」にドラッグ&ドロップします。

もうひとつの方法は

AddComponentからコンポーネントを追加

上画面のように「Add Component」をクリックし

Scripts→Move

を選びMoveスクリプトを「masasi」に追加出来ます。(下画面)

AddComponentからスクリプトファイルを追加

検索窓にMoveを入力し検索すると早いです。

Moveスクリプトの追加が終わると、「masasi」のインスペクタ上にMoveスクリプトというコンポーネントが追加されたのがわかります。(下画面)

Moveスクリプトコンポーネントが追加されたのを確認する

Moveスクリプトを「masasi」に設定する事が出来たので、Moveスクリプトの中身を記述していきます。

スポンサーリンク

変数の宣言とコンポーネントの取得方法

まず変数について通常のJavascriptと違う点を考えます。

UnityのJavascriptはオブジェクト指向の考え方を使っていますので、変数にはpublicやprivateといったアクセス制限を付けます。

publicを付けた変数はスクリプト内で値の変更も出来ますが、インスペクタ上で初期値を与えるという事も出来ます。

staticはプロジェクト全体で値を保持出来る変数でどのスクリプトからも参照出来ます。
ゲーム全体で保持したい値を入れておくのに便利かもしれません。

privateはその名の通りそのスクリプトでしか使用できません。

変数宣言は

public var test1 : int;
private var test2 : Vector3

等と宣言します。
これらの変数は関数外で宣言します。

関数外で宣言した変数はこのスクリプト全体で使用する事が出来ます。

関数内での宣言は普通のJavascript同様

var test3
var test3 : float;

等と宣言します。

:(コロン)の後は型指定で通常のintやfloatやコンポーネントを表す型の指定が出来ます。
関数内の型指定はなくてもコンパイルを通るみたいです。でも付けた方がいいのかな?

関数内で宣言した変数はその関数内でのみ使用出来ます。

それでは今回の移動に関するスクリプトを組んでいきます。

まずは、Moveスクリプト全体で使用する変数を宣言します。

private var cCon : CharacterController;
private var velocity : Vector3;

cCon変数はCharacterController型の値を入れる変数宣言です。
コンポーネントもこのようにして変数に入れる事が出来ます。
つまりcCon変数を使ってCharacterControllerの機能を使う事が出来ます。

velocityはVector3型の変数として宣言します。Vector3はx、y、zの値を保持する型なので、

velocity.x = 1
velocity.y = 0
velocity.z = 2
velocity = Vector3(1, 0, 2)

のように変数を扱う事が出来ます。
xは左右、yは上下、zは手前と奥の軸になります。

これで変数宣言が出来ました。
次に変数に値をセットします。

Start関数はそのインスタンスが登場した時に1回実行されるので、この時にCharacterControllerの機能を取得します。

GetComponentでどのコンポーネントを取得するか指定出来ます。

今回はMoveスクリプトが設定されているインスタンス(masasi君)にCharacterControllerを設定しているので、GetComponentだけでコンポーネントが取得出来ます。

他のインスタンスのコンポーネントや同じ階層にないコンポーネントを取得する場合はまた別の方法が必要です。

Unityでゲームオブジェクト、コンポーネントを取得する方法
Unityのスクリプトでゲームオブジェクト、コンポーネントを取得し変数に保存する方法

を参照してください。

キーボードの押しで速度を計算し、CharacterControllerの機能で移動させる

次にキーボードが押されているかどうかの判定をUpdate関数内に記述し、移動の処理をします。

んー・・・・急にわからなくなりましたねぇ・・・(;д;)

まずは上から見ていきます。

ifの条件文は条件が真ならば中身を実行し、偽ならば実行しません。

cCon.isGrounded

CharacterControlerのisGroundedでそのキャラクターが地面に接地しているかどうかがわかります。

「masasi」君のコライダが地面についているかどうかなのでCharacterControllerの当たり判定の範囲が違う所にあると空中に浮いたり、地面に埋没して見えたりします。

velocity.x = Input.GetAxis(“Horizontal”);
velocity.y = 0;
velocity.z = 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となります。

今回はx、y、zを別々に代入してますが

velocity = Vector3(Input.GetAxis(“Horizontal”), 0, Input.GetAxis(“Vertical”));

としても同じです。

Input.GetAxis(“Horizontal”)等でどのキーが押されたかの設定も変更する事が出来ます。

インプットマネジャー

Edit→Project Setting→Input

を選択すると上画面のようにインスペクタ上でキーの設定が行えます。

値の設定が出来たので、最後にCharacterControllerの機能を利用して動かします。

cCon.Move(velocity);

CharacterControllerのMoveは指定した引数の場所に移動させる機能です。

ではスクリプトが出来たので、Unityの実行ボタンを押してみましょう。

おお!
見事に何も反応しません・・・・(゚д゚)
まぁ・・・当たり前なんですが、気づきましたでしょうか?

そうなんです。地面に接地せず「masasi」君は空中浮遊していますから!!残念!(・Д・)ノ

重力の計算とパソコンの処理速度で移動力が変わらない為の処理

空中に浮いていちゃ困るので「masasi」君に重力を働かせます。

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をかけます。

cCon.Move(velocity * Time.deltaTime);

おお!動きました!1秒間に1m移動してますね?
やったー!と浮かれる前に重力の方も調整しとかなきゃいけません。

velocity.y += Physics.gravity.y * Time.deltaTime;

これで重力も1秒間に9.81下向きにかかる事になります。

移動する事は出来ましたが、1秒間に1mの移動というのは歩くのがちょっと遅いので、普通の大人の歩く平均速度1.25m/sに合わせてみます。(平均速度は不確か)

public var speed : float;

という一文を追加します。
いい機会なのでpublic変数を使いました。

スクリプト上でpublicとして宣言した場合、Unityのインスペクタ上で変数の初期値を与える事が出来ます。

public変数はインスペクタで値を設定出来る

上画面のようにMoveスクリプトの所にspeedという変数の値を設定する事が出来るようになります。
ここに1.25と入力しておきます。

cCon.Move(velocity * speed * Time.deltaTime)

設定したスピードをvelocityにかけます。これで1秒間に1.25m移動するキャラクターが作成できました。

キャラクター移動スクリプトのまとめ

これでUnityでのキャラクター移動のスクリプトは完成です。

あとは移動している時は歩行アニメーション、していない時は棒立ちのアニメーションの設定と、
移動する時に移動する方向に向きを変えるという事をしないといけません。

次回はアニメーションの設定をやろうと思います。

アニメーションの設定をやる前に「masasi」君のプレハブをHumanoidにしてなかった為、
再度シーンに配置する所からやり直さねばなりません・・・(;_;)

プレハブのArmy01をHumanoidにしたら「masasi」君が横向きになって修正出来なくなってもうたんです。

3Dグラフィックの取り扱いの勉強しないとダメだなぁ・・・・ふぅ
いや・・・Unityの取り扱いか・・・((+_+))

というわけで次回は「masasi」君の再設置から始まります。

スポンサーリンク

記事をシェアして頂ける方はこちら

フォローして頂くとやる気が出ます

コメント

  1. 名無し より:

    TPS式のキャラクターの移動が知りたいです。
    カメラが向いてる方向によって移動する時だけキャラクターの移動方向が変わるやつです。
    (できれば・CharacterController
         ・ジャンプ、シフトダッシュあり でお願いします。)

  2. 名無し より:

    ありがとうございます。
    参考にさせてもらいます。

  3. 匿名 より:

    アイテムを拾う機能って作れますか?
    アイテムが落ちていて、近くに行くと拾えるようになり対応のキーを押すと拾う、みたいなものです。

    • アイテムゲームオブジェクトにキャラクター検知エリアをコライダとして作り、キャラクターが侵入していたらUIで『アイテムを拾う』という文字を表示させ、

      キャラクターが拾うキーを押せるようにし、キーを押したら拾うアニメーションにしてはどうでしょう?

      キャラクターがアイテムを拾ったら所持アイテムにそのアイテムを追加してアイテムゲームオブジェクトを削除するという感じです。

      敵キャラが主人公を追いかけてくる機能

      https://gametukurikata.com/program/enemychasechara

      を応用すると出来ると思いますので参考にしてください。

      • 匿名 より:

        教えてもらったサイトから試行錯誤してみたのですが、指定した範囲内にあるアイテムが一括で拾えてしまいます。
        最もプレイヤーに近いアイテムを拾うにはどうしたら良いでしょうか?

        • ゲームオブジェクトの距離はVector3.Distanceを使ってキャラクターとアイテムとの距離を計算し、一番近いアイテムだけ取るようにしてはどうでしょう。

          そうなるとキャラクターの近くにあるアイテムをTransform型のリストや配列として保持しておき、それら全てのアイテムとの距離を計算し、一番近いアイテムを取るようにすればいいと思います。

          キャラクターが取れるアイテムが近くに多く発生する場合はアイテムゲームオブジェクトにキャラクターを検知するエリアを作成するより、キャラクターの子要素にアイテムを検知するエリアを作成して、
          キャラクター側でアイテムを検知した方がいいかもしれませんね。

          そこら辺の処理は

          https://gametukurikata.com/program/rotateenemy

          こちらの一番近くの敵を探す処理と同じような事をすればいいかもしれません。

        • すでに機能を作成済みかもしれませんが、アイテムドロップとアイテム取得の機能を記事としてアップしました。

          色々改良する必要はありそうですが・・・。