Unityで2Dカメラをマスターする:ゲーム開発者向けのチュートリアル

公開: 2022-03-11

開発者にとって、カメラはゲーム開発プロセスの基礎の1つです。 チェスアプリでゲームビューを表示するだけから、3D AAAゲームでカメラの動きを巧みに演出して映画のような効果を得るまで、カメラは基本的に、実際に「カメラ」と呼ばれる前であっても、これまでに作成されたすべてのビデオゲームで使用されます。

この記事では、2Dゲーム用のカメラシステムを設計する方法を説明します。また、最も人気のあるゲームエンジンの1つであるUnityにカメラシステムを実装する方法についてもいくつかのポイントを説明します。

2Dから2.5Dへ:拡張可能なカメラシステム

一緒に設計するカメラシステムは、モジュール式で拡張可能です。 基本的な機能を保証するいくつかのコンポーネントで構成される基本的なコアと、手元の状況に応じてオプションで使用できるさまざまなコンポーネント/エフェクトがあります。

ここで構築しているカメラシステムは、2Dプラットフォームゲームを対象としていますが、他のタイプの2Dゲーム、2.5Dゲーム、さらには3Dゲームにも簡単に拡張できます。

Unityで2Dカメラをマスターする:ゲーム開発者向けのチュートリアル

Unityで2Dカメラをマスターする:ゲーム開発者向けのチュートリアル
つぶやき

カメラ機能を、カメラトラッキングとカメラエフェクトの2つの主要なグループに分割します。

追跡

ここで行うカメラの動きのほとんどは、トラッキングに基づいています。 これは、オブジェクト(この場合はカメラ)がゲームシーン内を動き回るときに他のオブジェクトを追跡する機能です。 実装するトラッキングのタイプは、2Dプラットフォームゲームで遭遇するいくつかの一般的なシナリオを解決しますが、他の特定のシナリオの新しいタイプのトラッキングで拡張できます。

効果

手ぶれ、カメラズーム、カメラフェード、カラーオーバーレイなどのクールな効果を実装します。

入門

Unityで新しい2Dプロジェクトを作成し、標準アセット、特にRobotBoyキャラクターをインポートします。 次に、グラウンドボックスを作成し、キャラクターインスタンスを追加します。 現在のシーンでキャラクターと一緒に歩いたりジャンプしたりできるはずです。 カメラが正投影モードに設定されていることを確認します(デフォルトでは遠近法に設定されています)。

ターゲットの追跡

次のスクリプトは、メインカメラに基本的な追跡動作を追加します。 スクリプトは、シーンのメインカメラにコンポーネントとして添付する必要があり、追跡するターゲットオブジェクトを割り当てるためのフィールドを公開します。 次に、スクリプトは、カメラのx座標とy座標が、追跡するオブジェクトと同じであることを確認します。 この処理はすべて、更新ステップ中に実行されます。

 [SerializeField] protected Transform trackingTarget; // ... void Update() { transform.position = new Vector3(trackingTarget.position.x, trackingTarget.position.y, transform.position.z); }

RobotBoyキャラクターをシーン階層から、次の動作で表示される「トラッキングターゲット」フィールドにドラッグして、メインキャラクターのトラッキングを有効にします。

オフセットの追加

すべて良いですが、すぐに制限があります。キャラクターは常にシーンの中心にいます。 キャラクターの後ろにたくさん見ることができますが、これは通常私たちが興味を持っていないものであり、キャラクターの前にあるものが少なすぎて、ゲームプレイに悪影響を与える可能性があります。

これを解決するために、ターゲットからのオフセットでカメラを配置できるようにするいくつかの新しいフィールドをスクリプトに追加しています。

 [SerializeField] float xOffset; [SerializeField] float yOffset; // ... void Update() { transform.position = new Vector3(trackingTarget.position.x + xOffset, trackingTarget.position.y + yOffset, transform.position.z); }

