Unity 3D Place Objects on Mouse Click

NSDG | Dec 2, 2019 | 0 Comments
Unity 3D Place Objects on Mouse Click
66

Ability to spawn objects on mouse click is useful when you need to create a level editor or simply to allow players to place objects anywhere on the map.

In this tutorial I will be showing how to make a simple level editor with ability to spawn objects on mouse click.

So let's begin!

Steps

  • Open Scene with your level (In my case it will be a simple Scene with a plane and a Camera which points down)

  • Create new script, call it "SC_Object" and paste the code below inside it:

SC_Object.cs

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

public class SC_Object : MonoBehaviour
{
    public Renderer[] objectRenderers; //Assign object renderers

    public void ReplaceMaterials(Material material)
    {
        for(int i = 0; i < objectRenderers.Length; i++)
        {
            objectRenderers[i].sharedMaterial = material;
        }
    }
}
  • Create new script, call it "SC_ObjectSpawner" and paste the code below inside it:

SC_ObjectSpawner.cs

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

public class SC_ObjectSpawner : MonoBehaviour
{
    public SC_Object[] availableObjects;
    public Material overdrawMaterial;

    Vector2 scroll;
    Transform buildTarget;
    bool canPlace = false;
    SC_Object selectedObject;
    int selectedObjectindex = -1;
    Rect areaRect = new Rect(5, 5, 150, 150);

    // Start is called before the first frame update
    void Start()
    {
        //Create build target object
        GameObject tmpObj = new GameObject("BuildTarget");
        buildTarget = tmpObj.transform;
    }

    // Update is called once per frame
    void Update()
    {
        buildTarget.position = GetRaycastPosition();

        if (canPlace && selectedObject)
        {
            //Place Object
            if (Input.GetMouseButtonDown(0) && !areaRect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)) && !Input.GetKey(KeyCode.LeftControl))
            {
                Instantiate(availableObjects[selectedObjectindex], selectedObject.transform.position, selectedObject.transform.rotation);
            }

            //Rotate Left
            if (Input.GetKey(KeyCode.A))
            {
                selectedObject.transform.Rotate(Vector3.up, -2, Space.World);
            }
            //Rotate Right
            else if (Input.GetKey(KeyCode.D))
            {
                selectedObject.transform.Rotate(Vector3.up, 2, Space.World);
            }
        }

        //Delete Object
        if (Input.GetMouseButtonDown(0) && Input.GetKey(KeyCode.LeftControl))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit = new RaycastHit();
            if (Physics.Raycast(ray, out hit, 300))
            {
                if (hit.transform.root.GetComponent<SC_Object>())
                {
                    Destroy(hit.transform.parent.gameObject);
                }
            }
        }
        //Clear selection
        if (Input.GetKeyDown(KeyCode.LeftControl) && selectedObject)
        {
            Destroy(selectedObject.gameObject);
        }
    }

    Vector3 GetRaycastPosition()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit = new RaycastHit();
        canPlace = false;
        //Do Raycast hit only against UI layer
        if (Physics.Raycast(ray, out hit, 300, 1 << 5))
        {
            canPlace = true;
            return hit.point;
        }

        return new Vector3(5000, 5000, 5000);
    }

    void OnGUI()
    {
        GUILayout.BeginArea(areaRect, "", "box");

            GUILayout.Label("Select Object:");

            scroll = GUILayout.BeginScrollView(scroll);
                for(int i = 0; i < availableObjects.Length; i++)
                {
                    if (GUILayout.Button(availableObjects[i].name))
                    {
                        //Destroy previous selected object if there any
                        if (selectedObject)
                        {
                            Destroy(selectedObject.gameObject);
                        }
                        //Instantiate Object
                        selectedObject = Instantiate(availableObjects[i], buildTarget.position, buildTarget.rotation);
                        selectedObject.transform.SetParent(buildTarget);
                        selectedObject.ReplaceMaterials(overdrawMaterial);
                        selectedObjectindex = i;
                    }
                }
            GUILayout.EndScrollView();
        GUILayout.EndArea();
    }
}
  • Create new GameObject, name it "_ObjectSpawner"
  • Attach SC_ObjectSpawner script to "_ObjectSpawner"
  • Change the layer of a ground (In my case the Plane) to "UI"

  • Create new Shader (Create -> Shader -> Unlit Shader), call it "Overdraw" and paste the code below inside it

Overdraw.shader

Shader "FX/Overdraw" {
    Properties
    {
		_Color ("Main Color", Color) = (0,1,0.4,0.5)
    }
    SubShader
    {
        Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
        LOD 100
        Fog { Mode Off }
        ZWrite On
        ZTest LEqual
        Blend SrcAlpha OneMinusSrcAlpha
 
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
         
            #include "UnityCG.cginc"
 
            struct appdata
            {
                float4 vertex : POSITION;
            };
 
            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

			fixed4 _Color;
 
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }
         
            fixed4 frag (v2f i) : SV_Target
            {
                return _Color;
            }
            ENDCG
        }
    }
}
  • Create new Material and name it "overdraw_material" and change its Shader to "FX/Overdraw"

  • Assign "overdraw_material" material to Overdraw Material variable in SC_ObjectSpawner

Now it's time to setup Objects that will be spawned on mouse click. For this tutorial I will be using 3D Models from this Asset Store package.

  • Create new GameObject and place it at the bottom of a 3D model, like this:

  • Move the model inside that GameObject. (Rename the Object to whatever name you want to be displayed in the list, in my case I named it "Hangar 1")
  • Attach SC_Object script to the root GameObject and assign child mesh renderers to Object Renderers array:

  • Save the Object to Prefab and repeat the same process for the rest of the models:

  • Lastly assign the Prefabs to Available Objects array at "SC_ObjectSpawner"

  • Now press Play to test it:

Select any Object from the list and place it anywhere on the map by left clicking.

Press A, D to rotate the Object.

Hold Left Ctrl + Left Click to remove the Object.