diff --git a/tutorials/2d/2d_sprite_animation.rst b/tutorials/2d/2d_sprite_animation.rst index e640343c0..77e129e16 100644 --- a/tutorials/2d/2d_sprite_animation.rst +++ b/tutorials/2d/2d_sprite_animation.rst @@ -81,7 +81,7 @@ released. onready var _animated_sprite = $AnimatedSprite - func _process(delta): + func _process(_delta): if Input.is_action_pressed("ui_right"): _animated_sprite.play("run") else: @@ -98,7 +98,7 @@ released. _animatedSprite = GetNode("AnimatedSprite"); } - public override _Process(float delta) + public override _Process(float _delta) { if (Input.IsActionPressed("ui_right")) { @@ -220,7 +220,7 @@ released. onready var _animation_player = $AnimationPlayer - func _process(delta): + func _process(_delta): if Input.is_action_pressed("ui_right"): _animation_player.play("walk") else: @@ -237,7 +237,7 @@ released. _animationPlayer = GetNode("AnimationPlayer"); } - public override void _Process(float delta) + public override void _Process(float _delta) { if (Input.IsActionPressed("ui_right")) { diff --git a/tutorials/2d/custom_drawing_in_2d.rst b/tutorials/2d/custom_drawing_in_2d.rst index eaa41ebd9..c1acbdb10 100644 --- a/tutorials/2d/custom_drawing_in_2d.rst +++ b/tutorials/2d/custom_drawing_in_2d.rst @@ -364,13 +364,6 @@ calls ``_draw()``. This way, you can control when you want to refresh the frame. .. code-tab:: csharp - private float Wrap(float value, float minVal, float maxVal) - { - float f1 = value - minVal; - float f2 = maxVal - minVal; - return (f1 % f2) + minVal; - } - public override void _Process(float delta) { _angleFrom += _rotationAngle; @@ -379,8 +372,8 @@ calls ``_draw()``. This way, you can control when you want to refresh the frame. // We only wrap angles when both of them are bigger than 360. if (_angleFrom > 360 && _angleTo > 360) { - _angleFrom = Wrap(_angleFrom, 0, 360); - _angleTo = Wrap(_angleTo, 0, 360); + _angleFrom = Mathf.Wrap(_angleFrom, 0, 360); + _angleTo = Mathf.Wrap(_angleTo, 0, 360); } Update(); } diff --git a/tutorials/math/beziers_and_curves.rst b/tutorials/math/beziers_and_curves.rst index 28919abcd..bd45be3e8 100644 --- a/tutorials/math/beziers_and_curves.rst +++ b/tutorials/math/beziers_and_curves.rst @@ -34,6 +34,14 @@ change the value of ``t`` from 0 to 1. var q0 = p0.linear_interpolate(p1, t) var q1 = p1.linear_interpolate(p2, t) + .. code-tab:: csharp + + private Vector2 QuadraticBezier(Vector2 p0, Vector2 p1, Vector2 p2, float t) + { + Vector2 q0 = p0.LinearInterpolate(p1, t); + Vector2 q1 = p1.LinearInterpolate(p2, t); + } + We then interpolate ``q0`` and ``q1`` to obtain a single point ``r`` that moves along a curve. @@ -43,6 +51,11 @@ along a curve. var r = q0.linear_interpolate(q1, t) return r + .. code-tab:: csharp + + Vector2 r = q0.LinearInterpolate(q1, t); + return r; + This type of is called a *Quadratic Bezier* curve. .. image:: img/bezier_quadratic_points2.gif @@ -65,6 +78,13 @@ We first use a function with four parameters to take four points as an input, func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float): + .. code-tab:: csharp + + public Vector2 CubicBezier(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) + { + + } + We apply a linear interpolation to each couple of points to reduce them to three: @@ -75,6 +95,12 @@ three: var q1 = p1.linear_interpolate(p2, t) var q2 = p2.linear_interpolate(p3, t) + .. code-tab:: csharp + + Vector2 q0 = p0.LinearInterpolate(p1, t); + Vector2 q1 = p1.LinearInterpolate(p2, t); + Vector2 q2 = p2.LinearInterpolate(p3, t); + We then take our three points and reduce them to two: .. tabs:: @@ -83,6 +109,11 @@ We then take our three points and reduce them to two: var r0 = q0.linear_interpolate(q1, t) var r1 = q1.linear_interpolate(q2, t) + .. code-tab:: csharp + + Vector2 r0 = q0.LinearInterpolate(q1, t); + Vector2 r1 = q1.LinearInterpolate(q2, t); + And to one: .. tabs:: @@ -91,6 +122,11 @@ And to one: var s = r0.linear_interpolate(r1, t) return s + .. code-tab:: csharp + + Vector2 s = r0.LinearInterpolate(r1, t); + return s; + Here is the full function: .. tabs:: @@ -107,6 +143,21 @@ Here is the full function: var s = r0.linear_interpolate(r1, t) return s + .. code-tab:: csharp + + private Vector2 CubicBezier(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) + { + Vector2 q0 = p0.LinearInterpolate(p1, t); + Vector2 q1 = p1.LinearInterpolate(p2, t); + Vector2 q2 = p2.LinearInterpolate(p3, t); + + Vector2 r0 = q0.LinearInterpolate(q1, t); + Vector2 r1 = q1.LinearInterpolate(q2, t); + + Vector2 s = r0.LinearInterpolate(r1, t); + return s; + } + The result will be a smooth curve interpolating between all four points: .. image:: img/bezier_cubic_points.gif @@ -164,6 +215,15 @@ Let's do a simple example with the following pseudocode: t += delta position = _cubic_bezier(p0, p1, p2, p3, t) + .. code-tab:: csharp + + private float _t = 0.0f; + + public override void _Process(float delta) + { + _t += delta; + Position = CubicBezier(p0, p1, p2, p3, _t); + } .. image:: img/bezier_interpolation_speed.gif @@ -204,6 +264,16 @@ Traversal at constant speed, then, can be done with the following pseudo-code: t += delta position = curve.interpolate_baked(t * curve.get_baked_length(), true) + .. code-tab:: csharp + + private float _t = 0.0f; + + public override void _Process(float delta) + { + _t += delta; + Position = curve.InterpolateBaked(_t * curve.GetBakedLength(), true); + } + And the output will, then, move at constant speed: .. image:: img/bezier_interpolation_baked.gif diff --git a/tutorials/math/interpolation.rst b/tutorials/math/interpolation.rst index 8581b4789..c7de0efcf 100644 --- a/tutorials/math/interpolation.rst +++ b/tutorials/math/interpolation.rst @@ -11,15 +11,13 @@ As an example if ``t`` is 0, then the state is A. If ``t`` is 1, then the state Between two real (floating-point) numbers, a simple interpolation is usually described as: -.. tabs:: - .. code-tab:: gdscript GDScript +:: interpolation = A * (1 - t) + B * t And often simplified to: -.. tabs:: - .. code-tab:: gdscript GDScript +:: interpolation = A + (B - A) * t @@ -40,11 +38,28 @@ Here is simple pseudo-code for going from point A to B using interpolation: .. tabs:: .. code-tab:: gdscript GDScript + var t = 0.0 + func _physics_process(delta): t += delta * 0.4 $Sprite.position = $A.position.linear_interpolate($B.position, t) + .. code-tab:: csharp + + private float _t = 0.0f; + + public override void _PhysicsProcess(float delta) + { + _t += delta * 0.4f; + + Position2D a = GetNode("A"); + Position2D b = GetNode("B"); + Sprite sprite = GetNode("Sprite"); + + sprite.Position = a.Position.LinearInterpolate(b.Position, _t); + } + It will produce the following motion: .. image:: img/interpolation_vector.gif @@ -71,6 +86,21 @@ Using the following pseudocode: $Monkey.transform = $Position1.transform.interpolate_with($Position2.transform, t) + .. code-tab:: csharp + + private float _t = 0.0f; + + public override void _PhysicsProcess(float delta) + { + _t += delta; + + Position3D p1 = GetNode("Position1"); + Position3D p2 = GetNode("Position2"); + CSGMesh monkey = GetNode("Monkey"); + + monkey.Transform = p1.Transform.InterpolateWith(p2.Transform, _t); + } + And again, it will produce the following motion: .. image:: img/interpolation_monkey.gif @@ -91,6 +121,19 @@ Interpolation can be used to smooth movement, rotation, etc. Here is an example $Sprite.position = $Sprite.position.linear_interpolate(mouse_pos, delta * FOLLOW_SPEED) + .. code-tab:: csharp + + private const float FollowSpeed = 4.0f; + + public override void _PhysicsProcess(float delta) + { + Vector2 mousePos = GetLocalMousePosition(); + + Sprite sprite = GetNode("Sprite"); + + sprite.Position = sprite.Position.LinearInterpolate(mousePos, delta * FollowSpeed); + } + Here is how it looks: .. image:: img/interpolation_follow.gif diff --git a/tutorials/math/random_number_generation.rst b/tutorials/math/random_number_generation.rst index 19ff66015..1c1f2909e 100644 --- a/tutorials/math/random_number_generation.rst +++ b/tutorials/math/random_number_generation.rst @@ -42,25 +42,54 @@ 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()) -When using the RandomNumberGenerator class, you should call ``randomize()`` on -the instance since it has its own seed:: + .. code-tab:: csharp - var rng = RandomNumberGenerator.new() - rng.randomize() + public override void _Ready() + { + GD.Seed(12345); + GD.Seed("Hello world".Hash()); + } + +When using the RandomNumberGenerator class, you should call ``randomize()`` on +the instance since it has its own seed: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var random = RandomNumberGenerator.new() + random.randomize() + + .. code-tab:: csharp + + var random = new RandomNumberGenerator(); + random.Randomize(); Getting a random number ----------------------- @@ -71,7 +100,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 +111,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 @@ -87,83 +127,169 @@ other things. :ref:`randfn() ` returns a random floating-point number following a `normal distribution `__. This means the returned -value is more likely to be around the mean (0.0 by default), varying by the deviation (1.0 by default):: +value is more likely to be around the mean (0.0 by default), +varying by the deviation (1.0 by default): + +.. tabs:: + .. code-tab:: gdscript GDScript # Prints a random floating-point number from a normal distribution with a mean 0.0 and deviation 1.0. - var rng = RandomNumberGenerator.new() - rng.randomize() - print(rng.randfn()) + var random = RandomNumberGenerator.new() + random.randomize() + print(random.randfn()) + + .. code-tab:: csharp + + // Prints a normally distributed floating-point number between 0.0 and 1.0. + var random = new RandomNumberGenerator(); + random.Randomize(); + GD.Print(random.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 integer between -10 and 10. - var rng = RandomNumberGenerator.new() - rng.randomize() - print(rng.randi_range(-10, 10)) + var random = RandomNumberGenerator.new() + random.randomize() + print(random.randi_range(-10, 10)) + + .. code-tab:: csharp + + # Prints a random integer number between -10 and 10. + random.Randomize(); + GD.Print(random.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: - var fruits = ["apple", "orange", "pear", "banana"] +.. tabs:: + .. code-tab:: gdscript GDScript + var _fruits = ["apple", "orange", "pear", "banana"] func _ready(): randomize() - for i in 100: + for i in range(100): # Pick 100 fruits randomly. - # (``for i in 100`` is a faster shorthand for ``for i in range(100)``.) print(get_fruit()) func get_fruit(): - var random_fruit = fruits[randi() % fruits.size()] + var random_fruit = _fruits[randi() % _fruits.size()] # 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:: + .. code-tab:: csharp - var fruits = ["apple", "orange", "pear", "banana"] - var last_fruit = "" + private 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(GetFruit()); + } + } + + public string GetFruit() + { + string randomFruit = _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 randomFruit; + } + +To prevent the same fruit from being picked more than once in a row, we can add +more logic to this method: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var _fruits = ["apple", "orange", "pear", "banana"] + var _last_fruit = "" func _ready(): randomize() # Pick 100 fruits randomly. - # Note: ``for i in 100`` is a shorthand for ``for i in range(100)``. - for i in 100: + for i in range(100): print(get_fruit()) func get_fruit(): - var random_fruit = fruits[randi() % fruits.size()] - while random_fruit == last_fruit: + var random_fruit = _fruits[randi() % _fruits.size()] + while random_fruit == _last_fruit: # The last fruit was picked, try again until we get a different fruit. - random_fruit = fruits[randi() % fruits.size()] + random_fruit = _fruits[randi() % _fruits.size()] # Note: if the random element to pick is passed by reference, # such as an array or dictionary, - # use `last_fruit = random_fruit.duplicate()` instead. - last_fruit = random_fruit + # use `_last_fruit = random_fruit.duplicate()` instead. + _last_fruit = random_fruit # 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 + .. code-tab:: csharp + + private string[] _fruits = { "apple", "orange", "pear", "banana" }; + private string _lastFruit = ""; + + public override void _Ready() + { + GD.Randomize(); + + for (int i = 0; i < 100; i++) + { + // Pick 100 fruits randomly. + GD.Print(GetFruit()); + } + } + + 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. + randomFruit = _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 randomFruit; + } + 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 @@ -172,7 +298,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}, @@ -184,7 +313,7 @@ We can apply similar logic from arrays to dictionaries as well:: func _ready(): randomize() - for i in 20: + for i in range(20): print(get_metal()) @@ -194,7 +323,6 @@ We can apply similar logic from arrays to dictionaries as well:: # The same metal may be selected multiple times in succession. return random_metal - .. _doc_random_number_generation_weighted_random_probability: Weighted random probability @@ -202,12 +330,15 @@ 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() - for i in 100: + for i in range(100): print(get_item_rarity()) @@ -224,6 +355,39 @@ 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.8f) + { + // 80% chance of being returned. + return "Common"; + } + else if (randomFloat < 0.95f) + { + // 15% chance 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,29 +402,29 @@ 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:: - var fruits = ["apple", "orange", "pear", "banana"] + var _fruits = ["apple", "orange", "pear", "banana"] # A copy of the fruits array so we can restore the original value into `fruits`. - var fruits_full = [] + var _fruits_full = [] func _ready(): randomize() - fruits_full = fruits.duplicate() - fruits.shuffle() + _fruits_full = _fruits.duplicate() + _fruits.shuffle() for i in 100: print(get_fruit()) func get_fruit(): - if fruits.empty(): + if _fruits.empty(): # Fill the fruits array again and shuffle it. - fruits = fruits_full.duplicate() - fruits.shuffle() + _fruits = _fruits_full.duplicate() + _fruits.shuffle() # Get a random fruit, since we shuffled the array, - # and remove it from the `fruits` array. - var random_fruit = fruits.pop_front() + # and remove it from the `_fruits` array. + var random_fruit = _fruits.pop_front() # Prints "apple", "orange", "pear", or "banana" every time the code runs. return random_fruit @@ -279,19 +443,41 @@ 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: - var noise = OpenSimplexNoise.new() +.. tabs:: + .. code-tab:: gdscript GDScript + + var _noise = OpenSimplexNoise.new() func _ready(): randomize() # Configure the OpenSimplexNoise instance. - noise.seed = randi() - noise.octaves = 4 - noise.period = 20.0 - noise.persistence = 0.8 + _noise.seed = randi() + _noise.octaves = 4 + _noise.period = 20.0 + _noise.persistence = 0.8 for i in 100: # Prints a slowly-changing series of floating-point numbers # between -1.0 and 1.0. - print(noise.get_noise_1d(i)) + print(_noise.get_noise_1d(i)) + + .. code-tab:: csharp + + private OpenSimplexNoise _noise = new OpenSimplexNoise(); + + public override void _Ready() + { + 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)); + } + }