おもちゃラボ

Unityで遊びを作っていきます

Unityでマリオっぽいゲームを作るのに必要な5つのこと

ファミコンの横スクロールマリオの挙動をUnityで作ってみました。Physicsに全ておまかせ・・・というわけにはいかず、思っていたよりも大変です(笑)ということで、今回はそのレポートを書いてみます!

f:id:nn_hokuson:20171130210038p:plain:w400

今回の記事では、Unityでマリオの挙動を作るのに必要な項目を「ジャンプ編」「衝突判定編」「アニメーション編」「横スクロール編」「入力デバイス編」の5つに分けて紹介していきます。

ジャンプの挙動編

マリオのジャンプは普通のジャンプとは異なる点が3つあります。

  1. ジャンプボタンを押し続けると、ジャンプの高さが変わる
  2. ジャンプの軌跡は放物線ではない
  3. 空中で左右キーを押すと移動できる

なんか、これだけでもう大変そう・・・(笑)1つずつ、見ていくことにしましょう。

ジャンプボタンを押しっぱなしにしたときの挙動

2Dマリオでは、ジャンプボタンを押しっぱなしにしたときジャンプの高さが変化します。といっても、ボタンを押しているあいだ、常に上方向に加速度がかかり続けるわけではなく、放物線の頂点が変わるイメージです。

f:id:nn_hokuson:20171130192605p:plain:w270

ジャンプに使う放物線の方程式は次のものを使いました。横軸(x軸)はフレーム、縦軸(y軸)はピクセルです。

y = -0.045*(x-37)^2+62

図にするとこんな感じ。要するに最大37フレームで62ピクセル上昇するということです。

f:id:nn_hokuson:20171127205916p:plain:w300

ジャンプボタンが押されているあいだは、上の方程式に従ってマリオを移動します。

ジャンプ後、落下の軌跡

マリオのジャンプは物理法則に従った挙動にはなっていません(ボタンを押している間、ジャンプし続ける時点で超人ですが。笑)特にジャンプしたときの軌跡が放物線にならないのが特徴的です。

次の図のように上昇速度と下降速度が異なります。ジャンプするときの重力と落下するときの重力が変化する、といったほうが分かりやすいかもしれません。

f:id:nn_hokuson:20171130192624p:plain:w400

そこで、ジャンプボタンが離されたら、通常の重力計算に切り替えます。落下速度の計算は次の式を使います。

落下速度 += 重力加速度
マリオのy座標 += 落下速度

重力加速度は-0.02ピクセル/フレーム^2で、速度は-0.3ピクセル/フレーム以下はクリップします。この辺の値は、見た目で微調整しています。

空中で移動できる

マリオは空中で十字キーを押すと、矢印方向に移動できます。かなり意味不明ですね(笑)ただ空中移動の実装は簡単で、マリオの速度ベクトルに左右方向の加速度を加えるだけです。

当たり判定編

ジャンプにPhysicsを使っていないので、当たり判定も全てPhysicsにおまかせ!という訳にはいきません。といっても矩形同士の当たり判定を全て自前でやるのは結構大変です。

qiita.com

ということで、ここではフレーム毎に上下左右の4方向にRayを飛ばして当たり判定をすることにします。

f:id:nn_hokuson:20171130192642p:plain:w180

UnityでRayを飛ばすにはRaycastメソッドを使います。使い方は次のような感じになります。

RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.up, 0.5f);
if (hit.collider != null)
{
     //上方向に障害物があった場合
}	

上方向の衝突判定

上方向に当たった場合、ブロックに当たったので、ジャンプをやめて落下に切り替えます。落下に切り替える際にはy方向の速度は0にします。

f:id:nn_hokuson:20171130192655p:plain:w500

横方向の衝突判定

左右方向に当たった場合は、プレイヤのX方向の速度ベクトルを0にしています。これでジャンプ中に横からブロックに当たった場合は、壁に沿って滑りながら進むことになります。

f:id:nn_hokuson:20171130192713p:plain:w600

めり込み対策

また、ブロックに当たった場合はめり込みを押し戻しています。めり込みを押し戻す方法は、全てのブロックが1m単位のグリッド上に乗っていることを利用して、次の式で計算しています(かなり手抜きです。笑)

