public TileEntity PlaceNextTile()
    {
        if (pooledTiles.Count == 0)
        {
            return(null); // Pool is empty.
        }
        // Ensure tile position is at water height.
        lastTilePos.y = gameController.waterHeight;

        // Determine tile type from difficulty.
        TileEntity.Type tileType        = TileEntity.Type.LilyPad;
        int             difficultyCount = tileTypeDistributions[0].difficultyProbabilities.Length;

        if (tileDifficultyLevel > 0)
        {
            // Introduce new tile types through distribution.
            float distribValue           = Random.value;
            int   difficultyDistribCount = tileTypeDistributions[0].difficultyProbabilities.Length;
            int   distribIndex           = Mathf.Min(tileDifficultyLevel, difficultyDistribCount - 1);

            float distribStart = 0f;
            float distribEnd   = 0f;

            for (int i = 0; i < tileTypeDistributions.Length; i++)
            {
                float prob = tileTypeDistributions[i].difficultyProbabilities[distribIndex];
                distribEnd += prob;

                if (distribValue >= distribStart && distribValue < distribEnd)
                {
                    tileType = (TileEntity.Type)i;
                    break; // Select this tile.
                }

                distribStart += prob;
            }
        }

        int            tileIndex = (int)tileType;
        TileDifficulty tileDiff  = difficultyStats[tileIndex];

        // Place the next pad somewhere in front of the last tile position.
        float distFromFrog = Mathf.Min(baseDistance * tileDiff.distMultiplier * Random.Range(distanceVariance.x, distanceVariance.y), maxDistance);
        float angleRad     = Random.Range(-angleVariance, angleVariance) * Mathf.Deg2Rad;

        lastTilePos += new Vector3(Mathf.Sin(angleRad) * distFromFrog, 0f, Mathf.Cos(angleRad) * distFromFrog);

        TileEntity newTile  = pooledTiles.Dequeue();
        float      radius   = baseSizeRadius * Mathf.Max(minSizeMultiplier, tileDiff.sizeMultiplier * Random.Range(sizeVariance.x, sizeVariance.y));
        float      sinkTime = Mathf.Max(minSinkTime, baseSinkTime * tileDiff.sinkTimeMultiplier);

        if (tileType == TileEntity.Type.DriftingLog)
        {
            // Determine horizontal velocity.
            float heightFactor = Mathf.Max(minSizeMultiplier, tileDiff.sizeMultiplier * Random.Range(sizeVariance.x, sizeVariance.y)); // Calculate another random scale factor for height.
            float velocity     = Mathf.Min(baseDriftVelocity * tileDiff.velocityMultiplier, maxDriftVelocity) * Random.Range(velocityVariance.x, velocityVariance.y);

            if (Random.value < 0.5f)
            {
                velocity = -velocity; // 50% chance to go left.
            }
            newTile.Initialize(tileType, lastTilePos, radius, heightFactor, velocity, sinkTime, lastTileDepth, curBaseScoreReward);
        }
        else   // lilypad and floating box.
        {
            newTile.Initialize(tileType, lastTilePos, radius, 1f, 0f, sinkTime, lastTileDepth, curBaseScoreReward);
        }

        // Add to active list, since it is a valid thing to jump on.
        activeTiles.Add(newTile);

        // Increase difficulty for this specific tile.
        difficultyStats[tileIndex].AdvanceProgress();

        // Increase reward every X tiles.
        lastTileDepth++;

        if (lastTileDepth % gameController.increaseDifficultyInterval == 0)
        {
            curBaseScoreReward++;
        }

        UpdateTileBounds();
        return(newTile);
    }