4
\$\begingroup\$

I'm using Unity 5.5.0f3 with C#.

My prefab(wooden fence) is instantiated at my mouse location when I click a UI button and follows my mouse until I click somewhere in the scene to place it. The fence has a box collider on it.

Currently, the fence is able to be placed intersecting and even inside of anything else on the scene right now, and I want to eliminate this issue.

I would like my fence (and any other future prefabs) to check if it is intersecting an object's collider when it is following my mouse and turn red if it is, or else be green if it is not to indicate if it can or cannot be placed there and either allow or deny placement accordingly.

I have read that there is no way to do this using transform.position since this is essentially using teleportation on my fence. I tried to use GetComponent<Rigidbody>().MovePosition(hit.point + positionOffset); but I get all kinds of weird behavior when I do this.

The following are the three scripts I am using to make this happen:

Raycast Builder:

public class RaycastBuilder : MonoBehaviour {

    public GameObject defense;
    public Vector3 positionOffset;

    public bool IsPurchased = false;

    public float speed = 2f;

    Vector3 mousePosition;

    BuildManager buildManager;

    void Start ()
    {
        buildManager = BuildManager.instance;
    }

    void Update ()
    {
        GameObject defenseToBuild = buildManager.GetDefenseToBuild();
        if(defenseToBuild == null)
        {
            return;
        }

            //To get the current mouse position
            mousePosition = Input.mousePosition;

            Ray ray = Camera.main.ScreenPointToRay(mousePosition);
            RaycastHit hit;

            if (Physics.Raycast(ray, out hit, 1 << 8))
            {
            defenseToBuild.transform.position = hit.point + positionOffset;

            if (Input.GetKey(KeyCode.E))
                {
                    defenseToBuild.transform.Rotate(Vector3.up * speed * Time.deltaTime);
                }
            if (Input.GetKey(KeyCode.Q))
                {
                    defenseToBuild.transform.Rotate(-Vector3.up * speed * Time.deltaTime);
                }
        }

        if (Input.GetMouseButton(0)&& IsPurchased == true)
        {
            buildManager.SetDefenseToBuild(null);
            IsPurchased = false;
        }

        if(Input.GetKeyDown(KeyCode.Escape))
        {
            buildManager.SetDefenseToBuild(null);
            Destroy(defenseToBuild);
            IsPurchased = false;
        }
    }
}

Shop:

public class Shop : MonoBehaviour {

    BuildManager buildManager;

    void Start ()
    {
        buildManager = BuildManager.instance;
    }

    public void PurchaseWoodenFence ()
    {
        Camera.main.GetComponent<RaycastBuilder>().IsPurchased = true;
        Debug.Log("Wooden Fence Started");
        buildManager.SetDefenseToBuild((GameObject)Instantiate(buildManager.woodenFencePrefab, Vector3.up, Quaternion.identity));
    }

}

Build Manager:

public class BuildManager : MonoBehaviour {

    public static BuildManager instance;

    void Awake ()
    {
        if (instance != null)
        {
            Debug.Log("More than one BuildManager in Scene!");
            return;
        }
        instance = this;
    }

    public GameObject woodenFencePrefab;

    private GameObject defenseToBuild;

    public GameObject GetDefenseToBuild ()
    {
        return defenseToBuild;
    }

    public void SetDefenseToBuild (GameObject defense)
    {
        defenseToBuild = defense;
    }

}
\$\endgroup\$
  • 1
    \$\begingroup\$ Will your instantiated objects always use a single box collider (simple), or do you need to support arbitrary collision shapes & compounds (substantially more complex)? \$\endgroup\$ – DMGregory Jul 28 '17 at 22:48
  • \$\begingroup\$ I think just one box collider as far as I can tell, but I am still learning so that very well may change. \$\endgroup\$ – MjercerGameDev Jul 28 '17 at 23:47
1
\$\begingroup\$

I suggest using isTrigger property of collider instead of raycasting.

Basically instantiate the object you want to place in your scene with its isTrigger set to true, and when it is placed (by mouse click), set isTrigger to false to make it work as a normal collider.

Edit: Attaching my code to demonstrate trigger collider. Also, check out the unity documentation entry about colliders.

using UnityEngine;

public class Builder : MonoBehaviour
{
    private int colliderCount = 0;

    private void Update()
    {
        TrackMouse();
        Debug.Log("Number blocking colliders: " + colliderCount);
    }


    private void TrackMouse()
    {
        var mousePosition = Input.mousePosition;

        Ray ray = Camera.main.ScreenPointToRay(mousePosition);
        RaycastHit hit;

        if (Physics.Raycast(ray, out hit, 1 << 8))
            transform.position = new Vector3(hit.point.x, hit.point.y, transform.position.z);
    }

    private void OnTriggerEnter(Collider other)
    {
        colliderCount++;
    }

    private void OnTriggerExit (Collider other)
    {
        colliderCount--;
    }
}
\$\endgroup\$
  • \$\begingroup\$ I found references to IsTrigger when I was looking for answers. I tried to use it to see if I could get it to work with a Debug.Log that says "Defense already placed here", but it only printed that to the console when my player ran into it. It didn't say anything when the objects were intersecting while moving them or when they were placed while intersecting. Could you provide a little more insight on what to do with IsTrigger to make it work? perhaps I was using the wrong function when I tried to get it to respond. The fences had rigidbodies on them when I tried this way too. \$\endgroup\$ – MjercerGameDev Jul 28 '17 at 21:51
  • 1
    \$\begingroup\$ Are you sure that you have colliders on both installed and installing object? I will come back if I can make a working example myself. \$\endgroup\$ – ccld44 Jul 28 '17 at 22:01
  • 1
    \$\begingroup\$ I have edited my answer so you can try the code yourself. You might want to fix the raycasting part to suit your need, since I simplified it for the sake of testing. \$\endgroup\$ – ccld44 Jul 28 '17 at 22:53
  • 1
    \$\begingroup\$ Yes. Set the boolean to false OnTriggerEnter, and set it back to true OnTriggerExit. \$\endgroup\$ – ccld44 Jul 28 '17 at 23:04
  • 1
    \$\begingroup\$ Okay I tested the situation which you described there, and I think the easiest way to solve it is to track the number of colliders, rather than using a boolean flag. Edited the answer to reflect it, so tell me if you find any other problem with the code. \$\endgroup\$ – ccld44 Jul 29 '17 at 6:29

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.