以下に、2つの新しいフィールドの可能な構成を示します。

物事をスムーズにする

カメラの動きはかなり硬く、環境の絶え間ない動きから一部のプレーヤーにめまいが生じます。 これを修正するために、線形補間を使用したカメラトラッキングに遅延を追加し、キャラクターが位置を変更し始めた後のカメラの配置速度を制御する新しいフィールドを追加します。

 [SerializeField] protected float followSpeed; // ... protected override void Update() { float xTarget = trackingTarget.position.x + xOffset; float yTarget = trackingTarget.position.y + yOffset; float xNew = Mathf.Lerp(transform.position.x, xTarget, Time.deltaTime * followSpeed); float yNew = Mathf.Lerp(transform.position.y, yTarget, Time.deltaTime * followSpeed); transform.position = new Vector3(xNew, yNew, transform.position.z); } 

めまいを止める:軸のロック

キャラクターと一緒にカメラが上下するのを脳が見るのは気持ち悪いので、軸ロックを導入しています。 これは、追跡を1つの軸のみに制限できることを意味します。 次に、トラッキングコードを軸に依存しないトラッキングに分離し、新しいロックフラグを考慮に入れます。

 [SerializeField] protected bool isXLocked = false; [SerializeField] protected bool isYLocked = false; // ... float xNew = transform.position.x; if (!isXLocked) { xNew = Mathf.Lerp(transform.position.x, xTarget, Time.deltaTime * followSpeed); } float yNew = transform.position.y; if (!isYLocked) { yNew = Mathf.Lerp(transform.position.y, yTarget, Time.deltaTime * followSpeed); } 

レーンシステム

カメラがプレーヤーを水平方向にのみ追跡するようになったため、1つの画面の高さに制限されます。 キャラクターがはしごを登ったり、これより高くジャンプしたりする場合は、従う必要があります。 これを行う方法は、レーンシステムを使用することです。

次のシナリオを想像してみてください。

キャラクターは最初は下の車線にいます。 キャラクターがこのレーンの境界内にとどまっている間、カメラは設定可能なレーン固有の高さオフセット上で水平方向にのみ移動します。

キャラクターが別のレーンに入るとすぐに、カメラはそのレーンに移行し、次のレーン変更が発生するまでそこから水平方向に移動し続けます。

ジャンプなどのアクション中にレーンが高速に切り替わるのを防ぐために、レーンの設計に注意する必要があります。これにより、プレーヤーが混乱する可能性があります。 レーンは、プレイヤーのキャラクターがしばらくそのレーンに留まる場合にのみ変更する必要があります。

レーンのレベルは、デザイナーの特定のニーズに基づいてゲームレベル全体で変更することも、完全に中断して別のカメラ追跡システムを使用することもできます。 したがって、レーンゾーンを指定するためのリミッターが必要です。

実装

可能な実装は、シーンに単純なオブジェクトとしてレーンを追加することです。 上記のトラッキングスクリプトでYオフセットとペアになっているY位置座標を使用して、システムを実装します。 したがって、X座標とZ座標での位置は重要ではありません。

LaneSystemクラスをtrackingクラスとともにカメラに追加し、提供された配列にレーンオブジェクトを割り当てます。 また、プレイヤーキャラクターを[参照]フィールドに割り当てます。 基準はレーンと別のレーンの間に配置されるため、2つのうち下の方がカメラの配置に使用されます。

また、LaneSystemクラスは、参照位置に基づいて、レーン間でカメラを移動します。 ここでも、followSpeedを使用して位置を補間し、レーンの切り替えが急激になりすぎないようにします。

 [SerializeField] Transform reference; [SerializeField] List<Transform> lanes; [SerializeField] float followSpeed = 5f; // ... void Update() { float targetYCoord = transform.position.y; if (lanes.Count > 1) { int i = 0; for (i = 0; i < lanes.Count - 1; ++i) { if ((reference.position.y > lanes[i].position.y) && (reference.position.y <= lanes[i + 1].position.y)) { targetYCoord = lanes[i].position.y; break; } } if (i == lanes.Count - 1) targetYCoord = lanes[lanes.Count - 1].position.y; } else { targetYCoord = lanes[0].position.y; } float yCoord = Mathf.Lerp(transform.position.y, targetYCoord, Time.deltaTime * followSpeed); transform.position = new Vector3(transform.position.x, yCoord, transform.position.z); }

