-
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、2つの表示を切り替える事で
-
タイミング及びデザインを調整も
-
完成したアニメーションを
-
ステートマシーンを使って切り替える事が出来ます。
-
キャラクターの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バーはシンプルなスプライト2つで構成されています。
-
バーの外周部分とバーの部分です。
-
これらもPhotoshopでデザインして
-
2つのテクスチャを書き出しました。
-
このスプライトのImport Settingsで
-
Pivotを左の中央に設定して
-
スケールを小さくした時に左側に縮んでいく様にします。
-
この2つのスプライトは
-
プレイヤーに追従するスクリプトを追加した
-
空のGameObjectの子供として配置します。
-
プレイヤーのGameObjectの位置とオフセットを足すので
-
表示位置はInspectorで調整できます。
-
プレイヤーは残りHPが0になると
-
コライダーをトリガーモードに設定されるので
-
プレイヤーはステージを突き破って落ちます。
-
その時、最前面に描画される様に
-
SpriteRendererを”UI”ソートレイヤーに移動させます。
-
プレイヤーの死亡アニメーションは、2段階で構成されます。
-
最初の”Death”は、帽子と武器を落とします。
-
2番目は”Falling”という名前です。
-
アニメータにExit Timeのコンディションを使って
-
”Death”アニメーションが終わると
-
自動的に”Falling”に遷移します。
-
最後に、プレイヤの動きと攻撃を停止させるために
-
PlayerControlとGunのスクリプトを無効にします。
-
Die関数はPublicなので
-
どこからでも呼び出せます。
-
例えば、水に落ちた時。
-
プレイヤーが水に落ちるとゲームをリセットするために、
-
トリガーコライダーとスクリプトだけがある
-
KillTriggerという名前のGameObjectがあります。
-
Removerスクリプトには
-
川にザブンと飛び込んだ敵を消して
-
飛沫のアニメーションとサウンドを再生する役割があります。
-
しかしこのコライダーにプレイヤーが衝突すると
-
PlayerHealthスクリプトの”Die”関数呼び出して
-
プレイヤーを画面外に動かしている間カメラの追従を無効にし、
-
2秒後にステージをリセットするコルーチンを実行します。
-
さて、プレイヤーキャラクターの死について考えるのではなく、
-
彼が生き残るために何をあげればいいかを考えましょう。
-
このゲームでは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にして
-
爆弾から敵の方向に吹き飛ばします。
-
ループが終わったら
-
エフェクトを再生し
爆発の効果音を再生して
-
爆弾を消去します。
-
爆発には2つの要素で構成されています。
-
主な部分は、一時的に登場して消える丸です。
-
2番目の部分はロケットの爆発のスプライトを再利用した
-
星のパーティクルです。
-
効率よくこのパーティクルを使用するために
-
常にシーンに配置しておき
-
表示させたいタイミングで表示位置に動かして再生します。
-
メモリにパーティクルを常駐させる事で効率が良くなります。
-
これは、プレイヤーは1つしか爆弾を持てない、
-
つまり爆発も1つしか表示されないので出来る事です。
-
パーティクルを常駐させておくことで
-
毎回パーティクルのインスタンスを生成して削除するよりも効率的です。
-
ですが、ロケットの爆発は一度にたくさん出る事があるので
-
毎回生成と削除する事が必要です。
-
プレイヤーが敵を倒してポイントを得る処理を見てみましょう。
-
これは2つのパーツでできています。
-
プレイヤーが100ポイントを獲得した表示と
-
画面の上に表示されているスコアーが加算されてゆくUIです。
-
得点のアニメーションは1と0のスプライトで構成されています。
-
空のオブジェクトの子として配置し
-
アニメーションが終わると簡単なスクリプトで
-
シーンから削除されます。
-
アニメーションの最後にイベントを追加し
-
スクリプトの削除関数を呼び出します。
-
ScoreUIは、カスタムフォントと
-
プレイヤーの得点を管理するスクリプト付属された
-
シンプルなGUIコンポーネントです。
-
Scoreはパブリック変数です。
-
なので敵が倒された時、
-
Enemyのスクリプトからポイントを追加する事ができます。
-
さて、今度は敵の話です。
詳細を見てみましょう。
-
このゲームには2種類の敵が登場します。
-
緑のナメクジモンスターと
-
宇宙船に乗った宇宙人です。
-
挙動はほぼ同じなので同じスクリプトを使い回し、
-
Inspectorから移動速度とHPを調整して差を付けています。
-
各敵はそれぞれのWalkアニメーションがあります。
-
ナメクジには動く尾があり
-
宇宙船の敵は行ったり来たりします。
-
ナメクジの尾の部分は
-
スプライトインポータで独立したパーツとしてインポートしました。
-
なので、尾の部分を独立して動かす事ができ、
-
スプライト全体が伸び縮みししないようになっています。
-
Pivotを右にする事で
-
そこから尾が伸び縮みするアニメーションをつけます。
-
まぶたを上下に動かして
-
Z値でスプライトがソートされるようにし
-
目がまぶたの下に表示されるようにします。
-
次の敵はかなりシンプルです。
-
行ったり来たりを繰り返すアニメーションにします。
-
敵の動きには、Rigidbodyの速度をセットします。
-
宇宙船が障害物、例えば壁等への衝突は
-
ある点がコライダーと重なっているかどうかを
-
Physics.OverlapPoint関数を用いて検知します。
-
障害物、このステージ両端のタワーは
-
障害物としてタグが設定されています。
-
衝突を検知するとFlip関数を呼び出して
-
敵のXスケールを反対にして反対方向に向かわせます。
-
敵を倒す判定のために、
-
ロケットが敵に当たる度にHurt関数を呼び出します。
-
この関数の内部では、敵のHPを1づつ下げています。
-
FixedUpdate関数の中で敵のHPを監視しており、
-
0になっている場合はDeath関数が呼び出されます。
-
敵が死亡する時には様々な処理が実行されます。
-
まず、2Dのキャラクターの全てのスプライトを非表示にします。
-
これは、アニメーションのためにある複数のスプライトを
-
1つの死亡したキャラクターのスプライトに差し替えたいからです。
-
メインのSprite RendererにdeadEnemyのスプライトを設定します。
-
Scoreスクリプトのパブリック変数に対し
スコアを加算します。
-
敵の死亡のビジュアルをわかりやすくするため
-
Rigidbodyにトルクを追加して回転させます。
-
背景を突き抜けて川に落下させたいので
-
すべてのコライダーを探して
-
IsTriggerパラメーターをTrueにします。
-
これで背景のコライダーを突き抜ける事になります。
-
死亡時に再生するオーディオクリップ中の1つを選び再生し
-
前述の得点表示アニメーションを再生します。
-
敵をシーンから削除するため、
-
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ゲームが本当に楽しみです。
-
それでは、ご清聴ありがとうございました!