Search Unity

More efficient way of randomising using multiple different game objects?

Discussion in 'Scripting' started by Pocknell-r251, Jun 29, 2020.

  1. Pocknell-r251

    Pocknell-r251

    Joined:
    Jun 19, 2020
    Posts:
    16
    I'm trying to set up a randomisation feature for a player character creation menu in a 2D game.
    I've managed to set it up for changing the hair colour using the code bellow, but in order to get both the hair and eye colour to change, I needed to create two separate scripts for both the HairManager and the EyeManager as for some reason it would only change the eye colour as both HairManager and EyeManager has the same script. I thought this would just work, but obviously not.
    My question is, is there a more efficient way of randomising elements of an array that all come from the same script, or am I going to have to make separate scripts for shirt, boots, body, legwear, etc?

    This is the code for the randomise button, with comments so you can see what I'm trying to do.

    Code (CSharp):
    1. public class Randomize : MonoBehaviour
    2. {
    3.     private ChangingBit changeBit; // This is a reference to the script that's attached to all the "bits" of a character, such as the shirt, body, etc. This is has arrays inside of it as shown further below.
    4.     private ChangingColor changeColor; // This references the changing of the eyecolor, which was previously added to the hair manager as well
    5.     private HairChangeColor changeHairColor; // This references a new script that I had to make separate from the eye color, to attach to the hair manager separately
    6.  
    7.     private void Start()
    8.     {
    9.         changeBit = FindObjectOfType<ChangingBit>();
    10.         changeColor = FindObjectOfType<ChangingColor>();
    11.         changeHairColor = FindObjectOfType<HairChangeColor>();
    12.     }
    13.  
    14.     public void randomize()
    15.     {
    16.         changeColor.red.value = Random.Range(0f, 1f);
    17.         changeColor.green.value = Random.Range(0f, 1f); // These three change eye color
    18.         changeColor.blue.value = Random.Range(0f, 1f);
    19.  
    20.         changeHairColor.red.value = Random.Range(0f, 1f);
    21.         changeHairColor.green.value = Random.Range(0f, 1f); /These three change hair color
    22.         changeHairColor.blue.value = Random.Range(0f, 1f);
    23.  
    24.         changeBit.index = Random.Range(0, changeBit.options.Length); // For some reason this only changes the boots, but not legwear, shirt, body or hair shape or anything else that the ChangingBit script is attached to, so will I have to make a separate script for each bit??
    25.         changeBit.textIndex = changeBit.index;
    26.     }
    27. }
    This is the script for changing color (which is attached to the eyes) in case anyone needs it for further understanding. The same script is copy/pasted into a separate script which I've attached to hair. Previously both hair and eye color were on this script, until the randomise button wouldn't randomise either of them.

    Code (CSharp):
    1. public class ChangingColor : MonoBehaviour
    2. {
    3.     public SpriteRenderer color1;
    4.     public SpriteRenderer color2;
    5.  
    6.     public Slider red;
    7.     public Slider green;
    8.     public Slider blue;
    9.  
    10.     private void Start()
    11.     {
    12.         red.value = 0.5f;
    13.         green.value = 0.35f;
    14.         blue.value = 0.25f;
    15.     }
    16.  
    17.     public void OnEdit()
    18.     {
    19.         Color colorSet1 = color1.color;
    20.         Color colorSet2 = color2.color;
    21.  
    22.         colorSet1.r = red.value;
    23.         colorSet1.g = green.value;
    24.         colorSet1.b = blue.value;
    25.  
    26.         colorSet2.r = red.value;
    27.         colorSet2.g = green.value;
    28.         colorSet2.b = blue.value;
    29.  
    30.         color1.color = colorSet1;
    31.         color2.color = colorSet2;
    32.     }
    33. }
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    Your main issue I think is that your Randomize class uses FindObjectOfType<> to get your instances of these components. You can think of that function as essentially returning a random object of that type. So yeah, breaking it up into multiple classes will bandaid your problem, but it's not a good solution. The real solution is to use an alternative to FindObjectOfType. Maybe use the plural version that returns an array of all such objects of that type: https://docs.unity3d.com/ScriptReference/Object.FindObjectsOfType.html

    Then you can iterate over them all and randomize all of them.
     
    lordofduct likes this.
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    Definitely don't use FindObjectOfType, it's slow.

    Furthermore, the way your code works you're indirectly creating singletons with out properly enforcing them. Basically "Randomize" will only reference the FIRST ChangingBit, ChangingColor, and HairChangeColor in your scene. I have no idea what these classes represent... but lets just say they represe a single character in the game and the colours of their hair, eyes, etc... well then you can have ONLY ONE character in your seen that gets randomized.

    Instead make these three variables a member of Randomize and set them to the appropriate targets via the inspector (or other means if you create your characters on the fly at runtime).

    Also, 'Randomize' is a terrible class name. Randomize what?

    And finally, when generating random colors, just setting random rgb values actually creates odd results just due to the colour space that the rgb values represent. You can get better color results selecting from HSV due to the shape of the colour space:


    Using Color.HSVToRGB you can generate values better:
    https://docs.unity3d.com/ScriptReference/Color.HSVToRGB.html

    Basically you create 3 random floats
    H - hue
    S - saturation
    V - value

    By doing this you orient to this cylinder in the image and what you're doing is:
    H - generating the unsaturated color (red, blue, green, etc)
    S - selecting the saturation, or intensity, of the color
    V - selecting the value, or "lightness/darkness"

    By doing it like this you're "odds" generated by the random number generator come out with better colours. It also allows you control... like say you never want to see black... ok, generate V from 0.5->1.0, rather than 0->1.0. If you never want white, keep the S above like 0.2ish... or whatever values you like as ranges.

    If you did this in RGB it's way harder to suss out the "ranges" from r,g,b... if you blocked values < 0.25 for instance, well then you'd never get a colour that lacked any red (like a blue-green) or one that lacked any green (like a purple).
     
  4. Pocknell-r251

    Pocknell-r251

    Joined:
    Jun 19, 2020
    Posts:
    16
    Not a clue. Will come back to it tomorrow. Thnx x
     
  5. Pocknell-r251

    Pocknell-r251

    Joined:
    Jun 19, 2020
    Posts:
    16
    If anyone else stumbles across this whilst thinking similar wrongnesses, the answer is to use a list, and to avoid FindObjectsOfType entirely.

    Check out this tutorial for more infomation