Last active
May 9, 2019 12:02
-
-
Save desplesda/bc186ba8e6c7485604c2998afd70675b to your computer and use it in GitHub Desktop.
PropLinePlacer - a tool for placing lines of prefabs! Video: https://twitter.com/desplesda/status/1126410852658106368
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// PropLinePlacer - a tool for placing lines of prefabs | |
// By Jon Manning | |
// MIT License | |
// Copyright (c) 2019 Secret Lab Pty Ltd | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to | |
// permit persons to whom the Software is furnished to do so, subject to | |
// the following conditions: | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
using UnityEngine; | |
public class PropLinePlacer : MonoBehaviour | |
{ | |
/// <summary> | |
/// The end point of the line, in local space. | |
/// </summary> | |
/// <remarks>The start point is the transform of this object.</remarks> | |
public Vector3 endPoint = new Vector3(1, 0, 0); | |
/// <summary> | |
/// The possible prefabs that this line should contain. Prefabs will be | |
/// randomly selected from this list. | |
/// </summary> | |
public GameObject[] prefabs; | |
/// <summary> | |
/// The number of objects that should be created. | |
/// </summary> | |
public uint numberOfObjects = 3; | |
/// <summary> | |
/// If true, the created objects will be randomly rotated around the Y | |
/// axis. | |
/// </summary> | |
public bool randomizeYRotation = true; | |
/// <summary> | |
/// The amount of distance, in the X and Z directions, to randomly | |
/// offset our objects. | |
/// </summary> | |
public float randomXZDistance = 0.05f; | |
/// <summary> | |
/// The degree of distance along the line to shift our objects. | |
/// </summary> | |
public float maxLineDisplacement = 0f; | |
/// <summary> | |
/// A list of materials to randomly assign to the created objects. | |
/// </summary> | |
/// <remarks>If this array is empty, the created objects will not have | |
/// their materials changed.</remarks> | |
public Material[] materials; | |
/// <summary> | |
/// Produces an array of positions, in world space, for objects to be | |
/// created at. | |
/// </summary> | |
/// <returns>The positions, in world space, for objects to be | |
/// placed.</returns> | |
public Vector3[] GetSpawnPositions() | |
{ | |
// Early out if we're producing zero objects. | |
if (numberOfObjects == 0) | |
{ | |
return new Vector3[0]; | |
} | |
var result = new Vector3[numberOfObjects]; | |
// Save the state of the random number generator, because we're | |
// about to blow it away | |
var state = UnityEngine.Random.state; | |
// Reset the random number generator so that it's consistent | |
// between frames | |
Random.InitState(0); | |
// For each object we want to create, calculate its position | |
for (int i = 0; i < numberOfObjects; i++) | |
{ | |
// Figure out how far along the line we are, from 0 to 1 | |
var t = i / (float)(numberOfObjects - 1); | |
// Randomly displace this along the line (this is why we're | |
// preserving random state, otherwise the positions would | |
// change every frame that the object is selected in the | |
// editor) | |
t += Random.Range(-maxLineDisplacement, maxLineDisplacement) * 1f / numberOfObjects; | |
// Calculate this position along the line! | |
var position = Vector3.Lerp( | |
transform.position, // start point | |
endPoint + transform.position, // end point | |
t // position on the line (from 0 to 1) | |
); | |
result[i] = position; | |
} | |
// Restore the random state we saved earlier | |
Random.state = state; | |
// Return the list of positions! | |
return result; | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// PropLinePlacer - a tool for placing lines of prefabs | |
// By Jon Manning | |
// MIT License | |
// Copyright (c) 2019 Secret Lab Pty Ltd | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to | |
// permit persons to whom the Software is furnished to do so, subject to | |
// the following conditions: | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
using UnityEngine; | |
// If this file isn't in a folder named Editor, it will be compiled into | |
// the final project, and will cause build errors because the UnityEditor | |
// namespace isn't available in built players. Wrapping the whole thing in | |
// an #if-#endif avoids this problem. (You should still put this file in an | |
// Editor folder, because it's tidier.) | |
#if UNITY_EDITOR | |
using UnityEditor; | |
/// <summary> | |
/// Editor for the PropLinePlacer. | |
/// </summary> | |
[CustomEditor(typeof(PropLinePlacer)), CanEditMultipleObjects] | |
public class PropLinePlacerEditor : Editor | |
{ | |
/// <summary> | |
/// Draws the usual inspector GUI, but also adds a button that creates | |
/// the objects. | |
/// </summary> | |
public override void OnInspectorGUI() | |
{ | |
DrawDefaultInspector(); | |
// When the user clicks the Place Objects button, create objects | |
// along the line | |
if (GUILayout.Button("Place Objects")) | |
{ | |
// For each PropLinePlacer that is selected, create its | |
// objects. | |
foreach (PropLinePlacer t in targets) | |
{ | |
PlaceObjects(t); | |
} | |
} | |
} | |
/// <summary> | |
/// Places objects for a single PropLinePlacer. | |
/// </summary> | |
/// <param name="placer"></param> | |
private void PlaceObjects(PropLinePlacer placer) | |
{ | |
if (placer.prefabs.Length == 0) | |
{ | |
// No prefabs to place; nothing we can do here | |
return; | |
} | |
foreach (var position in placer.GetSpawnPositions()) | |
{ | |
// Select the prefab we want to use here | |
var prefab = placer.prefabs[Random.Range(0, placer.prefabs.Length - 1)]; | |
// Instantiate the prefab (note we're using | |
// PrefabUtility.InstantiatePrefab rather than | |
// GameObject.Instantiate, so what we're creating is linked to | |
// the prefab! | |
var instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab); | |
// Be nice and let the user undo this if they want | |
Undo.RegisterCreatedObjectUndo(instance, "Create object"); | |
// Place this instance under ourselves, and position and rotate | |
// it correctly | |
instance.transform.SetParent(placer.transform, false); | |
instance.transform.position = position; | |
instance.transform.rotation = placer.transform.rotation; | |
// Rotate around Y if we need to | |
if (placer.randomizeYRotation) | |
{ | |
var rotation = Random.Range(0, 359); | |
instance.transform.Rotate(0, rotation, 0); | |
} | |
// Offset our position in the X-Z plane by a random amount | |
float distance = placer.randomXZDistance; | |
var xDisplacement = Random.Range(-distance / 2f, distance / 2f); | |
var zDisplacement = Random.Range(-distance / 2f, distance / 2f); | |
instance.transform.Translate(xDisplacement, 0, zDisplacement); | |
// If we have materials to use, randomly select one, and apply | |
// it to the mesh renderer | |
var renderer = instance.GetComponent<MeshRenderer>(); | |
if (placer.materials.Length > 0 && renderer != null) | |
{ | |
var material = placer.materials[Random.Range(0, placer.materials.Length)]; | |
renderer.sharedMaterial = material; | |
} | |
} | |
} | |
/// <summary> | |
/// Draws the Scene GUI: the line itself, the positions we will spawn | |
/// objects, and allow for changing the line's end point interactively. | |
/// </summary> | |
private void OnSceneGUI() | |
{ | |
PropLinePlacer placer = (PropLinePlacer)target; | |
EditorGUI.BeginChangeCheck(); | |
var startPoint = placer.transform.position; | |
var endPoint = placer.endPoint + startPoint; | |
// Draw the handles that let us modify the end point | |
endPoint = Handles.PositionHandle(endPoint, Quaternion.identity); | |
if (EditorGUI.EndChangeCheck()) | |
{ | |
Undo.RecordObject(placer, "Change placer end point"); | |
placer.endPoint = endPoint - startPoint; | |
} | |
// Draw the line itself | |
Handles.DrawDottedLine(startPoint, endPoint, 5); | |
// Don't draw the markers on top of everything else | |
Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual; | |
foreach (var position in placer.GetSpawnPositions()) { | |
Handles.color = Color.yellow; | |
// Draw a marker for this location | |
Handles.SphereHandleCap(0, position, Quaternion.identity, 0.1f, EventType.Repaint); | |
// Draw a disc representing the possible area that this object | |
// can be placed | |
Handles.color = new Color(0, 0, 1, 0.25f); | |
Handles.DrawSolidDisc(position, Vector3.up, placer.randomXZDistance); | |
} | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment