ジャンプアクションゲームを作る05 – スクロールとカメラ移動

今回はプレイヤーの移動に伴うスクロールとカメラの移動を実装してみます。

今回できるもののWeb Player版。

その前にいくつか下準備をしておきます。

オブジェクトをプレハブ化する

今、<Hierarchy>ウィンドウにいくつかのオブジェクトが並んでいますが、これはプログラミングでいうところのインスタンスのようなものです。

本来クラスがあって、それをインスタンス化してゲーム内にオブジェクトを配置するのがよいはずなので、これらをプレハブ(クラスのようなもの)化しておきます。

<Hierarhy>ウィンドウからそれぞれを<Project>ウィンドウにドラッグ&ドロップします。

<Project>ウィンドウにオブジェクトと同名のアイテムができます。これがプレハブです。以降はこのプレハブをインスタンス化することで、ゲーム内にオブジェクトを配置していきます。

ついでに、<Project>ウィンドウ、[Create][Folder]でそれぞれを種類ごとに分類しておきます。ここでは図のように整理しました。

2013-10-jumpGame05-toPrefabs

床を配置する

一回<Hierarchy>ウィンドウからFloorを削除します。
<Project>でFloorプレハブを選び、Transformを下記のように設定しなおします。

Position(x, y, z) = 3, -1.5, 0
Scale(x, y, z) = 10, 1, 5

<Project>からFloorプレハブを、<Hierarchy>あるいは<Scene>にドラッグ&ドロップすると、床がインスタンス化されて表示されます。これを繰り返して、ゲームステージっぽく配置します。

ここでは図のようにしてみました。

2013-10-jumpGame05-floors

プレイヤーを横移動させる

