Sign in to follow this  

Unity Need help with a Random Number Generator in Unity C#

This topic is 402 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Greetings and Salutations!

 

I am hard at work on a mobile game project for IOS and have come to a crucial portion of development. The procedurally generated levels. My code is functional at the moment but it seems no matter what I try, I cannot get the Level layouts to vary from stage to stage. Ideally I would seed the RNG with the build index number(or another variable that varies stage to stage but is unique to each stage.) I want each stage's layout to appear random upon first encounter, and be the same each time the stage is entered. Below is the code I am using.  I am new to RNGs and am in my first year of programming, so definitely a novice. I have read up a bit about seeding, cryptographic RNG and various other techniques but everything I find online is outdated information that no longer works or at least I cannot get it to work in the current version of unity. Oh and as the title says I am using C#. Thank you for your help!

 

    public GameObject asteroid10hp;
    public GameObject asteroid20hp;
    public GameObject asteroid30hp;

 

    public int chance10hp;
    public int chance20hp;
    public int chance30hp;
 
    private GameObject chosenGameObject;

 

 

 void Start()

    {
        n = UnityEngine.Random.Range(1, 100);
    }
void SpawnUntilFull()
    {
        
        Transform freePosition = NextFreePosition();
        if (freePosition)
        { 
            
            MoveRandomly();
            RandomAngleGen();
            if (n <= chance30hp) { chosenGameObject = asteroid30hp; }
            else if (n <= chance20hp) { chosenGameObject = asteroid20hp; }
            else if (n <= chance10hp) { chosenGameObject = asteroid10hp; }
                if (n <= spawnChance)
                {
                    LevelManager.breakableCount += 1;
                    GameObject destructible = Instantiate(chosenGameObject, (freePosition.position + moved), Quaternion.Euler(0, 0, angle)) as GameObject;
                    destructible.transform.parent = freePosition;
                }
            
            if (NextFreePosition())
            {
                Invoke("SpawnUntilFull", spawnDelay);
            }
        }
    }
       Transform NextFreePosition()
        {
            foreach (Transform childPositionGameObject in transform)
            {
        
            if ((childPositionGameObject.childCount == 0) & (!childPositionGameObject.CompareTag("Attempted")))
                {
                    childPositionGameObject.tag = "Attempted";
                    return childPositionGameObject;
                }
            }
            return null;
        }

 

Share this post


Link to post
Share on other sites

I don't know very much about Unity, but I came across some difficulties with random numbers in C# too. Sometimes it generates the same numbers because you create a ton of random numbers during the same millisecond (with time-dependent random generators), or because you use the same seed over and over again, which leads to the same list of numbers.

When do you invoke Start()? When you start the game or when you start the level?

Edited by fedeTibaldo

Share this post


Link to post
Share on other sites

Probably UnityEngine.Random needs seeding with the system time at the start of the game. It might be seeding to a default value which is the same every execution. (The docs are surprisingly vague about this.)

Share this post


Link to post
Share on other sites

Probably UnityEngine.Random needs seeding with the system time at the start of the game. It might be seeding to a default value which is the same every execution. (The docs are surprisingly vague about this.)

This is my confusion. The docs seem to suggest that it seeds it to the system time but I am getting the same level layout at every level no matter when I load the levels. So it clearly is not doing that in the editor.Thank you, I will see if I can get it seeded with system time.(I'll be back if I can't figure it out)

Share this post


Link to post
Share on other sites

I wonder if it's something silly like it using the Editor's start time, as opposed to the time at which you hit Play in the editor.  I'd see if setting it yourself somewhere manually would be enough.

Share this post


Link to post
Share on other sites

There's a seeding function: https://docs.unity3d.com/ScriptReference/Random.InitState.html

 

Maybe I'm not understanding the question?

From the documentation it somewhat implies that it already does this:  Random.InitState(startupTime) as a default somewhere*.  But it doesn't appear to be so, at least according to the OP.  I haven't tested it myself.

 

Even the documentation is kind of hazy: 

 

 

The seed is normally set from some arbitrary value like the system clock before the random number functions are used. 

Share this post


Link to post
Share on other sites

Well I tried seeding with the following code:

 

 //Trying to seed with system time
        float timeF = Time.realtimeSinceStartup;
        int timeI = (int)timeF;
        UnityEngine.Random.InitState(timeI);
 