プレイヤの位置=ブロックの位置±1

f:id:nn_hokuson:20171130192730p:plain:w350

この方法で大体うまくいくのですが、土管だけは2m x 2mの大きさなので、この大きさのBox Colliderを使うと押し戻しの計算が正しくできません。

タグを使って処理を分けてもよいのですが、今回は1m x 1mの大きさのColliderを4つ配置して対応しています。これだとスクリプトで処理を分けなくてもよくて、様々な形の障害物にも対応できるので便利です!

f:id:nn_hokuson:20171130194417p:plain:w450

真面目にPhysicsを使わずに当たり判定をしたい場合は「ゲームプログラミングのためのリアルタイム衝突判定」が役に立ちます。

アニメーション編

プレイヤのステートは次の3つを実装しています。本当は急に走る方向を変えた場合のスリップ状態もあるのですが・・・勝手に省略しました(笑)

  • Idle
  • Run
  • Jump

次のようにIdleとJumpは1枚絵、Runは3枚のパラパラ漫画で作ります。

www.youtube.com

アニメーションの切り替えはAnimatorを利用して制御しています。次のように「Idle」「Run」「Jump」のステートを作り、プレイヤの状態によってアニメーションを切り替えます。

f:id:nn_hokuson:20171127213341j:plain:w600

「Idle」と「Run」の状態はプレイヤの横方向のスピードによって切り替えます。スピードが0.005ピクセル/フレーム以上だったら「Run」に切り替え、0.01以下だったら「Idle」に切り替えています。

このように、2つの閾値を変えてヒステリシスを持たせることで、アニメーションがパタつくのを防ぐことができます。

f:id:nn_hokuson:20171128210755p:plain:w400

また、ジャンプ制御はbool値で行います。ジャンプボタンが押されたときにフラグを立てて、地面に着地したときにフラグを折ります。これで、ジャンプ中はジャンプ用のスプライトが表示されます。

f:id:nn_hokuson:20171130192828p:plain:w400

Animatorの考え方や詳しい使い方は「Unityの教科書」でも紹介しているので、ぜひ参考にしてみて下さい!

横スクロール編

プレイヤの移動にあわせてカメラを動かすとき、単純に「カメラのX座標=プレイヤのX座標+offset」のように実装するとカメラがスムーズにスクロールせず、ガタガタしてしまいます。

これはプレイヤを動かすスクリプトとカメラを動かすスクリプトの実行順序が保証されていないことが原因です。順番に呼ばれないと、プレイヤが移動する前にカメラを移動してしまい、カメラの移動速度が一定にならないため、ガタガタして見えてしまいます。

f:id:nn_hokuson:20171130194344p:plain
Unityではメニューバーから「Edit」→「Project Settings」→「Script Execution Order」という機能を使ってスクリプトを実行する順序を指定できます。

今回は、プレイヤの移動→カメラの移動という順番になるようにスクリプトの実行順序を指定しました。

f:id:nn_hokuson:20171128213836p:plain:w350

コントローラ入力編

最後に、作成したマリオをキーボードだけではなく、ゲームパッドでも操作できるようにします。ゲームパッドの入力を取得するにはInput Managerを使います。

Input Managerは簡単に言うと、キー入力やゲームパッドの入力、タッチパネルからの入力に名前をつける仕組みです。次の例では「Aキー」と「十字キーの左ボタン」のどちらにも「Left」という名前をつけています。

f:id:nn_hokuson:20171120224625j:plain:w450

Input Managerを使うことで入力デバイスを仮想化できるため、異なる入力デバイスでもスクリプトからは同じ名前で扱うことが出来るようになるのです。

今回のゲームでは次のようにキーを割り当てました。

  • 十字キー:左右の移動
  • Aボタン:ジャンプ
  • Bボタン:ダッシュ

Input Managerからの入力は、GetButtonDownメソッドやGetAxisメソッドなどを使って取得できます。

void Update () {
	if(Input.GetButtonDown("Left")){
		Debug.Log("Left Key Pressed");
	}
}

Input Mnagerに関しても、次の記事で使い方を紹介しているので、参考にしてみて下さい。

nn-hokuson.hatenablog.com

まとめ

