読者です 読者をやめる 読者になる 読者になる

tanaka's Programming Memo

プログラミングについてのメモ。

(4)プレイヤーバーの実装(Unity5.0版)

前へ | 次へ


前の解説で作成したプロジェクトのダウンロード



プレイヤーを操作できるようにしてみよう。

プレイヤーへの設定

ジッドボディの追加

(3)の方針に従って、リジッドボディ(Rigidbody)コンポーネントを追加する。コライダーはCubeを作った時点で自動的に追加されているので、追加は不要。

  • [Project]ビューから[Game]シーンをダブルクリックしてゲーム画面に切り替える
  • [Hierarchy]ビューから[Player]を選択する
  • メニューから[Component]>[Physics]>[Rigidbody]で、リジッドボディの追加完了

以上が設定できたら、一度実行して、何が起きるかを観察してみよう。

ジッドボディの挙動を調整

引き続き、インスペクター(Inspector)で属性を設定する。

  • Mass
    • 物体の重さ。慣性や作用・反作用の影響の計算に使われる。1のままでよい
  • Drag
    • 移動する際の空気抵抗の設定。今回は空気抵抗は不要なので0でよい
  • Angular Drag
    • 回転時の抵抗。回転しないのでそのままでよい
  • Use Gravity
    • 重力によって落下するかを決めるフラグ。今回は重力は使わないのでチェックを外す(これにより、落下しなくなる)
  • Is Kinematic
    • 物理挙動を無効にする際にチェックを入れる。枠に跳ね返って欲しいのでチェックしなくてよい
  • Interpolate
    • Noneでよい
  • Collision Detection
    • 衝突判定を連続的に行うかを設定する。オブジェクトを飛び越えてしまうぐらい高速にものを動かす際に利用する。今回はDiscreteのままでよい
  • Constaints
    • 影響を与えない軸を決める。プレイヤーバーはX方向の移動以外は不要なので、Freeze PositionのX以外に全てチェックを入れる


以上で、剛体シミュレーション用の設定は完了。次に移動処理を行う。

プレイヤーバーのスクリプト作成

キャラクタを動かす際には、「Update()」関数か、或いは「FixedUpdate()」関数を利用する。これらは、Unityから自動的に呼ばれる関数で、それぞれ以下のタイミングで呼び出される。

Update()

  • 画面を1回更新するたびに、1回呼び出される更新処理
  • 特に問題がなければ、キャラクタ操作はこの関数で実装するとよい
  • 画面描画は、オブジェクトが多いと遅延して時間当たりの処理回数が変わる

FixedUpdate()

  • 物理シミュレーションを処理するたびに1回呼び出される更新処理
  • 物理挙動は画面の描画タイミングには影響を受けず、一定時間ごと(Time.fixedDeltaTime)に更新処理が行われる
  • 物理挙動と密接に関与する更新処理はこの関数内に記載する

今回のプログラムは、物理挙動で行うのでFixedUpdate()関数を利用する。

実装前の確認点を以下に挙げる。

  • 左右ボタンの確認は、Input.GetAxisRaw("Horizontal")で分かる。左の時、-1。右の時、1。押されていない時は0が返る
  • プレイヤーバーの移動速度は、インスペクターで設定できるようにしよう。VELという変数を作成し、1秒間に移動させたい距離(速度)を設定する
  • 座標計算は自前では行わず、リジッドボディに速度を与えることで、動作はUnityに任せる
  • ジッドボディの速度へは、velocityプロパティでアクセスできる