コードを下記のように書き換えます。

    // Update is called once per frame
    void Update () {
        // 右に自動移動する
        Vector3 currentVelocity = this.rigidbody.velocity;
        currentVelocity.x = 5.0f;
        this.rigidbody.velocity = currentVelocity;

        // マウスをクリックするとジャンプする
        ※略

プレイヤーに合わせてカメラを移動させる

ここまでのステップでゲームを再生すると、プレイヤーが画面の右端にすぐに消えてしまいます。これはカメラがプレイヤーを追いかけていないからです。

カメラを制御してプレイヤーを追いかけるようにします。

※他にもプレイヤーが床の側面に張り付いてしまう問題がありますが、これは後で修正します。

“CameraControl”という名前のスクリプトを新規作成し、”Main Camera”プレハブに適用します。コードは下記の通りです。

public class CameraControl : MonoBehaviour {
    private GameObject player = null;

    // Use this for initialization
    void Start () {
        // プレイヤーを探しておく
        player = GameObject.FindGameObjectWithTag("Player");
    }

    // Update is called once per frame
    void Update () {
        this.transform.position = new Vector3(player.transform.position.x,
                                              player.transform.position.y + 1.0f,
                                              this.transform.position.z);

    }
}

コード内でタグを頼りに各オブジェクトを探すので、Playerプレハブに”Player”タグを、Main Cameraに”MainCamera”タグを、それぞれ適用しておきます。※参照『二段ジャンプの実装

ここでゲームシーンを再生してみます。(Web Player版
確かにカメラがプレイヤーについてきますが、上下にめまぐるしく動いてなんだか落ち着きません。そこで、最後に接地した床を基準にY座標を決めるように処理を変更します。

PlayerControl.cs

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Floor")
        {
            // ジャンプ回数をリセットする
            restJumps = 2;
            // 回転角度をリセットする
            this.rigidbody.angularVelocity = Vector3.zero;
            this.rigidbody.transform.eulerAngles = Vector3.zero;

            // カメラ位置の基準フロアをセットする
            GameObject mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
            mainCamera.GetComponent<CameraControl>().SetCurrentFloor(collision.gameObject);
        }
    }

CameraControl.cs

public class CameraControl : MonoBehaviour {
    private GameObject player = null;
    private GameObject currentFloor = null;

    // 略

    // Update is called once per frame
    void Update () {
        float diffY = currentFloor.transform.position.y + 2.5f - this.transform.position.y;
        float deltaY = diffY / 10;
        this.transform.position = new Vector3(player.transform.position.x,
                                              this.transform.position.y + deltaY,
                                              this.transform.position.z);
    }

    public void SetCurrentFloor(GameObject targetFloor)
    {
        currentFloor = targetFloor;
    }
}

ゲームを再生すると、床の高さに合わせてスムーズに上下にスクロールする様子が確認できるはずです。最初のタイプとどちらが適切かは、ゲームの内容などにもよりますが今回はこれで行くことにします。

ジャンプアクションゲームを作る04 – ジャンプに味付けをする

前回の「ジャンプアクションゲームを作る03 – 二段ジャンプの実装」で実装ができましたが、今後のゲーム作りのためと色気を出すために味付けをしてみます。

変更する内容は下記の通りです。

  1. ジャンプの高さを指定できるようにする。
  2. きりもみジャンプにする。

今回できるもののWeb Player版

1. ジャンプの高さを指定できるようにする。

例えば、ジャンプの高さを身長の1.5倍にする、ゲーム内のブロック2つ分にする、など高さが指定できるとゲームを作る上で都合が良いことは多いはずです。

結論から言うと、ジャンプの高さを h、重力加速度を g とすると、ジャンプの初速 v0

v0 = 平方根(2 * g * h)

で求めることができます。

実はこの式は私のテキスト『ゲームの作り方 Unityで覚える遊びのアルゴリズム』でも紹介されていますが、解説がなかったので簡単に解説します。

この式を理解するには高校レベルの微積分と物理学を思い出す必要があります。使う知識は下記の通り。

  • 初速度を v0、加速度を a とすると、t 秒後の速度 vt
    vt = v0 + a*t
  • 速度を t で積分すると t 秒後の位置が求められる。
  • ジャンプの最高到達点では速度 vt はゼロ。

上記を数式にします。
ここでは t 秒後の位置を h にしたくて、加速度は重力加速度 -g 。つまり、

vt = v0 – g*t を積分すると、t 秒後の高さ h は、
h = v0*t + 1/2 * g * t2 ……1
vt = v0 + g*t = 0 ……2

1と2の連立方程式を解くと最初の式が求まります。

また、味付けとして2回目のジャンプは高さを半分にすることにします。

それではPlayerControl.csを下記のように書き換えます。

void Update () {
    // マウスをクリックするとジャンプする
    if (Input.GetMouseButtonDown(0))
    {
        if (restJumps == 2)
        {
            float jumpHeight = 2.0f;
            float gravity = Mathf.Abs(Physics.gravity.y);
            float velocity = Mathf.Sqrt(2 * gravity * jumpHeight);
            this.rigidbody.velocity = Vector3.up * velocity;
            restJumps--;
        }
        else if (restJumps == 1)
        {
            float jumpHeight = 2.0f / 2;
            float gravity = Mathf.Abs(Physics.gravity.y);
            float velocity = Mathf.Sqrt(2 * gravity * jumpHeight);
            this.rigidbody.velocity = Vector3.up * velocity;
            restJumps--;
        }
    }
}

※わかり易さのためにUpdate毎・ジャンプ回数毎に計算する冗長なコードになっていますが、実際には計算値をあらかじめ定数にしておいたり、Start関数の中で計算しておいたりというやり方になるかと思います。

2. きりもみジャンプにする。

何だかふわふわしていてジャンプしている感がないので、きりもみジャンプ(アクセルジャンプ)にしてみます。
ここでも1回目と2回目のジャンプを差別化して、1回目は着地までにちょうど1回転、2回目はその2倍の速さで回転させます。

ちょうど1回転する回転速度(角速度)は下記のように求められます。

ジャンプの初速を v0、ジャンプしてから着地までの時間を t とすると、運動量保存の法則により、着地の瞬間の速度は -v0なので、t 秒後の速度 vt は、

vt = v0 -g*t = -v0 これを変換して、
t = 2*v0 / g …1
t 秒で360度(2*π)回転する角速度ωは、
ω = 2*π / t …2

1と2の連立方程式を解いて、
ω = π * g / v0
が求まります。

それではこの式にしたがってコードをさらに書き換えます。

// Update is called once per frame
    void Update () {
        // マウスをクリックするとジャンプする
        if (Input.GetMouseButtonDown(0))
        {
            if (restJumps > 2)
            {
                // ジャンプの初速を計算する
                float velocity = Mathf.Sqrt(2 * gravity * jumpHeight);
                this.rigidbody.velocity = Vector3.up * velocity;
                float angularVelocity = Mathf.PI * gravity / velocity;
                this.rigidbody.angularVelocity = Vector3.up * angularVelocity;
                restJumps--;
            }
            else if (restJumps == 1)
            {
                float velocity = Mathf.Sqrt(2 * gravity * jumpHeight / 2);
                this.rigidbody.velocity = Vector3.up * velocity;
                float angularVelocity = Mathf.PI * gravity / velocity * 2;
                this.rigidbody.angularVelocity = Vector3.up * angularVelocity;
                restJumps--;
            }
        }
    }

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Floor")
        {
            // ジャンプ回数をリセットする
            restJumps = 2;
            // 回転角度をリセットする
            this.rigidbody.angularVelocity = Vector3.zero;
            this.rigidbody.transform.eulerAngles = Vector3.zero;
        }
    }

