1

I am trying to create a system in unity where a number is generated (with a range from 0 to 9) from 3 float inputs. This is to create a procedural game where being in the same location always gives the same output. Because of this, I don't just want to use an in-built random method, as it would give different results each time. However, I am struggling to create a method that can achieve this.

To summarise:

I want to be able to put in 3 values, e.g.:

[3.2344234, -44.33030, 0.22222]

And have the method come up with a number like:

[2]

But I don't want to use an in-built method.

This is what I have tried so far - It's a bit of a mess:

    public float randomNumber(float x, float y, float z)
    {
        float stage1 = (float)((Mathf.Sign(x + 512) / Mathf.Cos(z + 42) + Mathf.SmoothDampAngle(z,y,ref(z),x) * Mathf.Atan2(y - 15,z)) / 24 + 5 * 6)%7.2f;
        string stage2 = stage1.ToString();
        int stage3 = (int)stage2[5];
        float result = stage3;
        return result;
    }

Any suggestions to properly generate random numbers are most welcome.

Dpythonrobot
  • 46
  • 10

3 Answers3

2

If distribution is not that important, the following will do:

static int GetRandomNumber(float x, float y, float z)
{
    // convert the floats to byte arrays
    var b1 = BitConverter.GetBytes(x);
    var b2 = BitConverter.GetBytes(y);
    var b3 = BitConverter.GetBytes(z);

    // turn the byte arrays into integers
    var i1 = BitConverter.ToInt32(b1);
    var i2 = BitConverter.ToInt32(b2);
    var i3 = BitConverter.ToInt32(b3);

    // XOR the 3 integers
    var merged = i1 ^ i2 ^ i3;

    // get the positive value of the integer and do a modulo by 10
    // that will give you a value between 0 and 9
    var result = Math.Abs(merged) % 10;

    return result;
}

Same using unsafe:

static unsafe int GetRandomNumberUnsafe(float x, float y, float z)
{
    var i1 = *(int*)(&x);
    var i2 = *(int*)(&y);
    var i3 = *(int*)(&z);

    var merged = i1 ^ i2 ^ i3;

    var result = Math.Abs(merged) % 10;

    return result;
}

----- EDIT -----

Starting from .NET Core 2.2, you can also use HashCode.Combine:

static unsafe int GetRandomNumber(float x, float y, float z)
{
    var hash = HashCode.Combine(x, y, z);
    var result = Math.Abs(hash) % 10;
    return result;
}
AcidJunkie
  • 1,878
  • 18
  • 21
  • perhaps add a hashing step in between xor and modulo 10 to fix distribution: [MD5 example](https://stackoverflow.com/questions/26870267/generate-integer-based-on-any-given-string-without-gethashcode) – Tanque Sep 09 '21 at 07:29
  • When distribution is required, i'd rather take a different approach: create a byte array of 12 bytes size and copy the 3 float values into it. Use this byte array as input for the hash calculation. Reduce it to a positive 32-bit or 64-bit and from this value, do the modulo by 10 – AcidJunkie Sep 09 '21 at 10:08
  • Note that Unity uses .Net Framework and `HashCode.Combine` doesn't exist there – derHugo Sep 09 '21 at 14:25
1

Basically you can use

  • whatever hash algorithm you want that generates distributed values from the given input
  • use modulo on the hash in order to map it into your required range
  • since hashes might be negative values get the absolute value of the result

You could e.g. just do

public int randomNumber(float x, float y, float z)
{
    var hash = x.GetHashCode();
    hash = (hash * 397) ^ y.GetHashCode();
    hash = (hash * 397) ^ z.GetHashCode();
    return Mathf.Abs(hash % 10);
}

Or in newer version of Unity using .NET Framework 4.7.X you can actually use tuples and simply do e.g.

public int randomNumber(float x, float y, float z)
{
    return Mathf.Abs((x, y, z).GetHashCode() % 10);
}

Or as alternative assuming your values come from a Vector3 since you speak of a location you could also use your given Vector3 directly

public int randomNumber(Vector3 vector)
{
    return Mathf.Abs(vector.GetHashCode() % 10);
}
derHugo
  • 83,094
  • 9
  • 75
  • 115
0

Limit you map area for x, y and z axes and after that divide them to 10 as incremental numbers: For example x axis -100:100 freedom of movement. (30-(-30))/3=20 will give incremental number of x axis. Make an array of 10 imaginary boxes where different x, y and z location values will give different numbers according to your array. Your imaginary 3D object with cubes must include 10 members so 3x3x3 will give 9. Add one box(it is up to you) to one dimension and after that every increments step in one axis will change the number. If you want certain numbers will give always same result make as mod4(input_1)+mod4(input_2)+mod4(input_3)+mod2(input_1+input_2+input_3) may work. But I didn't check last formula, you should check it.