There comes a time in your life where you have to build a fence or place cones in long lines:
Placing the fence pieces or cones one-by-one is a kinda boring process, so a better way is to make an Editor script to help you. The first script you need is called ObjectManagerLine and you need to attach it to an empty gameobject in the Editor. This script is kinda basic if you've been into Unity and it will just remove gameobjects and hold some parameters we need. You also need to add a prefab to this script which could be a simple sphere.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ObjectManagerLine : MonoBehaviour { //The object we want to add public GameObject prefabGO; //Whats the size of the prefab we want to add? //You can increase the size if you want to have a gap between the objects public float objectSize; //Where is the line ending? It starts at the position of the gameobject the script is attached to public Vector3 endOfLinePos; //Kill all children to this gameobject public void KillAllChildren() { //Get an array with all children to this transform GameObject[] allChildren = GetAllChildren(); //Now destroy them foreach (GameObject child in allChildren) { DestroyImmediate(child); } } //Get an array with all children to this GO private GameObject[] GetAllChildren() { //This array will hold all children GameObject[] allChildren = new GameObject[transform.childCount]; //Fill the array int childCount = 0; foreach (Transform child in transform) { allChildren[childCount] = child.gameObject; childCount += 1; } return allChildren; } }
The next script you need to add is the Editor script that will improve the script above. In this script you will be able to move the start/end positions of the line - and if one of them has moved then the script will add prefabs along the line. It's really important that you place this Editor script in a folder called Editor. Remember that you can have multiple Editor folders to make it easier to organize your project.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; [CustomEditor(typeof(ObjectManagerLine))] public class ObjectManagerLineEditor : Editor { private ObjectManagerLine objectManager; private void OnEnable() { //This is a reference to the script objectManager = target as ObjectManagerLine; //Hide the handles of the GO Tools.hidden = true; } private void OnDisable() { //Unhide the handles of the GO Tools.hidden = false; } private void OnSceneGUI() { //Move the line's start and end positions and add objects if we have moved one of the positions //End position objectManager.endOfLinePos = MovePoint(objectManager.endOfLinePos); //Start position objectManager.transform.position = MovePoint(objectManager.transform.position); } private Vector3 MovePoint(Vector3 pos) { //Change position if (Tools.current == Tool.Move) { //Check if we have moved the point EditorGUI.BeginChangeCheck(); //Get the new position and display the position with axis pos = Handles.PositionHandle(pos, Quaternion.identity); //If we have moved the point if (EditorGUI.EndChangeCheck()) { MarkSceneAsDirty(); UpdateObjects(); } } return pos; } //Update the objects between the path private void UpdateObjects() { //First kill all current objects objectManager.KillAllChildren(); //How many object fit between the points? //Make sure the size of the object is not zero because then we can fit infinite amount of objects if (objectManager.objectSize == 0f) { return; } //The distance between the points float distanceBetween = (objectManager.endOfLinePos - objectManager.transform.position).magnitude; //If we divide the distance between the points and the size of one object we know how many can fit between int objects = Mathf.FloorToInt(distanceBetween / objectManager.objectSize); //The direction between the points Vector3 direction = (objectManager.endOfLinePos - objectManager.transform.position).normalized; //Where should we instantiate the first object Vector3 instantiatePos = objectManager.transform.position + direction * objectManager.objectSize * 0.5f; //Add the objects for (int i = 0; i < objects; i++) { GameObject newGO = PrefabUtility.InstantiatePrefab(objectManager.prefabGO) as GameObject; //Parent it so we can delete it by killing all children newGO.transform.parent = objectManager.transform; //Give it the position newGO.transform.position = instantiatePos; //Orient it by making it look at the position we are going to newGO.transform.forward = direction; //Move to the next object instantiatePos += direction * objectManager.objectSize; } } //Force unity to save changes or Unity may not save when we have instantiated/removed prefabs despite pressing save button private void MarkSceneAsDirty() { UnityEngine.SceneManagement.Scene activeScene = UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene(); UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(activeScene); } }
Now select the gamobject to which you attached the script. If everything is working, you should be able to do this if you move the start/end position of the line: