First day, got a question about script insurances.

I was messing around with controlling the aspects of a cube with a C# script and everything works as intended.

I attach the script to a 2nd cube and it end up with identical behavior even though the script is loaded with random number generation.

If I want to create a 2nd cube that uses the same script, how do I ensure that both cubes are running their own instance of that script?

EDIT: Sorry about the typo in the title, should be instances.

If you attach the script to each cube they are definitely running different instances. Most likely
whatever random number generator you are using is generating the same set of numbers each time. Are you seeding it somehow? Can you show the code?

They are separate instances. Some things that might have happened:

  • you made the fields that stored the chosen random number static, which means there is only one value shared among all instances; remove the static keyword.

  • you lucked out and identical random numbers are chosen

Forgive the noob code, I am 2 weeks in on C# and 1 day in on Unity.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Rotate : MonoBehaviour
{

    private int A_mod;
    private int B_mod;
    private int C_mod;

    private int A_cycles;
    private int B_cycles;
    private int C_cycles;

    private int A_count;
    private int B_count;
    private int C_count;

    private int A_invert;
    private int B_invert;
    private int C_invert;

    private float A_scale;
    private float B_scale;
    private float C_scale;

    private float A_scale_change;
    private float B_scale_change;
    private float C_scale_change;

    private int A_scale_D;
    private int B_scale_D;
    private int C_scale_D;

    private byte A_color;
    private byte B_color;
    private byte C_color;
    private byte D_color;

    private int A_color_step;
    private int B_color_step;
    private int C_color_step;
    private int D_color_step;

    private int A_color_D;
    private int B_color_D;
    private int C_color_D;
    private int D_color_D;

    private int A_color_temp;
    private int B_color_temp;
    private int C_color_temp;
    private int D_color_temp;

    private Color32 New_Color = new Color32();
    private MeshRenderer Color_renderer;

    private System.Random rnd = new System.Random();

    // Start is called before the first frame update
    void Start()
    {
        A_count = 1;
        A_cycles = rnd.Next(50, 200);
        A_mod = rnd.Next(1, 180);
        B_count = 1;
        B_cycles = rnd.Next(50, 200);
        B_mod = rnd.Next(1, 180);
        C_count = 1;
        C_cycles = rnd.Next(50, 200);
        C_mod = rnd.Next(1, 180);
        A_invert = (rnd.Next(3) - 1);
        B_invert = (rnd.Next(3) - 1);
        C_invert = (rnd.Next(3) - 1);
        A_scale_D = 1;
        B_scale_D = 1;
        C_scale_D = 1;
        A_scale = 1;
        B_scale = 1;
        C_scale = 1;
        A_scale_change = (rnd.Next(500, 1000) / 10000f);
        B_scale_change = (rnd.Next(500, 1000) / 10000f);
        C_scale_change = (rnd.Next(500, 1000) / 10000f);
        A_color_step = 1;
        A_color_D = 1;
        B_color_step = 1;
        B_color_D = 1;
        C_color_step = 1;
        C_color_D = 1;
        D_color_step = 1;
        D_color_D = 1;

        Color_renderer = GetComponent<MeshRenderer>();

        A_color = Convert.ToByte(rnd.Next(256));
        B_color = Convert.ToByte(rnd.Next(256));
        C_color = Convert.ToByte(rnd.Next(256));
        D_color = Convert.ToByte(rnd.Next(256));

        New_Color = new Color32(A_color, B_color, C_color, D_color);
    }

    // Update is called once per frame
    void Update()
    {
        New_Color = new Color32(Convert.ToByte(A_color), Convert.ToByte(B_color), Convert.ToByte(C_color), Convert.ToByte(D_color));

        Color_renderer.material.SetColor("_Color", New_Color);

        transform.localScale = new Vector3(A_scale, B_scale, C_scale);

        transform.Rotate(Vector3.up * A_mod * A_invert * Time.deltaTime);
        transform.Rotate(Vector3.right * B_mod * B_invert * Time.deltaTime);
        transform.Rotate(Vector3.forward * C_mod * C_invert * Time.deltaTime);

        A_scale = A_scale + (A_scale_change * A_scale_D);
        B_scale = B_scale + (B_scale_change * B_scale_D);
        C_scale = C_scale + (C_scale_change * C_scale_D);

        A_count++;
        B_count++;
        C_count++;

        A_color_temp = A_color + (A_color_step * A_color_D);
        B_color_temp = B_color + (B_color_step * B_color_D);
        C_color_temp = C_color + (C_color_step * C_color_D);
        D_color_temp = D_color + (D_color_step * D_color_D);

        if (A_color_temp < 0 || A_color_temp > 255)
        {
            A_color_D = A_color_D * -1;
            A_color_temp = A_color + (A_color_step * A_color_D);
            A_color_step = rnd.Next(1, 4);
        }
        A_color = Convert.ToByte(A_color_temp);
        if (B_color_temp < 0 || B_color_temp > 255)
        {
            B_color_D = B_color_D * -1;
            B_color_temp = B_color + (B_color_step * B_color_D);
            B_color_step = rnd.Next(1, 4);
        }
        B_color = Convert.ToByte(B_color_temp);
        if (C_color_temp < 0 || C_color_temp > 255)
        {
            C_color_D = C_color_D * -1;
            C_color_temp = C_color + (C_color_step * C_color_D);
            C_color_step = rnd.Next(1, 4);
        }
        C_color = Convert.ToByte(C_color_temp);
        if (D_color_temp < 0 || D_color_temp > 255)
        {
            D_color_D = D_color_D * -1;
            D_color_temp = D_color + (D_color_step * D_color_D);
            D_color_step = rnd.Next(1, 4);
        }
        D_color = Convert.ToByte(D_color_temp);

        if (A_scale > 5f || A_scale < .1f)
        {
            A_scale_D = A_scale_D * -1;
            A_scale = A_scale + (A_scale_change * A_scale_D);
            A_scale_change = (rnd.Next(500, 1000) / 10000f);
        }
        if (B_scale > 5f || B_scale < .1f)
        {
            B_scale_D = B_scale_D * -1;
            B_scale = B_scale + (B_scale_change * B_scale_D);
            B_scale_change = (rnd.Next(500, 1000) / 10000f);
        }
        if (C_scale > 5f || C_scale < .1f)
        {
            C_scale_D = C_scale_D * -1;
            C_scale = C_scale + (C_scale_change * C_scale_D);
            C_scale_change = (rnd.Next(500, 1000) / 10000f);
        }

        if (A_count == A_cycles)
        {
            A_count = 1;
            A_cycles = rnd.Next(50, 200);
            A_mod = rnd.Next(45, 180);
            A_invert = (rnd.Next(3) - 1);
        }
        if (B_count == B_cycles)
        {
            B_count = 1;
            B_cycles = rnd.Next(50, 200);
            B_mod = rnd.Next(45, 180);
            B_invert = (rnd.Next(3) - 1);
        }
        if (C_count == C_cycles)
        {
            C_count = 1;
            C_cycles = rnd.Next(50, 200);
            C_mod = rnd.Next(45, 180);
            C_invert = (rnd.Next(3) - 1);
        }
    }
}

