Unity 4.3 より新たな2D機能が追加されました。
我々の作成したデモを元に、
これらの機能を解説したいと思います。
この「Tower Bridge Defence」というゲームは
宇宙人の侵略に晒されている
ロンドンのタワーブリッジを描いたデモです。
これは、スプライトのみで構成された物理駆動のゲームで、
Unityにおける2Dゲーム制作の理解に役立つのではないかと思います。
この動画では
デモの背景と前景の作り方
キャラクター、エフェクト、カメラの追従
アニメーションとスクリプトについて解説します。
まずは、Unity 2Dの基本的な操作を説明しましょう。
まず、2D機能を使うにあたって
「Editor Behaviour Mode」を2Dにしてください。
新規プロジェクトウィザードのドロップダウン
またはメニューから Edit > Project Settings > Editor
で設定出来ます。
この設定により
インポートされたテクスチャーはデフォルトでスプライトに設定され、
「Scene View」のデフォルトの表示は2Dになります。
このモードでは2Dゲーム制作を行いやすい様に
ビューは並行投影になります。
そして、ビューの右上に表示されている3D Gizmoが非表示になり、
より広いスペースを使えるようになります。
この設定以外の点では
2Dのワークフローは従来の3Dゲームを作るアプローチとよく似ています。
なので、既にUnityを使ったことがある方なら
すぐに2Dゲームを作ることができると言うことです。
また、2Dと3Dの要素は混在できるので
2Dゲームに3Dの要素を加える事も
またその逆に3Dのゲームに2Dの要素を加えることもできます。
ではデモプロジェクトを元に
我々がどのようにステージを作成したのかを見てみましょう。
まず、Photoshopでステージ構成の概要を作成し
そこからレイアウトを決めました。
レイヤー毎に書き出す事で、
Unityのスプライトタイプとしてインポートできます
背景を視点に応じて移動させるため
いくつかの背景要素は別にして
Background Sorting レイヤーに配置します。
これはUnityの新たなもう一つの2D開発機能です。
このレイヤーにすべての背景を追加する事で、
Order In LayerというSprite Rendererのプロパティで
一括して描画順番を変更する事ができます。
配置位置が決まったなら
Sorting Layerの”Background”をロックする事で
前景の要素を追加するときに
間違えて背景を動かせることを予防できます。
右上のLayersドロップダウンで
この設定を指定できます。
背景は飾りの要素だけですので、
スプライト以外のコンポーネントは必要ありません。
背景オブジェクトの親オブジェクトには
視点に応じて位置を操作する
BackgroundParallaxという単純なスクリプトが追加してあります。
このスクリプトに関しての詳しい説明は
Scriptsフォルダにあるスクリプトのコメントを参照して下さい。
次は、前景の要素、キャラクターの作成です。
デザインはロンドンのタワーブリッジの中に
UFOが着陸した状況です。
プレイヤーはフィールド内を動き回り、
敵は空から登場して
フィールドの中を移動します。
そういった、キャラクターの移動のために、
各前景の部分にはコライダーが必要です。
大体のフィールドには2DのBox Colliderで良いのですが、
UFOの形はかなり複雑です。
UnityのPolygon Colliderはスプライトの形に基づき
自動的にコライダーを作成してくれます。
また、生成されたコライダーの形を手動で調整する事も可能です。
ポイントの移動、追加、削除で
コライダーの形を歩かせるのに適切な形にします。
前景を背景より手前に表示するために
Tags and Layersマネジャーに
"Foregrounds"ソートレイヤーを作成してあります。
次は、プレイヤーキャラクターを見てみましょう。
プレイヤーキャラクターもPhotoshopでデザインしました。
2Dゲームに合うように
Raymanの様に手足が独立したデザインです。
もしくは、スプライトアニメを用いた コマアニメーションで表現する事も可能です。
そのアプローチは後で背景の鳥に使用します。
キャラクターの手足を別々に動かしたいので
Unityが別のスプライトとしてインポートされる様に
それぞれ分割して配置します。
これにより、全てのスプライトを独立的に動く様に配置する事ができます。
新しい"Character"ソートレイヤーに配置して
PositionのZの値を入力して描画順を設定します。
プレイヤーキャラクターのスプライトは空のGameObjectの子供に設定し
親のオブジェクトにはコントロールスクリプト、コライダー、
フィジックス、その他を付加します。
その後、新しくなったAnimation Windowで
待機、移動、ジャンプ、攻撃、死亡の時の
各スプライトのパーツの動きを設定します。
Animation windowのDopesheetビューを使う事で
簡単に設定ができます。
親オブジェクトにアニメーションを追加して
子オブジェクトのKeyframeを作成します。
再生位置を動かし、キャラクターのパーツを動かして
自動的にKeyframeを作ってアニメーションを作る事ができます。
CurveとDopesheet、２つの表示を切り替える事で
タイミング及びデザインを調整も
完成したアニメーションを
ステートマシーンを使って切り替える事が出来ます。
キャラクターのAnimator Controllerは bipedを使用していないので
Apply Root Motionのチェックを解除して
Animate Physicsを有効にします。
これで、物理エンジンで正しくアニメーションされる様になります。
フィールド上を移動する為に
プレイヤーの足元にはCircle Colliderがあります。
また、体を囲うBox Colliderもあります。
これにより、段差をスムーズに乗り越える事ができ
ジャンプの時は頭が天井にをぶつかるようになります。
キャラクターのアニメーションを制御するために、
2Dフィジックスで動かすスクリプトを作りました。
フィジクスを使ってキャラクターと
敵を動かしてもっとダイナミックな
ゲームプレーが可能になります。
キャラクターのPlayerControlスクリプトでは
プレイヤーの入力をチェックします。
入力は物理制御を通してキャラクターを移動させ、
また、この入力をAnimatorに渡し
ステートから正しいアニメーションを選択して
それぞれのアニメーション間をスムーズに遷移させます。
アニメーションからステートを作成すると便利な点は
物理の速度に応じてアニメーションの速度を変化させられる事です。
速度に応じてアニメーションを切り替える必要はありません。
FixedUpdateという関数は物理更新の毎に呼び出されます。
この関数で水平方向の入力を取得し、
AnimatorのSpeedパラメータに入れます。
ステートマシーンの待機と移動間の遷移条件は
Speedパラメータが0.1以上かどうか、になります。
その状況になると、アニメーターは待機のステートから
移動のステートにブレンドします。
次は、プレイヤーのRigidbody2Dという2D物理要素に
力を加えてプレイヤーを動かします。
キャラクターの向いている向きを制御するのにも
水平方向の入力を利用します。
Unityでは、左キーを押すと-1が返り
右キーを押すと+1が返ってきます。
入力された値に応じて、Flip()関数で
キャラクターのXスケールを反転させて
反対側を向いている状態を表現します。
地面を接地しているかどうかを決めるために、
Groundというレーヤーを追加して
すべての歩ける地面に適用します。
そして2DのLinecastという関数を使い
キャラクターの足元にGroundレイヤーがあるかどうかを
チェックできます。
より簡単に変更できるようにするため、
接地判定用の空のGameObjectを追加しました
この空GameObjectにGizmoを追加する事で
接地判定の距離を調整できます。
ゲーム中は
キャラクターは地面に設置している状況でのみでジャンプできます。
プレイヤーの制御の詳細は、
スクリプトのコメントを確認してみてください。
プレイヤーの攻撃に関しては後ほどお話したいと思います。
次は、このデモで
カメラがどのような動きをするか見てみましょう。
2Dゲームにおいても、3Dゲームと変わらず
カメラの動作はとても重要です。
古典的2Dアクションのカメラを学ぶために
スーパーファミコンのスーパーマリオワールドを見てみましょう。
スーパーマリオワールドでは
カメラは横方向に追従しますが
画面の中心にはキャラクターに追従しない遊び幅があります。
この遊び幅の外側に動こうとすると
カメラはプレイヤーの位置に追従します。
高さ方向に対しては一定段階の高さを保ちますが、
我々のゲームに関してはその必要はありません。
ステージの横幅はそれほど長くなく
殆どが画面に収まる広さのステージです。
ですので、カメラの水平の動きと垂直の動きは同じ動きにします。
mainCameraのGameObjectに付属している
CameraFollowというスクリプトを見てみましょう。
どのような働きをするのか、コメントを確認してください。
色々な機能の中で最も重要なことは
ヒーローの宇宙人を倒す能力です。
ヒーローのバズーカは発砲後の反動アニメーションがあります。
このアクションはいくつかの部分に分かれています。
Fireキーの入力があると
ロケットを発射して、オーディオを再生して
アニメーションステートを再生します。
もっと詳しく調べてみましょう。
Runや他のアニメーションの再生中に
Shootアニメーションを再生するのため、
Shootingという別のレイヤーを作成しました。
Weightプロパティを1にすると
Shootingレイヤーに含まれるパーツは
ベースレイヤーの動きを完全に上書きします。
このレイヤーは、プログラムからShootトリガーを呼び出されると
いつでもShootアニメーションに切り替わります。
Gunスクリプトのこの機能を調べてみましょう。
ここで、アニメーターを操作して
ShootトリガーをTrueにします。
トリガーはスイッチのように
次のフレームにFalseに戻り、再び使用できるようになります。
これは攻撃のようなアクションにぴったりです。
アニメーションをセットし、
ロケットもこのスクリプトで発射します。
オーディオを再生して
プレイヤーの向いている方向に対して
ロケットの速度を設定します。
このスクリプトはヒーローの
Gunという空のGameObjectに付属します。
空のGameObjectにスクリプトを追加するのは、
簡単にロケットの発射位置を設定できるようにするためです。
バズーカの先に空のGameObjectを配置して
オブジェクトの位置にロケットが生成されるようにします。
ロケットは2DRigidbodyを持って
それに速度を与えて動かします。
スプライトの排気炎を付け
パーティクルシステムの煙も足します。
パーティクルシステムも新たなスプライト版機能が追加されました。
煙のコマが描かれたスプライトをマテリアルに追加する事で
簡単にスプライトアニメーションのパーティクルを作成できます。
敵またはフィールドにロケットが当たると
ロケットは消え
爆発が生成されます。
爆発は単純なコマアニメーションを行うスプライトです。
Sorting Layerで前景の最後に描画されるようにします。
このようなスプライトに基づいたアニメーションの追加は、
Project Panelからファイルを選んで
Sprite Modeを"Multiple"にします。
これでスプライトエディタを使い
手動、もしくは自動的に分割する事ができます。
ファイルをスプライトに設定する利点は、
この様にApplyをクリックするだけで
Unityはファイルの子供としてそれぞれのスプライトを生成し
そのスプライトをそのまま使う事が出来ます。
これがロケットの構造です。
どのようにして敵を倒すのかは、
後ほど敵の項目でお話したいと思います。
話をプレイヤーキャラクターに戻して
HPとダメージのことを調べましょう。
HPはfloatとして保存されています。
敵からのダメージはTakeDamage関数を呼び出して与えます。
プレイヤーが簡単に倒されてしまうのを避けるため、
前回のダメージからrepeatDamagePeriodが経過した場合のみ
ダメージを受けます。
また、敵から逃げ易くするため、
そしてプレイヤーのダメージを表現するため、
ダメージを受けたキャラクターをヒットバックさせます。
TakeDamage関数で一時的にプレイヤーのジャンプを止め、
敵からキャラクターへの方向に物理的な力を与えて動かします。
hurtForceの変数はInspectorで表示しているので、
スクリプトを編集せずにゲーム要素の調整が出来ます。
ダメージ表現に加えて
もちろんプレイヤーのHPも減らした上で
HPバーを更新します。
HPの差を表示するのためにバーの長さを縮め
色を緑から赤に補間します。
最大HPに対する現在のHPの比率を計算すれば出来ますね。
HPバーはシンプルなスプライト２つで構成されています。
バーの外周部分とバーの部分です。
これらもPhotoshopでデザインして
2つのテクスチャを書き出しました。
このスプライトのImport Settingsで
Pivotを左の中央に設定して
スケールを小さくした時に左側に縮んでいく様にします。
この2つのスプライトは
プレイヤーに追従するスクリプトを追加した
空のGameObjectの子供として配置します。
プレイヤーのGameObjectの位置とオフセットを足すので
表示位置はInspectorで調整できます。
プレイヤーは残りHPが0になると
コライダーをトリガーモードに設定されるので
プレイヤーはステージを突き破って落ちます。
その時、最前面に描画される様に
SpriteRendererを”UI”ソートレイヤーに移動させます。
プレイヤーの死亡アニメーションは、２段階で構成されます。
最初の”Death”は、帽子と武器を落とします。
２番目は”Falling”という名前です。
アニメータにExit Timeのコンディションを使って
”Death”アニメーションが終わると
自動的に”Falling”に遷移します。
最後に、プレイヤの動きと攻撃を停止させるために
PlayerControlとGunのスクリプトを無効にします。
Die関数はPublicなので
どこからでも呼び出せます。
例えば、水に落ちた時。
プレイヤーが水に落ちるとゲームをリセットするために、
トリガーコライダーとスクリプトだけがある
KillTriggerという名前のGameObjectがあります。
Removerスクリプトには
川にザブンと飛び込んだ敵を消して
飛沫のアニメーションとサウンドを再生する役割があります。
しかしこのコライダーにプレイヤーが衝突すると
PlayerHealthスクリプトの”Die”関数呼び出して
プレイヤーを画面外に動かしている間カメラの追従を無効にし、
2秒後にステージをリセットするコルーチンを実行します。
さて、プレイヤーキャラクターの死について考えるのではなく、
彼が生き残るために何をあげればいいかを考えましょう。
このゲームでは2種類のプレイヤーを助けるアイテムボックスが落下してきます。
一つは爆弾、もう一つは救急箱です。
このアイテムは２つの部分で構成されています。
箱本体と落下傘の部分です。
これらの要素は空のオブジェクトでグループにして動かす事ができます。
落下傘の中心を親オブジェクトの中心に位置します。
これで箱を、落下中左右に揺れるようにできます。
オブジェクトが落下させる為には
単にRigidbodyを追加するだけです。
そして、箱の着地やプレイヤーの取得を検出する為に
コライダーも追加します。
箱が着陸したら
落下傘をしぼませるステートに遷移します。
他のアニメータと同じ様に
アニメーターはオブジェクトのステートを管理しています。
デフォルトではfloatDownステートですが
LandトリガーがTrueになると着陸のステートに切り替わります。
この処理はスクリプトのonTriggerEnter関数で実行しています。
タグで地面を見て地面かどうかを判断しています。
箱を親オブジェクトからデタッチし、
Rigidbodyを追加します。
それにより、地形の影響を受け
斜面に着陸した場合でも正しい角度で停止します。
さて、爆弾を見てみましょう。
爆弾を取得する処理は
箱に付属したBombPickupスクリプトで行われます。
爆弾を拾うと、箱を消して
プレイヤーに付属したLayBombsスクリプト内の
爆弾を持つ数を加算します。
LayBombsのスクリプトは
プレイヤーが爆弾を持っているかどうかをチェックして
BombのPrefabを生成します。
BombのPrefabはBombDetonationというcoroutineで
待ってExplode関数を呼び出します。
Explode関数は様々な動作があります。
まず、他の爆弾が出現できる様にするため
変数bombLaidをリセットします。
そしてアイテム管理に新しい箱が追加可能な事を通知し
範囲内にいる全ての敵を倒します。
最後の部分がどのような働きをするのか注目して下さい。
残りHPに関係なく全ての敵を倒すために
Physics.OverlapCircleAllを使って
全てのEnemyタグが付けられたオブジェクトを取得します。
foreachループで見つかった全ての敵のHPを0にして
爆弾から敵の方向に吹き飛ばします。
ループが終わったら
エフェクトを再生し 爆発の効果音を再生して
爆弾を消去します。
爆発には２つの要素で構成されています。
主な部分は、一時的に登場して消える丸です。
２番目の部分はロケットの爆発のスプライトを再利用した
星のパーティクルです。
効率よくこのパーティクルを使用するために
常にシーンに配置しておき
表示させたいタイミングで表示位置に動かして再生します。
メモリにパーティクルを常駐させる事で効率が良くなります。
これは、プレイヤーは1つしか爆弾を持てない、
つまり爆発も1つしか表示されないので出来る事です。
パーティクルを常駐させておくことで
毎回パーティクルのインスタンスを生成して削除するよりも効率的です。
ですが、ロケットの爆発は一度にたくさん出る事があるので
毎回生成と削除する事が必要です。
プレイヤーが敵を倒してポイントを得る処理を見てみましょう。
これは2つのパーツでできています。
プレイヤーが100ポイントを獲得した表示と
画面の上に表示されているスコアーが加算されてゆくUIです。
得点のアニメーションは1と0のスプライトで構成されています。
空のオブジェクトの子として配置し
アニメーションが終わると簡単なスクリプトで
シーンから削除されます。
アニメーションの最後にイベントを追加し
スクリプトの削除関数を呼び出します。
ScoreUIは、カスタムフォントと
プレイヤーの得点を管理するスクリプト付属された
シンプルなGUIコンポーネントです。
Scoreはパブリック変数です。
なので敵が倒された時、
Enemyのスクリプトからポイントを追加する事ができます。
さて、今度は敵の話です。 詳細を見てみましょう。
このゲームには2種類の敵が登場します。
緑のナメクジモンスターと
宇宙船に乗った宇宙人です。
挙動はほぼ同じなので同じスクリプトを使い回し、
Inspectorから移動速度とHPを調整して差を付けています。
各敵はそれぞれのWalkアニメーションがあります。
ナメクジには動く尾があり
宇宙船の敵は行ったり来たりします。
ナメクジの尾の部分は
スプライトインポータで独立したパーツとしてインポートしました。
なので、尾の部分を独立して動かす事ができ、
スプライト全体が伸び縮みししないようになっています。
Pivotを右にする事で
そこから尾が伸び縮みするアニメーションをつけます。
まぶたを上下に動かして
Z値でスプライトがソートされるようにし
目がまぶたの下に表示されるようにします。
次の敵はかなりシンプルです。
行ったり来たりを繰り返すアニメーションにします。
敵の動きには、Rigidbodyの速度をセットします。
宇宙船が障害物、例えば壁等への衝突は
ある点がコライダーと重なっているかどうかを
Physics.OverlapPoint関数を用いて検知します。
障害物、このステージ両端のタワーは
障害物としてタグが設定されています。
衝突を検知するとFlip関数を呼び出して
敵のXスケールを反対にして反対方向に向かわせます。
敵を倒す判定のために、
ロケットが敵に当たる度にHurt関数を呼び出します。
この関数の内部では、敵のHPを１づつ下げています。
FixedUpdate関数の中で敵のHPを監視しており、
0になっている場合はDeath関数が呼び出されます。
敵が死亡する時には様々な処理が実行されます。
まず、2Dのキャラクターの全てのスプライトを非表示にします。
これは、アニメーションのためにある複数のスプライトを
1つの死亡したキャラクターのスプライトに差し替えたいからです。
メインのSprite RendererにdeadEnemyのスプライトを設定します。
Scoreスクリプトのパブリック変数に対し スコアを加算します。
敵の死亡のビジュアルをわかりやすくするため
Rigidbodyにトルクを追加して回転させます。
背景を突き抜けて川に落下させたいので
すべてのコライダーを探して
IsTriggerパラメーターをTrueにします。
これで背景のコライダーを突き抜ける事になります。
死亡時に再生するオーディオクリップ中の１つを選び再生し
前述の得点表示アニメーションを再生します。
敵をシーンから削除するため、
KillTriggerオブジェクトを使います。
これは先ほど説明した通り、 水しぶきをあげオブジェクトを消去する機能を持ちます。
プレイヤー以外のオブジェクトが触ると
水しぶきのエフェクトを表示して オブジェクトを削除します。
効果音は水しぶきエフェクトが表示される時に
付属しているAudio Clipが同時に再生されます。
なので、オブジェクトを生成するだけで
効果音も鳴るようになっています。
動的なオブジェクトを追加し
背景を飾ってステージを完成させましょう。
飛んでいる白鳥と
川岸を走るバスとタクシーを追加します。
まずは白鳥のスプライトシートをPhotoshopで描いて
InspectorのMultiple Sprite Modeを使ってインポートしました。
この方法では
Unityが全てのコマを選択し自動的にインポートして
アセットのヒエラルキーに追加してくれます。
このコマは連続したアニメーションのセットなので、
全てのコマをシーンにドラッグするだけで
Unityが自動的にアニメーションさせてくれます。
ね、簡単でしょ？
白鳥は背景に追加します。
速度を与えて画面中を動けるように
Rigidbody2Dを追加します。
バスとタクシーのように、白鳥もPrefabです。
バスとタクシーのPrefabには、
Sprite Importerで車体とタイヤを分割して
シンプルなアニメーションを生成します。
これらにもRigidbody2Dを追加して
速度の追加で画面内を動かす事ができます。
白鳥、バス、タクシー
この3種のオブジェクトの生成を
管理するスクリプトを作成してあります。
BackgroundPropsSpawnerスクリプトは
頻度、速度、そして最初の位置を管理しています。
3種のオブジェクトに同じスクリプトを使うことができます。
生成するPrefabとプロパティを変更します。
詳細は、スクリプトのコメントをご覧ください。
最後に、動いている雲、川の流れ、そして霧を追加しました。
親オブジェクトに2つのスプライトを持たせ、
ゆっくりと横に動かします。
アニメーションはループで動き続けます。
以上が、私たちがどのようにしてこのゲームを作ったか、の全てです。
このビデオが2Dゲームの作り方の参考になれば嬉しく思います。
将来的には、より基本的なプロジェクトと
チュートリアルをお届けしたいと考えています。
私達は、Unity4.3についての感想と
あなたの作る素晴らしい2Dゲームが本当に楽しみです。
それでは、ご清聴ありがとうございました！