0

I have two main problems in this case :

  1. NAVI is child of the player (FPSController) and therefore the NAVI is moving along with the player. But NAVI is also position a bit in front of the player and the result is that the raycast is hitting first time all the time the NAVI instead hitting the Interactable object.

  2. If there is a door between the player and some Interactable object it will detect the object. But if there is a door and other objects between the player and the interactable object/s it should not detect them. The player not see them but yet it's detecting.

This script is attached to the plaher (FPSController) :

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

public class DetectInteractable : UnityEngine.MonoBehaviour
{
    public Camera cam;
    public float distanceToSee;
    public string objectHit;
    public bool interactableObject = false;
    public Transform parentToSearch;
    public Scaling scaling;
    public int spinX = 0;
    public int spinY = 0;
    public int spinZ = 0;
    public GameObject navi;
    public GameObject itemsDescriptionCanvas;
    public Text itemsDescriptionText;

    private RaycastHit whatObjectHit;
    private Transform[] childrenToSearhc;

    private void Start()
    {
        childrenToSearhc = parentToSearch.GetComponentsInChildren<Transform>();
    }

    private void Update()
    {
        if (cam.enabled == true)
        {
            if (Input.GetMouseButtonDown(0) && !scaling.scaleUp)
            {
                if (whatObjectHit.collider != null)
                    ExecuteActions(whatObjectHit.collider.gameObject);
            }

            Debug.DrawRay(cam.transform.position, cam.transform.forward * distanceToSee, Color.magenta);
            if (Physics.Raycast(cam.transform.position, cam.transform.forward, out whatObjectHit, distanceToSee))
            {
                objectHit = whatObjectHit.collider.gameObject.name;
                interactableObject = true;
                print("Hit ! " + whatObjectHit.collider.gameObject.name);

                if (scaling.objectToScale.transform.localScale == scaling.minSize)
                {
                    scaling.objectToScale.transform.Rotate(spinX, spinY, spinZ);
                }
                ProcessItemsDescripations();
                itemsDescriptionCanvas.SetActive(true);
            }
            else
            {
                if (scaling.objectToScale.transform.localScale == scaling.minSize)
                {
                    navi.transform.rotation = new Quaternion(0, 0, 0, 0);
                }
                itemsDescriptionCanvas.SetActive(false);
                print("Not Hit !");
            }
        }
    }

    private void ExecuteActions(GameObject go)
    {
        var ia = go.GetComponent<ItemAction>();
        if (ia != null)
        {
            ia.ItemMove();
        }
    }

    void ProcessItemsDescripations()
    {
        foreach (Transform child in childrenToSearhc)
        {
            if (child.GetComponent<ItemInformation>() != null)
            {
                ItemInformation iteminformation = child.GetComponent<ItemInformation>();
                if (child.name == objectHit)
                {
                    itemsDescriptionText.text = iteminformation.description;
                }
            }
        }
    }

    public class ViewableObject : UnityEngine.MonoBehaviour
    {
        public string displayText;
        public bool isInteractable;
    }
}

And this small script is attach to each Interactable object :

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

public class ItemInformation : UnityEngine.MonoBehaviour
{
    [TextArea]
    public string description;
}

This is a screenshot of the player (FPSController) Inspector settings :

Player Hierarchy and Inspector

This screenshot is of the NAVI that is child of the player : You can see the NAVI at the left game window and scene window :

Navi

This is a screenshot example of the window. The window should be Interactable object and should be detected by the raycast :

The window Tag is set to Untagged and the Layer to Default

Windows Interactable object example

This is a screenshot while the game is working a situation when the player is standing behind a door in the other side of the door there is a room with the Interactable window. Nothing will prevent from the raycast to pas through the door and detect the window. And that's not logic. The player can't see the window. So the window should not be detected :

On the left the top the magenta color of the raycast pass through the door to the window. On the bottom left the player is standing behind the door and see the window at all but it's detecting the window :

Detect

But when using a breakpoint you can see that what was really hit is the NAVI not the door not the window but the NAVI since the NAVI is always standing in front of the player :

What was hit ? NAVI

Before all that I tried to use raycast mask layer in my script and then changed the window layer to be Interactable and then I saw it's detecting through the door.

At the end the main goal is :

