Maddison DeVlaeminck

Technical Designer

I designed this tool for a project in my technical design course. I wanted it to help level designers place assets more easily. I wanted it to visually show the prefabs and spawn radius, and be easily readable so the user could quickly understand how the tool works.

  • With those goals in mind, I started to prototype this tool!
    • First, before the tools’ functionality, I made the window using IMGUI to create the tools’ layout and try to make the tool easily understandable for the user.
// Opening the Tool -------------------------------
[MenuItem("Tools/Prefab Placement Tool")]
public static void ShowWindow() { GetWindow<PrefabPlacementTool>("PrefabPlacementTool"); }
 GUILayout.Space(8);
 GUILayout.Label("Select a Prefab to Spawn:", EditorStyles.boldLabel);

 EditorGUI.BeginChangeCheck();
 spawnPrefab = (GameObject)EditorGUILayout.ObjectField(spawnPrefab, typeof(GameObject), false);
 if (spawnPrefab)
 {
     GUILayout.Space(8);
     GUILayout.Label("Edit Values:", EditorStyles.boldLabel);
     radius = EditorGUILayout.FloatField("Radius:", radius);
     spawnCount = EditorGUILayout.IntField("Spawn Count:", spawnCount);

     GUILayout.Space(8);
     GUILayout.Label("Hot Keys:", EditorStyles.boldLabel);
     GUILayout.Label("- ALT + Scrollwheel to change radius in scene view");
     GUILayout.Label("- CTRL + Z to undo any created prefabs");
 }
 else
 {
     EditorGUILayout.HelpBox("Please select a prefab to spawn!", MessageType.Warning);
 }

 GUILayout.Space(8);
 GUILayout.Label("How to use:", EditorStyles.boldLabel);
 GUILayout.Label("- Select a prefab that you want to spawn in the scene view");
 GUILayout.Label("- Adjust the radius and spawn count to get the size and amount that you want");
 GUILayout.Label("- SPACE to spawn prefabs (make sure to be clicked into the scene view!)");
 GUILayout.Label("- Refer to the hot keys list to get more tips on the tool!");
  • For the tool’s functionality, I knew there were a few key parts that I needed for the script; the ability to spawn prefabs and visually see where prefabs will be placed in the scene view.
    • The first thing I did was raycasting! This helped me to detect and visualize where prefabs should be placed.
 void CreateRaycasts()
    {
        hitPoints.Clear();
        if (Physics.Raycast(ray, out RaycastHit hit))
        {
            // Setting up the space in the scene view
            Vector3 z = hit.normal; //the normal vector (blue)
            Vector3 x = Vector3.Cross(z, camTransform.up).normalized; //tangent (red)
            Vector3 y = Vector3.Cross(z, x); //bitangent vector (green)

            Handles.color = Color.blue;
            Handles.DrawAAPolyLine(5, hit.point, hit.point + z);
            Handles.color = Color.red;
            Handles.DrawAAPolyLine(5, hit.point, hit.point + x);
            Handles.color = Color.green;
            Handles.DrawAAPolyLine(5, hit.point, hit.point + y);

            Handles.color = Color.black;
            Handles.DrawAAPolyLine(5, hit.point, hit.point + hit.normal);
            Handles.DrawWireDisc(hit.point, hit.normal, radius);

            foreach (Vector2 point in randomPoints)
            {
                //create ray for the point
                Vector3 worldPos = hit.point + (x * point.x + y * point.y) * radius;
                worldPos += z * 2; //offset
                Vector3 direction = -z;
                Ray pointRay = new Ray(worldPos, direction);

                if (Physics.Raycast(pointRay, out RaycastHit pointHit))
                {
                    hitPoints.Add(pointHit);
                    //draw sphere and line on the surface
                    DrawSphere(pointHit.point);
                    Handles.DrawAAPolyLine(pointHit.point, pointHit.point + pointHit.normal);
                    //Drawing the mesh
                    if (spawnPrefab)
                    {
                        Mesh prefabMesh = spawnPrefab.GetComponent<MeshFilter>().sharedMesh;
                        Graphics.DrawMeshNow(prefabMesh, pointHit.point, Quaternion.LookRotation(pointHit.normal));
                    }
                }
            }
        }
    }
  • Then, for each prefab that gets spawned, I wanted them to fit inside a radius, and to do so, I made a circle draw in the scene view so the player can better visualize where prefabs are going to get spawned! Along with each like, you spawn prefabs, the points get randomized.
 void DrawSphere(Vector3 pos)
    {
        Handles.SphereHandleCap(-1, pos, Quaternion.identity, 0.3f, EventType.Repaint);
    }

    void GenerateRandomPoints()
    {
        randomPoints = new Vector2[spawnCount];
        for (int i = 0; i < spawnCount; i++)
        {
            randomPoints[i] = Random.insideUnitCircle;
        }
    }
  • After I had all the functionality done, this is what the final tool looked like, and throughout working on this tool, I also documented my progress!
  • I think the biggest challenge I encountered when working on this tool was all the math and figuring out the raycasts!
    • To get around this, I did a lot of research using the Unity API documentation! 🙂