and I am still running it during Start() and it has no effect on the results. It is as if the seed is always the same no matter whether I try to seed it or not. I start the game, and move between levels and every level has the same pattern every time.

Share this post


Link to post
Share on other sites

Time.realtimeSinceStartup will be zero at the start of every playthrough - the hint is in the name.

 

You want to seed it with real world time, which will be different every time you run the game - something like System.Datetime.UtcNow.Ticks.

Share this post


Link to post
Share on other sites

Time.realtimeSinceStartup will be zero at the start of every playthrough - the hint is in the name.

 

You want to seed it with real world time, which will be different every time you run the game - something like System.Datetime.UtcNow.Ticks.

 

Well the tooltip for Time.realtimeSinceStartup says time in second since game start, should mean since the application was started, not the scene was loaded. Which is its current implementation to my knowledge. I read it very carefully actually. Thank you, I will try that.

Share this post


Link to post
Share on other sites

So I tried to seed with System.DateTime.UtcNow.Ticks and it does not work. I think I have stumbled onto the problem. I have my spawn chance set to 100. That can't be right. Since n cannot be greater than 100 given the random functions I am using, it should be filling every space in the level with objects. It is not, its only filling about half of them. What is going on? I have figured out that with my current code, if I put 30 or less in for the spawnChance only 1 object spawns. Same place, every time. If I put 31 or higher I get the same 15 spawns in the same places every time. This mystery is driving me nuts! What is wrong with my code? It is not the RNG I think. It's the code structure. Or perhaps both.

Share this post


Link to post
Share on other sites
It is not, its only filling about half of them.

 

You have an error in the selection/getting of free positions. You are tagging the game objects as attempted in cases where you shouldn't.

You have 2 calls to NextFreePosition. 1 of the calls is just to check if there is a free position, and the other is actually using that position.

In the first case, the game object should not be tagged as attempted.

I would suggest moving the tagging line to where the game object is actually attempted to spawn (inside the if (freePosition) conditional).

 

EDIT: That is...

if (freePosition)
{
    childPositionGameObject.tag = "Attempted"; //MOVED TO HERE
    //...other stuff here
}
Transform NextFreePosition()
{
    foreach Transform childPositionGameObject in transform
    {
        if ((childPositionGameObject.childCount == 0) & (!childPositionGameObject.CompareTag("Attempted")))
        {
            //childPositionGameObject.tag = "Attempted"; //REMOVED FROM HERE
            return childPositionGameObject;
        }
    }
    return null;
}
Edited by Lactose!

Share this post


Link to post
Share on other sites

Thank you Lactose! that is definitely helpful, and it was indeed an error. Thank you. Now when I set the spawnchance variable to 100, everything spawns as it should. However, the RNG problem from my last post does persist. 30 and below spawn 1 object, 31 and above spawn all. Same problem as before really. Some other error is being missed. Thanks for your help everyone.


There are 30 spots in the level for objects. I just realized that, I doubt that is a coincidence.

Share this post


Link to post
Share on other sites

The other big bug in your original code is that you only actually ask for a single random number to assign to 'n'.


This, but I'm curious what is in the functions "RandomAngleGen" and "MoveRandomly"? Post the code to those. Those two functions are affected by the random number generator and are responsible for generating the level. What is going on inside them? I suspect that the error, at least in part, might be in one of those functions.

Share this post


Link to post
Share on other sites

I actually eliminated those functions from the problem already by testing without them. They seem to work well actually. They have their own baked in RNG. They rotate the objects, and also move the objects randomly just a bit to create a more organic nature feel to the levels. Here is the updated code in total:

 

