うにてぃブログ

UnityやUnreal Engineの記事を書いていきます

【Unity】Perlin worms を試してみる

洞窟生成アルゴリズムとして利用されている (多分) Perlin worms を試してみたくて Unity で簡単なものを作成してみました

今回複数のフレーム をまたぐ処理がうまいこといかなかったのでそちらに関しては実装していません

またシード固定のために Mathf.PerlinNoise ではなく、Unity.Mathematics.noise.cnoise を利用しています

Perlin Worms の解説やスクリプトは以下にあるのでこちらを参考にしてください

libnoise.sourceforge.net

実装結果

移動座標を LineRenderer を利用して表してみると以下のようになります、ミミズのようにうねってるのがわかると思います

スクリプト

元々のスクリプトGPL ライセンスだったので、こちらにもライセンス記載があります

特に複雑なことはしておらず、Perlin ノイズから移動先を決定しているだけです

//
// Copyright (C) 2004, 2005 Jason Bevins
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// (COPYING.txt) for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc., 59
// Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// The developer's email is jlbezigvins@gmzigail.com (for great email, take
// off every 'zig'.)
//
 
using System;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using Random = UnityEngine.Random;
 
[Serializable]
public class PerlinWorms
{
    [SerializeField]
    private float _noiseScale = 0.5f;
    [SerializeField]
    private float WormTwistiness = 4 / 256f;
    [SerializeField]
    private float WormSegmentLength = 1f / 64f;
    [SerializeField]
    private int WormSegmentCount = 112;

    [SerializeField]
    private int _seed;
    
    private List<Vector2> _positions = new List<Vector2>();
    public IEnumerable<Vector2> Positions => _positions;
     
    public void Calc(Vector2 start)
    {
        _positions.Clear();
        Segment(start);
    }
 
    private void Segment(Vector2 start)
    {
        var offsetPos = new Vector2();
        
        Random.InitState(_seed);
        var baseNoisePosition = new Vector2(
            Random.Range(0, 1f),
            Random.Range(0, 1f)
        );

        _positions.Add(start);

        var position = start;

        for (var i = 0; i < WormSegmentCount; i++)
        {
            var noisePosition = new float3(
                baseNoisePosition.x + i * WormTwistiness,
                baseNoisePosition.y,
                _seed
            ) * _noiseScale;
            
            var noise = Unity.Mathematics.noise.cnoise(noisePosition);
 
            // -1 ~ 1 の範囲へ
            noise = (noise * 2) - 1;
 
            offsetPos.x = Mathf.Cos(noise * Mathf.PI);
            offsetPos.y = Mathf.Sin(noise * Mathf.PI);
             
            offsetPos *= WormSegmentLength;
 
            position += offsetPos;
            _positions.Add(position);
        }
    }
}