private static void GenerateTerrainWithPerlinNoise(ref Tile[,] grid, float seaLevel = 0.33f)
    {
        PerlinNoise pn       = new PerlinNoise(perlinSeed);
        int         nOctaves = pn.CalculateMaxOctavesCount(Utils.MapSize);
        float       fBias    = 2f;

        float[,] perlinArray = new float[Utils.MapSize, Utils.MapSize];

        pn.Get2DPerlinNoise(Utils.MapSize, Utils.MapSize, nOctaves, fBias, ref perlinArray);

        //Min and max values that perlin array has
        float minP = 1;
        float maxP = 0;

        //calculating max and min values of perlin array
        for (int x = 0; x < Utils.MapSize; x++)
        {
            for (int y = 0; y < Utils.MapSize; y++)
            {
                float v = perlinArray[x, y];
                minP = Mathf.Min(v, minP);
                maxP = Mathf.Max(v, maxP);
            }
        }

        float elevationRange = maxP - minP;
        float sea            = minP + elevationRange * seaLevel;
        float sand           = sea + (maxP - sea) * 0.2f;

        for (int x = 0; x < Utils.MapSize; x++)
        {
            for (int y = 0; y < Utils.MapSize; y++)
            {
                float  height   = perlinArray[x, y];
                string dataName = "tile";

                if (height < sea)
                {
                    dataName = "water tile";
                }
                else if (height < sand)
                {
                    dataName = "sand tile";
                }
                else
                {
                    dataName = "grass tile";
                }

                grid[x, y] = Factory.CreateData <Tile>(dataName, new Vector2Int(x, y));
            }
        }
    }