From 5d4c1648247899cc05358d21ccdb827e77cf48d4 Mon Sep 17 00:00:00 2001 From: skyace65 Date: Sun, 17 Jan 2021 12:28:08 -0500 Subject: [PATCH] Add C# to random number generation page --- tutorials/math/random_number_generation.rst | 252 +++++++++++++++++++- 1 file changed, 239 insertions(+), 13 deletions(-) diff --git a/tutorials/math/random_number_generation.rst b/tutorials/math/random_number_generation.rst index 3f2c38252c3..8cfe898837a 100644 --- a/tutorials/math/random_number_generation.rst +++ b/tutorials/math/random_number_generation.rst @@ -42,26 +42,53 @@ In global scope, you can find a :ref:`randomize() once when your project starts to initialize the random seed.** Calling it multiple times is unnecessary and may impact performance negatively. -Putting it in your main scene script's ``_ready()`` method is a good choice:: +Putting it in your main scene script's ``_ready()`` method is a good choice: + +.. tabs:: + .. code-tab:: gdscript GDScript func _ready(): randomize() + .. code-tab:: csharp + + public override void _Ready() + { + GD.Randomize(); + } + + You can also set a fixed random seed instead using :ref:`seed() `. Doing so will give you *deterministic* results -across runs:: +across runs: + +.. tabs:: + .. code-tab:: gdscript GDScript func _ready(): seed(12345) # To use a string as a seed, you can hash it to a number. seed("Hello world".hash()) + .. code-tab:: csharp + + // To use a string as a seed, you can hash it to a number. + GD.Seed((ulong)"Hello world".Hash()); + When using the RandomNumberGenerator class, you should call ``randomize()`` on -the instance since it has its own seed:: +the instance since it has its own seed: + +.. tabs:: + .. code-tab:: gdscript GDScript var rng = RandomNumberGenerator.new() rng.randomize() + .. code-tab:: csharp + + var rng = new RandomNumberGenerator(); + rng.Randomize(); + Getting a random number ----------------------- @@ -71,7 +98,10 @@ random numbers in Godot. The function :ref:`randi() ` returns a random number between 0 and 2^32-1. Since the maximum value is huge, you most likely want to use the modulo operator (``%``) to bound the result between 0 and the -denominator:: +denominator: + +.. tabs:: + .. code-tab:: gdscript GDScript # Prints a random integer between 0 and 49. print(randi() % 50) @@ -79,6 +109,14 @@ denominator:: # Prints a random integer between 10 and 60. print(randi() % 51 + 10) + .. code-tab:: csharp + + // Prints a random integer between 0 and 49. + GD.Print(GD.Randi() % 50); + + // Prints a random integer between 10 and 60 + GD.Print(GD.Randi() % 51 + 10); + :ref:`randf() ` returns a random floating-point number between 0 and 1. This is useful to implement a :ref:`doc_random_number_generation_weighted_random_probability` system, among @@ -89,33 +127,64 @@ floating-point number between 0 and 1. Unlike :ref:`randf() ` which follows an uniform distribution, the returned number follows a `normal distribution `__. This means the returned -value is more likely to be around 0.5 compared to the extreme bounds (0 and 1):: +value is more likely to be around 0.5 compared to the extreme bounds (0 and 1): + +.. tabs:: + .. code-tab:: gdscript GDScript # Prints a normally distributed floating-point number between 0.0 and 1.0. var rng = RandomNumberGenerator.new() rng.randomize() print(rng.randfn()) + .. code-tab:: csharp + + // Prints a normally distributed floating-point number between 0.0 and 1.0. + var rng = new RandomNumberGenerator(); + rng.Randomize(); + GD.Print(rng.Randfn()); + :ref:`rand_range() ` takes two arguments ``from`` and ``to``, and returns a random floating-point number between ``from`` -and ``to``:: +and ``to``: + +.. tabs:: + .. code-tab:: gdscript GDScript # Prints a random floating-point number between -4 and 6.5. print(rand_range(-4, 6.5)) + .. code-tab:: csharp + + // Prints a random floating-point number between -4 and 6.5. + GD.Print(GD.RandRange(-4, 6.5)); + :ref:`RandomNumberGenerator.randi_range() ` takes two arguments ``from`` -and ``to``, and returns a random integer between ``from`` and ``to``:: +and ``to``, and returns a random integer between ``from`` and ``to``: + +.. tabs:: + .. code-tab:: gdscript GDScript # Prints a random floating-point number between -10 and 10. var rng = RandomNumberGenerator.new() rng.randomize() print(rng.randi_range(-10, 10)) + .. code-tab:: csharp + + // Prints a random floating-point number between -10 and 10. + var rng = new RandomNumberGenerator(); + rng.Randomize(); + GD.Print(rng.RandiRange(-10, 10)); + Get a random array element -------------------------- -We can use random integer generation to get a random element from an array:: +We can use random integer generation to get a random element from an array: + +.. tabs:: + .. code-tab:: gdscript GDScript var fruits = ["apple", "orange", "pear", "banana"] @@ -135,8 +204,34 @@ We can use random integer generation to get a random element from an array:: # We may get the same fruit multiple times in a row. return random_fruit + .. code-tab:: csharp + + string[] fruits = {"apple", "orange", "pear", "banana"}; + + public override void _Ready() + { + GD.Randomize(); + + for (int i = 0; i < 100; i++) + { + // Pick 100 fruits randomly. + GD.Print(get_fruit()); + } + } + + public string get_fruit() + { + string random_fruit = fruits[GD.Randi() % fruits.Length]; + // Returns "apple", "orange", "pear", or "banana" every time the code runs. + // We may get the same fruit multiple times in a row. + return random_fruit; + } + To prevent the same fruit from being picked more than once in a row, we can add -more logic to this method:: +more logic to this method: + +.. tabs:: + .. code-tab:: gdscript GDScript var fruits = ["apple", "orange", "pear", "banana"] var last_fruit = "" @@ -166,6 +261,38 @@ more logic to this method:: # The function will never return the same fruit more than once in a row. return random_fruit + .. code-tab:: csharp + + string[] fruits = {"apple", "orange", "pear", "banana"}; + public string lastFruit = ""; + + public override void _Ready() + { + GD.Randomize(); + + for (int i = 0; i < 100; i++) + { + // Pick 100 fruits randomly. + GD.Print(get_fruit()); + } + } + + public string GetFruit() + { + string RandomFruit = fruits[GD.Randi() % fruits.Length]; + while (RandomFruit == LastFruit) + { + // The last fruit was picked, try again until we get a different fruit. + random_fruit = fruits[GD.Randi() % fruits.Length]; + } + + LastFruit = RandomFruit; + + // Returns "apple", "orange", "pear", or "banana" every time the code runs + // The function will never return the same fruit more than once in a row. + return random_fruit; + } + This approach can be useful to make random number generation feel less repetitive. Still, it doesn't prevent results from "ping-ponging" between a limited set of values. To prevent this, use the :ref:`shuffle bag @@ -174,7 +301,10 @@ limited set of values. To prevent this, use the :ref:`shuffle bag Get a random dictionary value ----------------------------- -We can apply similar logic from arrays to dictionaries as well:: +We can apply similar logic from arrays to dictionaries as well: + +.. tabs:: + .. code-tab:: gdscript GDScript var metals = { "copper": {"quantity": 50, "price": 50}, @@ -196,6 +326,41 @@ We can apply similar logic from arrays to dictionaries as well:: # The same metal may be selected multiple times in succession. return random_metal + .. code-tab:: csharp + + struct MetalInfo + { + public MetalInfo(int quantity, int price) + { + this.quantity = quantity; + this.price = price; + } + public int quantity; + public int price; + } + + enum MetalType + { + copper, + silver, + gold, + } + Godot.Collections.Dictionary metals = new Godot.Collections.Dictionary + { + { MetalType.copper, new MetalInfo(50, 50) }, + { MetalType.silver, new MetalInfo(20, 150) }, + { MetalType.gold, new MetalInfo(3, 500) } + }; + + public override void _Ready() + { + GD.Randomize(); + for (int i = 0; i < 20; i++) + { + GD.Print(GetMetal()); + } + } + .. _doc_random_number_generation_weighted_random_probability: @@ -204,7 +369,10 @@ Weighted random probability The :ref:`randf() ` method returns a floating-point number between 0.0 and 1.0. We can use this to create a -"weighted" probability where different outcomes have different likelihoods:: +"weighted" probability where different outcomes have different likelihoods: + +.. tabs:: + .. code-tab:: gdscript GDScript func _ready(): randomize() @@ -226,6 +394,38 @@ floating-point number between 0.0 and 1.0. We can use this to create a # 5% chance of being returned. return "Rare" + .. code-tab:: csharp + + public override void _Ready() + { + GD.Randomize(); + + for (int i = 0; i < 100; i++) + { + GD.Print(GetItemRarity()); + } + } + + public string GetItemRarity() + { + float RandomFloat = GD.Randf(); + + if (RandomFloat < 0.8) + { + // 80% chance of being returned + return "common"; + } + if (RandomFloat > 0.95) + { + // 15% change of being returned + return "Uncommon"; + } + else + { + // 5% chance of being returned + return "rare"; + } + .. _doc_random_number_generation_shuffle_bags: "Better" randomness using shuffle bags @@ -238,7 +438,10 @@ could get the same fruit three or more times in a row. You can accomplish this using the *shuffle bag* pattern. It works by removing an element from the array after choosing it. After multiple selections, the array -ends up empty. When that happens, you reinitialize it to its default value:: +ends up empty. When that happens, you reinitialize it to its default value: + +.. tabs:: + .. code-tab:: gdscript GDScript var fruits = ["apple", "orange", "pear", "banana"] # A copy of the fruits array so we can restore the original value into `fruits`. @@ -266,6 +469,8 @@ ends up empty. When that happens, you reinitialize it to its default value:: # Prints "apple", "orange", "pear", or "banana" every time the code runs. return random_fruit + .. code-tab:: csharp + When running the above code, there is a chance to get the same fruit twice in a row. Once we picked a fruit, it will no longer be a possible return value unless the array is now empty. When the array is empty, we reset it back to its default @@ -281,7 +486,10 @@ time, or anything else. To achieve this, you can use random *noise* functions. Noise functions are especially popular in procedural generation to generate realistic-looking terrain. Godot provides :ref:`class_opensimplexnoise` for this, which supports -1D, 2D, 3D, and 4D noise. Here's an example with 1D noise:: +1D, 2D, 3D, and 4D noise. Here's an example with 1D noise: + +.. tabs:: + .. code-tab:: gdscript GDScript var noise = OpenSimplexNoise.new() @@ -297,3 +505,21 @@ terrain. Godot provides :ref:`class_opensimplexnoise` for this, which supports # Prints a slowly-changing series of floating-point numbers # between -1.0 and 1.0. print(noise.get_noise_1d(i)) + + .. code-tab:: csharp + + public override void _Ready() + { + var noise = new OpenSimplexNoise(); + GD.Randomize(); + // Configure the OpenSimplexNoise instance. + noise.Seed = (int)GD.Randi(); + noise.Octaves = 4; + noise.Period = 20.0f; + noise.Persistence = 0.8f; + + for (int i = 0; i < 100; i++) + { + GD.Print(noise.GetNoise1d(i)); + } + }