【コピペOK】Unityパララックス効果で背景に奥行きを出す方法!

ころもちゃん

キャラクターと一緒に背景を動かしてみたい!どうやったらいいんだろう?

本記事の内容

・横スクロールにおける背景をスクロールさせる方法
・カメラを横スクロールさせる方法
・背景をレイヤー分けし、各レイヤーごとに異なるスピードでスクロールさせる方法

本記事の解説は、以下の関連記事(背景を無限スクロールさせる)の内容が実装されている前提です。まだお読みでない方はぜひご覧になってください。

目次

背景をスクロールさせるには?

横スクロールの原理

横スクロールゲーム、好きですか?
2Dマリオ、メトロイドヴァニア、ロックマン、カービィ、悪魔城ドラキュラ、アングリーバードなど、横スクロールアクションは沢山の名作がありますね。

これらのゲームは、「①キャラクターが移動出来るエリア(床や壊せるオブジェクトなど)」と、「②キャラクターが移動出来ない背景」の2つの層に分かれて表現されています。

例えば、上の画像の「クッキーラン」というゲームだと、地面とキャラクター、獲得出来るアイテム以外の部分は背景オブジェクトになっています。

もしキャラクターが移動したら、本来②の背景も動いて欲しいですよね。ただ、本来遠くにあるはずの背景が、キャラクターの近くにある①と同じスピードで動いてしまうと違和感があります。

そこで、キャラクターの移動速度よりもゆっくりと背景をスクロールさせることで、より奥行きのある表現が出来ます。
さらに、遠くにある背景(太陽や雲など)と、近くにある背景(木など)でスクロールスピードを変えるとさらに奥行きが増します。これがパララックス効果です。

その実装方法を解説していきます。今回は、スクリプトの内容が少し多めで、とにかく動くものが欲しい!という方もいると思うので、スクリプトの解説は最後にまとめています。

①自動横スクロールの場合

カメラを自動スクロールさせる

まずは、カメラをスクロールさせます。
CameraAutoScrollというスクリプトを作成します。少しリッチに、横縦いずれにもスクロール可能なコードにしました。

using UnityEngine;

public class CameraAutoScroll : MonoBehaviour
{
    // スクロール方向の列挙型
    public enum ScrollDirection
    {
        Right,
        Left,
        Up,
        Down
    }

    [SerializeField]
    private ScrollDirection scrollDirection = ScrollDirection.Right; // スクロール方向(デフォルトは右)

    [SerializeField]
    private float scrollSpeed = 1f; // スクロール速度(ユニット/秒)

    void Update()
    {
        // スクロール方向に基づいてカメラを移動
        Vector3 moveDirection = Vector3.zero;
        switch (scrollDirection)
        {
            case ScrollDirection.Right:
                moveDirection = Vector3.right;
                break;
            case ScrollDirection.Left:
                moveDirection = Vector3.left;
                break;
            case ScrollDirection.Up:
                moveDirection = Vector3.up;
                break;
            case ScrollDirection.Down:
                moveDirection = Vector3.down;
                break;
        }
        transform.Translate(moveDirection * scrollSpeed * Time.deltaTime);
    }
}

次に、このスクリプトを、MainCameraオブジェクトにアタッチします。

そして、シーン内に背景オブジェクトをレイヤーに分けて配置します。
具体的な配置内容や、使用しているアセットなどは以下の関連記事を参考にされてください。

背景のスプライト画像をセットしました。
この状態で再生してみましょう。ちゃんとカメラが動いていますね。

しかし、これだけだと少し物足りないです。
冒頭に書いたように、背景によって距離感が違うにも関わらず、どれも同じスピードで進んでいるからです。
そこで、パララックス効果(視差効果)というものを実装します。

パララックス効果(視差効果)とは?

車で窓の外を見ているとき、近くの物体は速く動いているように見えますが、遠くの山や空はほとんど動いていないように見えますよね。物体が視点から遠いほど、その動きは遅く見えます。これがパララックス効果です。

この原理を利用して、画面上で奥行きのあるシーンを作り出すしていくためのスクリプトを作成します。

using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class ParallaxLayer
{
    public int layerNumber; // レイヤー番号
    public Transform layerTransform; // このレイヤーのTransform
    public float relativeMoveSpeed; // カメラの移動と比べた相対的移動速度
}

public class ParallaxController : MonoBehaviour
{
    [SerializeField] private List<ParallaxLayer> layers;
    private Camera mainCamera;
    private Vector3 lastCameraPosition;

