1
\$\begingroup\$

I have a standard Cube gameObject (with a BoxCollider and my CubeController.cs script) in my scene and I want to be able to click and drag on it to instantiate a new cube prefab (with a BoxCollider and my DragController.cs script) which then follows the mouse.

I have it nearly working but when the new cube prefab is instantiated at the same position as the clicked cube (but 10% bigger) you have to then click the new instantiated object again to begin dragging it. I have a debug line printing 'Prefab created' in DragController.cs so the prefab must be sensing that the mouse button is still down.

How can I do it so you click the original cube then the new prefab is created and draggable without having to click again?

CubeController.cs - placed on original Cube gameobject

using UnityEngine;

public class CubeController : MonoBehaviour
{
    RaycastHit hitInfo;
    Ray ray;

    public GameObject CubePrefab;
    private bool canCreate = true;

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        }

        if (Physics.Raycast(ray, out hitInfo))
        {
            GameObject objectHit = hitInfo.transform.gameObject;

            if (objectHit.tag == "Cube")
            {
                createCubePrefab();
            }
        }
    }

    void createCubePrefab()
    {
        if (canCreate)
        {
            canCreate = false;
            GameObject newCube = Instantiate(CubePrefab, hitInfo.transform.position, Quaternion.identity);
            newCube.transform.localScale = new Vector3(1.1f, 1.1f, 1.1f);
        }
    }
}

DragController.cs - placed on Cube prefab that is going to be instantiated

using UnityEngine;

public class DragController : MonoBehaviour
{
    private Vector3 screenPoint;
    private Vector3 offset;

    // Start is called before the first frame update
    void Start()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Debug.Log("Prefab created");
        }
    }  

    void OnMouseDown()
    {
        screenPoint = Camera.main.WorldToScreenPoint(gameObject.transform.position);
        offset = gameObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z));
    }

    void OnMouseDrag()
    {
        Vector3 cursorPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z);
        Vector3 cursorPosition = Camera.main.ScreenToWorldPoint(cursorPoint) + offset;
        transform.position = cursorPosition;
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ If an answer was useful to you, please use the up arrow to tell other users that the answer is useful, and if it solved your issue, please consider using the checkmark to mark the answer as accepted. \$\endgroup\$ – Vaillancourt Jul 30 at 13:38
0
\$\begingroup\$

Since the clone didn't exist when the mouse was clicked, it wasn't in line to have its OnMouseDown method called by the engine, or follow-up OnMouseDrag calls on subsequent frames. So it's up to us to call it.

We can do this with a little Coroutine:

IEnumerator SyntheticDrag() {
    // Process the start of our drag, and then wait a frame.
    OnMouseDown();
    yield return null;

    // Keep updating our dragged position each frame
    // until the mouse button is released.
    while(Input.GetMouseButton(0)) {
        OnMouseDrag();
        yield return null;
    }
}

Now we need to start this Coroutine. Two places you could do that, depending on which suits you best:

  1. Inside DragController.Start(), when you determine that the mouse was already pressed when the object was instantiated, do a raycast to verify that the mouse is pointing over this object. If so, call StartCoroutine(SyntheticDrag()).

    (The raycast check avoids false positives in case you use this script in any other circumstances where the object could spawn away from the mouse pointer, and the user just happened to have the mouse button pressed for an unrelated reason - you wouldn't want to start dragging in that circumstance)

    Or...

  2. Add to DragController a public method like this:

    public void StartDrag() { StartCoroutine(SyntheticDrag);}

    Inside CubeController.CreateCubePrefab() (use PascalCase for method names for consistency), after you've spawned your clone, use GetComponent<DragController> to get a reference to the drag script (or use this type for your prefab variable so you get the right component back from Instantiate() in one go). Then call StartDrag() on it to initiate the mouse drag.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.