More or less the code makes a cube go nuts, was just playing around with the controls seeing what I could get working.

When I put this on a 2nd cube it has identical everything.

OK, quick crash course on computer random numbers:

Random number generators aren’t truly random; they do complicated mathematical operations to produce a series of numbers that “look” random. But if you manage to get two random number generators in sync, then they will produce the same series of numbers forever.

To avoid getting in sync (or to guarantee it, if that’s what you want), you typically start off your random generator with a special value called the “seed”. Two generators that use the same algorithm and the same seed will produce the same output, but if you give them unrelated seeds their output will generally look unrelated.

If you want, when you create a new C# System.Random object, you can specify a seed value. (This is great if you want to make things happen the same every time, e.g. for debugging or so that your friend can play the same procedurally-generated level that you just played.)

If you don’t choose to specify a seed value, the system gives you one automatically…based on the current time.

This is generally a pretty good option, as it means if you quit the program and run it again, you will get a different seed. But if you create a bunch of copies of System.Random all at the same time, they tend to look the same.

Generating good seeds is tricky. Rather than trying to come up with a bunch of good seeds for a bunch of different random number generators controlling different parts of your game, usually it’s better to have just one random number generator that is shared by all the different things in your game that need random numbers. That way you only need one seed (and using the time is generally a fine way to get one, at least for gaming).

This is such a common thing to do, that Unity actually does it for you automatically: If you use UnityEngine.Random instead of System.Random, they have a single, globally-visible random number generator that they seed automatically and can be shared across your whole application.

But if you want to use System.Random instead, you probably want to use a single shared copy for everything that needs random numbers (unless you are trying to control the seed in some special way). The easiest way to do this is to put it in a static variable somewhere during your program’s initial start-up.

2 Likes

Take a look at the random documentation. Especially the
Instantiating the random number generator section

Otherwise, Unity offers Random.Range which may not have the same issue.

@Antistone beat me to it with the full details.

1 Like

Ahh so I was understanding the problem wrong from the start.

I don’t have any particular need to use System.Random, I was simply using what I already knew how to use.

I will research UnityEngine.Random and get familiar with it.

Thanks for the feedback :slight_smile: