Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

OnTriggerEnter() cannot modify private variable?

Discussion in 'Scripting' started by Xhitman, Feb 17, 2020.

  1. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    The below script work fine, no error.
    Code (CSharp):
    1.  
    2. // if change affectedUnit private, null error
    3. public Transform affectedUnit;
    4. bool start=false;      
    5.     void FixedUpdate()
    6.     {
    7.         Vector3 d;    
    8.     if(start)
    9.              d =  affectedUnit.position; // Null  error  here
    10.    
    11.     }
    12.  
    13.     // Move the hit robot to another tile based on the affectedUnitDirection from this collider
    14.     void OnTriggerEnter(Collider other)
    15.     {
    16.        
    17.         RobotAI rbAIOther = other.transform.root.GetComponent<RobotAI>();      
    18.         if (!triggerUnitList.Contains(other.transform.root) && rbAIOther!=null &&!other.GetComponent<PushBackDetector>())
    19.         {  start = true;
    20.             affectedUnit = other.transform.root;          
    21.            }
    22.            
    23.         }
    24.     }
    25. }
    26.  
    But if I change "public Transform affectedUnit;" to
    "Transform affectedUnit",
    I will get null error on "d = affectedUnitDestination - affectedUnit.position;"
     
  2. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    That is because only public attributes are initialized automatically. You must initialize private attributes yourself.
     
  3. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    But the value is assigned by OnTriggerEnter(), how come it is still null?
     
  4. SmartMediaNL

    SmartMediaNL

    Joined:
    Sep 29, 2016
    Posts:
    77
    In multithread it can be that you set the bool start to true, then you get the fixed update called by an other thread and after that the effectedUint is set. Between does two lines the effectedUnit is null still.

    So turn start = true after effectedUinit is set.
    That way start is never true when effectedUnit still is null.

    Code (CSharp):
    1.        
    2. if (!triggerUnitList.Contains(other.transform.root) && rbAIOther!=null &&!other.GetComponent<PushBackDetector>())
    3.         {
    4.             affectedUnit = other.transform.root;        
    5.             start = true;
    6.            
    7.            }
     
  5. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    How do you know that start isn't set to true set anyplace else? Insert a Debug.Log() Statement before accessing the transform in FixedUpdate and another one in OnTriggerEnter() to see if OnTriggerEnter is really always called before it is evaluated.
     
  6. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    While this is technically possible, I wasn't aware that FixedUpdate() and OnTriggerEnter() can be called on anything except the main thread. Did this Change?
     
  7. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    Test. Still get null.
     
  8. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    Because "start" can only become true by the OnTriggerEnter().
     
  9. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    Show the full code:

    Code (CSharp):
    1.  
    2.     [Header("Prefab")]
    3.     [SerializeField] GameObject RE_PushBackDetectPrefab;
    4.  
    5.     [Header("Assign when created")]
    6.     public Vector3 pushDirection;
    7.  
    8.     //-------- private cause error, no idea why
    9.  
    10.     public bool startToMoveAffectedUnit = false;  
    11.     // the point the parent robot finish move/knock back animation
    12.     // Destory the dectector when arrive
    13.     public Vector3 destinationUnitEndMove;
    14.  
    15.     public RobotAI rbAIThis;
    16.     public Transform affectedUnit;
    17.     public Vector3 affectedUnitDestination;  
    18.     const float moveSpeed = 1;
    19.     const float HexCenterToCenter = 43.3f;
    20.  
    21.     public List<Transform> triggerUnitList = new List<Transform>();
    22.    
    23.     private void Start()
    24.     {
    25.         rbAIThis = transform.root.GetComponent<RobotAI>();
    26.  
    27.         // caculcate the destination this detector should be destoryed
    28.         Vector3 d = pushDirection * HexCenterToCenter;      
    29.         Transform t = TileManagement.GetTile(transform.root.position + d);
    30.         // if on chess board, pick tile center position
    31.         // otherwise, just pick the rough position
    32.         if (t == null)      
    33.             destinationUnitEndMove = transform.root.position + d;      
    34.         else
    35.             destinationUnitEndMove = t.position;
    36.        
    37.     }
    38.  
    39.     // Update is called once per frame
    40.     void FixedUpdate()
    41.     {
    42.         Vector3 d;
    43.         if (startToMoveAffectedUnit)
    44.         {
    45.            // based on hit robot
    46.              d =  affectedUnitDestination - affectedUnit.position;
    47.  
    48.             if (d.sqrMagnitude >= 2)
    49.                 affectedUnit.Translate(pushDirection,Space.World);
    50.             else
    51.             {              
    52.                 transform.parent = null;
    53.                 Destroy(gameObject);              
    54.             }
    55.  
    56.             // If affected unit is push outside chess board, we need to add checkpoint here
    57.  
    58.         }
    59.         else
    60.         {
    61.             // not affect any unit,
    62.             // wait the parent unit to return to destination, and then destory this detector
    63.             d = transform.root.position - destinationUnitEndMove;          
    64.             if (d.sqrMagnitude <= 2)
    65.             {
    66.                 transform.parent = null;
    67.                 Destroy(gameObject);              
    68.             }          
    69.         }      
    70.     }
    71.  
    72.     // Move the hit robot to another tile based on the affectedUnitDirection from this collider
    73.     void OnTriggerEnter(Collider other)
    74.     {
    75.        
    76.         RobotAI rbAIOther = other.transform.root.GetComponent<RobotAI>();
    77.         // avoid repeat trigger, avoid hit non robot, avoid hit other pushbackdetector, terrian
    78.         if (!triggerUnitList.Contains(other.transform.root) && rbAIOther!=null &&!other.GetComponent<PushBackDetector>())
    79.         {
    80.            
    81.  
    82.             affectedUnit = other.transform.root;          
    83.             triggerUnitList.Add(affectedUnit);
    84.            
    85.             Transform destinationTile = TileManagement.GetTile(affectedUnit.position + pushDirection*HexCenterToCenter);
    86.             if (destinationTile == null)          
    87.                 affectedUnitDestination = affectedUnit.position + pushDirection * HexCenterToCenter;                          
    88.             else
    89.                 affectedUnitDestination = destinationTile.position;          
    90.                
    91.                 // create another push back detector on affected unit
    92.                 // such that when the affected unit move to default tile, it can push back another unit
    93.                 GameObject obj = Instantiate(RE_PushBackDetectPrefab) as GameObject;
    94.             obj.GetComponent<PushBackDetector>().pushDirection = pushDirection;                
    95.             Physics.IgnoreCollision(obj.GetComponent<Collider>(), transform.GetComponent<Collider>());
    96.  
    97.             rbAIThis.IgnoreCollider(obj.GetComponent<Collider>());          
    98.             rbAIOther.IgnoreCollider(obj.GetComponent<Collider>());
    99.  
    100.             obj.transform.parent = rbAIOther.chest;
    101.             obj.transform.localPosition = Vector3.zero;
    102.  
    103.             startToMoveAffectedUnit = true;
    104.         }
    105.     }
    106. }
     
  10. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    make startToMoveAffectedUnit private and see if any other script is trying to access it. You'll immediately get an error it it does.
     
  11. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    Oh - and your variable d: In Fixed update change that line to

    Code (CSharp):
    1. Vector3 d = new Vector3(0,0,0);
     
  12. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    No error. the public are for debug only. I can see the value in inspector.

    My question is OnTriggerEner(), not about debug the script. The script run fine as I say at the start.

    The point I dont understand is how come OnTriggerEnter() cannot assign the a private variable inside the function.
     
  13. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    Change

    Code (CSharp):
    1. if (startToMoveAffectedUnit)
    to
    Code (CSharp):
    1. if (startToMoveAffectedUnit && affectedUnit)
    If the error still occurs, it is because you did not initialize d.
     
  14. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    of course, I can set a null check there, but it doesn't answer my question "Why OnTriggerEnter cannot set the private variable?"
    "The variable have to be public, such that null error do not show up"
     
  15. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    Did you try to initialize d? I'm theorizing that c# uses a copy to the structure instead of simply copying the reference if affectedUnit is private, and copies the reference if it it public. Since you are not initializing local variable d, this could lead to a null reference error you are seeing. Not from <affectedUnit>, but from <d>. That's why I want you to try the guard.
     
  16. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    I always do that, so I don't believe vector must be initialized. Anyway, I tested, same null error.
     
  17. ZO5KmUG6R

    ZO5KmUG6R

    Joined:
    Jul 15, 2010
    Posts:
    490
    Private variables can be set from OnTriggerEnter etc. Something is setting your start somewhere (maybe for some reason Unity has serialized it as true? Debug.Log(start) in Start()).

    A Vector3 is a struct, and structs cannot be null unless they are wrapped in a Nullable.
     
    Last edited: Feb 17, 2020
  18. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    Wow, null error disappear when I restart my computer
     
  19. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    Yes and no. As you mentioned, even vectors (along with ints, floats and some other structs) can be boxed (wrapped in an object); the issue here was a surprising error, and we were discussing (at the end: guessing at) possible causes. I don't know how Unity's C# Compiler / optimizer handles all cases (i.e. if there is some implied boxing going on behind the scenes), and when hard pressed, that was the best I could come up with. As it turns out, restarting Unity seems to have resolved the issue. So all theorizing turned out to be just that: idle speculation. But, having written my own share of compilers, I hope not entirely meritless speculation :)
     
  20. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    not restart unity, but restart computer. I spend half day to deal with this weird error.:eek:
     
  21. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    I wonder is it because 2019 version, I have never experienced such "unassigned transform" in 2017,2018 version
     
  22. darkesco

    darkesco

    Joined:
    Mar 19, 2015
    Posts:
    61
    Same issue!. Driving me mad. I have 10 debug.log's for a bool IsProjectileDataRecieved. Followed it all the way down the OnTrigger. It sets it to True, then FixedUpdate reads it as False. Restarting computer.
     
  23. darkesco

    darkesco

    Joined:
    Mar 19, 2015
    Posts:
    61
    VS 2017 and Unity were out of sync. I don't know why, but Unity was executing Debug.Log code that I deleted (along with other deleted statements). I double-clicked the console messages and it opened up to an empty line of code. Seems to be working now.