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. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Ignoring range of float values while creating random floats

Discussion in 'Scripting' started by DevAigina, Jun 6, 2018.

  1. DevAigina

    DevAigina

    Joined:
    Jun 6, 2017
    Posts:
    29
    Hello everyone !
    First of all i made a research about this topic , i found some solutions but i couldn't make them work.
    Here is my question ;
    I have player that always stays at center of the screen. My goal is creating enemies around player at random positions.
    But i want to avoid positions that near player , so i believe the solution for this : Ignore a range of values that near to player while creating random numbers.
    I am trying to create random x and y values and return them with Vector2
    Here is my script that tries to create enemies at random positions around player ;

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. /// <summary>
    5. /// Spawns hazards / enemies at the random off-screen point
    6. /// </summary>
    7. class HazardManager : MonoBehaviour
    8. {
    9.     [ Header( "Public Properties" ) ]
    10.     public float              createRate     = 0.0f;
    11.     public List< GameObject > hazardPrefabs  = new List< GameObject >();
    12.  
    13.     /* Static declarations */
    14.     public static System.Random rndS      = new System.Random();
    15.     public static bool  continueCreate    = true;
    16.  
    17.     /// <summary>
    18.     /// Unity's callback function that will run on scene's start event
    19.     /// </summary>
    20.     private void Start()
    21.     {
    22.         StartCoroutine( "CreateHazard" );
    23.     }
    24.  
    25.     /// <summary>
    26.     /// Creates a hazard at random off-screen point
    27.     /// </summary>
    28.     private System.Collections.IEnumerator CreateHazard()
    29.     {
    30.         while( continueCreate )
    31.         {
    32.             Vector2 rndPos = GetRandomPoint();
    33.             GameObject tmp = ChooseHazardToCreate();
    34.             Instantiate( tmp , rndPos , Quaternion.identity );
    35.             yield return new WaitForSeconds( createRate );
    36.         }
    37.     }
    38.  
    39.     /// <summary>
    40.     /// Chooses random game object from list
    41.     /// <para> Returns : GameObject </para>
    42.     /// </summary>
    43.     private GameObject ChooseHazardToCreate()
    44.     {
    45.         int i = rndS.Next( hazardPrefabs.Count );
    46.         return ( hazardPrefabs[ i ] );
    47.     }
    48.  
    49.     /// <summary>
    50.     /// Chooses a random point at off-screen
    51.     /// <para> Returns : Random point </para>
    52.     /// </summary>
    53.     private Vector2 GetRandomPoint()
    54.     {
    55.         /* IgnoreX = ( -4 , 4 ) , IgnoreY = ( 6 , -6 ) */
    56.  
    57.         return ( Vector2.zero );
    58.     }
    59. }
    I need to ignore values for x -> between -4 and 4 ( inclusive )
    I need to ignore values for y -> between 6 and -6 ( inclusive )
    By the way game is 2D top-down game.
    Thanks for any help , have a nice day :)
     
  2. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    There's probably a cleaner way to write it, but just off the top of my head:

    Code (csharp):
    1. float maxDistance = 100f;  //set to whatever you want the maximum x or y distance
    2.  
    3. float randomX = Random.Range(4f, maxDistance);
    4. float randomY = Random.Range(6f, maxDistance);
    5.  
    6. int randomInt = Random.Range(0, 2);  //returns a 0 or 1
    7. if (randomInt == 0)  //if 0 lets make randomX a negative value
    8. {
    9.     randomX -= (2f * randomX);
    10. }
    11. randomInt = Random.Range(0, 2);
    12. if (randomInt == 0)  //if 0 lets make randomY a negative value
    13. {
    14.     randomY -= (2f * randomY);
    15. }
    16.  
    17. Vector2 randomPosition = new Vector2(randomX, randomY);  //random point relative to the player
    18. //X = -100f to -4f, 4f to 100f
    19. //Y = -100f to -6f, 6f to 100f
     
    DevAigina likes this.
  3. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    here is my two cents, not sure if i did while loop correctly.

    Code (CSharp):
    1. private Vector2 GetRandomPoint()
    2. {
    3.     /* IgnoreX = ( -4 , 4 ) , IgnoreY = ( 6 , -6 ) */
    4.     float x = GetRandomFloat(-Screen.width, Screen.width,4);
    5.     float y = GetRandomFloat(-Screen.height, Screen.height,6);
    6.     return new Vector2(x,y);
    7. }
    8.  
    9. private float GetRandomFloat(float min, float max, int ignorInt)
    10. {
    11.     float tempFloat = Random.Range(min,max);
    12.     while(tempFloat < -ignorInt || tempFloat > ignorInt)
    13.     {
    14.         tempFloat = Random.Range(min,max);
    15.     }
    16.     return tempFloat;
    17. }
     
    DevAigina likes this.
  4. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Yeah I thought of doing it johne5's way (just got the short circuit reference), but it is technically possible to get locked in the while loop for a long time if unlucky enough to consistently get random numbers returned in the outlawed range. Incredibly small chance of course though.
     
  5. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    Yep, that's the part I was worried about too. getting stuck in the loop is bad news.
    Also, I used screen.width and height, these will not work for the real game. the OP will need to change these values to the real size of his world.
     
  6. DevAigina

    DevAigina

    Joined:
    Jun 6, 2017
    Posts:
    29
    Hi
    @Joe-Censored I tried your code , it works really well but i don't understand why you are multiplying randomX or randomY with 2f.
    @johne5 The code you suggested was my first solution and then i realized it can be endless loop so i didn't use it.

    I will post the last state of code here so it might help someone who tries to get same thing like me ;
    Code (CSharp):
    1.  
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. /// <summary>
    6. /// Spawns hazards / enemies at the random off-screen point
    7. /// </summary>
    8. class HazardManager : MonoBehaviour
    9. {
    10.     [ Header( "Public Properties" ) ]
    11.     public float              createRate     = 0.0f;
    12.     public List< GameObject > hazardPrefabs  = new List< GameObject >();
    13.    
    14.     /* Static declarations */
    15.     public static System.Random rndS      = new System.Random();
    16.     public static bool  continueCreate    = true;
    17.  
    18.     private const float maxDistance       = 14.0f;
    19.  
    20.     /// <summary>
    21.     /// Unity's callback function that will run on scene's start event
    22.     /// </summary>
    23.     private void Start()
    24.     {
    25.         StartCoroutine( "CreateHazard" );
    26.     }
    27.  
    28.     /// <summary>
    29.     /// Creates a hazard at random off-screen point
    30.     /// </summary>
    31.     private System.Collections.IEnumerator CreateHazard()
    32.     {
    33.         while( continueCreate )
    34.         {
    35.             Vector2 rndPos = GetRandomPoint();
    36.             GameObject tmp = ChooseHazardToCreate();
    37.             Instantiate( tmp , rndPos , Quaternion.identity );
    38.             yield return new WaitForSeconds( createRate );
    39.         }
    40.     }
    41.  
    42.     /// <summary>
    43.     /// Chooses random game object from list
    44.     /// <para> Returns : GameObject </para>
    45.     /// </summary>
    46.     private GameObject ChooseHazardToCreate()
    47.     {
    48.         int i = rndS.Next( hazardPrefabs.Count );
    49.         return ( hazardPrefabs[ i ] );
    50.     }
    51.  
    52.     /// <summary>
    53.     /// Chooses a random point at off-screen while ignoring near points that near player
    54.     /// <para> Returns : Random point </para>
    55.     /// </summary>
    56.     private Vector2 GetRandomPoint()
    57.     {
    58.         /* IgnoreX = ( -4 , 4 ) , IgnoreY = ( 6 , -6 ) */
    59.  
    60.         float randomX = Random.Range( 4f , maxDistance );
    61.         float randomY = Random.Range( 6f , maxDistance );
    62.  
    63.         int   randomI = Random.Range( 0 , 2 );
    64.         if(   randomI == 0 )
    65.             randomX  -= ( 2.0f * randomX );
    66.  
    67.         randomI       = Random.Range( 0 , 2 );
    68.         if(   randomI == 0 )
    69.             randomY  -= ( 2.0f * randomY );
    70.  
    71.         Vector2 pos   = new Vector2( randomX , randomY );
    72.  
    73.         return ( pos );
    74.     }
    75. }
    Again thank you all for help :)
     
  7. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I'm doing that because an easy way to turn a positive number into a negative number is to subtract its value from itself twice.

    Start with 17 and turn it into -17

    17 - 17 -17 = -17

    Same as:
    -17 = 17 - (2 * 17)

    -x = x - (2 * x)

    So in C# terms with floats:
    randomX -= ( 2.0f * randomX );

    etc, etc
     
    DevAigina likes this.
  8. DevAigina

    DevAigina

    Joined:
    Jun 6, 2017
    Posts:
    29
  9. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    I did have a small flaw in my loop. here is the update code.
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. /// <summary>
    5. /// Spawns hazards / enemies at the random off-screen point
    6. /// </summary>
    7. class HazardManager : MonoBehaviour
    8. {
    9.     [Header("Public Properties")]
    10.     public float createRate = 0.0f;
    11.     public List<GameObject> hazardPrefabs = new List<GameObject>();
    12.  
    13.     /* Static declarations */
    14.     public static System.Random rndS = new System.Random();
    15.     public static bool continueCreate = true;
    16.  
    17.     /// <summary>
    18.     /// Unity's callback function that will run on scene's start event
    19.     /// </summary>
    20.     private void Start()
    21.     {
    22.         StartCoroutine("CreateHazard");
    23.     }
    24.  
    25.     /// <summary>
    26.     /// Creates a hazard at random off-screen point
    27.     /// </summary>
    28.     private System.Collections.IEnumerator CreateHazard()
    29.     {
    30.         while (continueCreate)
    31.         {
    32.             Vector2 rndPos = GetRandomPoint();
    33.             GameObject tmp = ChooseHazardToCreate();
    34.             Instantiate(tmp, rndPos, Quaternion.identity);
    35.             yield return new WaitForSeconds(createRate);
    36.         }
    37.     }
    38.  
    39.     /// <summary>
    40.     /// Chooses random game object from list
    41.     /// <para> Returns : GameObject </para>
    42.     /// </summary>
    43.     private GameObject ChooseHazardToCreate()
    44.     {
    45.         int i = rndS.Next(hazardPrefabs.Count);
    46.         return (hazardPrefabs[i]);
    47.     }
    48.  
    49.     /// <summary>
    50.     /// Chooses a random point at off-screen
    51.     /// <para> Returns : Random point </para>
    52.     /// </summary>
    53.     private Vector2 GetRandomPoint()
    54.     {
    55.         /* IgnoreX = ( -4 , 4 ) , IgnoreY = ( 6 , -6 ) */
    56.         float x = GetRandomFloat(-Screen.width, Screen.width, 4);
    57.         float y = GetRandomFloat(-Screen.height, Screen.height, 6);
    58.         return new Vector2(x, y);
    59.     }
    60.  
    61.     private float GetRandomFloat(float min, float max, int ignorInt)
    62.     {
    63.         float tempFloat = Random.Range(min, max);
    64.         while (-ignorInt > tempFloat && ignorInt < tempFloat)
    65.         {
    66.             tempFloat = Random.Range(min, max);
    67.         }
    68.         return tempFloat;
    69.     }
    70. }
     
    Joe-Censored and DevAigina like this.
  10. DevAigina

    DevAigina

    Joined:
    Jun 6, 2017
    Posts:
    29
    @johne5 But it still have chance to go endless right ?
     
  11. arfish

    arfish

    Joined:
    Jan 28, 2017
    Posts:
    777

    But this only works on positive numbers, why not just multiply with -1, or just use the minus sign? :)
    -17 - (-17) - (-17) = -17 + 2x17 = 17
     
  12. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    No reason. I'm not a big math guy and was just writing my original response off the top of my head. Either of those sound better.
     
  13. kru

    kru

    Joined:
    Jan 19, 2013
    Posts:
    452
    If the goal is to create a random vector that is within some min/max radius around the player, why not use polar coordinates?
    Code (csharp):
    1. private Vector3 GetRandomVectorAround(Vector3 center, float minRange, float maxRange)
    2. {
    3.     float angleDeg = Random.Range(0f, 360f);
    4.     float radius = Random.Range(minRange, maxRange);
    5.     // Assumes we want a vector on the XZ plane
    6.     Vector3 offset = new Vector3(Mathf.Cos(angleDeg), 0f, Mathf.Sin(angleDeg));
    7.     return center + offset * radius;
    8. }
    Or we can use unity's methods:
    Code (csharp):
    1.  
    2. private Vector3 GetRandomVectorAroundUsingUnitysMethods(Vector3 center, float minRange, float maxRange)
    3. {
    4.     Vector2 pointOnUnitCircle = Random.insideUnitCircle.normalized;
    5.     float radius = Random.Range(minRange, maxRange);
    6.     Vector3 pointOnUnitSphere = new Vector3(pointOnUnitCircle.x, 0f, pointOnUnitCircle.y);
    7.     return center + pointOnUnitSphere * radius;
    8. }
    9.  
     
    Last edited: Jun 7, 2018
    DevAigina likes this.
  14. DevAigina

    DevAigina

    Joined:
    Jun 6, 2017
    Posts:
    29
    @kru The goal is creating a random vector2 while ignoring points that near player.
    For example we can not create vector that contains a float number in range -> [-4 , 4] because it will be near player.
    So we need to ignore near float values while creating random vectors.
     
  15. kru

    kru

    Joined:
    Jan 19, 2013
    Posts:
    452
    Pure vector2 is even easier with the second method.
    Code (csharp):
    1. private Vector2 GetRandomVectorAround(Vector2 center, float minRange, float maxRange)
    2. {
    3.     Vector2 pointOnUnitCircle = Random.insideUnitCircle.normalized;
    4.     float radius = Random.Range(minRange, maxRange);
    5.     return center + pointOnUnitCircle * radius;
    6. }
    Call with GetRandomVectorAround(new Vector2(0, 0), 4, 10); and you'll be guaranteed a vector2 that is at least 4 units away from 0 (so outsize of [-4,4]), and, in this example case, within [-10,10].
     
    DevAigina likes this.
  16. DevAigina

    DevAigina

    Joined:
    Jun 6, 2017
    Posts:
    29
    @kru thanks for reply , it is very solid solution i believe