読者です 読者をやめる 読者になる 読者になる

テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

スプライトベースの多重スクロール[ゲーム入門]

多重スクロールFacebook助け合い所に多重スクロールについての質問があったので、メモしておこうと思う。

多重スクロール…の前にスクロールだが、実際2つの手段がある。一つはUVを利用した背景テクスチャのスクロール。これは単純にテクスチャのオフセットをリアルタイムで変更するだけなので、簡単に出来る。この際、テクスチャの設定がrepeatになってる必要がある。

Flappy Birdライクなゲームを作成した際のメモ - テラシュールブログ
第06回 背景を作る
Unityで多重スクロール
Unity2Dで多重スクロールを実験

ただし、このUVを変更する方法では複雑なパターンのスクロールを表現出来ない問題がある。出来るにはできるのだが、巨大なテクスチャが必要になったり、動的にテクスチャを生成しなければいけない等、余りメモリにやさしくない。

そこで、実際にオブジェクトをスクロールさせるが画面外に移動したら画面右へ戻すような感じの実装を検討してみる。

スマホでも動く無限横スクロールアクションのブラウザゲームを作る (2/5)





ローテーション

スクリーンショット_2014-05-24_1_15_16
このgifアニメは実際にスプライトベースの多重スクロールを実装した例だ。画面の白い枠線がカメラの範囲となっている。画面を見ると、カメラの範囲外へスプライトが移動した際にスプライトが画面右へ移動している事が分かる。
 

この方法でやるべき事は2つだけ。

  • 画面外へ出た事の検知
  • 画面外へ出た際、スプライトの末へ付ける



簡単な方法

まずは簡単な方法。

using UnityEngine;
using System.Collections;

public class DontShowScreenReset : MonoBehaviour {

    public float speed = 10;
    public int spriteCount = 3;

    void Update () {
       // 左へ移動
        transform.position += Vector3.left * speed * Time.deltaTime;
    }

    void OnBecameInvisible()
    {
       // スプライトの幅を取得
        float width = GetComponent<SpriteRenderer>().bounds.size.x;
       // 幅x個数分だけ右へ移動
        transform.position += Vector3.right * width * spriteCount;
    }
}

上記のスクリプトをスプライトに貼り付けるだけでOK。なおスプライトはミッチリと並べられていることが前提。vを押しながらスプライトを移動するとピポットに張り付くように移動するので、それで調整する。

この方法では、画面外へ消えた際にスプライト3つ分くらい右へ動かしている。カメラが消えた判定はOnBecameInvisibleを使用して判断している。

tsubakit1.hateblo.jp


但し、この処理には問題がある。この「カメラに写っている」条件には実はSceneViewも含まれており、SceneViewを表示しながらGameViewも表示するような画面構成だと、ゲーム外にスプライトが移動してもスプライトが消えなかったりする。

実機に転送して遊んだりする場合は問題ないのだが…




今回作った方法

なので、カメラの範囲を自前で取得し範囲外に移動したら処理を手動で呼び出すようにした。

まずカメラの範囲を取得。やってる事はスクリーンの左上と右下を確認し、それをRectへ登録しているだけ。
このクラスは色々な所から取り出すのでシングルトンに設定。

using UnityEngine;
using System.Collections;

public class ScreenManager
{
    static ScreenManager instance;

    public static ScreenManager Instance {
        get {
            if (instance == null) {
                instance = new ScreenManager ();
            }
            return instance;
        }
    }

    public Rect screenRect;

    public ScreenManager ()
    {
        Vector3 tl = Camera.main.ViewportToWorldPoint (
                        Vector3.zero + Vector3.forward * 10);
        Vector3 rd = Camera.main.ViewportToWorldPoint (
                        new Vector3 (110) + Vector3.forward * 10);

        screenRect.x = tl.x;
        screenRect.y = tl.y;
        screenRect.width = (rd - tl).x;
        screenRect.height = (rd - tl).y;
    }
}

で、後は範囲外に移動したら呼び出すようにする。やってる事はこっちも単純で、「画面左端が、スプライトの現在地+スプライトの幅分より大きければOnBecameInvisibleを呼び出す」だけ。

using UnityEngine;
using System.Collections;

public class DontShowScreenReset : MonoBehaviour
{
    [Range(010)]
    public float
        speed = 10;
    public int spriteCount = 3;
    Vector3 spriteSize;

    void Start ()
    {
        spriteSize = GetComponent<SpriteRenderer> ().bounds.size;
    }

    void Update ()
    {
        transform.position += Vector3.left * speed * Time.deltaTime;

        #if UNITY_EDITOR

        var spritex = (transform.position + spriteSize / 2).x ;
        ifspritex < ScreenManager.Instance.screenRect.x )
        {
            OnBecameInvisible();
        }

        #endif
    }

    void OnBecameInvisible ()
    {
        float width = GetComponent<SpriteRenderer> ().bounds.size.x;
        transform.position += Vector3.right * width * spriteCount;
    }
}

多重スクロールにするには、同じものを並べて速度を変えるだけ。