[3.x] Add C# code examples (salvage of VinnNo's PRs)

This commit is contained in:
Aaron Franke
2021-09-13 00:23:56 -05:00
committed by Hugo Locurcio
parent 264b2a1b91
commit 1ea8cccaf1
5 changed files with 362 additions and 70 deletions

View File

@@ -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>("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>("AnimationPlayer");
}
public override void _Process(float delta)
public override void _Process(float _delta)
{
if (Input.IsActionPressed("ui_right"))
{

View File

@@ -360,13 +360,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;
@@ -375,8 +368,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();
}

View File

@@ -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

View File

@@ -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<Position2D>("A");
Position2D b = GetNode<Position2D>("B");
Sprite sprite = GetNode<Sprite>("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<Position3D>("Position1");
Position3D p2 = GetNode<Position3D>("Position2");
CSGMesh monkey = GetNode<CSGMesh>("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");
sprite.Position = sprite.Position.LinearInterpolate(mousePos, delta * FollowSpeed);
}
Here is how it looks:
.. image:: img/interpolation_follow.gif

View File

@@ -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()
<class_@GDScript_method_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() <class_@GDScript_method_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() <class_@GDScript_method_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() <class_RandomNumberGenerator_method_randfn>` returns a random
floating-point number following a `normal distribution
<https://en.wikipedia.org/wiki/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() <class_@GDScript_method_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()
<class_RandomNumberGenerator_method_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() <class_@GDScript_method_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));
}
}