それでは実装してみよう。

  • [Project]ビューの[Scripts]フォルダを右クリックして、[Create]>[C# Script]を選択
  • 新規作成したスクリプト名を「CPlayer」にする
  • 作成した[CPlayer]をドラッグして、[Hierarchy]ビューの[Player]オブジェクトにドロップする。これで、CPlayerスクリプトがPlayerオブジェクトで実行されるようになる
  • 「CPlayer」をダブルクリックしてエディタで開く

移動速度をインスペクターで設定するための変数「VEL」を作る。

  • 「public class CPlayer: MonoBehaviour {」という行を探して、その下の行に以下のプログラムを記載する。
    public float VEL = 40f;
  • 以上で「float型(浮動小数点型)の変数「VEL」を、公開(public)変数として定義して、初期値に「40」を設定」という意味になる
  • 変数定義の頭に「public」をつけると、インスペクターにその変数が表示され、編集できるようになる
  • 40の後ろの「f」は、浮動小数値という意味
  • VELを、キー入力に掛けて、プレイヤーの速度にする

移動処理を追加する。

  • 「void Update() {」という行を探したら、「void FixedUpdate() {」に書き換える
  • 書き換えたら、その下のブロック内に、以下のプログラムを記載する。
        Vector3 vel = Vector3.zero;
        vel.x = VEL * Input.GetAxisRaw("Horizontal");
        GetComponent<Rigidbody>().velocity = vel;

プログラムは以下のような意味になる。

Vector3 vel = Vector3.zero;
  • 今回の移動速度を求めるための、Vector3型のローカル変数velを定義して、0で初期化する
  • Vector3型は、x,y,zの3つのfloat型の値を持てる3次元のベクトル型変数である
vel.x = VEL*Input.GetAxisRaw ("Horizontal");
  • x方向の速度(vel.x)に、速度を設定する
    • VEL
      • 速度。速度とは、1秒間に移動する距離のこと
    • Input.GetAxisRaw("Horizontal")
      • 左の時、-1。右の時、1。押されていない時、0を返す関数
  • よって、VELをInput.GetAxisRaw("Horizontal")に書けると、左が押されている時は-VEL、右が押されている時はVEL、何も押されていない時は0がvelに代入される
        GetComponent<Rigidbody>().velocity = vel;
  • GetComponent()とすることで、このゲームオブジェクトに加えられているリジッドボディを取り出すことができる
  • 取り出したリジッドボディのvelocityプロパティに、求めたプレイヤーの速度を代入

以上できたら実行してみよう。左右キーでプレイヤーバーが移動し、枠にぶつかると自動的に止まることが確認できるだろう。

(補足1)

Input.GetAxis()でもキー入力が得られる。これはアナログ入力に対応した値となるので、左を押してもすぐに-1にはならず、0から少しずつ-1に近づく動きになる。3Dキャラクターを柔らかく動かす場合などに利用するとよい。


Input.GetAxisRaw()は、キーが押されていればすぐに-1が得られるので、2Dゲームの入力に適している。


(補足2)

今回は、移動速度をリジッドボディのvelocityに代入して、移動と壁との衝突処理を物理エンジンに任せた。他の方法としては、「transform.positioin」を直接操作したり、リジッドボディのMovePosition()メソッドを使う方法がある。


transform.positionによる移動は、物理挙動を考えずにゲームオブジェクトをその座標にワープさせることになる。それによりキャラクタが衝突した時に予期しない動きをする場合があり、制御が面倒なので今回は採用したなかった。


MovePosition()は、速度を使って、移動先の座標を計算する必要があり、利用が面倒なので採用しなかった。

ボールをくっつける

ゲームが開始する時に、ボールがプレイヤーバーにくっついて動くようにしたい。このように、あるキャラクターに他のキャラクターがくっつくような処理では、親子階層を利用しよう。ボールをプレイヤーの子オブジェクトにすることで、勝手にくっついて動いてくれるようになる。

ボールのプレハブ化

ボールを登場させたり消したりを簡単に実現するには、ボールをプレハブ化しておくと便利である。プレハブ化しておくと、例えば複数のボールを出すようなことも簡単に出来るようになる。

  • [Hierarchy]ビューの[Ball]オブジェクトをドラッグして、[Project]ビューにドロップして、プレハブ化する
  • プレハブができたら[Hierarchy]ビューの[Ball]オブジェクトは不要である。選択して、[Delete]キーで削除する

ボールをプレイヤーバーにくっつける

それでは、プレイヤーバーにボールをくっつけてみよう。

  • [CPlayer]スクリプトをダブルクリックして、エディタを開く

5行目近辺の「public float VEL = 40f;」の行を探して、下に以下のプログラムを追加する。

    // ボールのプレハブ
    public GameObject prefBall = null;
    // ボールのインスタンス
    GameObject insBall = null;
  • prefBall
    • ボールのプレハブを記録しておくための変数。ボールを生成するのに利用する。「public」がついているので、インスペクターで編集できる
  • insBall
    • 生成したボールのインスタンス(実体)を記録しておくための変数。ボールを発射する時に利用する

次に、ボールをプレイヤーバーにくっつける関数を作ろう。「CPlayer」スクリプトの一番最後の「}」を見つけて、その上の行に以下のプログラムを追加する。

    // ボールを生成して、プレイヤーバーにくっつける
    void createHoldBall() {
        Vector3 bpos = transform.position;
        bpos.y += (GetComponent<Collider>().bounds.size.y + prefBall.transform.localScale.y) / 2f;
        insBall = (GameObject)Instantiate(prefBall, bpos, Quaternion.identity);
        insBall.transform.parent = transform;
    }

プログラムは以下のような意味である。

		Vector3 bpos = transform.position;
  • ローカル変数としてVector3型の「bpos」を宣言して、プレイヤーバーの現在位置(transform.position)を代入
		bpos.y += (collider.bounds.size.y + prefBall.transform.localScale.y) / 2f;
  • ボールをプレイヤーバーの上に配置する計算
  • bposのY座標(bpos.y)に、プレイヤーバーの高さ(collider.bounds.size.y)とボールの高さ(prefBall.transform.localScale.y)の合計を2で割ったものを加えると、求めたい座標が計算できる

		insBall = (GameObject)Instantiate(prefBall,bpos,Quaternion.identity);
  • 「prefBall」からボールの実体を生成(Instantiate)する
  • 座標はbpos
  • 回転はなし(Quaternion.identity)
		insBall.transform.parent = transform;
  • 生成したボールをプレイヤーバーの子オブジェクトにする
  • 生成したボールのインスタンス(insBall)の姿勢情報(transform)の親(parent)に、プレイヤーバーの姿勢情報(transform)を設定することで、子オブジェクトとすることが出来る

以上で、ボールを生成して、それをプレイヤーバーの子オブジェクトにする関数が作れた。インスペクターでボールのプレハブをスクリプトに渡した上で、Start()関数から呼び出せば、ボールを生成することが出来る。

  • 「void Start () {」という行を見つけて、その下の行に以下のプログラムを追加する
        createHoldBall();
  • スクリプトを保存して[Unity]に戻る
  • [Hierarchy]ビューから[Player]を選択する
  • [Project]ビューの[Ball]プレハブをドラッグして、インスペクターに表示されている[CPlayer(Script)]コンポーネントの要素の「Pref Ball」の項目にドロップする

以上で設定完了となる。実行して、ボールがプレイヤーバーの上に生成されて、移動した時にくっついてくるかを確認しよう。


プレイヤーがボールを発射する操作が残っているが、これはボール側に実現したいので、プレイヤーバーの実装は一旦、完了とする。

ここで作成した「CPlayer.cs」

using UnityEngine;
using System.Collections;

public class CPlayer : MonoBehaviour {
    public float VEL = 40f;
    
    // ボールのプレハブ
    public GameObject prefBall = null;
    // ボールのインスタンス
    GameObject insBall = null;

    // Use this for initialization
    void Start () {
        createHoldBall();
    }
	
    // Update is called once per frame
    void FixedUpdate () {
        Vector3 vel = Vector3.zero;
        vel.x = VEL * Input.GetAxisRaw("Horizontal");
        GetComponent<Rigidbody>().velocity = vel;
    }

    // ボールを生成して、プレイヤーバーにくっつける
    void createHoldBall()
    {
        Vector3 bpos = transform.position;
        bpos.y += (GetComponent<Collider>().bounds.size.y + prefBall.transform.localScale.y) / 2f;
        insBall = (GameObject)Instantiate(prefBall, bpos, Quaternion.identity);
        insBall.transform.parent = transform;
    }
}


前へ | 次へ