void Start()
    {
        ResetTags();
        SpawnUntilFull();
        RNG();
    }
        
    
   
    void SpawnUntilFull()
    {
        
        Transform freePosition = NextFreePosition();
        if (freePosition)
        {
            freePosition.tag = "Attempted";
            MoveRandomly();
            RandomAngleGen();
            ChooseRandomly();
                if (n <= spawnChance)
                {
                    LevelManager.breakableCount += 1;
                    GameObject destructible = Instantiate(chosenGameObject, (freePosition.position + moved), Quaternion.Euler(0, 0, angle)) as GameObject;
                    destructible.transform.parent = freePosition;
                }
            
            if (NextFreePosition())
            {
                Invoke("SpawnUntilFull", spawnDelay);
            }
        }
    }
        void RNG()
    {
        float timeF = System.DateTime.UtcNow.Ticks;
        int timeI = (int)timeF;
        UnityEngine.Random.InitState(timeI);
        n = UnityEngine.Random.Range(1, 101);
    }
        void ChooseRandomly()
    {
     
        if (n <= chance30hp) { chosenGameObject = asteroid30hp; }
        else if (n <= chance20hp) { chosenGameObject = asteroid20hp; }
        else if (n <= chance10hp) { chosenGameObject = asteroid10hp; }
    }
        void MoveRandomly()
        {
            float x = UnityEngine.Random.Range(-.4f, .4f);
            float y = UnityEngine.Random.Range(-.4f, .4f);
            moved = new Vector3(x, y, 0);
        }
        void RandomAngleGen()
    {
            angle = UnityEngine.Random.Range(0, 181);
        }
       
        Transform NextFreePosition()
        {
            foreach (Transform childPositionGameObject in transform)
            {
        
            if ((childPositionGameObject.childCount == 0) & (!childPositionGameObject.CompareTag("Attempted")))
                {
                    return childPositionGameObject;
                }
            }
            return null;
        }
 
    void OnDrawGizmos()
    {
        Gizmos.DrawWireCube(transform.position, new Vector3(width, height));
    }
    void ResetTags()
        {
            foreach (Transform childPositionGameObject in transform)
            {
                childPositionGameObject.tag = "Untagged";
            }
        }
 
    }
 
I created the RNG() function and only called it once because after my initial research that was something people mentioned as potentially causing the problem. Should I build in a fresh call in every loop of the main function?

Share this post


Link to post
Share on other sites

Initialise/seed the random number generator (in this case, UnityEngine.Random.InitState) exactly once. Ideally at the start of the program.

Ask the random number generator for a new value every time you need one.

 

Here, you have the Random.InitState and the Random.Range in the same RNG function, so it won't work. You end up basically just asking it for the system time whenever you call it.

 

And you still only ever set 'n' once so the outcome of ChooseRandomly is predetermined, and chosenGameObject will always be the same.

Share this post


Link to post
Share on other sites

Incredible! Its working perfectly. I have a splash screen where I create objects that are used throughout the game so I stuck a RNG initializing script onto that scene and I ask for a new number from RNG every time during the loop. It works like a charm! Thank you everyone for your help. This thread is done, please close it!

Share this post


Link to post
Share on other sites

This thread is done, please close it!

GDNet does not generally close threads.

Others reading this might still have questions, comments or suggestions -- closing threads would shut down the possibility of continuing the discussion even if the thread starter has achieved the results they want.

Share this post


Link to post
Share on other sites

