Something like "OnValidate" that executes just once?

It would seem to me that “OnValidate” is a very powerful function. I’ve used it already to make a float on my area exits that simultaneously controls the size of the collider, the boundaries of the particle system and the size of the particle system’s shape ECT.

So… I’m thinking now, I can use these sorts of scripts that execute outside of “Play” mode to do all sorts of things, like for instance, randomly alter the color and size of my trees, or even to instantiate them in random positions whilst checking proximity with other objects. Here’s the catch. OnValidate executes continuously while you are changing values in the inspector.

The question is: is there some similar function that executes only once in its lifetime, or each time a button is pressed, and is it possible to instantiate objects outside of Play mode with code?

Something like this is what I usually use (unless I’ve written a custom editor for the class).

1 Like

Brilliant. I was able to write a script that does exactly what I wanted it to (See Below). It has to be attached to a game object, but then it can be executed from the inspector’s context menu.

I made it such that the overlap capsule used to check for proximity takes its shape from a capsuleCollider2D that you can set in the inspector, alongside a layermask of layers to check against.

In order for the prefabs to check proximity with each other, I had to make this function into a coroutine so that it executes over multiple frames. It does the job. To make it more efficient, what I could have done instead would have been to make it generate the coordinates beforehand, and calculate the proximity numerically, then instantiate all the prefabs on the same frame. The original method can be used to check for proximity to other pre-existing game objects.

Tilemap composite collider geometry must be set to polygons instead of outlines for the proximity detection to work properly with them. They can be changed back afterwards.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class PlantTrees : MonoBehaviour
{
#if UNITY_EDITOR
    [SerializeField] GameObject treePrefab;
    [SerializeField] Transform parent;
    [SerializeField] int number = 1;
    [SerializeField] float minX, maxX, minY, maxY;
    [SerializeField] AnimationCurve sizeDistribution = AnimationCurve.Constant(0f,1f,1f);
    [SerializeField] float minScale =1f;
    [SerializeField] float maxScale = 1f;
    [SerializeField] Color[] colors;
    [SerializeField] LayerMask proximityLayerMask;
    [SerializeField] CapsuleCollider2D proximityShape;
    [SerializeField] int maxAttempts = 1000;
    private void OnValidate() {
        if (number<1) number = 1;
    }

    [ContextMenu("Generate Prefabs")]
    void InstantiatePrefabs() {StartCoroutine(InstantiatePrefabsRoutine());}
    IEnumerator InstantiatePrefabsRoutine()
    {
        for(int i =0;i<number;i++){

            GameObject tree;
            if (parent==null) {tree = PrefabUtility.InstantiatePrefab(treePrefab) as GameObject;}
            else {tree = PrefabUtility.InstantiatePrefab(treePrefab,parent) as GameObject;}

            Vector2 randomCoordinates = GetRandomCoordinates();
            int attempt = 0;
            while(ProximityCheck(randomCoordinates)){
                randomCoordinates = GetRandomCoordinates();
                attempt++;
                if(attempt>maxAttempts){
                    Debug.LogWarning("max attempts have been reached. Prefabs Instantiated: "+ i);
                    yield break;
                }
            }
            tree.transform.position = randomCoordinates;
            tree.transform.localScale = Vector3.one*(minScale+((maxScale-minScale)*sizeDistribution.Evaluate(Random.value)));
            if(colors.Length>0){
                tree.GetComponent<SpriteRenderer>().color= colors[Random.Range(0,colors.Length-1)];
            }
            yield return null;
        }
        Debug.Log("Success!");
    }
    private Vector2 GetRandomCoordinates(){
        return new Vector2(Random.Range(minX,maxX),Random.Range(minY,maxY));
    }
    private bool ProximityCheck(Vector2 coordinates){
        if(proximityShape==null) return false;
        return Physics2D.OverlapCapsule(coordinates,proximityShape.size,proximityShape.direction,0,proximityLayerMask);
    }
#endif
}

Privacy & Terms