To detect Interactable objects in the game but not to detect the NAVI he is not Interactable object so somehow to make that NAVI will not block the raycast !

And to be able to detect bject only when there is a logic view of the player to the objects. And not to detect objects from behind doors or walls or other objects.

yochi le
  • 489
  • 9
  • 21
  • 2
    Physics.Raycast allows you to pass a layermask which it should ignore (https://docs.unity3d.com/ScriptReference/Physics.Raycast.html) that link also shows how to make a raycast hit everything except for a specific layer which should help. – Kieran May 26 '19 at 23:27
  • 1
    Just out of curiosity, are you using some kind of premade asset? Because I swear to god I've seen at least 3 different SO accounts posting using near identical pictures of what looks like the same project. [No really](https://i.stack.imgur.com/pe9Qt.jpg). – Draco18s no longer trusts SE May 27 '19 at 00:31
  • @Draco18s No it's me I guess in a different account here in stackoverflow – yochi le May 27 '19 at 00:41
  • 2
    @yochile You should get them merged, then. There's really no reason not to. – Draco18s no longer trusts SE May 27 '19 at 01:02
  • 1
    @Draco18s haha thanks for asking .. I had already the same impression after answering [here](https://stackoverflow.com/questions/55407676/how-can-i-draw-a-list-and-all-its-items-in-inspector-editor-script) and [here](https://stackoverflow.com/questions/56180821/how-can-i-use-reorderablelist-with-a-list-in-the-inspector-and-adding-new-empty) ^^ – derHugo May 27 '19 at 06:25

1 Answers1

2

You can use the additional optional parameter layerMask of Physics.Raycast

A Layer mask that is used to selectively ignore Colliders when casting a ray.

I would recommned to use a LayerMask

public LayerMask targetLayer;

configure the wanted layers using the Unity Inspector (select every Layer you want to hit) and finally pass it to the raycast like

if(Physics.Raycast(
    cam.transform.position, 
    cam.transform.forward, 
    out whatObjectHit, 
    distanceToSee, 
    targetLayer.value))
{
    ...
}

In the example Unity used a bitmask directly (btw. in a very unefficient way in Update):

// Bit shift the index of the layer (8) to get a bit mask
int layerMask = 1 << 8;

// This would cast rays only against colliders in layer 8.
// But instead we want to collide against everything except layer 8. The ~ operator does this, it inverts a bitmask.
layerMask = ~layerMask;

as said I didn't like this way of hardcoding it. Additionally you now only exclude the layer 8 but hit everything else.

To rather include only exactly one specific layer remove the line

layerMask = ~layerMask;

so you only hit layer 8.

Anyway using my way from before is way more flexible and you rather select those layers you actually want to hit instead of excluding one. And using the LayerMask is way easier than having to code some bitmask operations in order to construct the layermask you want.

derHugo
  • 83,094
  • 9
  • 75
  • 115
  • I used the unity example and in the Update I added this two lines : int layerMask = 1 << 8; and layerMask = ~layerMask; then changed the raycast checking line to : if (Physics.Raycast(cam.transform.position, cam.transform.forward, out whatObjectHit, distanceToSee, layerMask)) And in the editor in the I added at layer 8 a layer name Not Interact and set the layer of NAVI to Not Interact. Now this is working fine the raycast is not detecting the NAVI but do detect everything else. – yochi le May 27 '19 at 10:24
  • Now after I excluded successfully the NAVI from being detected, How can I make that only specific object will be detected and not everything else ? Now the NAVI is not detected using the layermask but everything else is detected. I want to use the layermask also to detect only specific items now and not everything. – yochi le May 27 '19 at 10:25
  • In this link of pastebin is the script how it looks like now with the layer mask and how I'm using it to exclude the NAVI from being detecting. Now I want to use the layer mask also to detect only specific objects and not all the rest. https://pastebin.com/u0AD2nx4 – yochi le May 27 '19 at 10:27
  • That's why I said you should simply use the `public LayerMask targetLayers;` solution. It let's you configure it directly in the Unity Editor (you did read this right?). What the example does is negating the layermask using `layerMask = ~layerMask;` which excludes one layer from the raycast. My solution rather works the other way round and you select those layer you **want** to hit - not the ones you want to exclude – derHugo May 27 '19 at 10:43