Unityを使って2Dマリオの挙動を目コピしてみました。敵の挙動やキノコによるパワーアップなど、まだまだ未完成ではありますが、ジャンプの挙動などマリオをマリオたらしめている重要な要素は網羅できたかと思います。

【Unity】Input Managerの使い方まとめ。PS3やPS4のコントローラの入力を取得しよう

ゲームを作った場合、PS3やPS4などのコントローラ(その他ゲームパッド)を使って検証したいことがあります。ここではUnityでPS3のコントローラの入力を取る方法を紹介します。コントローラとPCはUSBで接続しておきましょう(Bluetoothでも大丈夫です)

ワイヤレスコントローラー(DUALSHOCK 4) ジェット・ブラック(CUH-ZCT2J)

ワイヤレスコントローラー(DUALSHOCK 4) ジェット・ブラック(CUH-ZCT2J)

今回の記事の内容は次のようになります。

手っ取り早くコントローラの入力を取る

まずは一番簡単な方法から紹介します。普通にコントローラのキーが押されたかを調べるだけであれば、次のコードだけで十分です。

// 左十字キーが押された場合
Input.GetButtonDown("joystick button 7");

マウスやキー入力とほぼ同じコードでPS3/4のコントローラ入力を取得出来るのがわかりますね。引数にはコントローラに割り振られたボタン番号を渡します。

PS3/4のコントローラのボタン番号は、Unity Asset Storeで配布されている「Controller Tester」というアセットで簡単に調べられます。

このアセットをインポートして、「Controller Tester」→「Scene」→「Controller Test」のシーンを開いて下さい。ゲームを実行すると次のような画面が表示されるので、コントローラのボタンを押すと、対応するボタン番号のところの表示が変化します。

f:id:nn_hokuson:20171120200246g:plain

ちなみに・・・手元にあるPS3のコントローラでは次のような割当になっていました。

f:id:nn_hokuson:20171120202638j:plain:w350

Input Managerを使ってコントローラの入力を取得する

とりあえず、Input.GetButton系のメソッドを使うことで、簡単にPS3やPS4のコントローラの入力が取れることはわかりました。

ただ、デバッグのときにはキーボードのASWDキーでプレイヤを動かしたいのだけど・・・となると入力部分を全てif文で分けるか置換するか・・・これは考えたくないですね(笑)

そこで、Input Managerの登場です。Input Managerは簡単に言えばキー入力やゲームパッドの入力、タッチパネルからの入力などに名前をつける仕組みです。そして、スクリプト側ではその名前を使って入力を取得します。

f:id:nn_hokuson:20171120224625j:plain:w450

つまり、入力デバイスとスクリプトの間にInput Managerというレイヤを一層噛ますことで、異なる入力でも同じ名前で扱うことが出来るようになるのです。

ここではPS3コントローラの左十字キーと、キーボードのAキーに「Left」という名前をつけてみましょう。

Unityのメニューバーから「Edit」→「Project Settings」→「Input」を選択し、インスペクタを開いて下さい。次のように入力が並んでいるのでSizeの欄の値を一つ増やして「19」と入力し、エンターを押して下さい。

f:id:nn_hokuson:20171120224645j:plain:w300

一番下に新たな入力(Cancel)が複製されるので、次のように設定して下さい。

項目名
Name Left
Positive Button joystick button 7
Alt Positive Button a
Type Key or Mouse Button

これでPS3の左十字キー(joystic button 7)とAキーに「Left」という名前をつけることができました。

最後にスクリプトで次のように記述することで左十字キーが押されたときとAキーが押されたとき、いずれも「Left Key Pressed」という文字が表示されます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour {

	void Update () {
		if(Input.GetButtonDown("Left")){
			Debug.Log("Left Key Pressed");
		}
	}
}

アナログスティックの値を取得する

アナログスティックの値も、ボタン入力とほぼ同じ方法で取得することができます。アナログスティックに関してはもともと用意されている「Horizontal」と「Vertical」の値を流用しましょう。

項目名
Name Horizontal
Type Joystick Axis
Axis X Axis
Joy Num Get Motion from all Joysticks
項目名
Name Vertical
Type Joystick Axis
Axis Y Axis
Joy Num Get Motion from all Joysticks