    void Start()
    {
        mainCamera = Camera.main;
        if (mainCamera == null)
        {
            Debug.LogWarning("ParallaxController: MainCamera not found.");
            return;
        }

        // AutoScroll コンポーネントの存在を確認
        if (!mainCamera.GetComponent<CameraAutoScroll>() || !mainCamera.GetComponent<FollowCharacterCamera>())
        {
            Debug.LogWarning("ParallaxController: CameraAutoScroll or FollowCharacterCamera component is not attached to MainCamera.");
        }

        lastCameraPosition = mainCamera.transform.position;
    }

    void Update()
    {
        Vector3 deltaMovement = mainCamera.transform.position - lastCameraPosition;
        foreach (var layer in layers)
        {
            float parallaxFactor = deltaMovement.x * layer.relativeMoveSpeed;
            layer.layerTransform.Translate(Vector3.right * parallaxFactor);
        }
        lastCameraPosition = mainCamera.transform.position;
    }
}

このスクリプトを、空のGameObjectにアタッチします。

次に、パラメータの「レイヤー」を開き、シーン内の背景がセットされているGameObjectのLayerをアタッチしていきます。

繰り返しになりますが、以下の関連記事の内容を行っている前提ですので、まだの方はぜひ御覧ください。

ヒエラルキーの構成
ParallaxControllerのパラメータ

RelativeMoveSpeedというパラメータは、カメラの移動速度に対して相対的にどれくらいの速度で移動させるか?というパラメータです。1に近いほど背景とカメラの速度が同期して、背景が動いていないように見えます。

数値を1にしてみた例がこちらです。カメラと同速で背景が追従しています。そのため、プレイすると背景が動いていないように見えます。

カメラと背景が同じ速度で移動


RelativeMoveSpeedが1に近い値の場合、カメラの移動にゆっくり追従します。そのため、遠いところにある背景の場合、0.1~0.9の値がオススメです。

ピンクの遠い山は0.6、紫の近い山は0.2で移動


RelativeMoveSpeedが0未満の場合、カメラと逆方向に背景が移動します。そのため、車で走り去っていくような表現が出来ます。

遠い森のパラメータは0、近い森のパラメータは-1

最後にこれらを組み合わせて再生してみましょう。

エンディングで流れているような良い絵が作れました!

②キャラクターの移動に合わせてスクロールする場合

キャラクターの移動に合わせてカメラをスクロールさせる

最後に、自動横スクロールではなく、キャラクターの移動に応じて背景をスクロールさせたい場合です。前の章で解説したパララックスについての処理が入っている前提で記載します。
まずは、FollowCharacterCameraというスクリプトを作成します。

using UnityEngine;

public class FollowCharacterCamera : MonoBehaviour
{
    [SerializeField] Transform characterTransform; // 追従するキャラクターのTransform
    [SerializeField] float smoothSpeed = 0.125f; // カメラの移動の滑らかさを調整
    private Vector3 offset; // キャラクターとカメラの間のオフセット
    public bool followHorizontal = true; // 横方向に追従するかどうか
    public bool followVertical = false; // 縦方向に追従するかどうか

    void Start()
    {
        // カメラとキャラクターの初期位置の差分をオフセットとして設定
        offset = transform.position - characterTransform.position;
    }

    void LateUpdate()
    {
        Vector3 targetPosition = characterTransform.position + offset;

        // 横方向の追従が有効な場合
        if (followHorizontal)
        {
            transform.position = new Vector3(
                Mathf.Lerp(transform.position.x, targetPosition.x, smoothSpeed),
                transform.position.y,
                transform.position.z
            );
        }

        // 縦方向の追従が有効な場合
        if (followVertical)
        {
            transform.position = new Vector3(
                transform.position.x,
                Mathf.Lerp(transform.position.y, targetPosition.y, smoothSpeed),
                transform.position.z
            );
        }
    }
}

このスクリプトを、MainCameraにアタッチします。
CharacterTransformに、追従させたい移動するオブジェクトをセットします。キーボード操作で動くキャラクターをシーンに配置して、セットしました。
キーボードでキャラクターを操作するには以下の関連の記事を参考にしてください。

これで準備は完了です。実行してみましょう!

なかなかに、奥行きのある雰囲気がつくれたのではないでしょうか?

興味がある方向け:スクリプトの解説

最後にスクリプトの内容解説です。プログラムをある程度読み書き出来る方向けの内容になっています。

Camera AutoScroll

こちらはあまり難しいことをやっていないので、カンタンに。

using UnityEngine;

public class CameraAutoScroll : MonoBehaviour
{
    // スクロール方向の列挙型
    public enum ScrollDirection
    {
        Right,
        Left,
        Up,
        Down
    }

    [SerializeField]
    private ScrollDirection scrollDirection = ScrollDirection.Right; // スクロール方向(デフォルトは右)

    [SerializeField]
    private float scrollSpeed = 1f; // スクロール速度(ユニット/秒)