この実装はWYSIWYGの実装ではなく、読者のための演習として残されています。

ロックノードシステム

カメラをレーン上で移動させるのは素晴らしいことですが、ゲームシーンのPOI(Point of Interest)である何かにカメラをロックする必要がある場合があります。

これは、シーンでそのようなPOIを構成し、トリガーコライダーをそれらに接続することで実現できます。 キャラクターがそのトリガーコライダーに入るたびに、カメラを動かしてPOIに留まります。 キャラクターが移動してPOIのトリガーコライダーを離れると、別のタイプの追跡に戻ります。通常は、標準のフォロー動作です。

カメラトラッキングのロックノードへの切り替えとロックノードへの切り替えは、単純なスイッチまたはスタックシステムのいずれかによって実行できます。スタックシステムでは、トラッキングモードがプッシュおよびポップされます。

実装

ロックノードを構成するには、オブジェクト(空または下のスクリーンショットのようなスプライト)を作成し、それに大きなCircle Collider 2Dコンポーネントをアタッチして、カメラが配置されるときにプレーヤーがいる領域をマークするだけです。ノードにフォーカスします。 どのタイプのコライダーも選択できます。ここでは例としてCircleを選択しています。 また、「CameraNode」のように簡単に確認できるタグを作成して、このオブジェクトに割り当てます。

カメラの追跡スクリプトに次のプロパティを追加します。

 public Transform TrackingTarget { get { return trackingTarget; } set { trackingTarget = value; } }