アナログスティックからの入力はGetAxisメソッドを使って取得できます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour {

	void Update () {
		float h = Input.GetAxis("Horizontal");
		float v = Input.GetAxis("Vertical");
		Debug.Log(h+","+v);
	}
}

アナログスティックを左に倒したときはGetAxisメソッドの戻り値は「-1」になり、アナログスティックを右に倒したときの戻り値は「1」になります。通常の状態では「0」が戻ってきます。

docs.unity3d.com

まとめ

InputManagerの使い方を紹介しました。InputManagerを使うことで、入力デバイスとスクリプトの間にレイヤを一層噛ますことができ、異なる入力でも同じ名前で扱うことが出来ました。

Unityの教科書 Unity 2017完全対応版  2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)

Unityの教科書 Unity 2017完全対応版 2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)

【Unity】JoyConを使ってSurvival ShooterをVR化する

f:id:nn_hokuson:20171114203601j:plain

色々な方が記事にされていますが、Nintendo SWITCHのJoyConはBLE通信を使っているのでPCやAndroidと簡単に接続することが出来ます。

www.taguchikun.com

そこで、今回はJoy-Con(ジョイコン)とGoogle Cardboardを使ってUnityの「Survival Shooter」をVR化する方法を紹介します。動画にするとこんな感じです。

f:id:nn_hokuson:20171114204640g:plain

Survival ShooterをVR化するまでの流れは次のようになります。

Joy-Conの入力をUnityで取得する

ジョイコンのデータをUnityで取得するにはInput Managerを使用します。メニューバーから「Edit」→「Project Settings」→「Input」を選択して下さい。

インスペクタの部分に入力軸が表示されるので、左右のジョイコン用に「Horizontal」と「Vertical」のデータを1組ずつ作ります。それぞれの項目を選択してから「Ctrl-D」でコピーして、それぞれ「Horizontal1」「Horizontal2」「Vertical1」「Vertical2」とリネームしてください。

f:id:nn_hokuson:20171110224457p:plain:w320

また、それぞれの軸の設定項目(Name, Axis, Joy Num)は次のように設定します。

Name Axis Joy Num
Horizontal1 6th axis(Joysticks) Joystick 1
Horizontal2 6th axis(Joysticks) Joystick 2
Vertical1 5th axis(Joysticks) Joystick 1
Vertical2 5th axis(Joysticks) Joystick 2


これでスクリプトからJoy-Conのアナログスティックの値をInput.GetAxisメソッドで受け取ることができるようになりました。このサンプルは左Joy-Conのアナログスティックの値を取得しています。

 float h = Input.GetAxis("Horizontal1");
 float v = Input.GetAxis("Vertical1");

ちなみに・・・

今回はJoy-ConのAxisを5thと6thに設定しましたが、環境によっては異なる値になることがあるようです。この場合はアセット「Controller Tester」を使うことで、Joy-ConのどのボタンがUnityのどの軸に割り振られているかを、簡単に確認できますよ。


Survival ShooterでJoy-conの入力を受け取る

Androidで実行したときにプレイヤの移動は、右ジョイコンのアナログスティック、プレイヤの回転は左ジョイコンのアナログスティックで操作できるようにしましょう。また、銃を撃つにはAボタンを使用します。

まずは、Asset Storeから「Survival Shooter」をダウンロードしましょう。

アセットがダウンロードできたら、Assets/_Complete-Game/Scripts/Player/PlayeMovement.csを開きFixedUpdateメソッドを次のように修正してください。

void FixedUpdate ()
{
        // Store the input axes.
        float h = Input.GetAxis("Horizontal1");
        float v = Input.GetAxis("Vertical1");
        float h2 = Input.GetAxis("Horizontal2");

        // Move the player around the scene.
        Move (-h, -v);

        // Turn the player to face the mouse cursor.
        //Turning ();
        transform.Rotate (0, h2*4, 0);

        // Animate the player.
        Animating (h, v);
}

ここでは、先程InputManagerで設定したHorizontalとVerticalの入力軸を使用して、Joy-Conのアナログスティックの入力を取得しています。
また、プレイヤの回転は左ジョイコンのアナログスティックの傾きに応じて、Rotateメソッドで回転しています。従来使用しているTurnメソッドはコメントアウトしています。