    void Update()
    {
        // スクロール方向に基づいてカメラを移動
        Vector3 moveDirection = Vector3.zero;
        switch (scrollDirection)
        {
            case ScrollDirection.Right:
                moveDirection = Vector3.right;
                break;
            case ScrollDirection.Left:
                moveDirection = Vector3.left;
                break;
            case ScrollDirection.Up:
                moveDirection = Vector3.up;
                break;
            case ScrollDirection.Down:
                moveDirection = Vector3.down;
                break;
        }
        transform.Translate(moveDirection * scrollSpeed * Time.deltaTime);
    }
}

ScrollDirectionでenum型の方向の種類を定義しています。ここで設定している方向の値に応じて、LateUpdateの中で、移動する方向を決めています。また、Camera移動は基本的にLateUpdateの中で行うのがよいとされています。

Parallax Controller

こちらは少し難しいことをしています。

using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class ParallaxLayer
{
    public int layerNumber; // レイヤー番号
    public Transform layerTransform; // このレイヤーのTransform
    public float relativeMoveSpeed; // カメラの移動と比べた相対的移動速度
}

public class ParallaxController : MonoBehaviour
{
    [SerializeField] private List<ParallaxLayer> layers;
    private Camera mainCamera;
    private Vector3 lastCameraPosition;

    void Start()
    {
        mainCamera = Camera.main;
        if (mainCamera == null)
        {
            Debug.LogWarning("ParallaxController: MainCamera not found.");
            return;
        }

        // AutoScroll コンポーネントの存在を確認
        if (!mainCamera.GetComponent<CameraAutoScroll>() || !mainCamera.GetComponent<FollowCharacterCamera>())
        {
            Debug.LogWarning("ParallaxController: CameraAutoScroll or FollowCharacterCamera component is not attached to MainCamera.");
        }

        lastCameraPosition = mainCamera.transform.position;
    }

    void Update()
    {
        Vector3 deltaMovement = mainCamera.transform.position - lastCameraPosition;
        foreach (var layer in layers)
        {
            float parallaxFactor = deltaMovement.x * layer.relativeMoveSpeed;
            layer.layerTransform.Translate(Vector3.right * parallaxFactor);
        }
        lastCameraPosition = mainCamera.transform.position;
    }
}

ParallaxLayerというクラスを作成して、1レイヤーごとに必要なパラメータを定義しています。
また、パララックスの効果は、カメラをスクロールしないと意味がないので、スクロールさせるためのスクリプトがアタッチされていない場合はワーニングを出力するようにしています。

Update文の中では、メインカメラの移動量を取得し、メインカメラの移動量に対して相対的どれくらい背景のオブジェクトを移動するかを、parallaxFactorを使ってセットする処理を、foreachを使ってすべてのレイヤーに適用しています。

FollowCharacterCamera

こちらも難しい内容はあまり無いです。

using UnityEngine;

public class FollowCharacterCamera : MonoBehaviour
{
    [SerializeField] Transform characterTransform; // 追従するキャラクターのTransform
    [SerializeField] float smoothSpeed = 0.125f; // カメラの移動の滑らかさを調整
    private Vector3 offset; // キャラクターとカメラの間のオフセット(自動計算)
    public bool followHorizontal = true; // 横方向に追従するかどうか
    public bool followVertical = false; // 縦方向に追従するかどうか

    void Start()
    {
        // カメラとキャラクターの初期位置の差分をオフセットとして設定
        offset = transform.position - characterTransform.position;
    }

    void LateUpdate()
    {
        Vector3 targetPosition = characterTransform.position + offset;

        // 横方向の追従が有効な場合
        if (followHorizontal)
        {
            transform.position = new Vector3(
                Mathf.Lerp(transform.position.x, targetPosition.x, smoothSpeed),
                transform.position.y,
                transform.position.z
            );
        }

        // 縦方向の追従が有効な場合
        if (followVertical)
        {
            transform.position = new Vector3(
                transform.position.x,
                Mathf.Lerp(transform.position.y, targetPosition.y, smoothSpeed),
                transform.position.z
            );
        }
    }
}

キャラクターとカメラの相対距離をオフセット値として定義。
キャラクターとカメラの相対距離を維持しつつ、キャラクターの移動に合わせてカメラを移動させているだけです。
Lerpを使うことで、急激にカメラが移動するのを防ぎ、smoothSpeedの速さでスムーズな移動が出来ます。

まとめ

以上、背景をスクロールさせる方法でした。背景が動くと、ゲームに奥行きが生まれて世界観がグッと広がりますね!

それでは素敵なゲーム制作ライフを!

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

ゲーム制作の敷居を下げ、もっと多くの人にゲーム作りを楽しんでもらうために、ゲームをカンタンに作る方法を”網羅的に”解説しています。
よかったらブックマークお願いします。

コメント

コメント一覧 (1件)

コメントする

目次