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

Question How to resolve the error NaN when using Range custom attribute?

Discussion in 'Scripting' started by Chocolade, Jun 30, 2023.

  1. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    916
    The propertyattribute code:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class GapRangeAttribute : PropertyAttribute
    5. {
    6.     public float minRange;
    7.     public float maxRange;
    8.     public float sensitivity;
    9.  
    10.     public GapRangeAttribute(float minRange, float maxRange, float sensitivity)
    11.     {
    12.         this.minRange = minRange;
    13.         this.maxRange = maxRange;
    14.         this.sensitivity = sensitivity;
    15.     }
    16. }
    17.  
    The propertydrawer script:

    Code (csharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. [CustomPropertyDrawer(typeof(GapRangeAttribute))]
    6. public class GapRangeDrawer : PropertyDrawer
    7. {
    8.     public float MinRange { get; set; }
    9.     public float MaxRange { get; set; }
    10.     public float Sensitivity { get; set; }
    11.  
    12.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    13.     {
    14.         if (property.propertyType == SerializedPropertyType.Float)
    15.         {
    16.             EditorGUI.BeginProperty(position, label, property);
    17.             EditorGUI.Slider(position, property, MinRange, MaxRange, label);
    18.             property.floatValue = RoundValue(property.floatValue);
    19.             EditorGUI.EndProperty();
    20.         }
    21.         else
    22.         {
    23.             EditorGUI.LabelField(position, label.text, "Use [GapRangeDrawer] with float values only!");
    24.         }
    25.     }
    26.  
    27.     private float RoundValue(float value)
    28.     {
    29.         return Mathf.Round(value / Sensitivity) * Sensitivity;
    30.     }
    31. }
    32.  
    and the mono script:

    Code (csharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. public class CubeSpawner : MonoBehaviour
    6. {
    7.     public GameObject cubePrefab;
    8.     public LayerMask terrainLayer;
    9.     [GapRange(0, 100, 0.1f)]
    10.     public float gap = 0f;
    11.     public float cameraMoveSpeed = 5f;
    12.     public float zoomSpeed = 5f;
    13.     public float rotationSpeed = 10f;
    14.  
    15.     private Vector3 lastCubePosition;
    16.     private bool isDragging = false;
    17.  
    18.     private void Update()
    19.     {
    20.         HandleCameraMovement();
    21.         HandleCameraZoom();
    22.         HandleCameraRotation();
    23.  
    24.         Vector3 mousePosition = Input.mousePosition;
    25.      
    26.         HandleRuntimeInput(mousePosition);
    27.     }
    28.  
    29.     private void HandleRuntimeInput(Vector3 mousePosition)
    30.     {
    31.         if (Input.GetMouseButtonDown(0))
    32.         {
    33.             isDragging = true;
    34.             Ray ray = Camera.main.ScreenPointToRay(mousePosition);
    35.             if (Physics.Raycast(ray, out RaycastHit hitInfo, Mathf.Infinity, terrainLayer))
    36.             {
    37.                 lastCubePosition = hitInfo.point;
    38.                 GenerateCube(lastCubePosition);
    39.             }
    40.         }
    41.         else if (Input.GetMouseButtonUp(0))
    42.         {
    43.             isDragging = false;
    44.         }
    45.         else if (isDragging && gap > 0f)
    46.         {
    47.             Ray ray = Camera.main.ScreenPointToRay(mousePosition);
    48.             if (Physics.Raycast(ray, out RaycastHit hitInfo, Mathf.Infinity, terrainLayer))
    49.             {
    50.                 if (Vector3.Distance(hitInfo.point, lastCubePosition) >= gap)
    51.                 {
    52.                     GenerateCubesBetween(lastCubePosition, hitInfo.point);
    53.                     lastCubePosition = hitInfo.point;
    54.                 }
    55.             }
    56.         }
    57.     }
    58.  
    59.     private void GenerateCube(Vector3 position)
    60.     {
    61.         float terrainHeight = Terrain.activeTerrain.SampleHeight(position);
    62.         Vector3 cubePosition = new Vector3(position.x, terrainHeight, position.z);
    63.         Instantiate(cubePrefab, cubePosition, Quaternion.identity);
    64.     }
    65.  
    66.     private void GenerateCubesBetween(Vector3 start, Vector3 end)
    67.     {
    68.         float distance = Vector3.Distance(start, end);
    69.         int numCubes = Mathf.RoundToInt(distance / gap);
    70.  
    71.         Vector3 direction = (end - start).normalized;
    72.         float stepSize = distance / numCubes;
    73.  
    74.         for (int i = 0; i < numCubes; i++)
    75.         {
    76.             Vector3 position = start + direction * (i * stepSize);
    77.             GenerateCube(position);
    78.         }
    79.     }
    80.  
    81.     private void HandleCameraMovement()
    82.     {
    83.         float horizontal = Input.GetAxis("Horizontal");
    84.         float vertical = Input.GetAxis("Vertical");
    85.  
    86.         Vector3 moveDirection = new Vector3(horizontal, 0f, vertical);
    87.         moveDirection.Normalize();
    88.  
    89.         transform.Translate(moveDirection * cameraMoveSpeed * Time.deltaTime, Space.World);
    90.     }
    91.  
    92.     private void HandleCameraZoom()
    93.     {
    94.         float zoom = Input.GetAxis("Mouse ScrollWheel");
    95.  
    96.         Vector3 zoomDirection = new Vector3(0f, 0f, zoom * zoomSpeed);
    97.         transform.Translate(zoomDirection, Space.Self);
    98.     }
    99.  
    100.     private void HandleCameraRotation()
    101.     {
    102.         if (Input.GetMouseButton(1))
    103.         {
    104.             float rotationX = Input.GetAxis("Mouse X") * rotationSpeed;
    105.             float rotationY = Input.GetAxis("Mouse Y") * rotationSpeed;
    106.  
    107.             transform.Rotate(Vector3.up, rotationX, Space.World);
    108.             transform.Rotate(Vector3.right, -rotationY, Space.Self);
    109.         }
    110.     }
    111. }
    112.  
    I know you can't divide by 0 , but how to resolve it by the code/s ?

    the problem is in the PropertyDrawer script the NaN error in the inspector is before I'm runing the game. I see that property.floatValue the floatValue is NaN so I tried to remove this line but it didn't change much:

    Code (csharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. [CustomPropertyDrawer(typeof(GapRangeAttribute))]
    6. public class GapRangeDrawer : PropertyDrawer
    7. {
    8.     public float MinRange { get; set; }
    9.     public float MaxRange { get; set; }
    10.     public float Sensitivity { get; set; }
    11.  
    12.     public GapRangeDrawer()
    13.     {
    14.         MinRange = 1f;
    15.         MaxRange = 100f;
    16.         Sensitivity = 0.1f;
    17.     }
    18.  
    19.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    20.     {
    21.         if (property.propertyType == SerializedPropertyType.Float)
    22.         {
    23.             EditorGUI.BeginProperty(position, label, property);
    24.             EditorGUI.Slider(position, property, MinRange, MaxRange, label);
    25.             //property.floatValue = RoundValue(property.floatValue);
    26.             EditorGUI.EndProperty();
    27.         }
    28.         else
    29.         {
    30.             EditorGUI.LabelField(position, label.text, "Use [GapRangeDrawer] with float values only!");
    31.         }
    32.     }
    33.  
    34.     private float RoundValue(float value)
    35.     {
    36.         return Mathf.Round(value / Sensitivity) * Sensitivity;
    37.     }
    38. }
    39.  
    The goal i to make a custom range slider attribute using it in the mono script using it in the editor inspector setting the range 0,100 and also to set the third parameter the sensitive of the slider movement in the inspector.
     
    Last edited: Jun 30, 2023
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,883
    Just check for values that would cause an error and guard against that?

    Or just check if your result is an NaN and return a default value instead.
     
    Chocolade likes this.
  3. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    916
    The problem is seems to be in the PropertyDrawer script the value of property.floatValue is NaN
    I tried to remove that line and also added constructor to set values for the min/max/sen but still getting NaN in the editor in the inspector. and the error NaN is before I'm running the game !

    Code (csharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. [CustomPropertyDrawer(typeof(GapRangeAttribute))]
    6. public class GapRangeDrawer : PropertyDrawer
    7. {
    8.     public float MinRange { get; set; }
    9.     public float MaxRange { get; set; }
    10.     public float Sensitivity { get; set; }
    11.  
    12.     public GapRangeDrawer()
    13.     {
    14.         MinRange = 1f;
    15.         MaxRange = 100f;
    16.         Sensitivity = 0.1f;
    17.     }
    18.  
    19.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    20.     {
    21.         if (property.propertyType == SerializedPropertyType.Float)
    22.         {
    23.             EditorGUI.BeginProperty(position, label, property);
    24.             EditorGUI.Slider(position, property, MinRange, MaxRange, label);
    25.             //property.floatValue = RoundValue(property.floatValue);
    26.             EditorGUI.EndProperty();
    27.         }
    28.         else
    29.         {
    30.             EditorGUI.LabelField(position, label.text, "Use [GapRangeDrawer] with float values only!");
    31.         }
    32.     }
    33.  
    34.     private float RoundValue(float value)
    35.     {
    36.         return Mathf.Round(value / Sensitivity) * Sensitivity;
    37.     }
    38. }
    39.  
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,883
    Probably the serialised value the property refers to has previously been set to NaN (potentially serialised NaN too?). You might want to remove the attribute and see what the default float drawer is showing, and fix that up as necessary.

    This is editor code so I'm not sure why this is surprising.
     
  5. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,528
    Well, your issue is that you defined those minRange, maxRange and sensitivity fields inside your attribute and then you kind of redefined MinRange, MaxRange and Sensitivity inside your PropertyDrawer. You seem to think that those are somehow related? They are not, at all ^^.

    An attribute is just pure metadata that is attached to the actual class and you would need to use reflection to access such metadata. Fortunately Unity's PropertyDrawer already provides you a field / property (attribute) that holds the PropertyAttribute instance. All you need to do is cast it to your actual type

    Code (CSharp):
    1. public class GapRangeDrawer : PropertyDrawer
    2. {
    3.     private GapRangeAttribute attr => (GapRangeAttribute)attribute;
    4.     // [ ... ]
    5.  
    Remove your "MinRange", "MaxRange" and "Sensitivity" properties and use
    attr.minRange
    ,
    attr.maxRange
    and
    attr.sensitivity
    instead.

    Note that a division by zero does not automatically result in a NaN value. Only
    0/0
    does. Since you never initialized your local properties, they were all zero by default. Therefore your value is set to 0 and since Sensitivity is also 0 you get NaN. Any nonzero value divided by zero would give you positive or negative infinity, depending on the sign of the number and the sign of the zero (yes the IEEE754 defines a signed zero for exactly this purpose).

    ps: when using integer values, you can not divide by zero at all as integers do not have the concept of infinity and also not the concept of NaN. Trying to divide two integers where the divisor is zero results in a DivisionByZero exception.
     
    spiney199 and KillDashNine like this.