Search Unity

Problems With Selection Materials. Please Help

Discussion in 'Scripting' started by JacobCompbook, Jun 19, 2020.

  1. JacobCompbook

    JacobCompbook

    Joined:
    Jan 25, 2020
    Posts:
    4
    So today I decided to add the ability to pick up objects you are looking at using a raycast. I was able to properly implement this, but ran into an error when trying to make the objects change their material if they were being selected. What I wanted was for objects to highlight when I was looking at them. But now I have gotten a null reference exception from my outline material script. The error comes from line 26 which has some code that I just barely understand. So what is wrong with my code?

    Here is my raycast pickup script (very repetitive, but i am pretty new to coding) just in case it helps, but again, the problem is with the other script:

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class RaycastPickup : MonoBehaviour
    7. {
    8.     public float grabDistance;
    9.     public Transform face;
    10.     public LayerMask objectLayer;
    11.  
    12.     public RaycastHit hitInfo;
    13.     public bool isSelecting;
    14.  
    15.     private void Start()
    16.     {
    17.         isSelecting = false;
    18.     }
    19.  
    20.     private void Update()
    21.     {
    22.         Ray ray = new Ray(face.position, face.forward);
    23.  
    24.         if(Physics.Raycast(ray, out hitInfo, grabDistance, objectLayer))
    25.         {
    26.             isSelecting = true;
    27.         }
    28.         else
    29.         {
    30.             Debug.Log("NOT SELECTING");
    31.             isSelecting = false;
    32.         }
    33.  
    34.         if(Input.GetKeyDown(KeyCode.E) && Physics.Raycast(ray, out hitInfo, grabDistance, objectLayer))
    35.         {
    36.             if(hitInfo.collider.gameObject.tag == "Branch" && hitInfo.collider.gameObject.GetComponent<SingleBranchDetection>().touchedGround == true)
    37.             {
    38.                 Destroy(hitInfo.collider.gameObject);
    39.                 BranchCounter.branchValue++;
    40.                 HoldingLogic.branchNum++;
    41.             }
    42.             else if (hitInfo.collider.gameObject.tag == "BranchPile")
    43.             {
    44.                 Destroy(hitInfo.collider.gameObject);
    45.                 BranchCounter.branchValue += 3;
    46.                 HoldingLogic.branchNum += 3;
    47.             }
    48.             else if (hitInfo.collider.gameObject.tag == "Grass" && hitInfo.collider.gameObject.GetComponent<SingleGrassDetection>().touchedGround == true)
    49.             {
    50.                 Destroy(hitInfo.collider.gameObject);
    51.                 GrassCounter.GrassValue++;
    52.                 HoldingLogic.grasssNum++;
    53.             }
    54.             else if (hitInfo.collider.gameObject.tag == "Rock" && hitInfo.collider.gameObject.GetComponent<SingleRockDetection>().touchedGround == true)
    55.             {
    56.                 Destroy(hitInfo.collider.gameObject);
    57.                 RockCounter.rockValue++;
    58.                 HoldingLogic.rockNum++;
    59.             }
    60.             else if (hitInfo.collider.gameObject.tag == "RockPile")
    61.             {
    62.                 Destroy(hitInfo.collider.gameObject);
    63.                 RockCounter.rockValue += 3;
    64.                 HoldingLogic.rockNum += 3;
    65.             }
    66.         }
    67.  
    68.         if(Physics.Raycast(ray, out hitInfo, grabDistance, objectLayer))
    69.         {
    70.             Debug.DrawLine(ray.origin, hitInfo.point, Color.red);
    71.         }
    72.         else
    73.         {
    74.             Debug.DrawLine(ray.origin, ray.origin + ray.direction * grabDistance, Color.green);
    75.         }
    76.     }
    77. }
    And here is my highlight script(it's called outline, but I will change that to highlight later). I know the parts where I am making the gameobject called "object" just a this.gameobject is a little unnecessary, but I was just trying to figure out what the problem was:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class OutliningScript : MonoBehaviour
    6. {
    7.     public Material Noramal;
    8.     public Material Selected;
    9.     MeshRenderer meshRen;
    10.  
    11.     GameObject playerObject;
    12.     GameObject Object;
    13.  
    14.     private void Start()
    15.     {
    16.         playerObject = GameObject.FindGameObjectWithTag("Player");
    17.  
    18.         meshRen = GetComponent<MeshRenderer>();
    19.         meshRen.material = Noramal;
    20.  
    21.         Object = this.gameObject;
    22.     }
    23.  
    24.     private void Update()
    25.     {
    26.         if (playerObject.GetComponent<RaycastPickup>().hitInfo.collider.gameObject == Object && playerObject.GetComponent<RaycastPickup>().isSelecting == true)
    27.         {
    28.             meshRen.material = Selected;
    29.         }
    30.         else
    31.         {
    32.             meshRen.material = Noramal;
    33.         }
    34.  
    35.         if (playerObject.GetComponent<RaycastPickup>().isSelecting == false)
    36.         {
    37.             meshRen.material = Noramal;
    38.         }
    39.     }
    40. }
    41.  
    Please either let me know how to fix this, or give me another way to do this. And if I ask for more specifics, then I am sorry, but I am still new and am trying to learn what everything does
     
  2. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    A null reference is what it states, it means that the object you are trying to access is null. In this case, either the playerObject isn't being populated in the start method or the object it is finding doesn't have a RaycastPickup script on it.

    I can see a few problems with your logic at the moment though. You don't want to be performing a getcomponent call on every highlightable object each frame and if your code didn't have a null reference, every object would highlight when the player looked at one of them.

    I would suggest using an interface on each collectible object and when the raycast hits one, it checks to see if it isn't already looking at that object. If it isn't, it deselects the last object it looked at if there is one and highlights a new one if it did.

    Then, if your press E and there is currently an object being looked at, it calls a method on that object to pick it up and that object can be responsible for which counters it increments in the player inventory.
     
    JacobCompbook likes this.
  3. JacobCompbook

    JacobCompbook

    Joined:
    Jan 25, 2020
    Posts:
    4
    So I took what you said and applied a script for every object so that not every object gets highlighted, and removed a lot of the unnecessary get components. But now I can tell that I get a null reference exception when ever my raycast collider isn't colliding with anything, like when I look into the air or when the object is out of my reaching distance. What is the solution for this problem? Here is the updated raycast script, the error comes from line 33:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class RaycastPickup : MonoBehaviour
    6. {
    7.     public float grabDistance;
    8.     public Transform face;
    9.  
    10.     public static RaycastHit hitInfo;
    11.  
    12.     public static GameObject lookingAt;
    13.     public static bool isSelecting;
    14.  
    15.     private void Start()
    16.     {
    17.         isSelecting = false;
    18.     }
    19.  
    20.     private void Update()
    21.     {
    22.         Ray ray = new Ray(face.position, face.forward);
    23.  
    24.         if(Physics.Raycast(ray, out hitInfo, grabDistance))
    25.         {
    26.             isSelecting = true;
    27.         }
    28.         else
    29.         {
    30.             isSelecting = false;
    31.         }
    32.  
    33.         if(hitInfo.collider.gameObject != null)
    34.         {
    35.             lookingAt = hitInfo.collider.gameObject;
    36.         }else if(hitInfo.collider.gameObject == null)
    37.         {
    38.             return;
    39.         }
    40.  
    41.         if(Input.GetKeyDown(KeyCode.E) && Physics.Raycast(ray, out hitInfo, grabDistance))
    42.         {
    43.             if(hitInfo.collider.gameObject.tag == "Branch" && hitInfo.collider.gameObject.GetComponent<SingleBranchDetection>().touchedGround == true)
    44.             {
    45.                 Destroy(hitInfo.collider.gameObject);
    46.                 BranchCounter.branchValue++;
    47.                 HoldingLogic.branchNum++;
    48.             }
    49.             else if (hitInfo.collider.gameObject.tag == "BranchPile")
    50.             {
    51.                 Destroy(hitInfo.collider.gameObject);
    52.                 BranchCounter.branchValue += 3;
    53.                 HoldingLogic.branchNum += 3;
    54.             }
    55.             else if (hitInfo.collider.gameObject.tag == "Grass" && hitInfo.collider.gameObject.GetComponent<SingleGrassDetection>().touchedGround == true)
    56.             {
    57.                 Destroy(hitInfo.collider.gameObject);
    58.                 GrassCounter.GrassValue++;
    59.                 HoldingLogic.grasssNum++;
    60.             }
    61.             else if (hitInfo.collider.gameObject.tag == "Rock" && hitInfo.collider.gameObject.GetComponent<SingleRockDetection>().touchedGround == true)
    62.             {
    63.                 Destroy(hitInfo.collider.gameObject);
    64.                 RockCounter.rockValue++;
    65.                 HoldingLogic.rockNum++;
    66.             }
    67.             else if (hitInfo.collider.gameObject.tag == "RockPile")
    68.             {
    69.                 Destroy(hitInfo.collider.gameObject);
    70.                 RockCounter.rockValue += 3;
    71.                 HoldingLogic.rockNum += 3;
    72.             }
    73.         }
    74.  
    75.         if(Physics.Raycast(ray, out hitInfo, grabDistance))
    76.         {
    77.             Debug.DrawLine(ray.origin, hitInfo.point, Color.red);
    78.         }
    79.         else
    80.         {
    81.             Debug.DrawLine(ray.origin, ray.origin + ray.direction * grabDistance, Color.green);
    82.         }
    83.     }
    84. }
     
    Last edited: Jun 19, 2020
  4. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    Set the found object reference to null when it doesn't hit anything.
     
  5. JacobCompbook

    JacobCompbook

    Joined:
    Jan 25, 2020
    Posts:
    4
    Sounds like a great idea except for 1 problem, I don't know how to do that. I'm sorry if this is a beginner level thing, but I just don't know how to check if the raycast is even hitting an object. All I know is how to tell what game object it is colliding with. This is my first time ever doing raycast, so I don't know very much about them
     
  6. JacobCompbook

    JacobCompbook

    Joined:
    Jan 25, 2020
    Posts:
    4
    never mind, I was able to figure it out by changing line 33 to say just hitInfo.collider !- null, instead of hitInfo.collider.gameobject != null
     
  7. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    I didn't notice your code before and it isn't quite what I meant. If you implement an interface on your objects which can be looked at, you can deal with what they do when they interact there rather than have a lot of if statements and component lookups.

    Something like this, you have a lookable interface
    Code (CSharp):
    1. public interface ILookable
    2. {
    3.     void OnLook();
    4.     void OnLookAway();
    5.     void OnInteract();
    6. }
    Then you implement that interface on your objects which can be looked at
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Interactable : MonoBehaviour, ILookable
    4. {
    5.     public void OnInteract()
    6.     {
    7.         Debug.Log(gameObject.name + " Interacted with");
    8.         //You can deal with logic like this in the item you are interacting with
    9.         /*
    10.         Destroy(gameObject);
    11.         BranchCounter.branchValue++;
    12.         HoldingLogic.branchNum++;
    13.         */
    14.     }
    15.  
    16.     public void OnLook()
    17.     {
    18.         //Can be used for highlighting the object for example
    19.         Debug.Log(gameObject.name + " Looked At");
    20.     }
    21.  
    22.     public void OnLookAway()
    23.     {
    24.         //Could be used for removing the highlight
    25.         Debug.Log(gameObject.name + " Looked Away");
    26.     }
    27. }
    Then on your object which looks, usually the camera, you raycast and see if it finds an object which implements the interface
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class RayCastLookAt : MonoBehaviour
    5. {
    6.     [SerializeField]
    7.     private float _rayCastDistance = 10f;
    8.  
    9.     [SerializeField]
    10.     private Transform _rayCastObj;
    11.  
    12.     private ILookable _currentLookable;
    13.  
    14.     private void Start()
    15.     {
    16.         if(_rayCastObj == null)
    17.         {
    18.             _rayCastObj = transform;
    19.         }
    20.     }
    21.  
    22.     private void Update()
    23.     {
    24.         //check if we have a lookable object already
    25.         var haveLookable = _currentLookable != null;
    26.  
    27.         if (Physics.Raycast(_rayCastObj.position, _rayCastObj.forward, out RaycastHit hit, _rayCastDistance))
    28.         {
    29.             var lookable = hit.collider.gameObject.GetComponent<ILookable>();
    30.          
    31.             //If we found a lookable object
    32.             if (lookable != null)
    33.             {
    34.                 //Check we weren't just looking at something or aren't looking at the same object
    35.                 if (haveLookable && lookable != _currentLookable)
    36.                 {
    37.                     //Look away from current object
    38.                     _currentLookable.OnLookAway();
    39.                     //Set the current object we are looking at
    40.                     _currentLookable = lookable;
    41.                     //Look at the current object
    42.                     _currentLookable.OnLook();
    43.                     haveLookable = true;
    44.                 }
    45.                 else if (!haveLookable)
    46.                 {
    47.                     _currentLookable = lookable;
    48.                     _currentLookable.OnLook();
    49.                     haveLookable = true;
    50.                 }
    51.             }
    52.         }
    53.         //If we aren't looking at anything and we just were
    54.         else if (haveLookable)
    55.         {
    56.             //Call the Look away method
    57.             _currentLookable.OnLookAway();
    58.             //Set the current lookable to null
    59.             _currentLookable = null;
    60.             haveLookable = false;
    61.         }
    62.  
    63.         if (!haveLookable)
    64.         {
    65.             //Nothing to do if we aren't looking at anything so exit the method
    66.             return;
    67.         }
    68.  
    69.         //We are looking at something so interact with it if we press the E key
    70.         if(Input.GetKeyDown(KeyCode.E))
    71.         {
    72.             _currentLookable.OnInteract();
    73.         }
    74.     }
    75. }
    76.  
     
    Last edited: Jun 20, 2020