キノコの自省録

日々適当クリエイト

Unity2Dアクションでスクロール速度の異なる背景を実装する

アクションゲームなどで、前景のスクロールに対して、後景がちょっとスクロールする遠近感のある背景、みたいなものを見かけると思います。格好いいですよね。例えばこんな感じです。

手前の線路のスクロール速度と比較すると、ゆっくりと山や雲が移動しています。要するに多重スクロールです。

オートスクロール(シューティングなど)の場合は、公式でのチュートリアル紹介があります。

Beginning 2D Game Development - Unity Learn

オートスクロールなので、カメラが動くのではなく、背景が自動で動くんですね、これ。 プレイヤーキャラクターに合わせて背景が動く場合、ちょっとどうやったものか悩みました。 特に2Dだと検索全然引っかからないので……。

ということで、Unity2Dで実装したので紹介。

スクロール

今回スクロールする背景は、

  1. プレーヤーと同じ速度でスクロール
  2. プレーヤーと共にスクロールするが、速度は遅い
  3. スクロールしない

の3構成とします。

オートスクロールとの違い

前述の通り、オートスクロールはこういう感じで、固定カメラに対して背景を動かします。プレーヤーは画面中央に配置して、そのプレーヤー自体は動かしません。

f:id:kinokorori:20190115212445p:plain
オートスクロールの背景

これで表現する場合、公式チュートにあるように、Updateの度に背景を移動させていきます。 単純に、(A)は速度x1.0、 (B)は速度x0.2、(C)は速度0というように、移動速度に差を付ければ表現できます。

プレーヤーと共に移動するアクションは、カメラ自体がキャラを追いかけて移動します。背景は移動させません。

f:id:kinokorori:20190115212414p:plain
プレーヤースクロール

背景(A)はプレーヤーとカメラがくっついてくるのでそのままでOKですが、背景(B),(C)の表現には工夫が必要です。

この撮影方法は、カメラ用語で”フォローパン”と言います。動く被写体をカメラが追いかけるという撮影方法です。付けパンとも言います。

実装

そういうわけで、どうするかと言いますと、カメラを3つ用意します。

  • 1カメラ……プレーヤーと共に移動する背景透過カメラ =背景(A)
  • 2カメラ……プレーヤーと共に移動するが、速度の遅い透過カメラ =背景(B)
  • 3カメラ……不透過の固定カメラ = 背景(C)

1カメは、たぶんメインカメラとして実装済みだと思います。 2カメ, 3カメの場所ですが、ゲームと無関係な位置、要するに1カメが使わない場所に配置します。リアルでもカメラ合成すると思いますが、その要領です。

f:id:kinokorori:20190115214532p:plain

忘れがちですが、2Dといっても3Dを応用してるんですよね。

カメラのプロパティ

具体的に、Unityのカメラプロパティをどう弄るかの話に移ります。調整が必要なのは、赤で印を付けた2つ、Clear FlagsとDepthです。

f:id:kinokorori:20190115214817p:plain
カメラプロパティ

  • 1カメ, 2カメは背景を透過します。なので、Clear FlagsをDepth Onlyに設定します。
  • 3カメは背景を透過しません。なので、Clear FlagsをSolid ColorかSkyboxに設定します。

Depthはカメラの描画順に関係します。1カメ > 2カメ > 3カメとしたいので、Depth設定をそのように弄ります。 例えば、1カメのDepthを0とした場合、2カメを-1, 3カメを-2にします。

カメラのスクリプト

1カメのスクリプトは各所で紹介されているので省略。公式のリンクを置いておきます。

Movement Basics - Unity Learn

3カメはスクリプト不要です。 2カメのスクリプトは、こんな感じです。

using UnityEngine;
using System.Collections;

public class MiddleCameraController : MonoBehaviour {

    private GameObject player;
    private Vector3 startPlayerOffset;
    private Vector3 startCameraPos;
    private static readonly float RATE = 0.12f;

    // Use this for initialization
    void Start() {
        player = GameObject.FindGameObjectWithTag("Player");
        startPlayerOffset = player.transform.position;
        startCameraPos = this.transform.position;
    }

    // Update is called once per frame
    void Update() {
        Vector3 v = (player.transform.position - startPlayerOffset) * RATE;
        this.transform.position = startCameraPos + v;
    }
}

何をしているかと言うと、プレーヤーと2カメの初期位置をStartで覚えておき、Updateでプレーヤーの初期位置からの移動差分*0.12を、2カメの初期位置に足し算しています。 0.12はなんとなくなので、背景に合わせてお好みで。

※当然ですが、背景(A)と背景(B)は、絵のない部分は透過状態にしておきます。

※uGuiのImageを使うのは、uGuiの本来的な使い方ではありません。ナシです。