tanaka's Programming Memo

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

(4)プレイヤーバーの実装(2015.1改訂版)

←(3)登場するオブジェクトの動かし方(2015.1改訂版)
(5)ボールを動かす(2015.1改訂版)→

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

ジッドボディの追加

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

  • 【プロジェクト(Project)】ビューから「Game」Sceneをダブルクリックしてゲーム画面に切り替える
  • 【階層(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)に更新処理が行われる
  • 物理挙動と密接に関与する更新処理はこの関数内に記載する

今回のプログラムは、物理挙動とそれほど連動させることは不要なので、Update()関数に実装する。

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

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

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

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

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

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

移動処理を追加する。

  • 「void Update() {」という行を探して、その下の行に以下のプログラムを記載する。
		Vector3 vel = Vector3.zero;
		vel.x = VEL*Input.GetAxisRaw ("Horizontal");
		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に代入される
		rigidbody.velocity = vel;
  • 求めた速度をプレイヤーのrigidbody.velocityに代入して、速度を設定する

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

(補足1)

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


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


(補足2)

今回は、移動速度を「rigidbody.velocity」に代入して、実際の移動はUnityの物理シミュレータに任せた。しかし、「transform.positioin」を直接いじるチュートリアルなどもある。


transform.positionを直接操作しないのは、物理挙動をUnityに任せるためである。transform.positionは物理挙動を考えずに、ゲームオブジェクトをいきなりその座標にワープさせることになるので、予期しない動きをする場合がある。リジッドボディを使う場合は、移動速度をrigidbody.velocityに代入して、あとの処理はUnityに任せてしまうのが楽である。

ボールをくっつける

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

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

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

それでは、プレイヤーバーにボールをくっつけてみよう。
5行目近辺の「public float VEL = 40f;」の行を探して、下に以下のプログラムを追加する。

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

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

	// ボールを生成して、プレイヤーバーにくっつける
	void createHoldBall() {
		Vector3 bpos = transform.position;
				bpos.y += (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)】ビューの「Prefabs」フォルダ内にある「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 Update () {
		Vector3 vel = Vector3.zero;
		vel.x = VEL*Input.GetAxisRaw ("Horizontal");
		rigidbody.velocity = vel;
	}

	void createHoldBall() {
		Vector3 bpos = transform.position;
		bpos.y += (collider.bounds.size.y + prefBall.transform.localScale.y) / 2f;
		insBall = (GameObject)Instantiate (prefBall, bpos, Quaternion.identity);
		insBall.transform.parent = transform;
	}
}


←(3)登場するオブジェクトの動かし方(2015.1改訂版)
(5)ボールを動かす(2015.1改訂版)→