6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Unity2D】RuleTileのランダム感を増す方法

Last updated at Posted at 2019-02-21

2d-extra にはRuleTileというカスタムタイルの実装例が載っており、これが中々に便利で愛用しています。
https://github.com/Unity-Technologies/2d-extras/blob/master/Assets/Tilemap/Tiles/Rule%20Tile/Scripts/RuleTile.cs

その便利な所については今回は割愛させていただくんですが(ひどい)
機能の一つである「ランダム表示」のランダム感がイマイチだと思ったので、その改善記録を載せておきます。

ここに4つのスプライトがあります。どれも横が繋がるようになっているので、ランダムに並べればパターン感の少ない石壁を作れると思っていました。
bricks.png

これらをRuleTileでランダム表示させると以下のようになります。
random1.png

なんか‥‥なんか同じスプライトのタイル多くない?

全部同じではないけど、ほとんど同じというかなり微妙なランダム感‥‥。
RuleTileには Perlin Scale というパラメータを指定できるのですが、調整しても大差ありませんでした。

コードを覗いてみる

どのスプライトを使うかは、以下のGetPerlinValue()が返す0.0~1.0の値で決まるということが分かりました。

RuleTile.cs
protected static float GetPerlinValue(Vector3Int position, float scale, float offset)
{
    return Mathf.PerlinNoise((position.x + offset) * scale, (position.y + offset) * scale);
}

パーリンノイズは滑らかに変化するノイズなので、隣接するセルの値が同じになりやすいという仮説を立ててみます。
そこで、ホワイトノイズに変更してみました。

ホワイトノイズに変更してみる

厳密には、これはpositionのハッシュ関数になってないので良い実装ではないのですが‥‥
(保存するたびに見た目がころころ変わります)

RuleTile.cs(変更後)
protected static float GetPerlinValue(Vector3Int position, float scale, float offset)
{
    return Random.Range(0f, 1f);
}

random2.png
とりあえず、パーリンノイズよりはマシになった気がします。
でも3段目の右など、まだ繰り返しが気になりますね。当然ながら、完全なランダムなので同じスプライトが隣接するのは避けられません。同じ値が隣接しないランダムというのは、簡単に実装できないものでしょかう?

辿り着いた答えは市松模様

グリッドを市松模様とみなして、

  • 黒の時は、0.0~0.5のランダム値
  • 白の時は、0.5~1.0のランダム値

と計算する方法を閃いたので試してみました。

RuleTile.cs(変更後)

private static float GetPerlinValue(Vector3Int position, float scale, float offset)
{
    if ((position.x + position.y) % 2 == 0)
    {
        return Random.Range(0f, 0.5f);
    }
    else
    {
        return Random.Range(0.5f, 1f);
    }
}

random3.png
大変良くなりました。この実装では上下左右に隣り合うスプライトは必ず異なる1ので、パターン感がとても少ないです。


ということで、RuleTileのランダム感に不満を感じたら、上のようにカスタマイズしてみてはいかがでしょうか。

※自分は上のコードで満足していますが、保存するたびに表示がコロコロ変わるのが嫌な方はRandom.Range()の代わりにMathf.PerlinNoise()を使って上手いことやりましょう。

参考情報

RuleTileではなくRandomTileの実装例では、ホワイトノイズっぽい雰囲気の秘伝のタレが公開されています。
https://github.com/Unity-Technologies/2d-extras/blob/master/Assets/Tilemap/Tiles/Random%20Tile/Scripts/RandomTile.cs

RandomTile.cs
public override void GetTileData(Vector3Int location, ITilemap tileMap, ref TileData tileData)
{
    base.GetTileData(location, tileMap, ref tileData);
    if ((m_Sprites != null) && (m_Sprites.Length > 0))
    {
        long hash = location.x;
        hash = (hash + 0xabcd1234) + (hash << 15);
        hash = (hash + 0x0987efab) ^ (hash >> 11);
        hash ^= location.y;
        hash = (hash + 0x46ac12fd) + (hash << 7);
        hash = (hash + 0xbe9730af) ^ (hash << 11);
        Random.InitState((int)hash);
        tileData.sprite = m_Sprites[(int) (m_Sprites.Length * Random.value)];
    }
}

そこまで計算しといて結局Random使うんかいと言いたくなりますが、とりあえずハッシュ関数的な動きにはなってそうなので、ホワイトノイズの代わりに使ってみるのもいいかもしれません。

  1. ランダムの候補の数が奇数だと、候補の真ん中にあるスプライトは連続することがあり得ます

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?