Home > other >  Unity C# I want to be able to raycast hit to an object, then get that object's name to fill in
Unity C# I want to be able to raycast hit to an object, then get that object's name to fill in

Time:10-08

The following is attached to my player and would call upon whatever object is hit to use the objects function. Think the player controls the pointing and clicking, but the object controls whatever the object will do, such as turn on a light.

void Interact()
{
    RaycastHit interactablehit;

    if (Physics.Raycast(PlayerCamera.transform.position, PlayerCamera.transform.forward, out interactablehit, MaxDistance))
    {
        // if raycast hits, then it checks if it hit an object with the tag Interactable.
        if (interactablehit.transform.tag == "Interactable")
        {
            object = interactablehit.transform.name;
            interactablehit.transform.gameObject.GetComponent<interactablehit.transform.name>().ObjectInteract();
        }
    }
}

CodePudding user response:

This is a job for either an interface or a common base class:

public interface IInteractable
{
    void ObjectInteract();
}

and then have your different implementations like e.g.

public class ExampleInteractable : MonoBevahiour, IInteractable
{
    public void ObjectInteract()
    {
        Debug.Log("Hello!");
    }
}

or

public class ToggleLightInteractable : MonoBevahiour, IInteractable
{
    [SerializeField] private Light light;

    public void ObjectInteract()
    {
        light.enabled = !light.enabled;
    }
}

and then simply do

void Interact()
{
    if (Physics.Raycast(PlayerCamera.transform.position, PlayerCamera.transform.forward, out var interactablehit, MaxDistance))
    {
        var interactable = interactablehit.transform.GetComponent<IInteractable>();
        if (interactable != null)
        {
            interactable.ObjctInteract();
        }
    }
}

Or the same with a common base class, usefull if you want/need some shared behaviour or default implementations

public class BaseInteractable
{
    public virtual void ObjectInteract()
    {
        Debug.Log("Hello!");

        // For demo reasons lets say per default this can be interacted with only once
        Destroy(this);
    }
}

and then have your different implementations like e.g.

public class ExampleInteractable : BaseInteractable
{
    public override void ObjectInteract()
    {
        // completely override the default behaviour
        Debug.Log("Hello World!");

        // this can be interacted with forever since the default behavior is not executed
    }
}

or

public class ToggleLightInteractable : BaseInteractable
{
    [SerializeField] private Light light;

    public override void ObjectInteract()
    {
        // keep the default behavior and only extend it with something additional
        base.ObjectInteract();

        light.enabled = !light.enabled;
    }
}

and then do

void Interact()
{
    if (Physics.Raycast(PlayerCamera.transform.position, PlayerCamera.transform.forward, out var interactablehit, MaxDistance))
    {
        if (interactablehit.transform.TryGetComponent<BaseInteractable>(out var interactable))
        {
            interactable.ObjctInteract();
        }
    }
}

If you really really for what reason ever (there shouldn't be any good one) need to stick to string you can (you shouldn't) use SendMessage like

void Interact()
{
    if (Physics.Raycast(PlayerCamera.transform.position, PlayerCamera.transform.forward, out var interactablehit, MaxDistance))
    {
        interactablehit.transform.gameObject.SendMessage(interactablehit.transform.name, options: SendMessageOptions.DontRequireReceiver);
    }
}

which would require your object to be called exactly like the method you want to call, regardless of how the component is called.

  • Related