Androidの実機で動作確認

Androidで動作確認するためには、まずJoy-conとAndroidをBluetoothで接続する必要があります。Androidの「設定」→「Bluetooth」を選択してください。

f:id:nn_hokuson:20171111081650j:plain:w200

この状態で、ジョイコンのペアリングボタンを3秒程度押してください。Joy-conのゲージが点滅し始めると、Androidの端末にJoy-con(R)と候補が表示されるので、タッチして接続します。同様の手順で左Joy-Conも接続してください。

f:id:nn_hokuson:20171111081742j:plain:w200

現在はJoy-conとAndroidが接続が完了しても、Joy-Con側のインジゲータの点滅は消えないようです・・・これでジョイコンとAndroidがBLEで接続されたので、Survival Shooterのアプリをインストールして動くか試してみましょう!

f:id:nn_hokuson:20171114200042j:plain

ちなみに・・・現在(2017/11)の段階では、iPhone(iOS)とジョイコンはBLEで接続できない仕様になっているようです。

Google Cardboardを使ってSurvival ShooterをVR化する

せっかくSurvival ShooterがJoy-conで遊べるようになったことですので、最後にVRでも遊べるようにしてみましょう。

今回はGoogle Cardboardを使ってVR化してみましょう。Unity2017付近からVRの開発環境がUnityと統合されたため、簡単にVRのゲームを作る事ができるようになっています。

nn-hokuson.hatenablog.com

ここで変更する項目は、入力とカメラのみになります。Androidで遊ぶ場合、プレイヤの回転はJoy-Conの右アナログスティックで行っていましたが、VRの場合はジャイロで自動的に計算されるので、不要になります。PlayerMovement.csの次の行をコメントアウトしておきましょう。

void FixedUpdate ()
{
        // float h2 = Input.GetAxis("Horizontal2");
           ・・・・・
       // transform.Rotate (0, h2*4, 0);
}


いったんMain Cameraの座標と姿勢をリセットしてから、カメラがプレイヤの視点に来るように移動させて下さい。

f:id:nn_hokuson:20171114203524j:plain

また、Projectionを「Perspective」、Field of Viewを「70」に設定します。

f:id:nn_hokuson:20171114203253p:plain:w400

Survival Shooterではプレイヤの向いている方向に銃を撃ちます。VRの場合はカメラの向いている方向に銃を撃つように、プレイヤをカメラの子要素にします。ヒエラルキーウインドウでMain CameraにPlayerをドラッグ&ドロップして下さい。

f:id:nn_hokuson:20171113070312p:plain:w300

最後にUnityでAndroidビルドします。いまのままでは通常の画面で出力されてしまうので、Player SettingsでVR映像が出力されるように設定します。

メニューバーから「FIle」→「Build Settings」を選択して下さい。PlattformをAndroidに変更後、Build Settingsの画面でPlayer Settingsボタンを押します。

f:id:nn_hokuson:20171113070815j:plain:w400

Unity2017.2以降はインスペクタのXR Settingsを選択し、Virtual Reality Supportedにチェックを入れ、Virtual Reality SDKからGoogle Cardboardを選択します。 (Unity2017.1以前はOther Settingsの中にVirtual Reality Supportedの項目があります)

f:id:nn_hokuson:20171113071055p:plain:w400

最後に「Bundle identifier」を任意の文字列に修正してから、「Build Settings」ウインドウで「Build & Run」をクリックして実機にインストールしてください

f:id:nn_hokuson:20171114202957j:plain

これで、ようやくGoogle CardobardとJoy-Conを使ってSurvival ShooterをVR化することができました。長かったですね〜^^。ぜひ一度実際に遊んでみて下さい!

Unityの教科書 Unity 2017完全対応版  2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)

Unityの教科書 Unity 2017完全対応版 2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)

UnityによるVRアプリケーション開発 ―作りながら学ぶバーチャルリアリティ入門

UnityによるVRアプリケーション開発 ―作りながら学ぶバーチャルリアリティ入門

  • 作者: Jonathan Linowes,高橋憲一,安藤幸央,江川崇,あんどうやすし
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2016/08/26
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (4件) を見る