Environment Generator

Perlin noise can generated endless seamless noise.

Placing different types of rocks, tree types and grass, etc using different value of the pixel in the noise texture can procedurally generate natural endless forest.

// Pseudo Code of pixel of noise to set environment element in the scene
for pixel in texture (x, y):
    if 1.0 >= pixel.value > 0.6 then Rock (A, B, C)
    if 0,6 >= pixel.value > 0.3 then Tree (A, B, C)
    if 0.3 >= pixel.value > 0.0 then Grass (A, B, C)
// Compute Perlin noise at coordinates x, y
float perlin(float x, float y) {

    // Determine grid cell coordinates
    int x0 = int(x);
    int x1 = x0 + 1;
    int y0 = int(y);
    int y1 = y0 + 1;

    // Determine interpolation weights
    // Could also use higher order polynomial/s-curve here
    float sx = x - (float)x0;
    float sy = y - (float)y0;

    // Interpolate between grid point gradients
    float n0, n1, ix0, ix1, value;
    n0 = dotGridGradient(x0, y0, x, y);
    n1 = dotGridGradient(x1, y0, x, y);
    ix0 = lerp(n0, n1, sx);
    n0 = dotGridGradient(x0, y1, x, y);
    n1 = dotGridGradient(x1, y1, x, y);
    ix1 = lerp(n0, n1, sx);
    value = lerp(ix0, ix1, sy);

    return value;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GroundGenerator : MonoBehaviourHelper {

    public GameObject Target;

    public PoolManager GeneralPool;
    public PoolManager PuzzlePool;
    public PoolManager EventPool;

    public int Size = 40;

    float[] AnchorPos = new float[2];
    float[] AnchorPosPrev = new float[2];

    float[] RandRotation = new float[4];

    [HideInInspector]
    public List<MeshRenderer> Plants = new List<MeshRenderer> ();

    [HideInInspector]
    public GameObject[] MapObjects = new GameObject[9];

    [HideInInspector]
    public List<FlockController> FlockButterflies = new List<FlockController>();

    GameObject[] MapObjectsTmp = new GameObject[9];

    Vector3[] CurrentMapPositions = new Vector3[9];

    float[] CoordRange = new float[3];

    // Use this for initialization
    void Start () {

        CoordRange [0] = Size * -1f;
        CoordRange [1] = 0f;
        CoordRange [2] = (float)Size;

        RandRotation [0] = 0f;
        RandRotation [1] = 90f;
        RandRotation [2] = 180f;
        RandRotation [3] = 270f;

        AnchorPos [0] = Mathf.Floor(((Target.transform.position.x + Size * 0.5f) / Size)) * Size;
        AnchorPos [1] = Mathf.Floor(((Target.transform.position.z + Size * 0.5f) / Size)) * Size;
        System.Array.Copy (AnchorPos, AnchorPosPrev, AnchorPos.Length);

        float DelayForLoading = 0.5f;

        Invoke ("Init", DelayForLoading);

        //InvokeRepeating ("ZoneSync", 0.1f, 0.1f); // call every 0.1 second

        InvokeRepeating ("PlantsVisibilityAndShadowCasterOptimizer", DelayForLoading, 0.2f);
        InvokeRepeating ("ButterflyVisibilityAndShadowCasterOptimizer", DelayForLoading, 0.2f);
    }
    
    // Update is called once per frame
    void Update () {
        ZoneSync ();
    }

    void ZoneSync(){

        // Set the anchor based on player movement
        AnchorPos [0] = Mathf.Floor(((Target.transform.position.x + Size * 0.5f) / Size)) * Size;
        AnchorPos [1] = Mathf.Floor(((Target.transform.position.z + Size * 0.5f) / Size)) * Size;

        if (AnchorPos[0] != AnchorPosPrev[0] || AnchorPos[1] != AnchorPosPrev[1]) {

            UpdateMapPositions ();

            ZoneReLink ();

            System.Array.Copy (AnchorPos, AnchorPosPrev, AnchorPos.Length);

            //Debug.Log ("Changed:::(" + AnchorPosPrev[0] + "," + AnchorPosPrev[1] + ")");
        }
    }

    void UpdateMapPositions(){
        int id = 0;
        for (int x = 0; x < CoordRange.Length; x++) {
            for (int z = 0; z < CoordRange.Length; z++) {
                Vector3 pos = new Vector3 (CoordRange [x] + AnchorPos [0], 0f, CoordRange [z] + AnchorPos [1]);
                CurrentMapPositions [id] = pos;
                id++;
            }
        }
    }

    void Init(){
        ZoneSync ();
        UpdateMapPositions ();
        for (int i = 0; i < CurrentMapPositions.Length; i++) {
            SetupGroundWithPosition(CurrentMapPositions[i], ref MapObjects[i]);
        }

        GlobalGameCharacter.gameObject.GetComponent<Rigidbody> ().useGravity = true; //  available character a bit later
    }

    void SetupGroundWithPosition(Vector3 Position, ref GameObject MapObject) {
        int MapID = EventData.GetPuzzleID(Position);
        if (MapID > -1)
        {
            MapObject = PuzzlePool.GetAsset(MapID);
            MapObject.transform.position = Position;
            return;
        }

        MapID = EventData.GetEventID(Position);
        if (MapID > -1)
        {
            MapObject = EventPool.GetAsset(MapID);
            MapObject.transform.position = Position;
            return;
        }

        if (MapID == -1)
        {
            MapObject = GeneralPool.GetAsset();
            MapObject.transform.position = Position;
            MapObject.GetComponentInChildren<TerranNoise> ().GetPropsIn (Mathf.FloorToInt(Position.z/40f));
            return;
        }
    }



    void PlantsVisibilityAndShadowCasterOptimizer(){

        foreach (MeshRenderer m in Plants) {

            // tree and non tree have to be separated..!
            if (!m)
                continue;

            if (!m.gameObject.transform.parent.gameObject.activeSelf)
                continue;

            float dist = Vector3.Distance (m.gameObject.transform.position, GlobalCamera.transform.position);

            if (dist > 55f) {
                m.gameObject.SetActive (false);
            } else if (dist <= 55f && dist > 35f) {
                m.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
                m.receiveShadows = false;
                m.gameObject.SetActive (true);
            } else {
                m.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
                m.receiveShadows = true;
                m.gameObject.SetActive (true);
            }
            //maybe do the DistanceJoint2D checked.. for each.. divide into 2 different height groups.. AndroidJNI for 
        }
    }

    void ButterflyVisibilityAndShadowCasterOptimizer(){

        // don't spawn any butterfly until the first puzzle.
        if (GlobalPuzzleData.EventInfos [0].State != MapState.Beated) {
            return;
        }

        foreach (FlockController m in FlockButterflies) {

            // tree and non tree have to be separated..!
            if (!m)
                continue;

            if (!m.gameObject.transform.parent.gameObject.activeSelf)
                continue;

            float dist = Vector3.Distance (m.gameObject.transform.position, GlobalCamera.transform.position);

            if (dist > 35f) {
                m.gameObject.SetActive (false);
            } else if (!m.gameObject.activeSelf) {
                //m.
                m.gameObject.SetActive (true);
            }
            //maybe do the DistanceJoint2D checked.. for each.. divide into 2 different height groups.. AndroidJNI for 
        }

    }

    bool Visibility(MeshRenderer m){
        Vector2 groundpos2d = new Vector2 (m.gameObject.transform.position.x, m.gameObject.transform.position.z);
        Vector2 campos2d = new Vector2 (GlobalCamera.transform.position.x, GlobalCamera.transform.position.z);
        Vector2 a = (campos2d - groundpos2d).normalized;
        Vector2 b = new Vector2 (GlobalCamera.transform.forward.x, GlobalCamera.transform.forward.z) * -1f;
        float res = Vector2.Dot (a, b);

        if (res > -0.2f) {
            return false;

        } else {
            return true;
        }
    }

    void ZoneReLink(){
        int id = 0;

        for (int i = 0; i < CurrentMapPositions.Length; i++) {

            //Debug.Log (id + " - " + CurrentMapPositions[i]);

            bool replaced = false;

            // CASE 1 : Found reuseable asset from existing
            for (int j = 0; j < MapObjects.Length; j++) {
                
                if (!MapObjects [j])
                    return;
                
                if (MapObjects [j].transform.position.Equals(CurrentMapPositions[i])) {
                    MapObjectsTmp [id] = MapObjects [j];
                    replaced = true;
                    break;
                }
            }
 
            // CASE 2 : Did not found asset - Get one from pool
            if (!replaced) {
                SetupGroundWithPosition(CurrentMapPositions[i], ref MapObjectsTmp[id]);
            }

            id++;

        }
            
        // Reset assets that are not in used anymore
        ZoneClean ();

        // Assign the ready-to-go array to MapsObjects now
        System.Array.Copy(MapObjectsTmp, MapObjects, MapObjects.Length);
        //System.Array.Clear (MapObjectsTmp, 0, MapObjectsTmp.Length);
    }
        
    void ZoneClean(){
        for (int i = 0; i < MapObjects.Length; i++) {
            bool Keep = false;

            for (int j = 0; j < CurrentMapPositions.Length; j++) {
                if (MapObjects [i].transform.position.Equals(CurrentMapPositions[j])) {
                    Keep = true;
                    break;
                }
            }

            if (!Keep) {
                if (MapObjects [i].tag == "EventGround") {
                    PuzzlePool.ResetAsset (MapObjects [i]);
                    MapObjects [i] = null;
                } else {
                    MapObjects [i].GetComponentInChildren<TerranNoise> ().ClearProps ();
                    GeneralPool.ResetAsset (MapObjects [i]);
                    MapObjects [i] = null;
                }

            }
        }
    }


}