(2013.10.21 13:58追記)最後に、「二段ジャンプの実装」でY軸回転をロックしていたので、RigidbodyのConstraintsから、Freeze Rotation y のチェックを外しておきます。

※浮動小数点の計算誤差や、ジャンプ点と着地点がの高さが違うこともあるので、着地時に回転角をリセットします。

以上で二段ジャンプの実装が完成しました。

ジャンプアクションゲームを作る03 – 二段ジャンプの実装

前回、Unityの基本的な操作をおさらいしたので、いよいよゲームっぽい動作、二段ジャンプの実装をしてみます。

今回できるもののWeb Player版

プロジェクトは前回のものをそのまま流用します。

スクリプトの準備

  • メインメニュー[Assets][Create][C# Script]を実行します。
  • <Project>ウィンドウに”NewBehaviourScript”が追加されるので、名前を”Player Control”に変更しておきます。
  • これを<Project>ウィンドウから<Hierarchy>ウィンドウのPlayerの上にドラッグ&ドロップします。
  • <Inspector>でPlayerにPlayer Control(Script)が追加されているのを確認します。

これでスクリプトを書く準備が整いました。

ジャンプするコードを書く

<Inspector>でPlayerControlをダブルクリックすると、Unityのスクリプトエディタが起動します。初期状態では下記のようになっているはずです。

using UnityEngine;
using System.Collections;

public class PlayerControl : MonoBehaviour {
    // Use this for initialization
    void Start () {
    }
    // Update is called once per frame
    void Update () {
    }
}

Unityはゲームフレーム毎に各オブジェクト(ここではPlayerControl)のUpdate関数を呼び出すので、Updateの中に任意のコードを追加することでPlayerを操ることができます。

Update関数を下記のように書き換えます。

// Update is called once per frame
void Update () {
    // マウスをクリックするとジャンプする
    if (Input.GetMouseButtonDown(0))
    {
        this.rigidbody.velocity = Vector3.up * 8.0f;
    }
}

再生ボタンを押してから、ゲーム画面でマウスをクリックするとジャンプできるようになりました。

Playerが傾いてしまう問題の修正

ジャンプボタンを繰り返していると、Playerが傾いてきてしまう問題があることに気づきます。これを修正するために移動・回転をロックします。

<Inspector>RigidbodyのConstraintsから下記にチェックをつけます。

Freeze Position z
Freeze Rotation  x, y, z

これでチェックを付けた軸の運動が無視されるようになりました。

何回でもジャンプできてしまう問題の修正

ジャンプの回数を覚えておく変数 restJumps を用意してジャンプの回数を管理します。
コードは下記のようになりました。

public class PlayerControl : MonoBehaviour
{
    private int restJumps = 2;

    ※中略※	

    // Update is called once per frame
    void Update ()
    {
        // マウスをクリックするとジャンプする
        if (Input.GetMouseButtonDown(0))
        {
            if (restJumps > 0)
            {
                this.rigidbody.velocity = Vector3.up * 8.0f;
                restJumps--;
            }
        }
    }
}

ちなみにここでは、残りジャンプ回数で管理していますが、numJumpsなどジャンプした回数で管理してもいいと思います。

接地する度にジャンプ回数をリセットする

さて、まだ問題が残っています。
今度は2回しかジャンプできなくなってしまたったので、接地する度にジャンプ回数をリセットするようにします。

<Hierarchy>でFloorを選んで、<Inspector>上部のTagメニューから[Add Tag...]を実行します。つづいて、TagMangerでElement 0に”Floor”と入力します。

2013-10-jumpGame03-tagManager

このタグを接地判定(当たり判定)の際の識別に使うことになります。

つづいてコードを修正して、下記の関数を追加します。

void OnCollisionEnter(Collision collision)
{
    if (collision.gameObject.tag == "Floor")
    {
        // ジャンプ回数をリセットする
        restJumps = 2;
    }
}

OnCollisionEnter関数はスクリプトに関連付けてあるオブジェクト(ここではPlayer)が他の物体と衝突すると呼ばれます。

最後に、Playerが空中にいるのもなんなので、座標をPosition(x, y, z) = 0, 0, 0にセットして今回の実装は終了です。

ジャンプアクションゲームを作る02 – Unityでの基本的な作業の復習

ジャンプアクションゲーム作り2回目です。
動くものを作ったほうがモチベーションがあがるので、今後何回かで二段ジャンプの実装実験をします。まずはUnityでの基本的な作業の流れの復習をします。

この回は私がテキストにしている『ゲームの作り方 Unityで覚える遊びのアルゴリズム』の第0章で学んだことの抜粋・アレンジにもなっています。

プロジェクトと物体のセットアップ

Unityを起動したら、下記の通りプロジェクトの作成および、物体のセットアップを行います。

メインメニュー[File][New Project...]から任意のフォルダを選んで(作って)、プロジェクトに好きな名前を付けます。ここではblog-jumpGame01としました。

続いてシーンにプレイヤーと床を配置します。

  1. [GameObject][Create Other][Cube]選択します。
    jumpGame02-menu
  2. 今作ったCubeが選択された状態のまま、Inspectorウィンドウから下記のように設定します。これがジャンプするプレイヤーになります。
    <Transform>
    Position(x, y, z) = 0, 2, 0
    Scale(x, y, z) = 1, 2, 1
    ついでに名前を”Player”に変えておきます。(Inspectorの一番上のCubeを編集 or HierarchyでCubeを選択してF2キー)
  3. 同じく床になるCubeを作成し、下記のようにします。
    <Transform>
    Position(x, y, z) = 0, -3.5, 0
    Scale(x, y, z) = 5, 5, 5
    このCubeは名前を”Floor”に変えておきます。
  4. 最初から配置されているMain Cameraを選び下記のようにします。
    <Transform>
    Position(x, y, z) = 0, 1, -10
    Scale(x, y, z) = 1, 1, 1

光源のセットアップ

  1. Gameウィンドウを見ると物体が暗くて見づらいのがわかります。これはシーンに光源が無いからです。光源を追加します。
    [GameObject][Create Other][Directional Light]を作って下記のように設定します。
    <Transform>
    Rotation(x, y, z) = 50, -30, 0
    これでGame画面が明るくなったのを確認してください。

マテリアルのセットアップ

画面をみると灰色一色で味気ないので、プレイヤーと床に色を付けます。

  1. [Assets][Create][Material]から新規マテリアルを作成し、Inspectorのカラーピッカーから青っぽい色を選びます。
  2. Projectウィンドウから”New Material”を”Player Material”にリネームします。
  3. Projectウィンドウから”Player Material”をHierarchyウィンドウの”Player”にドラッグ&ドロップします。GameウィンドウでPlayerが青くなったのを確認してください。
  4. 同じ要領で”Floor Material”という名前の黄色いマテリアルを作り、”Floor”を黄色にします。

2013-10-jumpGame02-lighted

物理計算のセットアップ

ここで一回ゲームシーンを再生してみます。
画面上部の再生ボタンを押してみてください。

2013-10-jumpGame02-playButton

・・・プレイヤーが落下すると期待した方、残念でした(笑)
この段階では何も起きません。なぜなら物理に関するコードを一切書いてないので当たり前といえば当たり前です。
※停止するにはもう一度再生ボタンを押せばOKです。

でも安心してください。基本的な物理処理はUnityがやってくれます。

SceneウィンドウかHierarchyウィンドウからPlayerを選択して、メインメニュー[Component][Physics][Rigidbody]を実行します。

これでPlayerにRigidbody(剛体)の物理法則が適用されるようになりました。

もう一度再生ボタンを押してみてください。

期待通りにPlayerが落下するのが確認できたはずです。

ジャンプアクションゲームを作る01 – ゲームのデザイン

はじめに」でも書いたように、『ゲームの作り方 Unityで覚える遊びのアルゴリズム
』という本を読んで勉強しながら小さなサンプルゲームを作っていきたいと思います。

まずは、第0章と1章を読んで得た知識を元に、簡単なジャンプアクションゲームを作るところまでを最初のゴールにしたいと思います。

まずは、ペンと紙を使って簡単なゲームデザインをしてみました。

jumpaction01-design

 

  • プレイヤーは左右に自動移動(壁にぶつかると反転)
  • 操作はジャンプのみ(2段ジャンプができる)
  • スタートからゴールを目指す
  • 障害物、敵、コインがある

これを実現するのに実装しなければいけない要素を挙げてみます。

  • ジャンプの操作(ユーザー入力、物理計算)
  • 当たり判定(障害物、敵、コイン)
  • 移動・スクロール処理(カメラ処理)

本当に単純なゲームですが、最初の作品としてはちょうどいいのではと思います。

Unityの単位

2013-10-unit-inspector意外と記述のみつからないUnityの単位(Unit)です。

エディタのインスペクター(Inspector)での単位は、

  • 距離やサイズがメートル法、つまり、1.0=1.0m
  • 角度が度数法、つまり、1.0=1.0度

スクリプトでは距離やサイズがエディタと同じで、

  • 角度がラジアン角、つまり、π=180度

です。

両者が異なるのは、インスペクターでは直感的にわかり易い度数法を、スクリプトでは数学的に扱いやすいラジアン角を採用しているのだと思います。

ちなみに、ラジアン角が使いやすいように

Mathf.PI

という定数が用意されています。

2013-10-unit-code

はじめに

こんにちはtowofuです。はじめにこのブログのコンセプト的なものを書かせてもらいます。

このブログについて

タイトルの通り、日曜プログラマーがUnityでのゲーム開発を独学する中で学んだことや気づいたことをつづるブログです。
100人いれば100通りの気づきがあると考えて、同じレベルにある人に少しでも参考になれば幸いです。

ちなみにコードは基本的にC#で記述しています。

続いて簡単に自己紹介をします。

プログラミング歴

15年くらい日曜プログラマーをやっている36歳です。(2013年現在)
大学で情報工学を学ぶも道を外れて、今は非IT系の中小企業の社長をやっています。
学生時代に初めて触ったDelphiを使い続けて、フリーソフトを公開したり、社内だけで使う小さなアプリの開発をしていましたが、社長業が忙しくしばらく離れていました。

最近Visual Studio Express 2012(C#)を使い始め、プログラミング熱が再燃。
時を同じくしてUnityの存在を知り、息抜きにゲーム開発を始めました。

ゲーム開発歴

当然ですがプロとしての開発歴はありませんが、高校生のときからBASICでミニゲームを作って遊んでいました。また海外ゲーム(主にFPS)のMODを作って公開したりしていました。

特にソースコードが公開されているRtCW(Return to Castle Wolfenstein)というFPSのMOD作りで、3Dゲームの基本的なロジックに触れていたので、Unityのドキュメントをざっと読んだ感じで共通点が多いと感じています。

プロを目指しているような方の参考にはならないかもしませんが、少しでも情報共有できればと思っています。

参考資料

最初は公式サイトのブロック崩しチュートリアルを読んでみましたが、なかなか更新されないことと、やはり本が欲しいと思って『ゲームの作り方 Unityで覚える遊びのアルゴリズム
』を購入しました。

現在、この本の0章「Unity概要」と1章「クリックアクションゲーム おに」を読んだだけですが、作りながら覚えたいと思い、このブログをスタートしました。