This topic is 402 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Forum Statistics

    • Total Topics
      628732
    • Total Posts
      2984438
  • Similar Content

    • By INTwindwolf
      THE PROJECT

      INT is a 3D Sci-fi RPG with a strong emphasis on story, role playing, and innovative RPG features such as randomized companions. The focus is on the journey through a war-torn world with fast-paced combat against hordes of enemies. The player must accomplish quests like a traditional RPG, complete objectives, and meet lively crew members who will aid in the player's survival. Throughout the game you can side and complete missions through criminal cartels, and the two major combatants, the UCE and ACP, of the Interstellar Civil War.
      Please note that all of our current positions are remote work. You will not be required to travel.
      Talent Needed
       
      Unity Engine Programmer
      Website Administrator
      3D Animator
      We have made great strides in the year 2017! INT has received a comprehensive face-lift compared to the start of the year. We look forward to a productive, fruitful year 2018!
      Revenue-Share
      This is the perfect opportunity to get into the game development industry. Being an Indie team we do not have the creative restrictions often imposed by publishers or other third parties. We are extremely conscientious of our work and continuously uphold a high level of quality throughout our project.
      We are unable to offer wages or per-item payments at this time. However revenue-sharing from crowd-funding is offered to team members who contribute 15-20 hours per week to company projects, as well as maintain constant communication and adhere to deadlines. Currently the crowd-funding campaign is scheduled for the year 2018. Your understanding is dearly appreciated.
       
      Thank you for your time! We look forward to hearing from you!
       
      John Shen
      HR Lead
      Starboard Games LLC
    • By Apollo Cabrera
      Energy particles being harnessed by collection multi-hedron energy matrix. Whuuuttt?
      Love it :)
    • By AndySv
        Total Music Collection (http://u3d.as/Pxo)   THE COLLECTION CONTAINS:   Mega Game Music Collection   Universal Music Collection   Huge library of high quality music for any project! All at an incredibly low price!   - 2,5GB of high quality audio - 100+ different music tracks - Loop and short versions   Action, fantasy, casual, horror, puzzle, epic, dramatic, romantic, positive, inspiring, motivational and more!
    • By Dafu
      FES Retro Game Framework is now available on the Unity Asset Store for your kind consideration!
      FES was born when I set out to start a retro pixel game project. I was looking around for an engine to try next. I tried a number of things, from GameMaker, to Fantasy Consoles, to MonoGame and Godot and then ended up back at Unity. Unity is just unbeatable in it's cross-platform support, and ease of deployment, but it sure as heck gets in the way of proper retro pixel games!
      So I poured over the Unity pipeline and found the lowest levels I could tie into and bring up a new retro game engine inside of Unity, but with a completely different source-code-only, classic game-loop retro blitting and bleeping API. Months of polishing and tweaking later I ended up with FES.
      Some FES features:
      Pixel perfect rendering RGB and Indexed color mode, with palette swapping support Primitive shape rendering, lines, rectangles, ellipses, pixels Multi-layered tilemaps with TMX file support Offscreen rendering Text rendering, with text alignment, overflow settings, and custom pixel font support Clipping Sound and Music APIs Simplified Input handling Wide pixel support (think Atari 2600) Post processing and transition effects, such as scanlines, screen wipes, screen shake, fade, pixelate and more Deploy to all Unity supported platforms I've put in lots of hours into a very detail documentation, you can flip through it here to get an better glimpse at the features and general overview: http://www.pixeltrollgames.com/fes/docs/index.html
      FES is carefully designed and well optimized (see live stress test demo below). Internally it uses batching, it chunks tilemaps, is careful about memory allocations, and tries to be smart about any heavy operations.
      Please have a quick look at the screenshots and live demos below and let me know what you think! I'd love to hear some opinions, feedback and questions!
      I hope I've tickled your retro feels!



      More images at: https://imgur.com/a/LFMAc
      Live demo feature reel: https://simmer.io/@Dafu/fes
      Live blitting stress test: https://simmer.io/@Dafu/fes-drawstress
      Unity Asset Store: https://www.assetstore.unity3d.com/#!/content/102064

      View full story
    • By Dafu
      FES Retro Game Framework is now available on the Unity Asset Store for your kind consideration!
      FES was born when I set out to start a retro pixel game project. I was looking around for an engine to try next. I tried a number of things, from GameMaker, to Fantasy Consoles, to MonoGame and Godot and then ended up back at Unity. Unity is just unbeatable in it's cross-platform support, and ease of deployment, but it sure as heck gets in the way of proper retro pixel games!
      So I poured over the Unity pipeline and found the lowest levels I could tie into and bring up a new retro game engine inside of Unity, but with a completely different source-code-only, classic game-loop retro blitting and bleeping API. Months of polishing and tweaking later I ended up with FES.
      Some FES features:
      Pixel perfect rendering RGB and Indexed color mode, with palette swapping support Primitive shape rendering, lines, rectangles, ellipses, pixels Multi-layered tilemaps with TMX file support Offscreen rendering Text rendering, with text alignment, overflow settings, and custom pixel font support Clipping Sound and Music APIs Simplified Input handling Wide pixel support (think Atari 2600) Post processing and transition effects, such as scanlines, screen wipes, screen shake, fade, pixelate and more Deploy to all Unity supported platforms I've put in lots of hours into a very detail documentation, you can flip through it here to get an better glimpse at the features and general overview: http://www.pixeltrollgames.com/fes/docs/index.html
      FES is carefully designed and well optimized (see live stress test demo below). Internally it uses batching, it chunks tilemaps, is careful about memory allocations, and tries to be smart about any heavy operations.
      Please have a quick look at the screenshots and live demos below and let me know what you think! I'd love to hear some opinions, feedback and questions!
      I hope I've tickled your retro feels!



      More images at: https://imgur.com/a/LFMAc
      Live demo feature reel: https://simmer.io/@Dafu/fes
      Live blitting stress test: https://simmer.io/@Dafu/fes-drawstress
      Unity Asset Store: https://www.assetstore.unity3d.com/#!/content/102064
  • Popular Now