次に、次のスクリプトをプレーヤーに添付します。これにより、カメラのターゲットを、設定したロックノードに一時的に切り替えることができます。 スクリプトは以前のターゲットも記憶しているため、プレーヤーがトリガーエリアから出たときにそのターゲットに戻ることができます。 必要に応じて、これをフルスタックに変換できますが、私たちの目的では、複数のロックノードをオーバーラップさせないため、これで十分です。 また、Circle Collider 2Dの位置を微調整したり、他の種類のコライダーを追加してカメラロックをトリガーしたりできることにも注意してください。これは単なる例です。

 public class LockBehavior : MonoBehaviour { #region Public Fields [SerializeField] Camera camera; [SerializeField] string tag; #endregion #region Private private Transform previousTarget; private TrackingBehavior trackingBehavior; private bool isLocked = false; #endregion // Use this for initialization void Start() { trackingBehavior = camera.GetComponent<TrackingBehavior>(); } void OnTriggerEnter2D(Collider2D other) { if (other.tag == tag && !isLocked) { isLocked = true; PushTarget(other.transform); } } void OnTriggerExit2D(Collider2D other) { if (other.tag == tag && isLocked) { isLocked = false; PopTarget(); } } private void PushTarget(Transform newTarget) { previousTarget = trackingBehavior.TrackingTarget; trackingBehavior.TrackingTarget = newTarget; } private void PopTarget() { trackingBehavior.TrackingTarget = previousTarget; } } 

カメラズーム

カメラズームは、ユーザー入力で実行することも、POIやレベル内のより狭い領域などに焦点を合わせたい場合はアニメーションとして実行することもできます。

Unity 3Dでの2Dカメラズームは、カメラのorthographicSizeを操作することで実現できます。 次のスクリプトをコンポーネントとしてカメラにアタッチし、SetZoomメソッドを使用してズーム率を変更すると、目的の効果が得られます。 1.0はズームなし、0.5は2回のズームイン、2は2回のズームアウトなどを意味します。

 [SerializeField] float zoomFactor = 1.0f; [SerializeField] float zoomSpeed = 5.0f; private float originalSize = 0f; private Camera thisCamera; // Use this for initialization void Start() { thisCamera = GetComponent<Camera>(); originalSize = thisCamera.orthographicSize; } // Update is called once per frame void Update() { float targetSize = originalSize * zoomFactor; if (targetSize != thisCamera.orthographicSize) { thisCamera.orthographicSize = Mathf.Lerp(thisCamera.orthographicSize, targetSize, Time.deltaTime * zoomSpeed); } } void SetZoom(float zoomFactor) { this.zoomFactor = zoomFactor; }

スクリーンシェイク

ゲームで地震や爆発などの効果を表示する必要がある場合は、手ぶれ効果が便利です。

これを行う方法の実装例は、GitHubで入手できます:gist.github.com/ftvs/5822103。 実装はかなり簡単です。 これまでに取り上げた他の効果とは異なり、それは少しランダム性に依存しています。

フェード&オーバーレイ

レベルが開始または終了するとき、フェードインまたはフェードアウト効果は素晴らしいです。 これを実装するには、画面全体に広がるパネルに相互作用しないUIテクスチャを追加します。 最初は透明ですが、これを任意の色と不透明度で塗りつぶしたり、アニメーション化して目的の効果を実現したりできます。

この構成の例を次に示します。メインカメラオブジェクトの「カメラオーバーレイ」の子に割り当てられているUIパネルオブジェクトに注意してください。 Camera Overlayは、次の機能を備えたOverlayと呼ばれるスクリプトを公開します。

 [SerializeField] Image overlay; // ... public void SetOverlayColor(Color color) { overlay.color = color; } 

フェードイン効果を得るには、次のスクリプトのようにSetOverlayColorで設定したターゲットカラーに補間を追加してオーバーレイスクリプトを変更し、パネルの初期カラーを黒(または白)とターゲットカラーに設定します。オーバーレイの最終的な色に。 あなたはあなたのニーズに合うものにfadeSpeedを変えることができます、私は0.8が初心者にとって良いものだと思います。 およびfadeSpeedの値は、時間修飾子として機能します。 1.0は、複数のフレームで発生しますが、1秒の時間枠内で発生することを意味します。 0.8は、実際に完了するのに1 / 0.8=1.25秒かかることを意味します。

 public class Overlay : MonoBehaviour { #region Fields [SerializeField] Image overlay; [SerializeField] float fadeSpeed = 5f; [SerializeField] Color targetColor; #endregion void Update() { if (overlay.color != targetColor) { overlay.color = Color.Lerp(overlay.color, targetColor, Time.deltaTime * fadeSpeed); } } #region Public public void SetOverlayColor(Color color) { targetColor = color; } #endregion }

要約

この記事では、ゲームにモジュラー2Dカメラシステムを配置するために必要な基本コンポーネントと、それを設計するために必要な考え方を示しました。 当然、すべてのゲームには特定のニーズがありますが、ここで説明する基本的な追跡と単純な効果を使用すると、長い道のりを歩むことができ、独自の効果を実装するための青写真も得られます。 次に、さらに進んで、すべてを再利用可能なUnity 3Dパッケージにパックし、他のプロジェクトに転送することもできます。

カメラシステムは、プレーヤーに適切な雰囲気を伝える上で非常に重要です。 私が使いたい良い比較は、古典演劇と映画の違いを考えるときです。 カメラとフィルム自体がシーンに非常に多くの可能性をもたらしたため、最終的にはそれ自体がアートに進化しました。したがって、別の「ポン」ゲームを実装する予定がない場合は、高度なカメラをゲームプロジェクトで選択する必要があります。これから着手します。

関連: MVCとのUnity:ゲーム開発をレベルアップする方法