Removed tabs.

This commit is contained in:
Relintai 2023-01-12 18:31:02 +01:00
parent 108cb20c13
commit 3beb0ac5d1
85 changed files with 1127 additions and 6313 deletions

View File

@ -203,20 +203,12 @@ workflow.
The above will display as: The above will display as:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript ```
func _ready(): func _ready():
var sprite = get_node("Sprite") var sprite = get_node("Sprite")
print(sprite.get_pos()) print(sprite.get_pos())
```
.. code-tab:: csharp
public override void _Ready()
{
var sprite = GetNode("Sprite");
GD.Print(sprite.GetPos());
}
To denote important information, add a paragraph starting with "[b]Note:[/b]" at To denote important information, add a paragraph starting with "[b]Note:[/b]" at
the end of the description: the end of the description:

View File

@ -47,18 +47,18 @@ Step by step
2. Set this key as environment variable in the console that you will use to 2. Set this key as environment variable in the console that you will use to
compile Godot, like this: compile Godot, like this:
.. tabs::
.. code-tab:: bash Linux/macOS
export SCRIPT_AES256_ENCRYPTION_KEY="your_generated_key" bash Linux/macOS
.. code-tab:: bat Windows (cmd) ``` export SCRIPT_AES256_ENCRYPTION_KEY="your_generated_key" ```
set SCRIPT_AES256_ENCRYPTION_KEY=your_generated_key bat Windows (cmd)
.. code-tab:: bat Windows (PowerShell) ``` set SCRIPT_AES256_ENCRYPTION_KEY=your_generated_key ````
$env:SCRIPT_AES256_ENCRYPTION_KEY="your_generated_key" bat Windows (PowerShell)
``` $env:SCRIPT_AES256_ENCRYPTION_KEY="your_generated_key" ```
3. Compile Godot export templates and set them as custom export templates 3. Compile Godot export templates and set them as custom export templates
in the export preset options. in the export preset options.

View File

@ -297,18 +297,17 @@ options automatically without having to supply them via the command line.
For instance, you may want to build Godot in parallel with the aforementioned For instance, you may want to build Godot in parallel with the aforementioned
``-j`` option for all the future builds: ``-j`` option for all the future builds:
.. tabs:: bash Linux/macOS
.. code-tab:: bash Linux/macOS
export SCONSFLAGS="-j4" ``` export SCONSFLAGS="-j4" ```
.. code-tab:: bat Windows (cmd) bat Windows (cmd)
set SCONSFLAGS=-j4 ``` set SCONSFLAGS=-j4 ```
.. code-tab:: powershell Windows (powershell) powershell Windows (powershell)
$env:SCONSFLAGS="-j4" ``` $env:SCONSFLAGS="-j4" ```
Export templates Export templates
---------------- ----------------

View File

@ -33,9 +33,9 @@ Importing the project
- Within the ``tasks.json`` file find the ``"tasks"`` array and add a new section to it: - Within the ``tasks.json`` file find the ``"tasks"`` array and add a new section to it:
.. tabs:: js Linux/X11
.. code-tab:: js Linux/X11
```
{ {
"label": "build", "label": "build",
"group": "build", "group": "build",
@ -46,9 +46,11 @@ Importing the project
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
} }
```
.. code-tab:: js Windows js Windows
```
{ {
"label": "build", "label": "build",
"group": "build", "group": "build",
@ -62,6 +64,7 @@ Importing the project
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
} }
```
.. figure:: img/vscode_3_tasks.json.png .. figure:: img/vscode_3_tasks.json.png
:figclass: figure-w480 :figclass: figure-w480
@ -87,9 +90,9 @@ To run and debug the project you need to create a new configuration in the ``lau
adjust the configuration example provided accordingly. adjust the configuration example provided accordingly.
- Within the ``launch.json`` file find the ``"configurations"`` array and add a new section to it: - Within the ``launch.json`` file find the ``"configurations"`` array and add a new section to it:
.. tabs:: js X11
.. code-tab:: js X11
```
{ {
"name": "Launch Project", "name": "Launch Project",
"type": "lldb", "type": "lldb",
@ -105,8 +108,12 @@ To run and debug the project you need to create a new configuration in the ``lau
"externalConsole": false, "externalConsole": false,
"preLaunchTask": "build" "preLaunchTask": "build"
} }
.. code-tab:: js X11_gdb
```
js X11_gdb
```
{ {
"name": "Launch Project", "name": "Launch Project",
"type": "cppdbg", "type": "cppdbg",
@ -130,9 +137,11 @@ To run and debug the project you need to create a new configuration in the ``lau
], ],
"preLaunchTask": "build" "preLaunchTask": "build"
} }
```
.. code-tab:: js Windows js Windows
```
{ {
"name": "Launch Project", "name": "Launch Project",
"type": "cppvsdbg", "type": "cppvsdbg",
@ -148,6 +157,7 @@ To run and debug the project you need to create a new configuration in the ``lau
"visualizerFile": "${workspaceFolder}/platform/windows/godot.natvis", "visualizerFile": "${workspaceFolder}/platform/windows/godot.natvis",
"preLaunchTask": "build" "preLaunchTask": "build"
} }
```
.. figure:: img/vscode_2_launch.json.png .. figure:: img/vscode_2_launch.json.png
:figclass: figure-w480 :figclass: figure-w480

View File

@ -230,15 +230,14 @@ Using the module
You can now use your newly created module from any script: You can now use your newly created module from any script:
.. tabs:: ```
.. code-tab:: gdscript GDScript
var s = Summator.new() var s = Summator.new()
s.add(10) s.add(10)
s.add(20) s.add(20)
s.add(30) s.add(30)
print(s.get_total()) print(s.get_total())
s.reset() s.reset()
```
The output will be ``60``. The output will be ``60``.

View File

@ -9,40 +9,14 @@ Launch Godot and create a new project.
.. image:: img/new-project-button.png .. image:: img/new-project-button.png
.. tabs:: GDScript
.. tab:: GDScript
```
Download :download:`dodge_assets.zip <files/dodge_assets.zip>`. Download :download:`dodge_assets.zip <files/dodge_assets.zip>`.
The archive contains the images and sounds you'll be using The archive contains the images and sounds you'll be using
to make the game. Extract the archive and move the ``art/`` to make the game. Extract the archive and move the ``art/``
and ``fonts/`` directories to your project's directory. and ``fonts/`` directories to your project's directory.
```
.. tab:: C#
Download :download:`dodge_assets.zip <files/dodge_assets.zip>`.
The archive contains the images and sounds you'll be using
to make the game. Extract the archive and move the ``art/``
and ``fonts/`` directories to your project's directory.
Ensure that you have the required dependencies to use C# in Godot.
You need the .NET Core 3.1 SDK, and an editor such as VS Code.
See :ref:`doc_c_sharp_setup`.
.. tab:: GDNative C++
Download :download:`dodge_assets_with_gdnative.zip
<files/dodge_assets_with_gdnative.zip>`.
The archive contains the images and sounds you'll be using
to make the game. It also contains a starter GDNative project
including a ``SConstruct`` file, a ``dodge_the_creeps.gdnlib``
file, a ``player.gdns`` file, and an ``entry.cpp`` file.
Ensure that you have the required dependencies to use GDNative C++.
You need a C++ compiler such as GCC or Clang or MSVC that supports C++14.
On Windows you can download Visual Studio 2019 and select the C++ workload.
You also need SCons to use the build system (the SConstruct file).
Then you need to `download the Godot C++ bindings <https://github.com/godotengine/godot-cpp>`_
and place them in your project.
Your project folder should look like this. Your project folder should look like this.

View File

@ -25,63 +25,14 @@ click "Create":
Start by declaring the member variables this object will need: Start by declaring the member variables this object will need:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Area2D extends Area2D
export var speed = 400 # How fast the player will move (pixels/sec). export var speed = 400 # How fast the player will move (pixels/sec).
var screen_size # Size of the game window. var screen_size # Size of the game window.
```
.. code-tab:: csharp
using Godot;
using System;
public class Player : Area2D
{
[Export]
public int Speed = 400; // How fast the player will move (pixels/sec).
public Vector2 ScreenSize; // Size of the game window.
}
.. code-tab:: cpp
// A `player.gdns` file has already been created for you. Attach it to the Player node.
// Create two files `player.cpp` and `player.hpp` next to `entry.cpp` in `src`.
// This code goes in `player.hpp`. We also define the methods we'll be using here.
#ifndef PLAYER_H
#define PLAYER_H
#include <AnimatedSprite.hpp>
#include <Area2D.hpp>
#include <CollisionShape2D.hpp>
#include <Godot.hpp>
#include <Input.hpp>
class Player : public godot::Area2D {
GODOT_CLASS(Player, godot::Area2D)
godot::AnimatedSprite *_animated_sprite;
godot::CollisionShape2D *_collision_shape;
godot::Input *_input;
godot::Vector2 _screen_size; // Size of the game window.
public:
real_t speed = 400; // How fast the player will move (pixels/sec).
void _init() {}
void _ready();
void _process(const double p_delta);
void start(const godot::Vector2 p_position);
void _on_Player_body_entered(godot::Node2D *_body);
static void _register_methods();
};
#endif // PLAYER_H
Using the ``export`` keyword on the first variable ``speed`` allows us to set Using the ``export`` keyword on the first variable ``speed`` allows us to set
its value in the Inspector. This can be handy for values that you want to be its value in the Inspector. This can be handy for values that you want to be
@ -90,41 +41,15 @@ node and you'll see the property now appears in the "Script Variables" section
of the Inspector. Remember, if you change the value here, it will override the of the Inspector. Remember, if you change the value here, it will override the
value written in the script. value written in the script.
.. warning:: If you're using C#, you need to (re)build the project assemblies
whenever you want to see new export variables or signals. This
build can be manually triggered by clicking the word "Mono" at the
bottom of the editor window to reveal the Mono Panel, then clicking
the "Build Project" button.
.. image:: img/export_variable.png .. image:: img/export_variable.png
The ``_ready()`` function is called when a node enters the scene tree, which is The ``_ready()`` function is called when a node enters the scene tree, which is
a good time to find the size of the game window: a good time to find the size of the game window:
.. tabs:: ```
.. code-tab:: gdscript GDScript
func _ready(): func _ready():
screen_size = get_viewport_rect().size screen_size = get_viewport_rect().size
```
.. code-tab:: csharp
public override void _Ready()
{
ScreenSize = GetViewportRect().Size;
}
.. code-tab:: cpp
// This code goes in `player.cpp`.
#include "player.hpp"
void Player::_ready() {
_animated_sprite = get_node<godot::AnimatedSprite>("AnimatedSprite");
_collision_shape = get_node<godot::CollisionShape2D>("CollisionShape2D");
_input = godot::Input::get_singleton();
_screen_size = get_viewport_rect().size;
}
Now we can use the ``_process()`` function to define what the player will do. Now we can use the ``_process()`` function to define what the player will do.
``_process()`` is called every frame, so we'll use it to update elements of our ``_process()`` is called every frame, so we'll use it to update elements of our
@ -173,9 +98,8 @@ Click the "Close" button to close the project settings.
You can detect whether a key is pressed using ``Input.is_action_pressed()``, You can detect whether a key is pressed using ``Input.is_action_pressed()``,
which returns ``true`` if it's pressed or ``false`` if it isn't. which returns ``true`` if it's pressed or ``false`` if it isn't.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript ```
func _process(delta): func _process(delta):
var velocity = Vector2.ZERO # The player's movement vector. var velocity = Vector2.ZERO # The player's movement vector.
if Input.is_action_pressed("move_right"): if Input.is_action_pressed("move_right"):
@ -192,62 +116,7 @@ which returns ``true`` if it's pressed or ``false`` if it isn't.
$AnimatedSprite.play() $AnimatedSprite.play()
else: else:
$AnimatedSprite.stop() $AnimatedSprite.stop()
```
.. code-tab:: csharp
public override void _Process(float delta)
{
var velocity = Vector2.Zero; // The player's movement vector.
if (Input.IsActionPressed("move_right"))
{
velocity.x += 1;
}
if (Input.IsActionPressed("move_left"))
{
velocity.x -= 1;
}
if (Input.IsActionPressed("move_down"))
{
velocity.y += 1;
}
if (Input.IsActionPressed("move_up"))
{
velocity.y -= 1;
}
var animatedSprite = GetNode<AnimatedSprite>("AnimatedSprite");
if (velocity.Length() > 0)
{
velocity = velocity.Normalized() * Speed;
animatedSprite.Play();
}
else
{
animatedSprite.Stop();
}
}
.. code-tab:: cpp
// This code goes in `player.cpp`.
void Player::_process(const double p_delta) {
godot::Vector2 velocity(0, 0);
velocity.x = _input->get_action_strength("move_right") - _input->get_action_strength("move_left");
velocity.y = _input->get_action_strength("move_down") - _input->get_action_strength("move_up");
if (velocity.length() > 0) {
velocity = velocity.normalized() * speed;
_animated_sprite->play();
} else {
_animated_sprite->stop();
}
}
We start by setting the ``velocity`` to ``(0, 0)`` - by default, the player We start by setting the ``velocity`` to ``(0, 0)`` - by default, the player
should not be moving. Then we check each input and add/subtract from the should not be moving. Then we check each input and add/subtract from the
@ -281,28 +150,13 @@ can also use ``clamp()`` to prevent it from leaving the screen. *Clamping* a
value means restricting it to a given range. Add the following to the bottom of value means restricting it to a given range. Add the following to the bottom of
the ``_process`` function (make sure it's not indented under the `else`): the ``_process`` function (make sure it's not indented under the `else`):
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
position += velocity * delta position += velocity * delta
position.x = clamp(position.x, 0, screen_size.x) position.x = clamp(position.x, 0, screen_size.x)
position.y = clamp(position.y, 0, screen_size.y) position.y = clamp(position.y, 0, screen_size.y)
```
.. code-tab:: csharp
Position += velocity * delta;
Position = new Vector2(
x: Mathf.Clamp(Position.x, 0, ScreenSize.x),
y: Mathf.Clamp(Position.y, 0, ScreenSize.y)
);
.. code-tab:: cpp
godot::Vector2 position = get_position();
position += velocity * (real_t)p_delta;
position.x = godot::Math::clamp(position.x, (real_t)0.0, _screen_size.x);
position.y = godot::Math::clamp(position.y, (real_t)0.0, _screen_size.y);
set_position(position);
.. tip:: The `delta` parameter in the `_process()` function refers to the *frame .. tip:: The `delta` parameter in the `_process()` function refers to the *frame
length* - the amount of time that the previous frame took to complete. length* - the amount of time that the previous frame took to complete.
@ -331,9 +185,9 @@ horizontally using the ``flip_h`` property for left movement. We also have the
"up" animation, which should be flipped vertically with ``flip_v`` for downward "up" animation, which should be flipped vertically with ``flip_v`` for downward
movement. Let's place this code at the end of the ``_process()`` function: movement. Let's place this code at the end of the ``_process()`` function:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
if velocity.x != 0: if velocity.x != 0:
$AnimatedSprite.animation = "walk" $AnimatedSprite.animation = "walk"
$AnimatedSprite.flip_v = false $AnimatedSprite.flip_v = false
@ -342,57 +196,21 @@ movement. Let's place this code at the end of the ``_process()`` function:
elif velocity.y != 0: elif velocity.y != 0:
$AnimatedSprite.animation = "up" $AnimatedSprite.animation = "up"
$AnimatedSprite.flip_v = velocity.y > 0 $AnimatedSprite.flip_v = velocity.y > 0
```
.. code-tab:: csharp
if (velocity.x != 0)
{
animatedSprite.Animation = "walk";
animatedSprite.FlipV = false;
// See the note below about boolean assignment.
animatedSprite.FlipH = velocity.x < 0;
}
else if (velocity.y != 0)
{
animatedSprite.Animation = "up";
animatedSprite.FlipV = velocity.y > 0;
}
.. code-tab:: cpp
if (velocity.x != 0) {
_animated_sprite->set_animation("walk");
_animated_sprite->set_flip_v(false);
// See the note below about boolean assignment.
_animated_sprite->set_flip_h(velocity.x < 0);
} else if (velocity.y != 0) {
_animated_sprite->set_animation("up");
_animated_sprite->set_flip_v(velocity.y > 0);
}
.. Note:: The boolean assignments in the code above are a common shorthand for .. Note:: The boolean assignments in the code above are a common shorthand for
programmers. Since we're doing a comparison test (boolean) and also programmers. Since we're doing a comparison test (boolean) and also
*assigning* a boolean value, we can do both at the same time. Consider *assigning* a boolean value, we can do both at the same time. Consider
this code versus the one-line boolean assignment above: this code versus the one-line boolean assignment above:
.. tabs:: ```
.. code-tab :: gdscript GDScript
if velocity.x < 0: if velocity.x < 0:
$AnimatedSprite.flip_h = true $AnimatedSprite.flip_h = true
else: else:
$AnimatedSprite.flip_h = false $AnimatedSprite.flip_h = false
.. code-tab:: csharp ```
if (velocity.x < 0)
{
animatedSprite.FlipH = true;
}
else
{
animatedSprite.FlipH = false;
}
Play the scene again and check that the animations are correct in each of the Play the scene again and check that the animations are correct in each of the
directions. directions.
@ -405,18 +223,11 @@ directions.
When you're sure the movement is working correctly, add this line to When you're sure the movement is working correctly, add this line to
``_ready()``, so the player will be hidden when the game starts: ``_ready()``, so the player will be hidden when the game starts:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
hide() hide()
```
.. code-tab:: csharp
Hide();
.. code-tab:: cpp
hide();
Preparing for collisions Preparing for collisions
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
@ -427,32 +238,11 @@ functionality to make it work.
Add the following at the top of the script, after ``extends Area2D``: Add the following at the top of the script, after ``extends Area2D``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
signal hit signal hit
```
.. code-tab:: csharp
// Don't forget to rebuild the project so the editor knows about the new signal.
[Signal]
public delegate void Hit();
.. code-tab:: cpp
// This code goes in `player.cpp`.
// We need to register the signal here, and while we're here, we can also
// register the other methods and register the speed property.
void Player::_register_methods() {
godot::register_method("_ready", &Player::_ready);
godot::register_method("_process", &Player::_process);
godot::register_method("start", &Player::start);
godot::register_method("_on_Player_body_entered", &Player::_on_Player_body_entered);
godot::register_property("speed", &Player::speed, (real_t)400.0);
// This below line is the signal.
godot::register_signal<Player>("hit", godot::Dictionary());
}
This defines a custom signal called "hit" that we will have our player emit This defines a custom signal called "hit" that we will have our player emit
(send out) when it collides with an enemy. We will use ``Area2D`` to detect the (send out) when it collides with an enemy. We will use ``Area2D`` to detect the
@ -473,34 +263,15 @@ your player's script.
Note the green icon indicating that a signal is connected to this function. Add Note the green icon indicating that a signal is connected to this function. Add
this code to the function: this code to the function:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_Player_body_entered(body): func _on_Player_body_entered(body):
hide() # Player disappears after being hit. hide() # Player disappears after being hit.
emit_signal("hit") emit_signal("hit")
# Must be deferred as we can't change physics properties on a physics callback. # Must be deferred as we can't change physics properties on a physics callback.
$CollisionShape2D.set_deferred("disabled", true) $CollisionShape2D.set_deferred("disabled", true)
```
.. code-tab:: csharp
public void OnPlayerBodyEntered(PhysicsBody2D body)
{
Hide(); // Player disappears after being hit.
EmitSignal(nameof(Hit));
// Must be deferred as we can't change physics properties on a physics callback.
GetNode<CollisionShape2D>("CollisionShape2D").SetDeferred("disabled", true);
}
.. code-tab:: cpp
// This code goes in `player.cpp`.
void Player::_on_Player_body_entered(godot::Node2D *_body) {
hide(); // Player disappears after being hit.
emit_signal("hit");
// Must be deferred as we can't change physics properties on a physics callback.
_collision_shape->set_deferred("disabled", true);
}
Each time an enemy hits the player, the signal is going to be emitted. We need Each time an enemy hits the player, the signal is going to be emitted. We need
to disable the player's collision so that we don't trigger the ``hit`` signal to disable the player's collision so that we don't trigger the ``hit`` signal
@ -514,30 +285,13 @@ more than once.
The last piece is to add a function we can call to reset the player when The last piece is to add a function we can call to reset the player when
starting a new game. starting a new game.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func start(pos): func start(pos):
position = pos position = pos
show() show()
$CollisionShape2D.disabled = false $CollisionShape2D.disabled = false
```
.. code-tab:: csharp
public void Start(Vector2 pos)
{
Position = pos;
Show();
GetNode<CollisionShape2D>("CollisionShape2D").Disabled = false;
}
.. code-tab:: cpp
// This code goes in `player.cpp`.
void Player::start(const godot::Vector2 p_position) {
set_position(p_position);
show();
_collision_shape->set_disabled(false);
}
With the player working, we'll work on the enemy in the next lesson. With the player working, we'll work on the enemy in the next lesson.

View File

@ -58,84 +58,23 @@ Enemy script
Add a script to the ``Mob`` like this: Add a script to the ``Mob`` like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends RigidBody2D extends RigidBody2D
```
.. code-tab:: csharp
public class Mob : RigidBody2D
{
// Don't forget to rebuild the project.
}
.. code-tab:: cpp
// Copy `player.gdns` to `mob.gdns` and replace `Player` with `Mob`.
// Attach the `mob.gdns` file to the Mob node.
// Create two files `mob.cpp` and `mob.hpp` next to `entry.cpp` in `src`.
// This code goes in `mob.hpp`. We also define the methods we'll be using here.
#ifndef MOB_H
#define MOB_H
#include <AnimatedSprite.hpp>
#include <Godot.hpp>
#include <RigidBody2D.hpp>
class Mob : public godot::RigidBody2D {
GODOT_CLASS(Mob, godot::RigidBody2D)
godot::AnimatedSprite *_animated_sprite;
public:
void _init() {}
void _ready();
void _on_VisibilityNotifier2D_screen_exited();
static void _register_methods();
};
#endif // MOB_H
Now let's look at the rest of the script. In ``_ready()`` we play the animation Now let's look at the rest of the script. In ``_ready()`` we play the animation
and randomly choose one of the three animation types: and randomly choose one of the three animation types:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
$AnimatedSprite.playing = true $AnimatedSprite.playing = true
var mob_types = $AnimatedSprite.frames.get_animation_names() var mob_types = $AnimatedSprite.frames.get_animation_names()
$AnimatedSprite.animation = mob_types[randi() % mob_types.size()] $AnimatedSprite.animation = mob_types[randi() % mob_types.size()]
```
.. code-tab:: csharp
public override void _Ready()
{
var animSprite = GetNode<AnimatedSprite>("AnimatedSprite");
animSprite.Playing = true;
string[] mobTypes = animSprite.Frames.GetAnimationNames();
animSprite.Animation = mobTypes[GD.Randi() % mobTypes.Length];
}
.. code-tab:: cpp
// This code goes in `mob.cpp`.
#include "mob.hpp"
#include <RandomNumberGenerator.hpp>
#include <SpriteFrames.hpp>
void Mob::_ready() {
godot::Ref<godot::RandomNumberGenerator> random = godot::RandomNumberGenerator::_new();
random->randomize();
_animated_sprite = get_node<godot::AnimatedSprite>("AnimatedSprite");
_animated_sprite->_set_playing(true);
godot::PoolStringArray mob_types = _animated_sprite->get_sprite_frames()->get_animation_names();
_animated_sprite->set_animation(mob_types[random->randi() % mob_types.size()]);
}
First, we get the list of animation names from the AnimatedSprite's ``frames`` First, we get the list of animation names from the AnimatedSprite's ``frames``
property. This returns an Array containing all three animation names: ``["walk", property. This returns an Array containing all three animation names: ``["walk",
@ -153,25 +92,12 @@ The last piece is to make the mobs delete themselves when they leave the screen.
Connect the ``screen_exited()`` signal of the ``VisibilityNotifier2D`` node and Connect the ``screen_exited()`` signal of the ``VisibilityNotifier2D`` node and
add this code: add this code:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_VisibilityNotifier2D_screen_exited(): func _on_VisibilityNotifier2D_screen_exited():
queue_free() queue_free()
```
.. code-tab:: csharp
public void OnVisibilityNotifier2DScreenExited()
{
QueueFree();
}
.. code-tab:: cpp
// This code goes in `mob.cpp`.
void Mob::_on_VisibilityNotifier2D_screen_exited() {
queue_free();
}
This completes the `Mob` scene. This completes the `Mob` scene.

View File

@ -76,121 +76,24 @@ Main script
Add a script to ``Main``. At the top of the script, we use ``export Add a script to ``Main``. At the top of the script, we use ``export
(PackedScene)`` to allow us to choose the Mob scene we want to instance. (PackedScene)`` to allow us to choose the Mob scene we want to instance.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
export(PackedScene) var mob_scene export(PackedScene) var mob_scene
var score var score
```
.. code-tab:: csharp
public class Main : Node
{
// Don't forget to rebuild the project so the editor knows about the new export variable.
#pragma warning disable 649
// We assign this in the editor, so we don't need the warning about not being assigned.
[Export]
public PackedScene MobScene;
#pragma warning restore 649
public int Score;
}
.. code-tab:: cpp
// Copy `player.gdns` to `main.gdns` and replace `Player` with `Main`.
// Attach the `main.gdns` file to the Main node.
// Create two files `main.cpp` and `main.hpp` next to `entry.cpp` in `src`.
// This code goes in `main.hpp`. We also define the methods we'll be using here.
#ifndef MAIN_H
#define MAIN_H
#include <AudioStreamPlayer.hpp>
#include <CanvasLayer.hpp>
#include <Godot.hpp>
#include <Node.hpp>
#include <PackedScene.hpp>
#include <PathFollow2D.hpp>
#include <RandomNumberGenerator.hpp>
#include <Timer.hpp>
#include "hud.hpp"
#include "player.hpp"
class Main : public godot::Node {
GODOT_CLASS(Main, godot::Node)
int score;
HUD *_hud;
Player *_player;
godot::Node2D *_start_position;
godot::PathFollow2D *_mob_spawn_location;
godot::Timer *_mob_timer;
godot::Timer *_score_timer;
godot::Timer *_start_timer;
godot::AudioStreamPlayer *_music;
godot::AudioStreamPlayer *_death_sound;
godot::Ref<godot::RandomNumberGenerator> _random;
public:
godot::Ref<godot::PackedScene> mob_scene;
void _init() {}
void _ready();
void game_over();
void new_game();
void _on_MobTimer_timeout();
void _on_ScoreTimer_timeout();
void _on_StartTimer_timeout();
static void _register_methods();
};
#endif // MAIN_H
We also add a call to ``randomize()`` here so that the random number We also add a call to ``randomize()`` here so that the random number
generator generates different random numbers each time the game is run: generator generates different random numbers each time the game is run:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
randomize() randomize()
```
.. code-tab:: csharp
public override void _Ready()
{
GD.Randomize();
}
.. code-tab:: cpp
// This code goes in `main.cpp`.
#include "main.hpp"
#include <SceneTree.hpp>
#include "mob.hpp"
void Main::_ready() {
_hud = get_node<HUD>("HUD");
_player = get_node<Player>("Player");
_start_position = get_node<godot::Node2D>("StartPosition");
_mob_spawn_location = get_node<godot::PathFollow2D>("MobPath/MobSpawnLocation");
_mob_timer = get_node<godot::Timer>("MobTimer");
_score_timer = get_node<godot::Timer>("ScoreTimer");
_start_timer = get_node<godot::Timer>("StartTimer");
// Uncomment these after adding the nodes in the "Sound effects" section of "Finishing up".
//_music = get_node<godot::AudioStreamPlayer>("Music");
//_death_sound = get_node<godot::AudioStreamPlayer>("DeathSound");
_random = (godot::Ref<godot::RandomNumberGenerator>)godot::RandomNumberGenerator::_new();
_random->randomize();
}
Click the ``Main`` node and you will see the ``Mob Scene`` property in the Inspector Click the ``Main`` node and you will see the ``Mob Scene`` property in the Inspector
under "Script Variables". under "Script Variables".
@ -213,9 +116,9 @@ signal connection dialog and click "Connect". Add the following code to the new
function, as well as a ``new_game`` function that will set everything up for a function, as well as a ``new_game`` function that will set everything up for a
new game: new game:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func game_over(): func game_over():
$ScoreTimer.stop() $ScoreTimer.stop()
$MobTimer.stop() $MobTimer.stop()
@ -224,89 +127,22 @@ new game:
score = 0 score = 0
$Player.start($StartPosition.position) $Player.start($StartPosition.position)
$StartTimer.start() $StartTimer.start()
```
.. code-tab:: csharp
public void GameOver()
{
GetNode<Timer>("MobTimer").Stop();
GetNode<Timer>("ScoreTimer").Stop();
}
public void NewGame()
{
Score = 0;
var player = GetNode<Player>("Player");
var startPosition = GetNode<Position2D>("StartPosition");
player.Start(startPosition.Position);
GetNode<Timer>("StartTimer").Start();
}
.. code-tab:: cpp
// This code goes in `main.cpp`.
void Main::game_over() {
_score_timer->stop();
_mob_timer->stop();
}
void Main::new_game() {
score = 0;
_player->start(_start_position->get_position());
_start_timer->start();
}
Now connect the ``timeout()`` signal of each of the Timer nodes (``StartTimer``, Now connect the ``timeout()`` signal of each of the Timer nodes (``StartTimer``,
``ScoreTimer`` , and ``MobTimer``) to the main script. ``StartTimer`` will start ``ScoreTimer`` , and ``MobTimer``) to the main script. ``StartTimer`` will start
the other two timers. ``ScoreTimer`` will increment the score by 1. the other two timers. ``ScoreTimer`` will increment the score by 1.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_ScoreTimer_timeout(): func _on_ScoreTimer_timeout():
score += 1 score += 1
func _on_StartTimer_timeout(): func _on_StartTimer_timeout():
$MobTimer.start() $MobTimer.start()
$ScoreTimer.start() $ScoreTimer.start()
```
.. code-tab:: csharp
public void OnScoreTimerTimeout()
{
Score++;
}
public void OnStartTimerTimeout()
{
GetNode<Timer>("MobTimer").Start();
GetNode<Timer>("ScoreTimer").Start();
}
.. code-tab:: cpp
// This code goes in `main.cpp`.
void Main::_on_ScoreTimer_timeout() {
score += 1;
}
void Main::_on_StartTimer_timeout() {
_mob_timer->start();
_score_timer->start();
}
// Also add this to register all methods and the mob scene property.
void Main::_register_methods() {
godot::register_method("_ready", &Main::_ready);
godot::register_method("game_over", &Main::game_over);
godot::register_method("new_game", &Main::new_game);
godot::register_method("_on_MobTimer_timeout", &Main::_on_MobTimer_timeout);
godot::register_method("_on_ScoreTimer_timeout", &Main::_on_ScoreTimer_timeout);
godot::register_method("_on_StartTimer_timeout", &Main::_on_StartTimer_timeout);
godot::register_property("mob_scene", &Main::mob_scene, (godot::Ref<godot::PackedScene>)nullptr);
}
In ``_on_MobTimer_timeout()``, we will create a mob instance, pick a random In ``_on_MobTimer_timeout()``, we will create a mob instance, pick a random
starting location along the ``Path2D``, and set the mob in motion. The starting location along the ``Path2D``, and set the mob in motion. The
@ -318,9 +154,9 @@ all moving at the same speed).
Note that a new instance must be added to the scene using ``add_child()``. Note that a new instance must be added to the scene using ``add_child()``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_MobTimer_timeout(): func _on_MobTimer_timeout():
# Create a new instance of the Mob scene. # Create a new instance of the Mob scene.
var mob = mob_scene.instance() var mob = mob_scene.instance()
@ -345,67 +181,7 @@ Note that a new instance must be added to the scene using ``add_child()``.
# Spawn the mob by adding it to the Main scene. # Spawn the mob by adding it to the Main scene.
add_child(mob) add_child(mob)
```
.. code-tab:: csharp
public void OnMobTimerTimeout()
{
// Note: Normally it is best to use explicit types rather than the `var`
// keyword. However, var is acceptable to use here because the types are
// obviously Mob and PathFollow2D, since they appear later on the line.
// Create a new instance of the Mob scene.
var mob = (Mob)MobScene.Instance();
// Choose a random location on Path2D.
var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");
mobSpawnLocation.Offset = GD.Randi();
// Set the mob's direction perpendicular to the path direction.
float direction = mobSpawnLocation.Rotation + Mathf.Pi / 2;
// Set the mob's position to a random location.
mob.Position = mobSpawnLocation.Position;
// Add some randomness to the direction.
direction += (float)GD.RandRange(-Mathf.Pi / 4, Mathf.Pi / 4);
mob.Rotation = direction;
// Choose the velocity.
var velocity = new Vector2((float)GD.RandRange(150.0, 250.0), 0);
mob.LinearVelocity = velocity.Rotated(direction);
// Spawn the mob by adding it to the Main scene.
AddChild(mob);
}
.. code-tab:: cpp
// This code goes in `main.cpp`.
void Main::_on_MobTimer_timeout() {
// Create a new instance of the Mob scene.
godot::Node *mob = mob_scene->instance();
// Choose a random location on Path2D.
_mob_spawn_location->set_offset((real_t)_random->randi());
// Set the mob's direction perpendicular to the path direction.
real_t direction = _mob_spawn_location->get_rotation() + (real_t)Math_PI / 2;
// Set the mob's position to a random location.
mob->set("position", _mob_spawn_location->get_position());
// Add some randomness to the direction.
direction += _random->randf_range((real_t)-Math_PI / 4, (real_t)Math_PI / 4);
mob->set("rotation", direction);
// Choose the velocity for the mob.
godot::Vector2 velocity = godot::Vector2(_random->randf_range(150.0, 250.0), 0.0);
mob->set("linear_velocity", velocity.rotated(direction));
// Spawn the mob by adding it to the Main scene.
add_child(mob);
}
.. important:: Why ``PI``? In functions requiring angles, Godot uses *radians*, .. important:: Why ``PI``? In functions requiring angles, Godot uses *radians*,
not degrees. Pi represents a half turn in radians, about not degrees. Pi represents a half turn in radians, about
@ -420,26 +196,13 @@ Testing the scene
Let's test the scene to make sure everything is working. Add this ``new_game`` Let's test the scene to make sure everything is working. Add this ``new_game``
call to ``_ready()``: call to ``_ready()``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
randomize() randomize()
new_game() new_game()
```
.. code-tab:: csharp
public override void _Ready()
{
NewGame();
}
.. code-tab:: cpp
// This code goes in `main.cpp`.
void Main::_ready() {
new_game();
}
Let's also assign ``Main`` as our "Main Scene" - the one that runs automatically Let's also assign ``Main`` as our "Main Scene" - the one that runs automatically
when the game launches. Press the "Play" button and select ``Main.tscn`` when when the game launches. Press the "Play" button and select ``Main.tscn`` when

View File

@ -97,117 +97,32 @@ property to "On".
Now add this script to ``HUD``: Now add this script to ``HUD``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends CanvasLayer extends CanvasLayer
signal start_game signal start_game
```
.. code-tab:: csharp
public class HUD : CanvasLayer
{
// Don't forget to rebuild the project so the editor knows about the new signal.
[Signal]
public delegate void StartGame();
}
.. code-tab:: cpp
// Copy `player.gdns` to `hud.gdns` and replace `Player` with `HUD`.
// Attach the `hud.gdns` file to the HUD node.
// Create two files `hud.cpp` and `hud.hpp` next to `entry.cpp` in `src`.
// This code goes in `hud.hpp`. We also define the methods we'll be using here.
#ifndef HUD_H
#define HUD_H
#include <Button.hpp>
#include <CanvasLayer.hpp>
#include <Godot.hpp>
#include <Label.hpp>
#include <Timer.hpp>
class HUD : public godot::CanvasLayer {
GODOT_CLASS(HUD, godot::CanvasLayer)
godot::Label *_score_label;
godot::Label *_message_label;
godot::Timer *_start_message_timer;
godot::Timer *_get_ready_message_timer;
godot::Button *_start_button;
godot::Timer *_start_button_timer;
public:
void _init() {}
void _ready();
void show_get_ready();
void show_game_over();
void update_score(const int score);
void _on_StartButton_pressed();
void _on_StartMessageTimer_timeout();
void _on_GetReadyMessageTimer_timeout();
static void _register_methods();
};
#endif // HUD_H
The ``start_game`` signal tells the ``Main`` node that the button The ``start_game`` signal tells the ``Main`` node that the button
has been pressed. has been pressed.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func show_message(text): func show_message(text):
$Message.text = text $Message.text = text
$Message.show() $Message.show()
$MessageTimer.start() $MessageTimer.start()
```
.. code-tab:: csharp
public void ShowMessage(string text)
{
var message = GetNode<Label>("Message");
message.Text = text;
message.Show();
GetNode<Timer>("MessageTimer").Start();
}
.. code-tab:: cpp
// This code goes in `hud.cpp`.
#include "hud.hpp"
void HUD::_ready() {
_score_label = get_node<godot::Label>("ScoreLabel");
_message_label = get_node<godot::Label>("MessageLabel");
_start_message_timer = get_node<godot::Timer>("StartMessageTimer");
_get_ready_message_timer = get_node<godot::Timer>("GetReadyMessageTimer");
_start_button = get_node<godot::Button>("StartButton");
_start_button_timer = get_node<godot::Timer>("StartButtonTimer");
}
void HUD::_register_methods() {
godot::register_method("_ready", &HUD::_ready);
godot::register_method("show_get_ready", &HUD::show_get_ready);
godot::register_method("show_game_over", &HUD::show_game_over);
godot::register_method("update_score", &HUD::update_score);
godot::register_method("_on_StartButton_pressed", &HUD::_on_StartButton_pressed);
godot::register_method("_on_StartMessageTimer_timeout", &HUD::_on_StartMessageTimer_timeout);
godot::register_method("_on_GetReadyMessageTimer_timeout", &HUD::_on_GetReadyMessageTimer_timeout);
godot::register_signal<HUD>("start_game", godot::Dictionary());
}
This function is called when we want to display a message This function is called when we want to display a message
temporarily, such as "Get Ready". temporarily, such as "Get Ready".
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func show_game_over(): func show_game_over():
show_message("Game Over") show_message("Game Over")
# Wait until the MessageTimer has counted down. # Wait until the MessageTimer has counted down.
@ -218,40 +133,7 @@ temporarily, such as "Get Ready".
# Make a one-shot timer and wait for it to finish. # Make a one-shot timer and wait for it to finish.
yield(get_tree().create_timer(1), "timeout") yield(get_tree().create_timer(1), "timeout")
$StartButton.show() $StartButton.show()
```
.. code-tab:: csharp
async public void ShowGameOver()
{
ShowMessage("Game Over");
var messageTimer = GetNode<Timer>("MessageTimer");
await ToSignal(messageTimer, "timeout");
var message = GetNode<Label>("Message");
message.Text = "Dodge the\nCreeps!";
message.Show();
await ToSignal(GetTree().CreateTimer(1), "timeout");
GetNode<Button>("StartButton").Show();
}
.. code-tab:: cpp
// This code goes in `hud.cpp`.
// There is no `yield` in GDNative, so we need to have every
// step be its own method that is called on timer timeout.
void HUD::show_get_ready() {
_message_label->set_text("Get Ready");
_message_label->show();
_get_ready_message_timer->start();
}
void HUD::show_game_over() {
_message_label->set_text("Game Over");
_message_label->show();
_start_message_timer->start();
}
This function is called when the player loses. It will show "Game Over" for 2 This function is called when the player loses. It will show "Game Over" for 2
seconds, then return to the title screen and, after a brief pause, show the seconds, then return to the title screen and, after a brief pause, show the
@ -262,72 +144,28 @@ seconds, then return to the title screen and, after a brief pause, show the
can be very useful to add delays such as in the above code, where we can be very useful to add delays such as in the above code, where we
want to wait some time before showing the "Start" button. want to wait some time before showing the "Start" button.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func update_score(score): func update_score(score):
$ScoreLabel.text = str(score) $ScoreLabel.text = str(score)
```
.. code-tab:: csharp
public void UpdateScore(int score)
{
GetNode<Label>("ScoreLabel").Text = score.ToString();
}
.. code-tab:: cpp
// This code goes in `hud.cpp`.
void HUD::update_score(const int p_score) {
_score_label->set_text(godot::Variant(p_score));
}
This function is called by ``Main`` whenever the score changes. This function is called by ``Main`` whenever the score changes.
Connect the ``timeout()`` signal of ``MessageTimer`` and the ``pressed()`` Connect the ``timeout()`` signal of ``MessageTimer`` and the ``pressed()``
signal of ``StartButton`` and add the following code to the new functions: signal of ``StartButton`` and add the following code to the new functions:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_StartButton_pressed(): func _on_StartButton_pressed():
$StartButton.hide() $StartButton.hide()
emit_signal("start_game") emit_signal("start_game")
func _on_MessageTimer_timeout(): func _on_MessageTimer_timeout():
$Message.hide() $Message.hide()
```
.. code-tab:: csharp
public void OnStartButtonPressed()
{
GetNode<Button>("StartButton").Hide();
EmitSignal("StartGame");
}
public void OnMessageTimerTimeout()
{
GetNode<Label>("Message").Hide();
}
.. code-tab:: cpp
// This code goes in `hud.cpp`.
void HUD::_on_StartButton_pressed() {
_start_button_timer->stop();
_start_button->hide();
emit_signal("start_game");
}
void HUD::_on_StartMessageTimer_timeout() {
_message_label->set_text("Dodge the\nCreeps");
_message_label->show();
_start_button_timer->start();
}
void HUD::_on_GetReadyMessageTimer_timeout() {
_message_label->hide();
}
Connecting HUD to Main Connecting HUD to Main
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
@ -348,53 +186,29 @@ next to ``func new_game()`` in the script.
In ``new_game()``, update the score display and show the "Get Ready" message: In ``new_game()``, update the score display and show the "Get Ready" message:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
$HUD.update_score(score) $HUD.update_score(score)
$HUD.show_message("Get Ready") $HUD.show_message("Get Ready")
```
.. code-tab:: csharp
var hud = GetNode<HUD>("HUD");
hud.UpdateScore(Score);
hud.ShowMessage("Get Ready!");
.. code-tab:: cpp
_hud->update_score(score);
_hud->show_get_ready();
In ``game_over()`` we need to call the corresponding ``HUD`` function: In ``game_over()`` we need to call the corresponding ``HUD`` function:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
$HUD.show_game_over() $HUD.show_game_over()
```
.. code-tab:: csharp
GetNode<HUD>("HUD").ShowGameOver();
.. code-tab:: cpp
_hud->show_game_over();
Finally, add this to ``_on_ScoreTimer_timeout()`` to keep the display in sync Finally, add this to ``_on_ScoreTimer_timeout()`` to keep the display in sync
with the changing score: with the changing score:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
$HUD.update_score(score) $HUD.update_score(score)
```
.. code-tab:: csharp
GetNode<HUD>("HUD").UpdateScore(Score);
.. code-tab:: cpp
_hud->update_score(score);
Now you're ready to play! Click the "Play the Project" button. You will be asked Now you're ready to play! Click the "Play the Project" button. You will be asked
to select a main scene, so choose ``Main.tscn``. to select a main scene, so choose ``Main.tscn``.
@ -416,20 +230,11 @@ click "Groups" and you can type a new group name and click "Add".
Now all mobs will be in the "mobs" group. We can then add the following line to Now all mobs will be in the "mobs" group. We can then add the following line to
the ``new_game()`` function in ``Main``: the ``new_game()`` function in ``Main``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
get_tree().call_group("mobs", "queue_free") get_tree().call_group("mobs", "queue_free")
```
.. code-tab:: csharp
// Note that for calling Godot-provided methods with strings,
// we have to use the original Godot snake_case name.
GetTree().CallGroup("mobs", "queue_free");
.. code-tab:: cpp
get_tree()->call_group("mobs", "queue_free");
The ``call_group()`` function calls the named function on every node in a The ``call_group()`` function calls the named function on every node in a
group - in this case we are telling every mob to delete itself. group - in this case we are telling every mob to delete itself.

View File

@ -16,9 +16,9 @@ Let's start with the class's properties. We're going to define a movement speed,
a fall acceleration representing gravity, and a velocity we'll use to move the a fall acceleration representing gravity, and a velocity we'll use to move the
character. character.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody extends KinematicBody
# How fast the player moves in meters per second. # How fast the player moves in meters per second.
@ -27,22 +27,7 @@ character.
export var fall_acceleration = 75 export var fall_acceleration = 75
var velocity = Vector3.ZERO var velocity = Vector3.ZERO
```
.. code-tab:: csharp
public class Player : KinematicBody
{
// Don't forget to rebuild the project so the editor knows about the new export variable.
// How fast the player moves in meters per second.
[Export]
public int Speed = 14;
// The downward acceleration when in the air, in meters per second squared.
[Export]
public int FallAcceleration = 75;
private Vector3 _velocity = Vector3.Zero;
}
These are common properties for a moving body. The ``velocity`` is a 3D vector These are common properties for a moving body. The ``velocity`` is a 3D vector
@ -58,9 +43,9 @@ we want to update and reuse its value across frames.
Let's code the movement now. We start by calculating the input direction vector Let's code the movement now. We start by calculating the input direction vector
using the global ``Input`` object, in ``_physics_process()``. using the global ``Input`` object, in ``_physics_process()``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _physics_process(delta): func _physics_process(delta):
# We create a local variable to store the input direction. # We create a local variable to store the input direction.
var direction = Vector3.ZERO var direction = Vector3.ZERO
@ -76,34 +61,7 @@ using the global ``Input`` object, in ``_physics_process()``.
direction.z += 1 direction.z += 1
if Input.is_action_pressed("move_forward"): if Input.is_action_pressed("move_forward"):
direction.z -= 1 direction.z -= 1
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
// We create a local variable to store the input direction.
var direction = Vector3.Zero;
// We check for each move input and update the direction accordingly
if (Input.IsActionPressed("move_right"))
{
direction.x += 1f;
}
if (Input.IsActionPressed("move_left"))
{
direction.x -= 1f;
}
if (Input.IsActionPressed("move_back"))
{
// Notice how we are working with the vector's x and z axes.
// In 3D, the XZ plane is the ground plane.
direction.z += 1f;
}
if (Input.IsActionPressed("move_forward"))
{
direction.z -= 1f;
}
}
Here, we're going to make all calculations using the ``_physics_process()`` Here, we're going to make all calculations using the ``_physics_process()``
virtual function. Like ``_process()``, it allows you to update the node every virtual function. Like ``_process()``, it allows you to update the node every
@ -127,28 +85,16 @@ have a length of about ``1.4``. But if they press a single key, it will have a
length of ``1``. We want the vector's length to be consistent. To do so, we can length of ``1``. We want the vector's length to be consistent. To do so, we can
call its ``normalize()`` method. call its ``normalize()`` method.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
#func _physics_process(delta): #func _physics_process(delta):
#... #...
if direction != Vector3.ZERO: if direction != Vector3.ZERO:
direction = direction.normalized() direction = direction.normalized()
$Pivot.look_at(translation + direction, Vector3.UP) $Pivot.look_at(translation + direction, Vector3.UP)
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
// ...
if (direction != Vector3.Zero)
{
direction = direction.Normalized();
GetNode<Spatial>("Pivot").LookAt(Translation + direction, Vector3.Up);
}
}
Here, we only normalize the vector if the direction has a length greater than Here, we only normalize the vector if the direction has a length greater than
zero, which means the player is pressing a direction key. zero, which means the player is pressing a direction key.
@ -171,9 +117,9 @@ Then, we update the velocity. We have to calculate the ground velocity and the
fall speed separately. Be sure to go back one tab so the lines are inside the fall speed separately. Be sure to go back one tab so the lines are inside the
``_physics_process()`` function but outside the condition we just wrote. ``_physics_process()`` function but outside the condition we just wrote.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _physics_process(delta): func _physics_process(delta):
#... #...
if direction != Vector3.ZERO: if direction != Vector3.ZERO:
@ -186,21 +132,7 @@ fall speed separately. Be sure to go back one tab so the lines are inside the
velocity.y -= fall_acceleration * delta velocity.y -= fall_acceleration * delta
# Moving the character # Moving the character
velocity = move_and_slide(velocity, Vector3.UP) velocity = move_and_slide(velocity, Vector3.UP)
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
// ...
// Ground velocity
_velocity.x = direction.x * Speed;
_velocity.z = direction.z * Speed;
// Vertical velocity
_velocity.y -= FallAcceleration * delta;
// Moving the character
_velocity = MoveAndSlide(_velocity, Vector3.Up);
}
For the vertical velocity, we subtract the fall acceleration multiplied by the For the vertical velocity, we subtract the fall acceleration multiplied by the
delta time every frame. Notice the use of the ``-=`` operator, which is a delta time every frame. Notice the use of the ``-=`` operator, which is a
@ -230,9 +162,9 @@ And that's all the code you need to move the character on the floor.
Here is the complete ``Player.gd`` code for reference. Here is the complete ``Player.gd`` code for reference.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody extends KinematicBody
# How fast the player moves in meters per second. # How fast the player moves in meters per second.
@ -263,60 +195,7 @@ Here is the complete ``Player.gd`` code for reference.
velocity.z = direction.z * speed velocity.z = direction.z * speed
velocity.y -= fall_acceleration * delta velocity.y -= fall_acceleration * delta
velocity = move_and_slide(velocity, Vector3.UP) velocity = move_and_slide(velocity, Vector3.UP)
```
.. code-tab:: csharp
public class Player : KinematicBody
{
// How fast the player moves in meters per second.
[Export]
public int Speed = 14;
// The downward acceleration when in the air, in meters per second squared.
[Export]
public int FallAcceleration = 75;
private Vector3 _velocity = Vector3.Zero;
public override void _PhysicsProcess(float delta)
{
// We create a local variable to store the input direction.
var direction = Vector3.Zero;
// We check for each move input and update the direction accordingly
if (Input.IsActionPressed("move_right"))
{
direction.x += 1f;
}
if (Input.IsActionPressed("move_left"))
{
direction.x -= 1f;
}
if (Input.IsActionPressed("move_back"))
{
// Notice how we are working with the vector's x and z axes.
// In 3D, the XZ plane is the ground plane.
direction.z += 1f;
}
if (Input.IsActionPressed("move_forward"))
{
direction.z -= 1f;
}
if (direction != Vector3.Zero)
{
direction = direction.Normalized();
GetNode<Spatial>("Pivot").LookAt(Translation + direction, Vector3.Up);
}
// Ground velocity
_velocity.x = direction.x * Speed;
_velocity.z = direction.z * Speed;
// Vertical velocity
_velocity.y -= FallAcceleration * delta;
// Moving the character
_velocity = MoveAndSlide(_velocity, Vector3.Up);
}
}
Testing our player's movement Testing our player's movement
----------------------------- -----------------------------

View File

@ -97,9 +97,9 @@ Here's the movement code to start with. We define two properties, ``min_speed``
and ``max_speed``, to define a random speed range. We then define and initialize and ``max_speed``, to define a random speed range. We then define and initialize
the ``velocity``. the ``velocity``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody extends KinematicBody
# Minimum speed of the mob in meters per second. # Minimum speed of the mob in meters per second.
@ -112,27 +112,7 @@ the ``velocity``.
func _physics_process(_delta): func _physics_process(_delta):
move_and_slide(velocity) move_and_slide(velocity)
```
.. code-tab:: csharp
public class Mob : KinematicBody
{
// Don't forget to rebuild the project so the editor knows about the new export variable.
// Minimum speed of the mob in meters per second
[Export]
public int MinSpeed = 10;
// Maximum speed of the mob in meters per second
[Export]
public int MaxSpeed = 18;
private Vector3 _velocity = Vector3.Zero;
public override void _PhysicsProcess(float delta)
{
MoveAndSlide(_velocity);
}
}
Similarly to the player, we move the mob every frame by calling Similarly to the player, we move the mob every frame by calling
``KinematicBody``\ 's ``move_and_slide()`` method. This time, we don't update ``KinematicBody``\ 's ``move_and_slide()`` method. This time, we don't update
@ -158,26 +138,16 @@ the ``look_at_from_position()`` method, and randomize the angle by rotating a
random amount around the Y axis. Below, ``rand_range()`` outputs a random value random amount around the Y axis. Below, ``rand_range()`` outputs a random value
between ``-PI / 4`` radians and ``PI / 4`` radians. between ``-PI / 4`` radians and ``PI / 4`` radians.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# We will call this function from the Main scene. # We will call this function from the Main scene.
func initialize(start_position, player_position): func initialize(start_position, player_position):
# We position the mob and turn it so that it looks at the player. # We position the mob and turn it so that it looks at the player.
look_at_from_position(start_position, player_position, Vector3.UP) look_at_from_position(start_position, player_position, Vector3.UP)
# And rotate it randomly so it doesn't move exactly toward the player. # And rotate it randomly so it doesn't move exactly toward the player.
rotate_y(rand_range(-PI / 4, PI / 4)) rotate_y(rand_range(-PI / 4, PI / 4))
```
.. code-tab:: csharp
// We will call this function from the Main scene
public void Initialize(Vector3 startPosition, Vector3 playerPosition)
{
// We position the mob and turn it so that it looks at the player.
LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
// And rotate it randomly so it doesn't move exactly toward the player.
RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
}
We then calculate a random speed using ``rand_range()`` once again and we use it We then calculate a random speed using ``rand_range()`` once again and we use it
to calculate the velocity. to calculate the velocity.
@ -186,9 +156,9 @@ We start by creating a 3D vector pointing forward, multiply it by our
``random_speed``, and finally rotate it using the ``Vector3`` class's ``random_speed``, and finally rotate it using the ``Vector3`` class's
``rotated()`` method. ``rotated()`` method.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func initialize(start_position, player_position): func initialize(start_position, player_position):
# ... # ...
@ -198,20 +168,7 @@ We start by creating a 3D vector pointing forward, multiply it by our
velocity = Vector3.FORWARD * random_speed velocity = Vector3.FORWARD * random_speed
# We then rotate the vector based on the mob's Y rotation to move in the direction it's looking. # We then rotate the vector based on the mob's Y rotation to move in the direction it's looking.
velocity = velocity.rotated(Vector3.UP, rotation.y) velocity = velocity.rotated(Vector3.UP, rotation.y)
```
.. code-tab:: csharp
public void Initialize(Vector3 startPosition, Vector3 playerPosition)
{
// ...
// We calculate a random speed.
float randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
// We calculate a forward velocity that represents the speed.
_velocity = Vector3.Forward * randomSpeed;
// We then rotate the vector based on the mob's Y rotation to move in the direction it's looking
_velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
}
Leaving the screen Leaving the screen
------------------ ------------------
@ -238,29 +195,21 @@ This will take you back to the script editor and add a new function for you,
method. This will destroy the mob instance when the *VisibilityNotifier* \'s box method. This will destroy the mob instance when the *VisibilityNotifier* \'s box
leaves the screen. leaves the screen.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_VisibilityNotifier_screen_exited(): func _on_VisibilityNotifier_screen_exited():
queue_free() queue_free()
```
.. code-tab:: csharp
// We also specified this function name in PascalCase in the editor's connection window
public void OnVisibilityNotifierScreenExited()
{
QueueFree();
}
Our monster is ready to enter the game! In the next part, you will spawn Our monster is ready to enter the game! In the next part, you will spawn
monsters in the game level. monsters in the game level.
Here is the complete ``Mob.gd`` script for reference. Here is the complete ``Mob.gd`` script for reference.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody extends KinematicBody
# Minimum speed of the mob in meters per second. # Minimum speed of the mob in meters per second.
@ -285,42 +234,7 @@ Here is the complete ``Mob.gd`` script for reference.
func _on_VisibilityNotifier_screen_exited(): func _on_VisibilityNotifier_screen_exited():
queue_free() queue_free()
```
.. code-tab:: csharp
public class Mob : KinematicBody
{
// Minimum speed of the mob in meters per second
[Export]
public int MinSpeed = 10;
// Maximum speed of the mob in meters per second
[Export]
public int MaxSpeed = 18;
private Vector3 _velocity = Vector3.Zero;
public override void _PhysicsProcess(float delta)
{
MoveAndSlide(_velocity);
}
// We will call this function from the Main scene
public void Initialize(Vector3 startPosition, Vector3 playerPosition)
{
LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
var randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
_velocity = Vector3.Forward * randomSpeed;
_velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
}
// We also specified this function name in PascalCase in the editor's connection window
public void OnVisibilityNotifierScreenExited()
{
QueueFree();
}
}
.. |image0| image:: img/04.mob_scene/01.initial_three_nodes.png .. |image0| image:: img/04.mob_scene/01.initial_three_nodes.png
.. |image1| image:: img/04.mob_scene/02.add_child_node.png .. |image1| image:: img/04.mob_scene/02.add_child_node.png

View File

@ -158,9 +158,9 @@ Then, as we're going to spawn the monsters procedurally, we want to randomize
numbers every time we play the game. If we don't do that, the monsters will numbers every time we play the game. If we don't do that, the monsters will
always spawn following the same sequence. always spawn following the same sequence.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
export (PackedScene) var mob_scene export (PackedScene) var mob_scene
@ -168,24 +168,7 @@ always spawn following the same sequence.
func _ready(): func _ready():
randomize() randomize()
```
.. code-tab:: csharp
public class Main : Node
{
// Don't forget to rebuild the project so the editor knows about the new export variable.
#pragma warning disable 649
// We assign this in the editor, so we don't need the warning about not being assigned.
[Export]
public PackedScene MobScene;
#pragma warning restore 649
public override void _Ready()
{
GD.Randomize();
}
}
We want to spawn mobs at regular time intervals. To do this, we need to go back We want to spawn mobs at regular time intervals. To do this, we need to go back
to the scene and add a timer. Before that, though, we need to assign the to the scene and add a timer. Before that, though, we need to assign the
@ -231,9 +214,8 @@ Let's code the mob spawning logic. We're going to:
the player's position. the player's position.
5. Add the mob as a child of the *Main* node. 5. Add the mob as a child of the *Main* node.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript ```
func _on_MobTimer_timeout(): func _on_MobTimer_timeout():
# Create a new instance of the Mob scene. # Create a new instance of the Mob scene.
var mob = mob_scene.instance() var mob = mob_scene.instance()
@ -248,36 +230,16 @@ Let's code the mob spawning logic. We're going to:
mob.initialize(mob_spawn_location.translation, player_position) mob.initialize(mob_spawn_location.translation, player_position)
add_child(mob) add_child(mob)
```
.. code-tab:: csharp
// We also specified this function name in PascalCase in the editor's connection window
public void OnMobTimerTimeout()
{
// Create a new instance of the Mob scene.
Mob mob = (Mob)MobScene.Instance();
// Choose a random location on the SpawnPath.
// We store the reference to the SpawnLocation node.
var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
// And give it a random offset.
mobSpawnLocation.UnitOffset = GD.Randf();
Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
mob.Initialize(mobSpawnLocation.Translation, playerPosition);
AddChild(mob);
}
Above, ``randf()`` produces a random value between ``0`` and ``1``, which is Above, ``randf()`` produces a random value between ``0`` and ``1``, which is
what the *PathFollow* node's ``unit_offset`` expects. what the *PathFollow* node's ``unit_offset`` expects.
Here is the complete ``Main.gd`` script so far, for reference. Here is the complete ``Main.gd`` script so far, for reference.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
export (PackedScene) var mob_scene export (PackedScene) var mob_scene
@ -296,34 +258,7 @@ Here is the complete ``Main.gd`` script so far, for reference.
mob.initialize(mob_spawn_location.translation, player_position) mob.initialize(mob_spawn_location.translation, player_position)
add_child(mob) add_child(mob)
```
.. code-tab:: csharp
public class Main : Node
{
#pragma warning disable 649
[Export]
public PackedScene MobScene;
#pragma warning restore 649
public override void _Ready()
{
GD.Randomize();
}
public void OnMobTimerTimeout()
{
Mob mob = (Mob)MobScene.Instance();
var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
mobSpawnLocation.UnitOffset = GD.Randf();
Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
mob.Initialize(mobSpawnLocation.Translation, playerPosition);
AddChild(mob);
}
}
You can test the scene by pressing :kbd:`F6`. You should see the monsters spawn and You can test the scene by pressing :kbd:`F6`. You should see the monsters spawn and
move in a straight line. move in a straight line.

View File

@ -109,28 +109,20 @@ script. We need a value to control the jump's strength and update
After the line that defines ``fall_acceleration``, at the top of the script, add After the line that defines ``fall_acceleration``, at the top of the script, add
the ``jump_impulse``. the ``jump_impulse``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
#... #...
# Vertical impulse applied to the character upon jumping in meters per second. # Vertical impulse applied to the character upon jumping in meters per second.
export var jump_impulse = 20 export var jump_impulse = 20
```
.. code-tab:: csharp
// Don't forget to rebuild the project so the editor knows about the new export variable.
// ...
// Vertical impulse applied to the character upon jumping in meters per second.
[Export]
public int JumpImpulse = 20;
Inside ``_physics_process()``, add the following code before the line where we Inside ``_physics_process()``, add the following code before the line where we
called ``move_and_slide()``. called ``move_and_slide()``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _physics_process(delta): func _physics_process(delta):
#... #...
@ -139,21 +131,7 @@ called ``move_and_slide()``.
velocity.y += jump_impulse velocity.y += jump_impulse
#... #...
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
// ...
// Jumping.
if (IsOnFloor() && Input.IsActionJustPressed("jump"))
{
_velocity.y += JumpImpulse;
}
// ...
}
That's all you need to jump! That's all you need to jump!
@ -207,20 +185,13 @@ At the top of the script, we need another property, ``bounce_impulse``. When
squashing an enemy, we don't necessarily want the character to go as high up as squashing an enemy, we don't necessarily want the character to go as high up as
when jumping. when jumping.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Vertical impulse applied to the character upon bouncing over a mob in # Vertical impulse applied to the character upon bouncing over a mob in
# meters per second. # meters per second.
export var bounce_impulse = 16 export var bounce_impulse = 16
```
.. code-tab:: csharp
// Don't forget to rebuild the project so the editor knows about the new export variable.
// Vertical impulse applied to the character upon bouncing over a mob in meters per second.
[Export]
public int BounceImpulse = 16;
Then, at the bottom of ``_physics_process()``, add the following loop. With Then, at the bottom of ``_physics_process()``, add the following loop. With
``move_and_slide()``, Godot makes the body move sometimes multiple times in a ``move_and_slide()``, Godot makes the body move sometimes multiple times in a
@ -232,9 +203,9 @@ it and bounce.
With this code, if no collisions occurred on a given frame, the loop won't run. With this code, if no collisions occurred on a given frame, the loop won't run.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _physics_process(delta): func _physics_process(delta):
#... #...
for index in range(get_slide_count()): for index in range(get_slide_count()):
@ -248,30 +219,7 @@ With this code, if no collisions occurred on a given frame, the loop won't run.
# If so, we squash it and bounce. # If so, we squash it and bounce.
mob.squash() mob.squash()
velocity.y = bounce_impulse velocity.y = bounce_impulse
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
// ...
for (int index = 0; index < GetSlideCount(); index++)
{
// We check every collision that occurred this frame.
KinematicCollision collision = GetSlideCollision(index);
// If we collide with a monster...
if (collision.Collider is Mob mob && mob.IsInGroup("mob"))
{
// ...we check that we are hitting it from above.
if (Vector3.Up.Dot(collision.Normal) > 0.1f)
{
// If so, we squash it and bounce.
mob.Squash();
_velocity.y = BounceImpulse;
}
}
}
}
That's a lot of new functions. Here's some more information about them. That's a lot of new functions. Here's some more information about them.
@ -306,9 +254,9 @@ the top of the script, we want to define a new signal named ``squashed``. And at
the bottom, you can add the squash function, where we emit the signal and the bottom, you can add the squash function, where we emit the signal and
destroy the mob. destroy the mob.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Emitted when the player jumped on the mob. # Emitted when the player jumped on the mob.
signal squashed signal squashed
@ -318,22 +266,7 @@ destroy the mob.
func squash(): func squash():
emit_signal("squashed") emit_signal("squashed")
queue_free() queue_free()
```
.. code-tab:: csharp
// Don't forget to rebuild the project so the editor knows about the new signal.
// Emitted when the played jumped on the mob.
[Signal]
public delegate void Squashed();
// ...
public void Squash()
{
EmitSignal(nameof(Squashed));
QueueFree();
}
We will use the signal to add points to the score in the next lesson. We will use the signal to add points to the score in the next lesson.

View File

@ -60,9 +60,9 @@ Code-wise, we're going to do two things: emit a signal we'll later use
to end the game and destroy the player. We can wrap these operations in to end the game and destroy the player. We can wrap these operations in
a ``die()`` function that helps us put a descriptive label on the code. a ``die()`` function that helps us put a descriptive label on the code.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Emitted when the player was hit by a mob. # Emitted when the player was hit by a mob.
# Put this at the top of the script. # Put this at the top of the script.
signal hit signal hit
@ -76,28 +76,7 @@ a ``die()`` function that helps us put a descriptive label on the code.
func _on_MobDetector_body_entered(_body): func _on_MobDetector_body_entered(_body):
die() die()
```
.. code-tab:: csharp
// Don't forget to rebuild the project so the editor knows about the new signal.
// Emitted when the player was hit by a mob.
[Signal]
public delegate void Hit();
// ...
private void Die()
{
EmitSignal(nameof(Hit));
QueueFree();
}
// We also specified this function name in PascalCase in the editor's connection window
public void OnMobDetectorBodyEntered(Node body)
{
Die();
}
Try the game again by pressing :kbd:`F5`. If everything is set up correctly, Try the game again by pressing :kbd:`F5`. If everything is set up correctly,
the character should die when an enemy runs into it. the character should die when an enemy runs into it.
@ -120,19 +99,12 @@ connect its ``hit`` signal to the *Main* node.
Get and stop the timer in the ``_on_Player_hit()`` function. Get and stop the timer in the ``_on_Player_hit()`` function.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_Player_hit(): func _on_Player_hit():
$MobTimer.stop() $MobTimer.stop()
```
.. code-tab:: csharp
// We also specified this function name in PascalCase in the editor's connection window
public void OnPlayerHit()
{
GetNode<Timer>("MobTimer").Stop();
}
If you try the game now, the monsters will stop spawning when you die, If you try the game now, the monsters will stop spawning when you die,
and the remaining ones will leave the screen. and the remaining ones will leave the screen.
@ -152,9 +124,9 @@ for reference. You can use them to compare and check your code.
Starting with ``Main.gd``. Starting with ``Main.gd``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
export(PackedScene) var mob_scene export(PackedScene) var mob_scene
@ -183,51 +155,13 @@ Starting with ``Main.gd``.
func _on_Player_hit(): func _on_Player_hit():
$MobTimer.stop() $MobTimer.stop()
```
.. code-tab:: csharp
public class Main : Node
{
#pragma warning disable 649
[Export]
public PackedScene MobScene;
#pragma warning restore 649
public override void _Ready()
{
GD.Randomize();
}
public void OnMobTimerTimeout()
{
// Create a new instance of the Mob scene.
var mob = (Mob)MobScene.Instance();
// Choose a random location on the SpawnPath.
// We store the reference to the SpawnLocation node.
var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
// And give it a random offset.
mobSpawnLocation.UnitOffset = GD.Randf();
// Communicate the spawn location and the player's location to the mob.
Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
mob.Initialize(mobSpawnLocation.Translation, playerPosition);
// Spawn the mob by adding it to the Main scene.
AddChild(mob);
}
public void OnPlayerHit()
{
GetNode<Timer>("MobTimer").Stop();
}
}
Next is ``Mob.gd``. Next is ``Mob.gd``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody extends KinematicBody
# Emitted when the player jumped on the mob. # Emitted when the player jumped on the mob.
@ -261,56 +195,13 @@ Next is ``Mob.gd``.
func _on_VisibilityNotifier_screen_exited(): func _on_VisibilityNotifier_screen_exited():
queue_free() queue_free()
```
.. code-tab:: csharp
public class Mob : KinematicBody
{
// Emitted when the played jumped on the mob.
[Signal]
public delegate void Squashed();
// Minimum speed of the mob in meters per second
[Export]
public int MinSpeed = 10;
// Maximum speed of the mob in meters per second
[Export]
public int MaxSpeed = 18;
private Vector3 _velocity = Vector3.Zero;
public override void _PhysicsProcess(float delta)
{
MoveAndSlide(_velocity);
}
public void Initialize(Vector3 startPosition, Vector3 playerPosition)
{
LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
float randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
_velocity = Vector3.Forward * randomSpeed;
_velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
}
public void Squash()
{
EmitSignal(nameof(Squashed));
QueueFree();
}
public void OnVisibilityNotifierScreenExited()
{
QueueFree();
}
}
Finally, the longest script, ``Player.gd``. Finally, the longest script, ``Player.gd``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody extends KinematicBody
# Emitted when a mob hit the player. # Emitted when a mob hit the player.
@ -370,94 +261,7 @@ Finally, the longest script, ``Player.gd``.
func _on_MobDetector_body_entered(_body): func _on_MobDetector_body_entered(_body):
die() die()
```
.. code-tab:: csharp
public class Player : KinematicBody
{
// Emitted when the player was hit by a mob.
[Signal]
public delegate void Hit();
// How fast the player moves in meters per second.
[Export]
public int Speed = 14;
// The downward acceleration when in the air, in meters per second squared.
[Export]
public int FallAcceleration = 75;
// Vertical impulse applied to the character upon jumping in meters per second.
[Export]
public int JumpImpulse = 20;
// Vertical impulse applied to the character upon bouncing over a mob in meters per second.
[Export]
public int BounceImpulse = 16;
private Vector3 _velocity = Vector3.Zero;
public override void _PhysicsProcess(float delta)
{
var direction = Vector3.Zero;
if (Input.IsActionPressed("move_right"))
{
direction.x += 1f;
}
if (Input.IsActionPressed("move_left"))
{
direction.x -= 1f;
}
if (Input.IsActionPressed("move_back"))
{
direction.z += 1f;
}
if (Input.IsActionPressed("move_forward"))
{
direction.z -= 1f;
}
if (direction != Vector3.Zero)
{
direction = direction.Normalized();
GetNode<Spatial>("Pivot").LookAt(Translation + direction, Vector3.Up);
}
_velocity.x = direction.x * Speed;
_velocity.z = direction.z * Speed;
// Jumping.
if (IsOnFloor() && Input.IsActionJustPressed("jump"))
{
_velocity.y += JumpImpulse;
}
_velocity.y -= FallAcceleration * delta;
_velocity = MoveAndSlide(_velocity, Vector3.Up);
for (int index = 0; index < GetSlideCount(); index++)
{
KinematicCollision collision = GetSlideCollision(index);
if (collision.Collider is Mob mob && mob.IsInGroup("mob"))
{
if (Vector3.Up.Dot(collision.Normal) > 0.1f)
{
mob.Squash();
_velocity.y = BounceImpulse;
}
}
}
}
private void Die()
{
EmitSignal(nameof(Hit));
QueueFree();
}
public void OnMobDetectorBodyEntered(Node body)
{
Die();
}
}
See you in the next lesson to add the score and the retry option. See you in the next lesson to add the score and the retry option.

View File

@ -95,19 +95,13 @@ Keeping track of the score
Let's work on the score next. Attach a new script to the *ScoreLabel* and define Let's work on the score next. Attach a new script to the *ScoreLabel* and define
the ``score`` variable. the ``score`` variable.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Label extends Label
var score = 0 var score = 0
```
.. code-tab:: csharp
public class ScoreLabel : Label
{
private int _score = 0;
}
The score should increase by ``1`` every time we squash a monster. We can use The score should increase by ``1`` every time we squash a monster. We can use
their ``squashed`` signal to know when that happens. However, as we instantiate their ``squashed`` signal to know when that happens. However, as we instantiate
@ -127,22 +121,14 @@ dock.
At the bottom of the ``_on_MobTimer_timeout()`` function, add the following At the bottom of the ``_on_MobTimer_timeout()`` function, add the following
line. line.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_MobTimer_timeout(): func _on_MobTimer_timeout():
#... #...
# We connect the mob to the score label to update the score upon squashing one. # We connect the mob to the score label to update the score upon squashing one.
mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed") mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed")
```
.. code-tab:: csharp
public void OnMobTimerTimeout()
{
// ...
// We connect the mob to the score label to update the score upon squashing one.
mob.Connect(nameof(Mob.Squashed), GetNode<ScoreLabel>("UserInterface/ScoreLabel"), nameof(ScoreLabel.OnMobSquashed));
}
This line means that when the mob emits the ``squashed`` signal, the This line means that when the mob emits the ``squashed`` signal, the
*ScoreLabel* node will receive it and call the function ``_on_Mob_squashed()``. *ScoreLabel* node will receive it and call the function ``_on_Mob_squashed()``.
@ -152,20 +138,13 @@ callback function.
There, we increment the score and update the displayed text. There, we increment the score and update the displayed text.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_Mob_squashed(): func _on_Mob_squashed():
score += 1 score += 1
text = "Score: %s" % score text = "Score: %s" % score
```
.. code-tab:: csharp
public void OnMobSquashed()
{
_score += 1;
Text = string.Format("Score: {0}", _score);
}
The second line uses the value of the ``score`` variable to replace the The second line uses the value of the ``score`` variable to replace the
placeholder ``%s``. When using this feature, Godot automatically converts values placeholder ``%s``. When using this feature, Godot automatically converts values
@ -247,37 +226,24 @@ dies and plays again.
Open the script ``Main.gd``. First, we want to hide the overlay at the start of Open the script ``Main.gd``. First, we want to hide the overlay at the start of
the game. Add this line to the ``_ready()`` function. the game. Add this line to the ``_ready()`` function.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
#... #...
$UserInterface/Retry.hide() $UserInterface/Retry.hide()
```
.. code-tab:: csharp
public override void _Ready()
{
// ...
GetNode<Control>("UserInterface/Retry").Hide();
}
Then, when the player gets hit, we show the overlay. Then, when the player gets hit, we show the overlay.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_Player_hit(): func _on_Player_hit():
#... #...
$UserInterface/Retry.show() $UserInterface/Retry.show()
```
.. code-tab:: csharp
public void OnPlayerHit()
{
//...
GetNode<Control>("UserInterface/Retry").Show();
}
Finally, when the *Retry* node is visible, we need to listen to the player's Finally, when the *Retry* node is visible, we need to listen to the player's
input and restart the game if they press enter. To do this, we use the built-in input and restart the game if they press enter. To do this, we use the built-in
@ -286,24 +252,14 @@ input and restart the game if they press enter. To do this, we use the built-in
If the player pressed the predefined ``ui_accept`` input action and *Retry* is If the player pressed the predefined ``ui_accept`` input action and *Retry* is
visible, we reload the current scene. visible, we reload the current scene.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _unhandled_input(event): func _unhandled_input(event):
if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible: if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
# This restarts the current scene. # This restarts the current scene.
get_tree().reload_current_scene() get_tree().reload_current_scene()
```
.. code-tab:: csharp
public override void _UnhandledInput(InputEvent @event)
{
if (@event.IsActionPressed("ui_accept") && GetNode<Control>("UserInterface/Retry").Visible)
{
// This restarts the current scene.
GetTree().ReloadCurrentScene();
}
}
The function ``get_tree()`` gives us access to the global :ref:`SceneTree The function ``get_tree()`` gives us access to the global :ref:`SceneTree
<class_SceneTree>` object, which allows us to reload and restart the current <class_SceneTree>` object, which allows us to reload and restart the current
@ -374,9 +330,9 @@ make the game both look and feel much nicer.
Here is the complete ``Main.gd`` script for reference. Here is the complete ``Main.gd`` script for reference.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
export (PackedScene) var mob_scene export (PackedScene) var mob_scene
@ -408,51 +364,7 @@ Here is the complete ``Main.gd`` script for reference.
func _on_Player_hit(): func _on_Player_hit():
$MobTimer.stop() $MobTimer.stop()
$UserInterface/Retry.show() $UserInterface/Retry.show()
```
.. code-tab:: csharp
public class Main : Node
{
#pragma warning disable 649
[Export]
public PackedScene MobScene;
#pragma warning restore 649
public override void _Ready()
{
GD.Randomize();
GetNode<Control>("UserInterface/Retry").Hide();
}
public override void _UnhandledInput(InputEvent @event)
{
if (@event.IsActionPressed("ui_accept") && GetNode<Control>("UserInterface/Retry").Visible)
{
GetTree().ReloadCurrentScene();
}
}
public void OnMobTimerTimeout()
{
Mob mob = (Mob)MobScene.Instance();
var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
mobSpawnLocation.UnitOffset = GD.Randf();
Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
mob.Initialize(mobSpawnLocation.Translation, playerPosition);
AddChild(mob);
mob.Connect(nameof(Mob.Squashed), GetNode<ScoreLabel>("UserInterface/ScoreLabel"), nameof(ScoreLabel.OnMobSquashed));
}
public void OnPlayerHit()
{
GetNode<Timer>("MobTimer").Stop();
GetNode<Control>("UserInterface/Retry").Show();
}
}
.. |image0| image:: img/08.score_and_replay/01.label_node.png .. |image0| image:: img/08.score_and_replay/01.label_node.png
.. |image1| image:: img/08.score_and_replay/02.score_placeholder.png .. |image1| image:: img/08.score_and_replay/02.score_placeholder.png

View File

@ -186,9 +186,9 @@ Open the *Player*'s script by clicking the script icon next to it.
In ``_physics_process()``, after the line where we check the ``direction`` In ``_physics_process()``, after the line where we check the ``direction``
vector, add the following code. vector, add the following code.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _physics_process(delta): func _physics_process(delta):
#... #...
#if direction != Vector3.ZERO: #if direction != Vector3.ZERO:
@ -196,22 +196,7 @@ vector, add the following code.
$AnimationPlayer.playback_speed = 4 $AnimationPlayer.playback_speed = 4
else: else:
$AnimationPlayer.playback_speed = 1 $AnimationPlayer.playback_speed = 1
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
// ...
if (direction != Vector3.Zero)
{
// ...
GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 4;
}
else
{
GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 1;
}
}
This code makes it so when the player moves, we multiply the playback speed by This code makes it so when the player moves, we multiply the playback speed by
``4``. When they stop, we reset it to normal. ``4``. When they stop, we reset it to normal.
@ -220,21 +205,13 @@ We mentioned that the pivot could layer transforms on top of the animation. We
can make the character arc when jumping using the following line of code. Add it can make the character arc when jumping using the following line of code. Add it
at the end of ``_physics_process()``. at the end of ``_physics_process()``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _physics_process(delta): func _physics_process(delta):
#... #...
$Pivot.rotation.x = PI / 6 * velocity.y / jump_impulse $Pivot.rotation.x = PI / 6 * velocity.y / jump_impulse
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
// ...
var pivot = GetNode<Spatial>("Pivot");
pivot.Rotation = new Vector3(Mathf.Pi / 6f * _velocity.y / JumpImpulse, pivot.Rotation.y, pivot.Rotation.z);
}
Animating the mobs Animating the mobs
------------------ ------------------
@ -254,20 +231,13 @@ We can change the playback speed based on the creature's ``random_speed``. Open
the *Mob*'s script and at the end of the ``initialize()`` function, add the the *Mob*'s script and at the end of the ``initialize()`` function, add the
following line. following line.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func initialize(start_position, player_position): func initialize(start_position, player_position):
#... #...
$AnimationPlayer.playback_speed = random_speed / min_speed $AnimationPlayer.playback_speed = random_speed / min_speed
```
.. code-tab:: csharp
public void Initialize(Vector3 startPosition, Vector3 playerPosition)
{
// ...
GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = randomSpeed / MinSpeed;
}
And with that, you finished coding your first complete 3D game. And with that, you finished coding your first complete 3D game.
@ -279,9 +249,9 @@ to keep learning more. But for now, here are the complete ``Player.gd`` and
Here's the *Player* script. Here's the *Player* script.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody extends KinematicBody
# Emitted when the player was hit by a mob. # Emitted when the player was hit by a mob.
@ -346,109 +316,13 @@ Here's the *Player* script.
func _on_MobDetector_body_entered(_body): func _on_MobDetector_body_entered(_body):
die() die()
```
.. code-tab:: csharp
public class Player : KinematicBody
{
// Emitted when the player was hit by a mob.
[Signal]
public delegate void Hit();
// How fast the player moves in meters per second.
[Export]
public int Speed = 14;
// The downward acceleration when in the air, in meters per second squared.
[Export]
public int FallAcceleration = 75;
// Vertical impulse applied to the character upon jumping in meters per second.
[Export]
public int JumpImpulse = 20;
// Vertical impulse applied to the character upon bouncing over a mob in meters per second.
[Export]
public int BounceImpulse = 16;
private Vector3 _velocity = Vector3.Zero;
public override void _PhysicsProcess(float delta)
{
var direction = Vector3.Zero;
if (Input.IsActionPressed("move_right"))
{
direction.x += 1f;
}
if (Input.IsActionPressed("move_left"))
{
direction.x -= 1f;
}
if (Input.IsActionPressed("move_back"))
{
direction.z += 1f;
}
if (Input.IsActionPressed("move_forward"))
{
direction.z -= 1f;
}
if (direction != Vector3.Zero)
{
direction = direction.Normalized();
GetNode<Spatial>("Pivot").LookAt(Translation + direction, Vector3.Up);
GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 4;
}
else
{
GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 1;
}
_velocity.x = direction.x * Speed;
_velocity.z = direction.z * Speed;
// Jumping.
if (IsOnFloor() && Input.IsActionJustPressed("jump"))
{
_velocity.y += JumpImpulse;
}
_velocity.y -= FallAcceleration * delta;
_velocity = MoveAndSlide(_velocity, Vector3.Up);
for (int index = 0; index < GetSlideCount(); index++)
{
KinematicCollision collision = GetSlideCollision(index);
if (collision.Collider is Mob mob && mob.IsInGroup("mob"))
{
if (Vector3.Up.Dot(collision.Normal) > 0.1f)
{
mob.Squash();
_velocity.y = BounceImpulse;
}
}
}
var pivot = GetNode<Spatial>("Pivot");
pivot.Rotation = new Vector3(Mathf.Pi / 6f * _velocity.y / JumpImpulse, pivot.Rotation.y, pivot.Rotation.z);
}
private void Die()
{
EmitSignal(nameof(Hit));
QueueFree();
}
public void OnMobDetectorBodyEntered(Node body)
{
Die();
}
}
And the *Mob*'s script. And the *Mob*'s script.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody extends KinematicBody
# Emitted when the player jumped on the mob. # Emitted when the player jumped on the mob.
@ -484,52 +358,7 @@ And the *Mob*'s script.
func _on_VisibilityNotifier_screen_exited(): func _on_VisibilityNotifier_screen_exited():
queue_free() queue_free()
```
.. code-tab:: csharp
public class Mob : KinematicBody
{
// Emitted when the played jumped on the mob.
[Signal]
public delegate void Squashed();
// Minimum speed of the mob in meters per second
[Export]
public int MinSpeed = 10;
// Maximum speed of the mob in meters per second
[Export]
public int MaxSpeed = 18;
private Vector3 _velocity = Vector3.Zero;
public override void _PhysicsProcess(float delta)
{
MoveAndSlide(_velocity);
}
public void Initialize(Vector3 startPosition, Vector3 playerPosition)
{
LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
float randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
_velocity = Vector3.Forward * randomSpeed;
_velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = randomSpeed / MinSpeed;
}
public void Squash()
{
EmitSignal(nameof(Squashed));
QueueFree();
}
public void OnVisibilityNotifierScreenExited()
{
QueueFree();
}
}
.. |image0| image:: img/squash-the-creeps-final.gif .. |image0| image:: img/squash-the-creeps-final.gif
.. |image1| image:: img/09.adding_animations/01.animation_player_dock.png .. |image1| image:: img/09.adding_animations/01.animation_player_dock.png

View File

@ -89,29 +89,11 @@ other options by default and click the Create button to create the script.
The Script workspace should appear with your new ``Sprite.gd`` file open and the The Script workspace should appear with your new ``Sprite.gd`` file open and the
following line of code: following line of code:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Sprite extends Sprite
```
.. code-tab:: csharp C#
public class Sprite : Godot.Sprite
// Declare member variables here. Examples:
// private int a = 2;
// private string b = "text";
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
}
// // Called every frame. 'delta' is the elapsed time since the previous frame.
// public override void _Process(float delta)
// {
//
// }
Every GDScript file is implicitly a class. The ``extends`` keyword defines the Every GDScript file is implicitly a class. The ``extends`` keyword defines the
class this script inherits or extends. In this case, it's ``Sprite``, meaning class this script inherits or extends. In this case, it's ``Sprite``, meaning
@ -143,18 +125,12 @@ world!" to the Output bottom panel to get started.
Add the following code to your script: Add the following code to your script:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _init(): func _init():
print("Hello, world!") print("Hello, world!")
```
.. code-tab:: csharp C#
public Sprite()
{
GD.Print("Hello, world!");
}
Let's break it down. The ``func`` keyword defines a new function named Let's break it down. The ``func`` keyword defines a new function named
@ -183,16 +159,12 @@ It's time to make our node move and rotate. To do so, we're going to add two
member variables to our script: the movement speed in pixels per second and the member variables to our script: the movement speed in pixels per second and the
angular speed in radians per second. angular speed in radians per second.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var speed = 400 var speed = 400
var angular_speed = PI var angular_speed = PI
```
.. code-tab:: csharp C#
private int Speed = 400;
private float AngularSpeed = Mathf.Pi;
Member variables sit near the top of the script, after any "extends" lines, Member variables sit near the top of the script, after any "extends" lines,
but before functions. Every node but before functions. Every node
@ -224,18 +196,12 @@ time elapsed since the last frame.
At the bottom of the script, define the function: At the bottom of the script, define the function:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _process(delta): func _process(delta):
rotation += angular_speed * delta rotation += angular_speed * delta
```
.. code-tab:: csharp C#
public override void _Process(float delta)
{
Rotation += AngularSpeed * delta;
}
The ``func`` keyword defines a new function. After it, we have to write the The ``func`` keyword defines a new function. After it, we have to write the
function's name and arguments it takes in parentheses. A colon ends the function's name and arguments it takes in parentheses. A colon ends the
@ -267,18 +233,13 @@ Let's now make the node move. Add the following two lines to the ``_process()``
function, ensuring the new lines are indented the same way as the one before function, ensuring the new lines are indented the same way as the one before
them. them.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var velocity = Vector2.UP.rotated(rotation) * speed var velocity = Vector2.UP.rotated(rotation) * speed
position += velocity * delta position += velocity * delta
```
.. code-tab:: csharp C#
var velocity = Vector2.Up.Rotated(Rotation) * Speed;
Position += velocity * delta;
As we already saw, the ``var`` keyword defines a new variable. If you put it at As we already saw, the ``var`` keyword defines a new variable. If you put it at
the top of the script, it defines a property of the class. Inside a function, it the top of the script, it defines a property of the class. Inside a function, it
@ -311,9 +272,9 @@ Complete script
Here is the complete ``Sprite.gd`` file for reference. Here is the complete ``Sprite.gd`` file for reference.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Sprite extends Sprite
var speed = 400 var speed = 400
@ -326,22 +287,4 @@ Here is the complete ``Sprite.gd`` file for reference.
var velocity = Vector2.UP.rotated(rotation) * speed var velocity = Vector2.UP.rotated(rotation) * speed
position += velocity * delta position += velocity * delta
```
.. code-tab:: csharp C#
using Godot;
public class Sprite : Godot.Sprite
{
private int Speed = 400;
private float AngularSpeed = Mathf.Pi;
public override void _Process(float delta)
{
Rotation += AngularSpeed * delta;
var velocity = Vector2.Up.Rotated(Rotation) * Speed;
Position += velocity * delta;
}
}

View File

@ -31,9 +31,9 @@ For turning, we should use a new variable: ``direction``. In our ``_process()``
function, replace the ``rotation += angular_speed * delta`` line with the function, replace the ``rotation += angular_speed * delta`` line with the
code below. code below.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var direction = 0 var direction = 0
if Input.is_action_pressed("ui_left"): if Input.is_action_pressed("ui_left"):
direction = -1 direction = -1
@ -41,20 +41,7 @@ code below.
direction = 1 direction = 1
rotation += angular_speed * direction * delta rotation += angular_speed * direction * delta
```
.. code-tab:: csharp C#
var direction = 0;
if (Input.IsActionPressed("ui_left"))
{
direction = -1;
}
if (Input.IsActionPressed("ui_right"))
{
direction = 1;
}
Rotation += AngularSpeed * direction * delta;
Our ``direction`` local variable is a multiplier representing the direction in Our ``direction`` local variable is a multiplier representing the direction in
which the player wants to turn. A value of ``0`` means the player isn't pressing which the player wants to turn. A value of ``0`` means the player isn't pressing
@ -88,20 +75,13 @@ Moving when pressing "up"
To only move when pressing a key, we need to modify the code that calculates the To only move when pressing a key, we need to modify the code that calculates the
velocity. Replace the line starting with ``var velocity`` with the code below. velocity. Replace the line starting with ``var velocity`` with the code below.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var velocity = Vector2.ZERO var velocity = Vector2.ZERO
if Input.is_action_pressed("ui_up"): if Input.is_action_pressed("ui_up"):
velocity = Vector2.UP.rotated(rotation) * speed velocity = Vector2.UP.rotated(rotation) * speed
```
.. code-tab:: csharp C#
var velocity = Vector2.Zero;
if (Input.IsActionPressed("ui_up"))
{
velocity = Vector2.Up.Rotated(Rotation) * Speed;
}
We initialize the ``velocity`` with a value of ``Vector2.ZERO``, another We initialize the ``velocity`` with a value of ``Vector2.ZERO``, another
constant of the built-in ``Vector`` type representing a 2D vector of length 0. constant of the built-in ``Vector`` type representing a 2D vector of length 0.
@ -114,9 +94,9 @@ Complete script
Here is the complete ``Sprite.gd`` file for reference. Here is the complete ``Sprite.gd`` file for reference.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Sprite extends Sprite
var speed = 400 var speed = 400
@ -137,39 +117,7 @@ Here is the complete ``Sprite.gd`` file for reference.
velocity = Vector2.UP.rotated(rotation) * speed velocity = Vector2.UP.rotated(rotation) * speed
position += velocity * delta position += velocity * delta
```
.. code-tab:: csharp C#
using Godot;
public class Sprite : Godot.Sprite
{
private float Speed = 400;
private float AngularSpeed = Mathf.Pi;
public override void _Process(float delta)
{
var direction = 0;
if (Input.IsActionPressed("ui_left"))
{
direction = -1;
}
if (Input.IsActionPressed("ui_right"))
{
direction = 1;
}
Rotation += AngularSpeed * direction * delta;
var velocity = Vector2.Zero;
if (Input.IsActionPressed("ui_up"))
{
velocity = Vector2.Up.Rotated(Rotation) * Speed;
}
Position += velocity * delta;
}
}
If you run the scene, you should now be able to rotate with the left and right If you run the scene, you should now be able to rotate with the left and right
arrow keys and move forward by pressing :kbd:`Up`. arrow keys and move forward by pressing :kbd:`Up`.

View File

@ -146,11 +146,12 @@ method to toggle processing on and off: :ref:`Node.set_process()
``is_processing()``, returns ``true`` if idle processing is active. We can use ``is_processing()``, returns ``true`` if idle processing is active. We can use
the ``not`` keyword to invert the value. the ``not`` keyword to invert the value.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_Button_pressed(): func _on_Button_pressed():
set_process(not is_processing()) set_process(not is_processing())
```
This function will toggle processing and, in turn, the icon's motion on and off This function will toggle processing and, in turn, the icon's motion on and off
upon pressing the button. upon pressing the button.
@ -159,19 +160,20 @@ Before trying the game, we need to simplify our ``_process()`` function to move
the node automatically and not wait for user input. Replace it with the the node automatically and not wait for user input. Replace it with the
following code, which we saw two lessons ago: following code, which we saw two lessons ago:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _process(delta): func _process(delta):
rotation += angular_speed * delta rotation += angular_speed * delta
var velocity = Vector2.UP.rotated(rotation) * speed var velocity = Vector2.UP.rotated(rotation) * speed
position += velocity * delta position += velocity * delta
```
Your complete ``Sprite.gd`` code should look like the following. Your complete ``Sprite.gd`` code should look like the following.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Sprite extends Sprite
var speed = 400 var speed = 400
@ -186,6 +188,7 @@ Your complete ``Sprite.gd`` code should look like the following.
func _on_Button_pressed(): func _on_Button_pressed():
set_process(not is_processing()) set_process(not is_processing())
```
Run the scene now and click the button to see the sprite start and stop. Run the scene now and click the button to see the sprite start and stop.
@ -233,11 +236,12 @@ To get a reference to a node relative to the current one, we use the method
:ref:`Node.get_node() <class_Node_method_get_node>`. We can store the reference :ref:`Node.get_node() <class_Node_method_get_node>`. We can store the reference
in a variable. in a variable.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
var timer = get_node("Timer") var timer = get_node("Timer")
```
The function ``get_node()`` looks at the Sprite's children and gets nodes by The function ``get_node()`` looks at the Sprite's children and gets nodes by
their name. For example, if you renamed the Timer node to "BlinkingTimer" in the their name. For example, if you renamed the Timer node to "BlinkingTimer" in the
@ -247,23 +251,25 @@ editor, you would have to change the call to ``get_node("BlinkingTimer")``.
We can now connect the Timer to the Sprite in the ``_ready()`` function. We can now connect the Timer to the Sprite in the ``_ready()`` function.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
var timer = get_node("Timer") var timer = get_node("Timer")
timer.connect("timeout", self, "_on_Timer_timeout") timer.connect("timeout", self, "_on_Timer_timeout")
```
The line reads like so: we connect the Timer's "timeout" signal to the node to The line reads like so: we connect the Timer's "timeout" signal to the node to
which the script is attached (``self``). When the Timer emits "timeout", we want which the script is attached (``self``). When the Timer emits "timeout", we want
to call the function "_on_Timer_timeout", that we need to define. Let's add it to call the function "_on_Timer_timeout", that we need to define. Let's add it
at the bottom of our script and use it to toggle our sprite's visibility. at the bottom of our script and use it to toggle our sprite's visibility.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_Timer_timeout(): func _on_Timer_timeout():
visible = not visible visible = not visible
```
The ``visible`` property is a boolean that controls the visibility of our node. The ``visible`` property is a boolean that controls the visibility of our node.
The line ``visible = not visible`` toggles the value. If ``visible`` is The line ``visible = not visible`` toggles the value. If ``visible`` is
@ -278,9 +284,9 @@ Complete script
That's it for our little moving and blinking Godot icon demo! That's it for our little moving and blinking Godot icon demo!
Here is the complete ``Sprite.gd`` file for reference. Here is the complete ``Sprite.gd`` file for reference.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Sprite extends Sprite
var speed = 400 var speed = 400
@ -304,6 +310,7 @@ Here is the complete ``Sprite.gd`` file for reference.
func _on_Timer_timeout(): func _on_Timer_timeout():
visible = not visible visible = not visible
```
Custom signals Custom signals
-------------- --------------
@ -316,14 +323,15 @@ show a game over screen when the player's health reaches zero. To do so, you
could define a signal named "died" or "health_depleted" when their health could define a signal named "died" or "health_depleted" when their health
reaches 0. reaches 0.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node2D extends Node2D
signal health_depleted signal health_depleted
var health = 10 var health = 10
```
.. note:: As signals represent events that just occurred, we generally use an .. note:: As signals represent events that just occurred, we generally use an
action verb in the past tense in their names. action verb in the past tense in their names.
@ -335,23 +343,25 @@ you can connect to them like any other.
To emit a signal in your scripts, call ``emit_signal()``. To emit a signal in your scripts, call ``emit_signal()``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func take_damage(amount): func take_damage(amount):
health -= amount health -= amount
if health <= 0: if health <= 0:
emit_signal("health_depleted") emit_signal("health_depleted")
```
A signal can optionally declare one or more arguments. Specify the argument A signal can optionally declare one or more arguments. Specify the argument
names between parentheses: names between parentheses:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
signal health_changed(old_value, new_value) signal health_changed(old_value, new_value)
```
.. note:: .. note::
@ -363,13 +373,14 @@ names between parentheses:
To emit values along with the signal, add them as extra arguments to the To emit values along with the signal, add them as extra arguments to the
``emit_signal()`` function: ``emit_signal()`` function:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func take_damage(amount): func take_damage(amount):
var old_health = health var old_health = health
health -= amount health -= amount
emit_signal("health_changed", old_health, health) emit_signal("health_changed", old_health, health)
```
Summary Summary
------- -------

View File

@ -38,9 +38,9 @@ fact that the player can move diagonally by pressing two keys at the same time.
Add a script to the kinematic body and add the following code: Add a script to the kinematic body and add the following code:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
export (int) var speed = 200 export (int) var speed = 200
@ -62,43 +62,7 @@ Add a script to the kinematic body and add the following code:
func _physics_process(delta): func _physics_process(delta):
get_input() get_input()
velocity = move_and_slide(velocity) velocity = move_and_slide(velocity)
```
.. code-tab:: csharp
using Godot;
using System;
public class Movement : KinematicBody2D
{
[Export] public int speed = 200;
public Vector2 velocity = new Vector2();
public void GetInput()
{
velocity = new Vector2();
if (Input.IsActionPressed("right"))
velocity.x += 1;
if (Input.IsActionPressed("left"))
velocity.x -= 1;
if (Input.IsActionPressed("down"))
velocity.y += 1;
if (Input.IsActionPressed("up"))
velocity.y -= 1;
velocity = velocity.Normalized() * speed;
}
public override void _PhysicsProcess(float delta)
{
GetInput();
velocity = MoveAndSlide(velocity);
}
}
In the ``get_input()`` function, we check for the four key events and sum them In the ``get_input()`` function, we check for the four key events and sum them
up to get the velocity vector. This has the benefit of making two opposite keys up to get the velocity vector. This has the benefit of making two opposite keys
@ -126,9 +90,9 @@ while up/down moves it forward or backward in whatever direction it's facing.
.. image:: img/movement_rotate1.gif .. image:: img/movement_rotate1.gif
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
export (int) var speed = 200 export (int) var speed = 200
@ -153,47 +117,7 @@ while up/down moves it forward or backward in whatever direction it's facing.
get_input() get_input()
rotation += rotation_dir * rotation_speed * delta rotation += rotation_dir * rotation_speed * delta
velocity = move_and_slide(velocity) velocity = move_and_slide(velocity)
```
.. code-tab:: csharp
using Godot;
using System;
public class Movement : KinematicBody2D
{
[Export] public int speed = 200;
[Export] public float rotationSpeed = 1.5f;
public Vector2 velocity = new Vector2();
public int rotationDir = 0;
public void GetInput()
{
rotationDir = 0;
velocity = new Vector2();
if (Input.IsActionPressed("right"))
rotationDir += 1;
if (Input.IsActionPressed("left"))
rotationDir -= 1;
if (Input.IsActionPressed("down"))
velocity = new Vector2(-speed, 0).Rotated(Rotation);
if (Input.IsActionPressed("up"))
velocity = new Vector2(speed, 0).Rotated(Rotation);
velocity = velocity.Normalized() * speed;
}
public override void _PhysicsProcess(float delta)
{
GetInput();
Rotation += rotationDir * rotationSpeed * delta;
velocity = MoveAndSlide(velocity);
}
}
Here we've added two new variables to track our rotation direction and speed. Here we've added two new variables to track our rotation direction and speed.
Again, pressing both keys at once will cancel out and result in no rotation. Again, pressing both keys at once will cancel out and result in no rotation.
@ -213,9 +137,9 @@ is set by the mouse position instead of the keyboard. The character will always
.. image:: img/movement_rotate2.gif .. image:: img/movement_rotate2.gif
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
export (int) var speed = 200 export (int) var speed = 200
@ -233,51 +157,17 @@ is set by the mouse position instead of the keyboard. The character will always
func _physics_process(delta): func _physics_process(delta):
get_input() get_input()
velocity = move_and_slide(velocity) velocity = move_and_slide(velocity)
```
.. code-tab:: csharp
using Godot;
using System;
public class Movement : KinematicBody2D
{
[Export] public int speed = 200;
public Vector2 velocity = new Vector2();
public void GetInput()
{
LookAt(GetGlobalMousePosition());
velocity = new Vector2();
if (Input.IsActionPressed("down"))
velocity = new Vector2(-speed, 0).Rotated(Rotation);
if (Input.IsActionPressed("up"))
velocity = new Vector2(speed, 0).Rotated(Rotation);
velocity = velocity.Normalized() * speed;
}
public override void _PhysicsProcess(float delta)
{
GetInput();
velocity = MoveAndSlide(velocity);
}
}
Here we're using the :ref:`Node2D <class_Node2D>` ``look_at()`` method to Here we're using the :ref:`Node2D <class_Node2D>` ``look_at()`` method to
point the player towards a given position. Without this function, you point the player towards a given position. Without this function, you
could get the same effect by setting the angle like this: could get the same effect by setting the angle like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
rotation = get_global_mouse_position().angle_to_point(position) rotation = get_global_mouse_position().angle_to_point(position)
```
.. code-tab:: csharp
var rotation = GetGlobalMousePosition().AngleToPoint(Position);
Click-and-move Click-and-move
@ -288,9 +178,9 @@ on the screen will cause the player to move to the target location.
.. image:: img/movement_click.gif .. image:: img/movement_click.gif
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
export (int) var speed = 200 export (int) var speed = 200
@ -307,42 +197,7 @@ on the screen will cause the player to move to the target location.
# look_at(target) # look_at(target)
if position.distance_to(target) > 5: if position.distance_to(target) > 5:
velocity = move_and_slide(velocity) velocity = move_and_slide(velocity)
```
.. code-tab:: csharp
using Godot;
using System;
public class Movement : KinematicBody2D
{
[Export] public int speed = 200;
public Vector2 target;
public Vector2 velocity = new Vector2();
public override void _Ready()
{
target = Position;
}
public override void _Input(InputEvent @event)
{
if (@event.IsActionPressed("click"))
{
target = GetGlobalMousePosition();
}
}
public override void _PhysicsProcess(float delta)
{
velocity = Position.DirectionTo(target) * speed;
// LookAt(target);
if (Position.DistanceTo(target) > 5)
{
velocity = MoveAndSlide(velocity);
}
}
}
Note the ``distance_to()`` check we make prior to movement. Without this test, Note the ``distance_to()`` check we make prior to movement. Without this test,

View File

@ -74,9 +74,9 @@ the ``play()`` and ``stop()`` methods. Here is a brief example to play the
animation while the right arrow key is held, and stop it when the key is animation while the right arrow key is held, and stop it when the key is
released. released.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
onready var _animated_sprite = $AnimatedSprite onready var _animated_sprite = $AnimatedSprite
@ -86,30 +86,7 @@ released.
_animated_sprite.play("run") _animated_sprite.play("run")
else: else:
_animated_sprite.stop() _animated_sprite.stop()
```
.. code-tab:: csharp
public class Character : KinematicBody2D
{
private AnimatedSprite _animatedSprite;
public override void _Ready()
{
_animatedSprite = GetNode<AnimatedSprite>("AnimatedSprite");
}
public override _Process(float _delta)
{
if (Input.IsActionPressed("ui_right"))
{
_animatedSprite.Play("run");
}
else
{
_animatedSprite.Stop();
}
}
}
Sprite sheet with AnimatedSprite Sprite sheet with AnimatedSprite
@ -213,9 +190,9 @@ the ``play()`` and ``stop()`` methods. Again, here is an example to play the
animation while the right arrow key is held, and stop it when the key is animation while the right arrow key is held, and stop it when the key is
released. released.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
onready var _animation_player = $AnimationPlayer onready var _animation_player = $AnimationPlayer
@ -225,30 +202,7 @@ released.
_animation_player.play("walk") _animation_player.play("walk")
else: else:
_animation_player.stop() _animation_player.stop()
```
.. code-tab:: csharp
public class Character : KinematicBody2D
{
private AnimationPlayer _animationPlayer;
public override void _Ready()
{
_animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
}
public override void _Process(float _delta)
{
if (Input.IsActionPressed("ui_right"))
{
_animationPlayer.Play("walk");
}
else
{
_animationPlayer.Stop();
}
}
}
.. note:: If updating both an animation and a separate property at once .. note:: If updating both an animation and a separate property at once
(for example, a platformer may update the sprite's ``h_flip``/``v_flip`` (for example, a platformer may update the sprite's ``h_flip``/``v_flip``

View File

@ -72,14 +72,11 @@ Obtaining each transform can be achieved with the following functions:
Finally, then, to convert a CanvasItem local coordinates to screen Finally, then, to convert a CanvasItem local coordinates to screen
coordinates, just multiply in the following order: coordinates, just multiply in the following order:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var screen_coord = get_viewport_transform() * (get_global_transform() * local_pos) var screen_coord = get_viewport_transform() * (get_global_transform() * local_pos)
```
.. code-tab:: csharp
var screenCord = (GetViewportTransform() * GetGlobalTransform()).Xform(localPos);
Keep in mind, however, that it is generally not desired to work with Keep in mind, however, that it is generally not desired to work with
screen coordinates. The recommended approach is to simply work in Canvas screen coordinates. The recommended approach is to simply work in Canvas
@ -93,19 +90,12 @@ It is often desired to feed custom input events to the scene tree. With
the above knowledge, to correctly do this, it must be done the following the above knowledge, to correctly do this, it must be done the following
way: way:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var local_pos = Vector2(10, 20) # local to Control/Node2D var local_pos = Vector2(10, 20) # local to Control/Node2D
var ie = InputEventMouseButton.new() var ie = InputEventMouseButton.new()
ie.button_index = BUTTON_LEFT ie.button_index = BUTTON_LEFT
ie.position = get_viewport_transform() * (get_global_transform() * local_pos) ie.position = get_viewport_transform() * (get_global_transform() * local_pos)
get_tree().input_event(ie) get_tree().input_event(ie)
```
.. code-tab:: csharp
var localPos = new Vector2(10,20); // local to Control/Node2D
var ie = new InputEventMouseButton();
ie.ButtonIndex = (int)ButtonList.Left;
ie.Position = (GetViewportTransform() * GetGlobalTransform()).Xform(localPos);
GetTree().InputEvent(ie);

View File

@ -34,21 +34,15 @@ Add a script to any :ref:`CanvasItem <class_CanvasItem>`
derived node, like :ref:`Control <class_Control>` or derived node, like :ref:`Control <class_Control>` or
:ref:`Node2D <class_Node2D>`. Then override the ``_draw()`` function. :ref:`Node2D <class_Node2D>`. Then override the ``_draw()`` function.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node2D extends Node2D
func _draw(): func _draw():
# Your draw commands here # Your draw commands here
pass pass
```
.. code-tab:: csharp
public override void _Draw()
{
// Your draw commands here
}
Draw commands are described in the :ref:`CanvasItem <class_CanvasItem>` Draw commands are described in the :ref:`CanvasItem <class_CanvasItem>`
class reference. There are plenty of them. class reference. There are plenty of them.
@ -66,9 +60,9 @@ in that same node and a new ``_draw()`` call will happen.
Here is a little more complex example, a texture variable that will be Here is a little more complex example, a texture variable that will be
redrawn if modified: redrawn if modified:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node2D extends Node2D
export (Texture) var texture setget _set_texture export (Texture) var texture setget _set_texture
@ -81,38 +75,14 @@ redrawn if modified:
func _draw(): func _draw():
draw_texture(texture, Vector2()) draw_texture(texture, Vector2())
```
.. code-tab:: csharp
public class CustomNode2D : Node2D
{
private Texture _texture;
public Texture Texture
{
get
{
return _texture;
}
set
{
_texture = value;
Update();
}
}
public override void _Draw()
{
DrawTexture(_texture, new Vector2());
}
}
In some cases, it may be desired to draw every frame. For this, just In some cases, it may be desired to draw every frame. For this, just
call ``update()`` from the ``_process()`` callback, like this: call ``update()`` from the ``_process()`` callback, like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node2D extends Node2D
func _draw(): func _draw():
@ -121,21 +91,7 @@ call ``update()`` from the ``_process()`` callback, like this:
func _process(delta): func _process(delta):
update() update()
```
.. code-tab:: csharp
public class CustomNode2D : Node2D
{
public override void _Draw()
{
// Your draw commands here
}
public override void _Process(float delta)
{
Update();
}
}
An example: drawing circular arcs An example: drawing circular arcs
@ -162,9 +118,9 @@ it being angular-looking. On the contrary, if your shape is small (or in 3D, far
you may decrease its number of points to save processing costs; this is known as *Level of Detail (LOD)*. you may decrease its number of points to save processing costs; this is known as *Level of Detail (LOD)*.
In our example, we will simply use a fixed number of points, no matter the radius. In our example, we will simply use a fixed number of points, no matter the radius.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func draw_circle_arc(center, radius, angle_from, angle_to, color): func draw_circle_arc(center, radius, angle_from, angle_to, color):
var nb_points = 32 var nb_points = 32
var points_arc = PoolVector2Array() var points_arc = PoolVector2Array()
@ -175,23 +131,7 @@ In our example, we will simply use a fixed number of points, no matter the radiu
for index_point in range(nb_points): for index_point in range(nb_points):
draw_line(points_arc[index_point], points_arc[index_point + 1], color) draw_line(points_arc[index_point], points_arc[index_point + 1], color)
```
.. code-tab:: csharp
public void DrawCircleArc(Vector2 center, float radius, float angleFrom, float angleTo, Color color)
{
int nbPoints = 32;
var pointsArc = new Vector2[nbPoints];
for (int i = 0; i < nbPoints; ++i)
{
float anglePoint = Mathf.Deg2Rad(angleFrom + i * (angleTo - angleFrom) / nbPoints - 90f);
pointsArc[i] = center + new Vector2(Mathf.Cos(anglePoint), Mathf.Sin(anglePoint)) * radius;
}
for (int i = 0; i < nbPoints - 1; ++i)
DrawLine(pointsArc[i], pointsArc[i + 1], color);
}
Remember the number of points our shape has to be decomposed into? We fixed this Remember the number of points our shape has to be decomposed into? We fixed this
@ -232,10 +172,9 @@ Draw the arc on the screen
We now have a function that draws stuff on the screen; We now have a function that draws stuff on the screen;
it is time to call it inside the ``_draw()`` function: it is time to call it inside the ``_draw()`` function:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _draw(): func _draw():
var center = Vector2(200, 200) var center = Vector2(200, 200)
var radius = 80 var radius = 80
@ -243,18 +182,7 @@ it is time to call it inside the ``_draw()`` function:
var angle_to = 195 var angle_to = 195
var color = Color(1.0, 0.0, 0.0) var color = Color(1.0, 0.0, 0.0)
draw_circle_arc(center, radius, angle_from, angle_to, color) draw_circle_arc(center, radius, angle_from, angle_to, color)
```
.. code-tab:: csharp
public override void _Draw()
{
var center = new Vector2(200, 200);
float radius = 80;
float angleFrom = 75;
float angleTo = 195;
var color = new Color(1, 0, 0);
DrawCircleArc(center, radius, angleFrom, angleTo, color);
}
Result: Result:
@ -267,9 +195,9 @@ We can take this a step further and not only write a function that draws the pla
portion of the disc defined by the arc, but also its shape. The method is exactly portion of the disc defined by the arc, but also its shape. The method is exactly
the same as before, except that we draw a polygon instead of lines: the same as before, except that we draw a polygon instead of lines:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func draw_circle_arc_poly(center, radius, angle_from, angle_to, color): func draw_circle_arc_poly(center, radius, angle_from, angle_to, color):
var nb_points = 32 var nb_points = 32
var points_arc = PoolVector2Array() var points_arc = PoolVector2Array()
@ -280,25 +208,7 @@ the same as before, except that we draw a polygon instead of lines:
var angle_point = deg2rad(angle_from + i * (angle_to - angle_from) / nb_points - 90) var angle_point = deg2rad(angle_from + i * (angle_to - angle_from) / nb_points - 90)
points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius) points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
draw_polygon(points_arc, colors) draw_polygon(points_arc, colors)
```
.. code-tab:: csharp
public void DrawCircleArcPoly(Vector2 center, float radius, float angleFrom, float angleTo, Color color)
{
int nbPoints = 32;
var pointsArc = new Vector2[nbPoints + 1];
pointsArc[0] = center;
var colors = new Color[] { color };
for (int i = 0; i < nbPoints; ++i)
{
float anglePoint = Mathf.Deg2Rad(angleFrom + i * (angleTo - angleFrom) / nbPoints - 90);
pointsArc[i + 1] = center + new Vector2(Mathf.Cos(anglePoint), Mathf.Sin(anglePoint)) * radius;
}
DrawPolygon(pointsArc, colors);
}
.. image:: img/result_drawarc_poly.png .. image:: img/result_drawarc_poly.png
@ -315,23 +225,15 @@ First, we have to make both angle_from and angle_to variables global at the top
of our script. Also note that you can store them in other nodes and access them of our script. Also note that you can store them in other nodes and access them
using ``get_node()``. using ``get_node()``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node2D extends Node2D
var rotation_angle = 50 var rotation_angle = 50
var angle_from = 75 var angle_from = 75
var angle_to = 195 var angle_to = 195
```
.. code-tab:: csharp
public class CustomNode2D : Node2D
{
private float _rotationAngle = 50;
private float _angleFrom = 75;
private float _angleTo = 195;
}
We make these values change in the _process(delta) function. We make these values change in the _process(delta) function.
@ -345,9 +247,9 @@ When this happens, Godot may crash or produce unexpected behavior.
Finally, we must not forget to call the ``update()`` function, which automatically Finally, we must not forget to call the ``update()`` function, which automatically
calls ``_draw()``. This way, you can control when you want to refresh the frame. calls ``_draw()``. This way, you can control when you want to refresh the frame.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _process(delta): func _process(delta):
angle_from += rotation_angle angle_from += rotation_angle
angle_to += rotation_angle angle_to += rotation_angle
@ -357,47 +259,20 @@ calls ``_draw()``. This way, you can control when you want to refresh the frame.
angle_from = wrapf(angle_from, 0, 360) angle_from = wrapf(angle_from, 0, 360)
angle_to = wrapf(angle_to, 0, 360) angle_to = wrapf(angle_to, 0, 360)
update() update()
```
.. code-tab:: csharp
public override void _Process(float delta)
{
_angleFrom += _rotationAngle;
_angleTo += _rotationAngle;
// We only wrap angles when both of them are bigger than 360.
if (_angleFrom > 360 && _angleTo > 360)
{
_angleFrom = Mathf.Wrap(_angleFrom, 0, 360);
_angleTo = Mathf.Wrap(_angleTo, 0, 360);
}
Update();
}
Also, don't forget to modify the ``_draw()`` function to make use of these variables: Also, don't forget to modify the ``_draw()`` function to make use of these variables:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _draw(): func _draw():
var center = Vector2(200, 200) var center = Vector2(200, 200)
var radius = 80 var radius = 80
var color = Color(1.0, 0.0, 0.0) var color = Color(1.0, 0.0, 0.0)
draw_circle_arc( center, radius, angle_from, angle_to, color ) draw_circle_arc( center, radius, angle_from, angle_to, color )
```
.. code-tab:: csharp
public override void _Draw()
{
var center = new Vector2(200, 200);
float radius = 80;
var color = new Color(1, 0, 0);
DrawCircleArc(center, radius, _angleFrom, _angleTo, color);
}
Let's run! Let's run!
It works, but the arc is rotating insanely fast! What's wrong? It works, but the arc is rotating insanely fast! What's wrong?
@ -414,9 +289,9 @@ In our case, we simply need to multiply our ``rotation_angle`` variable by ``del
in the ``_process()`` function. This way, our 2 angles will be increased by a much in the ``_process()`` function. This way, our 2 angles will be increased by a much
smaller value, which directly depends on the rendering speed. smaller value, which directly depends on the rendering speed.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _process(delta): func _process(delta):
angle_from += rotation_angle * delta angle_from += rotation_angle * delta
angle_to += rotation_angle * delta angle_to += rotation_angle * delta
@ -426,23 +301,7 @@ smaller value, which directly depends on the rendering speed.
angle_from = wrapf(angle_from, 0, 360) angle_from = wrapf(angle_from, 0, 360)
angle_to = wrapf(angle_to, 0, 360) angle_to = wrapf(angle_to, 0, 360)
update() update()
```
.. code-tab:: csharp
public override void _Process(float delta)
{
_angleFrom += _rotationAngle * delta;
_angleTo += _rotationAngle * delta;
// 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);
}
Update();
}
Let's run again! This time, the rotation displays fine! Let's run again! This time, the rotation displays fine!

View File

@ -86,52 +86,56 @@ Next, add a script to the MeshInstance.
Under ``_ready()``, create a new Array. Under ``_ready()``, create a new Array.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var surface_array = [] var surface_array = []
```
This will be the array that we keep our surface information in - it will hold This will be the array that we keep our surface information in - it will hold
all the arrays of data that the surface needs. Godot will expect it to be of all the arrays of data that the surface needs. Godot will expect it to be of
size ``Mesh.ARRAY_MAX``, so resize it accordingly. size ``Mesh.ARRAY_MAX``, so resize it accordingly.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var surface_array = [] var surface_array = []
surface_array.resize(Mesh.ARRAY_MAX) surface_array.resize(Mesh.ARRAY_MAX)
```
Next create the arrays for each data type you will use. Next create the arrays for each data type you will use.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var verts = PoolVector3Array() var verts = PoolVector3Array()
var uvs = PoolVector2Array() var uvs = PoolVector2Array()
var normals = PoolVector3Array() var normals = PoolVector3Array()
var indices = PoolIntArray() var indices = PoolIntArray()
```
Once you have filled your data arrays with your geometry you can create a mesh Once you have filled your data arrays with your geometry you can create a mesh
by adding each array to ``surface_array`` and then committing to the mesh. by adding each array to ``surface_array`` and then committing to the mesh.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
surface_array[Mesh.ARRAY_VERTEX] = verts surface_array[Mesh.ARRAY_VERTEX] = verts
surface_array[Mesh.ARRAY_TEX_UV] = uvs surface_array[Mesh.ARRAY_TEX_UV] = uvs
surface_array[Mesh.ARRAY_NORMAL] = normals surface_array[Mesh.ARRAY_NORMAL] = normals
surface_array[Mesh.ARRAY_INDEX] = indices surface_array[Mesh.ARRAY_INDEX] = indices
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) # No blendshapes or compression used. mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) # No blendshapes or compression used.
```
.. note:: In this example, we used ``Mesh.PRIMITIVE_TRIANGLES``, but you can use any primitive type .. note:: In this example, we used ``Mesh.PRIMITIVE_TRIANGLES``, but you can use any primitive type
available from mesh. available from mesh.
Put together, the full code looks like: Put together, the full code looks like:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends MeshInstance extends MeshInstance
func _ready(): func _ready():
@ -156,7 +160,7 @@ Put together, the full code looks like:
# Create mesh surface from mesh array. # Create mesh surface from mesh array.
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) # No blendshapes or compression used. mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) # No blendshapes or compression used.
```
The code that goes in the middle can be whatever you want. Below we will present some example code The code that goes in the middle can be whatever you want. Below we will present some example code
for generating a sphere. for generating a sphere.
@ -171,9 +175,9 @@ generic approach to generating a sphere. If you are having trouble understanding
or want to learn more about procedural geometry in general, you can use any tutorial or want to learn more about procedural geometry in general, you can use any tutorial
that you find online. that you find online.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends MeshInstance extends MeshInstance
var rings = 50 var rings = 50
@ -230,6 +234,7 @@ that you find online.
thisrow = point thisrow = point
# Insert committing to the ArrayMesh here. # Insert committing to the ArrayMesh here.
```
Saving Saving
------ ------
@ -237,8 +242,9 @@ Saving
Finally, we can use the :ref:`ResourceSaver <class_resourcesaver>` class to save the ArrayMesh. Finally, we can use the :ref:`ResourceSaver <class_resourcesaver>` class to save the ArrayMesh.
This is useful when you want to generate a mesh and then use it later without having to re-generate it. This is useful when you want to generate a mesh and then use it later without having to re-generate it.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Saves mesh to a .tres file with compression enabled. # Saves mesh to a .tres file with compression enabled.
ResourceSaver.save("res://sphere.tres", mesh, ResourceSaver.FLAG_COMPRESS) ResourceSaver.save("res://sphere.tres", mesh, ResourceSaver.FLAG_COMPRESS)
```

View File

@ -23,13 +23,14 @@ Once you have called ``begin()`` you are ready to start adding vertices. You add
First you add vertex specific attributes such as normals or UVs using ``set_****()`` (e.g. ``set_normal()``). First you add vertex specific attributes such as normals or UVs using ``set_****()`` (e.g. ``set_normal()``).
Then you call ``add_vertex()`` to add a vertex with those attributes. For example: Then you call ``add_vertex()`` to add a vertex with those attributes. For example:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Add a vertex with normal and uv. # Add a vertex with normal and uv.
set_normal(Vector3(0, 1, 0)) set_normal(Vector3(0, 1, 0))
set_uv(Vector2(1, 1)) set_uv(Vector2(1, 1))
add_vertex(Vector3(0, 0, 1)) add_vertex(Vector3(0, 0, 1))
```
Only attributes added before the call to ``add_vertex()`` will be included in that vertex. Only attributes added before the call to ``add_vertex()`` will be included in that vertex.
@ -37,9 +38,9 @@ Finally, once you have added all your vertices call ``end()`` to signal that you
The example code below draws a single triangle. The example code below draws a single triangle.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends ImmediateGeometry extends ImmediateGeometry
func _process(_delta): func _process(_delta):
@ -65,3 +66,4 @@ The example code below draws a single triangle.
# End drawing. # End drawing.
end() end()
```

View File

@ -18,11 +18,12 @@ calling ``create_from_surface()`` will clear it for you. Alternatively, you can
In the examples below, assume an ArrayMesh called ``mesh`` has already been created. See :ref:`ArrayMesh tutorial <doc_arraymesh>` for an example of mesh generation. In the examples below, assume an ArrayMesh called ``mesh`` has already been created. See :ref:`ArrayMesh tutorial <doc_arraymesh>` for an example of mesh generation.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var mdt = MeshDataTool.new() var mdt = MeshDataTool.new()
mdt.create_from_surface(mesh, 0) mdt.create_from_surface(mesh, 0)
```
``create_from_surface()`` uses the vertex arrays from the ArrayMesh to calculate two additional arrays, ``create_from_surface()`` uses the vertex arrays from the ArrayMesh to calculate two additional arrays,
one for edges and one for faces, for a total of three arrays. one for edges and one for faces, for a total of three arrays.
@ -38,40 +39,43 @@ with each vertex.
To access information from these arrays you use a function of the form ``get_****()``: To access information from these arrays you use a function of the form ``get_****()``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
mdt.get_vertex_count() # Returns number of vertices in vertex array. mdt.get_vertex_count() # Returns number of vertices in vertex array.
mdt.get_vertex_faces(0) # Returns array of faces that contain vertex[0]. mdt.get_vertex_faces(0) # Returns array of faces that contain vertex[0].
mdt.get_face_normal(1) # Calculates and returns face normal of the second face. mdt.get_face_normal(1) # Calculates and returns face normal of the second face.
mdt.get_edge_vertex(10, 1) # Returns the second vertex comprising the edge at index 10. mdt.get_edge_vertex(10, 1) # Returns the second vertex comprising the edge at index 10.
```
What you choose to do with these functions is up to you. A common use case is to iterate over all vertices What you choose to do with these functions is up to you. A common use case is to iterate over all vertices
and transform them in some way: and transform them in some way:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
for i in range(get_vertex_count): for i in range(get_vertex_count):
var vert = mdt.get_vertex(i) var vert = mdt.get_vertex(i)
vert *= 2.0 # Scales the vertex by doubling size. vert *= 2.0 # Scales the vertex by doubling size.
mdt.set_vertex(i, vert) mdt.set_vertex(i, vert)
```
These modifications are not done in place on the ArrayMesh. If you are dynamically updating an existing ArrayMesh, These modifications are not done in place on the ArrayMesh. If you are dynamically updating an existing ArrayMesh,
first delete the existing surface before adding a new one using :ref:`commit_to_surface() <class_meshdatatool_method_commit_to_surface>`: first delete the existing surface before adding a new one using :ref:`commit_to_surface() <class_meshdatatool_method_commit_to_surface>`:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
mesh.surface_remove(0) # Deletes the first surface of the mesh. mesh.surface_remove(0) # Deletes the first surface of the mesh.
mdt.commit_to_surface(mesh) mdt.commit_to_surface(mesh)
```
Below is a complete example that turns a spherical mesh called ``mesh`` into a randomly deformed blob complete with updated normals and vertex colors. Below is a complete example that turns a spherical mesh called ``mesh`` into a randomly deformed blob complete with updated normals and vertex colors.
See :ref:`ArrayMesh tutorial <doc_arraymesh>` for how to generate the base mesh. See :ref:`ArrayMesh tutorial <doc_arraymesh>` for how to generate the base mesh.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends MeshInstance extends MeshInstance
var sn = OpenSimplexNoise.new() var sn = OpenSimplexNoise.new()
@ -115,3 +119,4 @@ See :ref:`ArrayMesh tutorial <doc_arraymesh>` for how to generate the base mesh.
mesh.surface_remove(0) mesh.surface_remove(0)
mdt.commit_to_surface(mesh) mdt.commit_to_surface(mesh)
```

View File

@ -12,32 +12,34 @@ The SurfaceTool also provides some useful helper functions like ``index()`` and
Attributes are added before each vertex is added: Attributes are added before each vertex is added:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
st.add_normal() # Overwritten by normal below. st.add_normal() # Overwritten by normal below.
st.add_normal() # Added to next vertex. st.add_normal() # Added to next vertex.
st.add_color() # Added to next vertex. st.add_color() # Added to next vertex.
st.add_vertex() # Captures normal and color above. st.add_vertex() # Captures normal and color above.
st.add_normal() # Normal never added to a vertex. st.add_normal() # Normal never added to a vertex.
```
When finished generating your geometry with the :ref:`SurfaceTool <class_surfacetool>` When finished generating your geometry with the :ref:`SurfaceTool <class_surfacetool>`
call ``commit()`` to finish generating the mesh. If an :ref:`ArrayMesh <class_ArrayMesh>` is passed call ``commit()`` to finish generating the mesh. If an :ref:`ArrayMesh <class_ArrayMesh>` is passed
to ``commit()`` then it appends a new surface to the end of the ArrayMesh. While if nothing is passed to ``commit()`` then it appends a new surface to the end of the ArrayMesh. While if nothing is passed
in, ``commit()`` returns an ArrayMesh. in, ``commit()`` returns an ArrayMesh.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
st.commit(mesh) st.commit(mesh)
# Or: # Or:
var mesh = st.commit() var mesh = st.commit()
```
Code creates a triangle with indices Code creates a triangle with indices
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var st = SurfaceTool.new() var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES) st.begin(Mesh.PRIMITIVE_TRIANGLES)
@ -58,14 +60,15 @@ Code creates a triangle with indices
# Commit to a mesh. # Commit to a mesh.
var mesh = st.commit() var mesh = st.commit()
```
You can optionally add an index array, either by calling ``add_index()`` and adding You can optionally add an index array, either by calling ``add_index()`` and adding
vertices to the index array or by calling ``index()`` which shrinks the vertex array vertices to the index array or by calling ``index()`` which shrinks the vertex array
to remove duplicate vertices. to remove duplicate vertices.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Creates a quad from four corner vertices. # Creates a quad from four corner vertices.
# Add_index does not need to be called before add_vertex. # Add_index does not need to be called before add_vertex.
st.add_index(0) st.add_index(0)
@ -78,14 +81,16 @@ to remove duplicate vertices.
# Alternatively: # Alternatively:
st.index() st.index()
```
Similarly, if you have an index array, but you want each vertex to be unique (e.g. because Similarly, if you have an index array, but you want each vertex to be unique (e.g. because
you want to use unique normals or colors per face instead of per-vertex), you can call ``deindex()``. you want to use unique normals or colors per face instead of per-vertex), you can call ``deindex()``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
st.deindex() st.deindex()
```
If you don't add custom normals yourself, you can add them using ``generate_normals()``, which should If you don't add custom normals yourself, you can add them using ``generate_normals()``, which should
be called after generating geometry and before committing the mesh using ``commit()`` or be called after generating geometry and before committing the mesh using ``commit()`` or
@ -95,11 +100,12 @@ note, ``generate_normals()`` only works if the primitive type is set to ``Mesh.P
If you don't add custom tangents, they can be added with ``generate_tangents()``, but it requires If you don't add custom tangents, they can be added with ``generate_tangents()``, but it requires
that each vertex have UVs and normals set already. that each vertex have UVs and normals set already.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
st.generate_normals() st.generate_normals()
st.generate_tangents() st.generate_tangents()
```
By default, when generating normals, they will be calculated on a per-face basis. If you want By default, when generating normals, they will be calculated on a per-face basis. If you want
smooth vertex normals, when adding vertices, call ``add_smooth_group()``. ``add_smooth_group()`` smooth vertex normals, when adding vertices, call ``add_smooth_group()``. ``add_smooth_group()``

View File

@ -94,31 +94,15 @@ A transform has a :ref:`class_Basis` (transform.basis sub-property), which consi
A default basis (unmodified) is akin to: A default basis (unmodified) is akin to:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var basis = Basis() var basis = Basis()
# Contains the following default values: # Contains the following default values:
basis.x = Vector3(1, 0, 0) # Vector pointing along the X axis basis.x = Vector3(1, 0, 0) # Vector pointing along the X axis
basis.y = Vector3(0, 1, 0) # Vector pointing along the Y axis basis.y = Vector3(0, 1, 0) # Vector pointing along the Y axis
basis.z = Vector3(0, 0, 1) # Vector pointing along the Z axis basis.z = Vector3(0, 0, 1) # Vector pointing along the Z axis
```
.. code-tab:: csharp
// Due to technical limitations on structs in C# the default
// constructor will contain zero values for all fields.
var defaultBasis = new Basis();
GD.Print(defaultBasis); // prints: ((0, 0, 0), (0, 0, 0), (0, 0, 0))
// Instead we can use the Identity property.
var identityBasis = Basis.Identity;
GD.Print(identityBasis.x); // prints: (1, 0, 0)
GD.Print(identityBasis.y); // prints: (0, 1, 0)
GD.Print(identityBasis.z); // prints: (0, 0, 1)
// The Identity basis is equivalent to:
var basis = new Basis(Vector3.Right, Vector3.Up, Vector3.Back);
GD.Print(basis); // prints: ((1, 0, 0), (0, 1, 0), (0, 0, 1))
This is also an analog of a 3x3 identity matrix. This is also an analog of a 3x3 identity matrix.
@ -146,56 +130,38 @@ Of course, transforms are not as straightforward to manipulate as angles and hav
It is possible to rotate a transform, either by multiplying its basis by another (this is called accumulation), or by using the rotation methods. It is possible to rotate a transform, either by multiplying its basis by another (this is called accumulation), or by using the rotation methods.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var axis = Vector3(1, 0, 0) # Or Vector3.RIGHT var axis = Vector3(1, 0, 0) # Or Vector3.RIGHT
var rotation_amount = 0.1 var rotation_amount = 0.1
# Rotate the transform around the X axis by 0.1 radians. # Rotate the transform around the X axis by 0.1 radians.
transform.basis = Basis(axis, rotation_amount) * transform.basis transform.basis = Basis(axis, rotation_amount) * transform.basis
# shortened # shortened
transform.basis = transform.basis.rotated(axis, rotation_amount) transform.basis = transform.basis.rotated(axis, rotation_amount)
```
.. code-tab:: csharp
Vector3 axis = new Vector3(1, 0, 0); // Or Vector3.Right
float rotationAmount = 0.1f;
// Rotate the transform around the X axis by 0.1 radians.
transform.basis = new Basis(axis, rotationAmount) * transform.basis;
// shortened
transform.basis = transform.basis.Rotated(axis, rotationAmount);
A method in Spatial simplifies this: A method in Spatial simplifies this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Rotate the transform around the X axis by 0.1 radians. # Rotate the transform around the X axis by 0.1 radians.
rotate(Vector3(1, 0, 0), 0.1) rotate(Vector3(1, 0, 0), 0.1)
# shortened # shortened
rotate_x(0.1) rotate_x(0.1)
```
.. code-tab:: csharp
// Rotate the transform around the X axis by 0.1 radians.
Rotate(new Vector3(1, 0, 0), 0.1f);
// shortened
RotateX(0.1f);
This rotates the node relative to the parent node. This rotates the node relative to the parent node.
To rotate relative to object space (the node's own transform), use the following: To rotate relative to object space (the node's own transform), use the following:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Rotate around the object's local X axis by 0.1 radians. # Rotate around the object's local X axis by 0.1 radians.
rotate_object_local(Vector3(1, 0, 0), 0.1) rotate_object_local(Vector3(1, 0, 0), 0.1)
```
.. code-tab:: csharp
// Rotate around the object's local X axis by 0.1 radians.
RotateObjectLocal(new Vector3(1, 0, 0), 0.1f);
Precision errors Precision errors
================ ================
@ -206,29 +172,22 @@ If a transform is rotated every frame, it will eventually start deforming over t
There are two different ways to handle this. The first is to *orthonormalize* the transform after some time (maybe once per frame if you modify it every frame): There are two different ways to handle this. The first is to *orthonormalize* the transform after some time (maybe once per frame if you modify it every frame):
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
transform = transform.orthonormalized() transform = transform.orthonormalized()
```
.. code-tab:: csharp
transform = transform.Orthonormalized();
This will make all axes have ``1.0`` length again and be ``90`` degrees from each other. However, any scale applied to the transform will be lost. This will make all axes have ``1.0`` length again and be ``90`` degrees from each other. However, any scale applied to the transform will be lost.
It is recommended you not scale nodes that are going to be manipulated; scale their children nodes instead (such as MeshInstance). If you absolutely must scale the node, then re-apply it at the end: It is recommended you not scale nodes that are going to be manipulated; scale their children nodes instead (such as MeshInstance). If you absolutely must scale the node, then re-apply it at the end:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
transform = transform.orthonormalized() transform = transform.orthonormalized()
transform = transform.scaled(scale) transform = transform.scaled(scale)
```
.. code-tab:: csharp
transform = transform.Orthonormalized();
transform = transform.Scaled(scale);
Obtaining information Obtaining information
===================== =====================
@ -237,71 +196,45 @@ You might be thinking at this point: **"Ok, but how do I get angles from a trans
Imagine you need to shoot a bullet in the direction your player is facing. Just use the forward axis (commonly ``Z`` or ``-Z``). Imagine you need to shoot a bullet in the direction your player is facing. Just use the forward axis (commonly ``Z`` or ``-Z``).
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
bullet.transform = transform bullet.transform = transform
bullet.speed = transform.basis.z * BULLET_SPEED bullet.speed = transform.basis.z * BULLET_SPEED
```
.. code-tab:: csharp
bullet.Transform = transform;
bullet.LinearVelocity = transform.basis.z * BulletSpeed;
Is the enemy looking at the player? Use the dot product for this (see the :ref:`doc_vector_math` tutorial for an explanation of the dot product): Is the enemy looking at the player? Use the dot product for this (see the :ref:`doc_vector_math` tutorial for an explanation of the dot product):
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Get the direction vector from player to enemy # Get the direction vector from player to enemy
var direction = enemy.transform.origin - player.transform.origin var direction = enemy.transform.origin - player.transform.origin
if direction.dot(enemy.transform.basis.z) > 0: if direction.dot(enemy.transform.basis.z) > 0:
enemy.im_watching_you(player) enemy.im_watching_you(player)
```
.. code-tab:: csharp
// Get the direction vector from player to enemy
Vector3 direction = enemy.Transform.origin - player.Transform.origin;
if (direction.Dot(enemy.Transform.basis.z) > 0)
{
enemy.ImWatchingYou(player);
}
Strafe left: Strafe left:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Remember that +X is right # Remember that +X is right
if Input.is_action_pressed("strafe_left"): if Input.is_action_pressed("strafe_left"):
translate_object_local(-transform.basis.x) translate_object_local(-transform.basis.x)
```
.. code-tab:: csharp
// Remember that +X is right
if (Input.IsActionPressed("strafe_left"))
{
TranslateObjectLocal(-Transform.basis.x);
}
Jump: Jump:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Keep in mind Y is up-axis # Keep in mind Y is up-axis
if Input.is_action_just_pressed("jump"): if Input.is_action_just_pressed("jump"):
velocity.y = JUMP_SPEED velocity.y = JUMP_SPEED
velocity = move_and_slide(velocity) velocity = move_and_slide(velocity)
```
.. code-tab:: csharp
// Keep in mind Y is up-axis
if (Input.IsActionJustPressed("jump"))
velocity.y = JumpSpeed;
velocity = MoveAndSlide(velocity);
All common behaviors and logic can be done with just vectors. All common behaviors and logic can be done with just vectors.
@ -314,9 +247,9 @@ For such cases, keep the angles and rotations *outside* the transform and set th
Example of looking around, FPS style: Example of looking around, FPS style:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# accumulators # accumulators
var rot_x = 0 var rot_x = 0
var rot_y = 0 var rot_y = 0
@ -329,30 +262,7 @@ Example of looking around, FPS style:
transform.basis = Basis() # reset rotation transform.basis = Basis() # reset rotation
rotate_object_local(Vector3(0, 1, 0), rot_x) # first rotate in Y rotate_object_local(Vector3(0, 1, 0), rot_x) # first rotate in Y
rotate_object_local(Vector3(1, 0, 0), rot_y) # then rotate in X rotate_object_local(Vector3(1, 0, 0), rot_y) # then rotate in X
```
.. code-tab:: csharp
// accumulators
private float _rotationX = 0f;
private float _rotationY = 0f;
public override void _Input(InputEvent @event)
{
if (@event is InputEventMouseMotion mouseMotion)
{
// modify accumulated mouse rotation
_rotationX += mouseMotion.Relative.x * LookAroundSpeed;
_rotationY += mouseMotion.Relative.y * LookAroundSpeed;
// reset rotation
Transform transform = Transform;
transform.basis = Basis.Identity;
Transform = transform;
RotateObjectLocal(Vector3.Up, _rotationX); // first rotate about Y
RotateObjectLocal(Vector3.Right, _rotationY); // then rotate about X
}
}
As you can see, in such cases it's even simpler to keep the rotation outside, then use the transform as the *final* orientation. As you can see, in such cases it's even simpler to keep the rotation outside, then use the transform as the *final* orientation.
@ -363,9 +273,9 @@ Interpolating between two transforms can efficiently be done with quaternions. M
Converting a rotation to quaternion is straightforward. Converting a rotation to quaternion is straightforward.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Convert basis to quaternion, keep in mind scale is lost # Convert basis to quaternion, keep in mind scale is lost
var a = Quat(transform.basis) var a = Quat(transform.basis)
var b = Quat(transform2.basis) var b = Quat(transform2.basis)
@ -373,16 +283,7 @@ Converting a rotation to quaternion is straightforward.
var c = a.slerp(b,0.5) # find halfway point between a and b var c = a.slerp(b,0.5) # find halfway point between a and b
# Apply back # Apply back
transform.basis = Basis(c) transform.basis = Basis(c)
```
.. code-tab:: csharp
// Convert basis to quaternion, keep in mind scale is lost
var a = transform.basis.Quat();
var b = transform2.basis.Quat();
// Interpolate using spherical-linear interpolation (SLERP).
var c = a.Slerp(b, 0.5f); // find halfway point between a and b
// Apply back
transform.basis = new Basis(c);
The :ref:`class_Quat` type reference has more information on the datatype (it The :ref:`class_Quat` type reference has more information on the datatype (it
can also do transform accumulation, transform points, etc., though this is used can also do transform accumulation, transform points, etc., though this is used

View File

@ -100,9 +100,9 @@ This node can be used to cause a seek command to happen to any sub-children of t
After setting the time and changing the animation playback, the seek node automatically goes into sleep mode on the next process frame by setting its ``seek_position`` value to ``-1.0``. After setting the time and changing the animation playback, the seek node automatically goes into sleep mode on the next process frame by setting its ``seek_position`` value to ``-1.0``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Play child animation from the start. # Play child animation from the start.
anim_tree.set("parameters/Seek/seek_position", 0.0) anim_tree.set("parameters/Seek/seek_position", 0.0)
# Alternative syntax (same result as above). # Alternative syntax (same result as above).
@ -112,14 +112,7 @@ After setting the time and changing the animation playback, the seek node automa
anim_tree.set("parameters/Seek/seek_position", 12.0) anim_tree.set("parameters/Seek/seek_position", 12.0)
# Alternative syntax (same result as above). # Alternative syntax (same result as above).
anim_tree["parameters/Seek/seek_position"] = 12.0 anim_tree["parameters/Seek/seek_position"] = 12.0
```
.. code-tab:: csharp
// Play child animation from the start.
animTree.Set("parameters/Seek/seek_position", 0.0);
// Play child animation from 12 second timestamp.
animTree.Set("parameters/Seek/seek_position", 12.0);
TimeScale TimeScale
^^^^^^^^^ ^^^^^^^^^
@ -202,14 +195,11 @@ transformation visually (the animation will stay in place).
Afterwards, the actual motion can be retrieved via the :ref:`AnimationTree <class_AnimationTree>` API as a transform: Afterwards, the actual motion can be retrieved via the :ref:`AnimationTree <class_AnimationTree>` API as a transform:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
anim_tree.get_root_motion_transform() anim_tree.get_root_motion_transform()
```
.. code-tab:: csharp
animTree.GetRootMotionTransform();
This can be fed to functions such as :ref:`KinematicBody.move_and_slide <class_KinematicBody_method_move_and_slide>` to control the character movement. This can be fed to functions such as :ref:`KinematicBody.move_and_slide <class_KinematicBody_method_move_and_slide>` to control the character movement.
@ -243,16 +233,13 @@ To modify these values from code, the property path must be obtained. This is do
Which allows setting them or reading them: Which allows setting them or reading them:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
anim_tree.set("parameters/eye_blend/blend_amount", 1.0) anim_tree.set("parameters/eye_blend/blend_amount", 1.0)
# Simpler alternative form: # Simpler alternative form:
anim_tree["parameters/eye_blend/blend_amount"] = 1.0 anim_tree["parameters/eye_blend/blend_amount"] = 1.0
```
.. code-tab:: csharp
animTree.Set("parameters/eye_blend/blend_amount", 1.0);
State machine travel State machine travel
@ -267,25 +254,19 @@ To use the travel ability, you should first retrieve the :ref:`AnimationNodeStat
object from the ``AnimationTree`` node (it is exported as a property). object from the ``AnimationTree`` node (it is exported as a property).
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var state_machine = anim_tree["parameters/playback"] var state_machine = anim_tree["parameters/playback"]
```
.. code-tab:: csharp
AnimationNodeStateMachinePlayback stateMachine = (AnimationNodeStateMachinePlayback)animTree.Get("parameters/playback");
Once retrieved, it can be used by calling one of the many functions it offers: Once retrieved, it can be used by calling one of the many functions it offers:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
state_machine.travel("SomeState") state_machine.travel("SomeState")
```
.. code-tab:: csharp
stateMachine.Travel("SomeState");
The state machine must be running before you can travel. Make sure to either call ``start()`` or choose a node to **Autoplay on Load**. The state machine must be running before you can travel. Make sure to either call ``start()`` or choose a node to **Autoplay on Load**.

View File

@ -26,9 +26,9 @@ An ``AudioStreamPlayer`` named ``AudioStreamRecord`` is used for recording.
.. image:: img/record_stream_player.png .. image:: img/record_stream_player.png
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var effect var effect
var recording var recording
@ -39,20 +39,7 @@ An ``AudioStreamPlayer`` named ``AudioStreamRecord`` is used for recording.
# And use it to retrieve its first effect, which has been defined # And use it to retrieve its first effect, which has been defined
# as an "AudioEffectRecord" resource. # as an "AudioEffectRecord" resource.
effect = AudioServer.get_bus_effect(idx, 0) effect = AudioServer.get_bus_effect(idx, 0)
```
.. code-tab:: csharp
private AudioEffectRecord _effect;
private AudioStreamSample _recording;
public override void _Ready()
{
// We get the index of the "Record" bus.
int idx = AudioServer.GetBusIndex("Record");
// And use it to retrieve its first effect, which has been defined
// as an "AudioEffectRecord" resource.
_effect = (AudioEffectRecord)AudioServer.GetBusEffect(idx, 0);
}
The audio recording is handled by the :ref:`class_AudioEffectRecord` resource The audio recording is handled by the :ref:`class_AudioEffectRecord` resource
which has three methods: which has three methods:
@ -60,9 +47,9 @@ which has three methods:
:ref:`is_recording_active() <class_AudioEffectRecord_method_is_recording_active>`, :ref:`is_recording_active() <class_AudioEffectRecord_method_is_recording_active>`,
and :ref:`set_recording_active() <class_AudioEffectRecord_method_set_recording_active>`. and :ref:`set_recording_active() <class_AudioEffectRecord_method_set_recording_active>`.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_RecordButton_pressed(): func _on_RecordButton_pressed():
if effect.is_recording_active(): if effect.is_recording_active():
recording = effect.get_recording() recording = effect.get_recording()
@ -77,29 +64,8 @@ and :ref:`set_recording_active() <class_AudioEffectRecord_method_set_recording_a
effect.set_recording_active(true) effect.set_recording_active(true)
$RecordButton.text = "Stop" $RecordButton.text = "Stop"
$Status.text = "Recording..." $Status.text = "Recording..."
```
.. code-tab:: csharp
public void OnRecordButtonPressed()
{
if (_effect.IsRecordingActive())
{
_recording = _effect.GetRecording();
GetNode<Button>("PlayButton").Disabled = false;
GetNode<Button>("SaveButton").Disabled = false;
_effect.SetRecordingActive(false);
GetNode<Button>("RecordButton").Text = "Record";
GetNode<Label>("Status").Text = "";
}
else
{
GetNode<Button>("PlayButton").Disabled = true;
GetNode<Button>("SaveButton").Disabled = true;
_effect.SetRecordingActive(true);
GetNode<Button>("RecordButton").Text = "Stop";
GetNode<Label>("Status").Text = "Recording...";
}
}
At the start of the demo, the recording effect is not active. When the user At the start of the demo, the recording effect is not active. When the user
presses the ``RecordButton``, the effect is enabled with presses the ``RecordButton``, the effect is enabled with
@ -109,9 +75,9 @@ On the next button press, as ``effect.is_recording_active()`` is ``true``,
the recorded stream can be stored into the ``recording`` variable by calling the recorded stream can be stored into the ``recording`` variable by calling
``effect.get_recording()``. ``effect.get_recording()``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_PlayButton_pressed(): func _on_PlayButton_pressed():
print(recording) print(recording)
print(recording.format) print(recording.format)
@ -122,42 +88,20 @@ the recorded stream can be stored into the ``recording`` variable by calling
print(data.size()) print(data.size())
$AudioStreamPlayer.stream = recording $AudioStreamPlayer.stream = recording
$AudioStreamPlayer.play() $AudioStreamPlayer.play()
```
.. code-tab:: csharp
public void OnPlayButtonPressed()
{
GD.Print(_recording);
GD.Print(_recording.Format);
GD.Print(_recording.MixRate);
GD.Print(_recording.Stereo);
byte[] data = _recording.Data;
GD.Print(data);
GD.Print(data.Length);
var audioStreamPlayer = GetNode<AudioStreamPlayer>("AudioStreamPlayer");
audioStreamPlayer.Stream = _recording;
audioStreamPlayer.Play();
}
To playback the recording, you assign the recording as the stream of the To playback the recording, you assign the recording as the stream of the
``AudioStreamPlayer`` and call ``play()``. ``AudioStreamPlayer`` and call ``play()``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_SaveButton_pressed(): func _on_SaveButton_pressed():
var save_path = $SaveButton/Filename.text var save_path = $SaveButton/Filename.text
recording.save_to_wav(save_path) recording.save_to_wav(save_path)
$Status.text = "Saved WAV file to: %s\n(%s)" % [save_path, ProjectSettings.globalize_path(save_path)] $Status.text = "Saved WAV file to: %s\n(%s)" % [save_path, ProjectSettings.globalize_path(save_path)]
```
.. code-tab:: csharp
public void OnSavebuttonPressed()
{
string savePath = GetNode<LineEdit>("SaveButton/Filename").Text;
_recording.SaveToWav(savePath);
GetNode<Label>("Status").Text = string.Format("Saved WAV file to: {0}\n({1})", savePath, ProjectSettings.GlobalizePath(savePath));
}
To save the recording, you call ``save_to_wav()`` with the path to a file. To save the recording, you call ``save_to_wav()`` with the path to a file.
In this demo, the path is defined by the user via a ``LineEdit`` input box. In this demo, the path is defined by the user via a ``LineEdit`` input box.

View File

@ -34,9 +34,9 @@ The output latency (what happens after the mix) can also be estimated by calling
Add these two and it's possible to guess almost exactly when sound or music will begin playing in the speakers during *_process()*: Add these two and it's possible to guess almost exactly when sound or music will begin playing in the speakers during *_process()*:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var time_begin var time_begin
var time_delay var time_delay
@ -55,25 +55,7 @@ Add these two and it's possible to guess almost exactly when sound or music will
# May be below 0 (did not begin yet). # May be below 0 (did not begin yet).
time = max(0, time) time = max(0, time)
print("Time is: ", time) print("Time is: ", time)
```
.. code-tab:: csharp
private double _timeBegin;
private double _timeDelay;
public override void _Ready()
{
_timeBegin = OS.GetTicksUsec();
_timeDelay = AudioServer.GetTimeToNextMix() + AudioServer.GetOutputLatency();
GetNode<AudioStreamPlayer>("Player").Play();
}
public override void _Process(float _delta)
{
double time = (OS.GetTicksUsec() - _timeBegin) / 1000000.0d;
time = Math.Max(0.0d, time - _timeDelay);
GD.Print(string.Format("Time is: {0}", time));
}
In the long run, though, as the sound hardware clock is never exactly in sync with the system clock, the timing information will slowly drift away. In the long run, though, as the sound hardware clock is never exactly in sync with the system clock, the timing information will slowly drift away.
@ -90,35 +72,27 @@ To compensate for the "chunked" output, there is a function that can help: :ref:
Adding the return value from this function to *get_playback_position()* increases precision: Adding the return value from this function to *get_playback_position()* increases precision:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var time = $Player.get_playback_position() + AudioServer.get_time_since_last_mix() var time = $Player.get_playback_position() + AudioServer.get_time_since_last_mix()
```
.. code-tab:: csharp
double time = GetNode<AudioStreamPlayer>("Player").GetPlaybackPosition() + AudioServer.GetTimeSinceLastMix();
To increase precision, subtract the latency information (how much it takes for the audio to be heard after it was mixed): To increase precision, subtract the latency information (how much it takes for the audio to be heard after it was mixed):
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var time = $Player.get_playback_position() + AudioServer.get_time_since_last_mix() - AudioServer.get_output_latency() var time = $Player.get_playback_position() + AudioServer.get_time_since_last_mix() - AudioServer.get_output_latency()
```
.. code-tab:: csharp
double time = GetNode<AudioStreamPlayer>("Player").GetPlaybackPosition() + AudioServer.GetTimeSinceLastMix() - AudioServer.GetOutputLatency();
The result may be a bit jittery due how multiple threads work. Just check that the value is not less than in the previous frame (discard it if so). This is also a less precise approach than the one before, but it will work for songs of any length, or synchronizing anything (sound effects, as an example) to music. The result may be a bit jittery due how multiple threads work. Just check that the value is not less than in the previous frame (discard it if so). This is also a less precise approach than the one before, but it will work for songs of any length, or synchronizing anything (sound effects, as an example) to music.
Here is the same code as before using this approach: Here is the same code as before using this approach:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
$Player.play() $Player.play()
@ -128,18 +102,4 @@ Here is the same code as before using this approach:
# Compensate for output latency. # Compensate for output latency.
time -= AudioServer.get_output_latency() time -= AudioServer.get_output_latency()
print("Time is: ", time) print("Time is: ", time)
```
.. code-tab:: csharp
public override void _Ready()
{
GetNode<AudioStreamPlayer>("Player").Play();
}
public override void _Process(float _delta)
{
double time = GetNode<AudioStreamPlayer>("Player").GetPlaybackPosition() + AudioServer.GetTimeSinceLastMix();
// Compensate for output latency.
time -= AudioServer.GetOutputLatency();
GD.Print(string.Format("Time is: {0}", time));
}

View File

@ -230,9 +230,9 @@ class contains things that won't be relevant to one's custom data structure.
As such, it can be helpful to construct one's own node type when building As such, it can be helpful to construct one's own node type when building
tree structures. tree structures.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Object extends Object
class_name TreeNode class_name TreeNode
@ -245,33 +245,7 @@ tree structures.
# Destructor. # Destructor.
for a_child in _children: for a_child in _children:
a_child.free() a_child.free()
```
.. code-tab:: csharp
// Can decide whether to expose getters/setters for properties later
public class TreeNode : Object
{
private TreeNode _parent = null;
private object[] _children = new object[0];
public override void Notification(int what)
{
switch (what)
{
case NotificationPredelete:
foreach (object child in _children)
{
TreeNode node = child as TreeNode;
if (node != null)
node.Free();
}
break;
default:
break;
}
}
}
From here, one can then create their own structures with specific features, From here, one can then create their own structures with specific features,
limited only by their imagination. limited only by their imagination.

View File

@ -18,16 +18,12 @@ Acquiring object references
For all :ref:`Object <class_Object>`\s, the most basic way of referencing them For all :ref:`Object <class_Object>`\s, the most basic way of referencing them
is to get a reference to an existing object from another acquired instance. is to get a reference to an existing object from another acquired instance.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var obj = node.object # Property access. var obj = node.object # Property access.
var obj = node.get_object() # Method access. var obj = node.get_object() # Method access.
```
.. code-tab:: csharp
Object obj = node.Object; // Property access.
Object obj = node.GetObject(); // Method access.
The same principle applies for :ref:`Reference <class_Reference>` objects. The same principle applies for :ref:`Reference <class_Reference>` objects.
While users often access :ref:`Node <class_Node>` and While users often access :ref:`Node <class_Node>` and
@ -36,9 +32,9 @@ While users often access :ref:`Node <class_Node>` and
Instead of property or method access, one can get Resources by load Instead of property or method access, one can get Resources by load
access. access.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var preres = preload(path) # Load resource during scene load var preres = preload(path) # Load resource during scene load
var res = load(path) # Load resource when program reaches statement var res = load(path) # Load resource when program reaches statement
@ -65,52 +61,7 @@ access.
if not const_script: if not const_script:
return "Must initialize property 'const_script'." return "Must initialize property 'const_script'."
return "" return ""
```
.. code-tab:: csharp
// Tool script added for the sake of the "const [Export]" example.
[Tool]
public MyType
{
// Property initializations load during Script instancing, i.e. .new().
// No "preload" loads during scene load exists in C#.
// Initialize with a value. Editable at runtime.
public Script MyScript = GD.Load<Script>("MyScript.cs");
// Initialize with same value. Value cannot be changed.
public readonly Script MyConstScript = GD.Load<Script>("MyScript.cs");
// Like 'readonly' due to inaccessible setter.
// But, value can be set during constructor, i.e. MyType().
public Script Library { get; } = GD.Load<Script>("res://addons/plugin/library.gd");
// If need a "const [Export]" (which doesn't exist), use a
// conditional setter for a tool script that checks if it's executing
// in the editor.
private PackedScene _enemyScn;
[Export]
public PackedScene EnemyScn
{
get { return _enemyScn; }
set
{
if (Engine.IsEditorHint())
{
_enemyScn = value;
}
}
};
// Warn users if the value hasn't been set.
public String _GetConfigurationWarning()
{
if (EnemyScn == null)
return "Must initialize property 'EnemyScn'.";
return "";
}
}
Note the following: Note the following:
@ -126,9 +77,9 @@ Note the following:
Nodes likewise have an alternative access point: the SceneTree. Nodes likewise have an alternative access point: the SceneTree.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
# Slow. # Slow.
@ -178,66 +129,7 @@ Nodes likewise have an alternative access point: the SceneTree.
print(globals) print(globals)
print(globals.prop) print(globals.prop)
print(globals.my_getter()) print(globals.my_getter())
```
.. code-tab:: csharp
public class MyNode
{
// Slow
public void DynamicLookupWithDynamicNodePath()
{
GD.Print(GetNode(NodePath("Child")));
}
// Fastest. Lookup node and cache for future access.
// Doesn't break if node moves later.
public Node Child;
public void _Ready()
{
Child = GetNode(NodePath("Child"));
}
public void LookupAndCacheForFutureAccess()
{
GD.Print(Child);
}
// Delegate reference assignment to an external source.
// Con: need to perform a validation check.
// Pro: node makes no requirements of its external structure.
// 'prop' can come from anywhere.
public object Prop;
public void CallMeAfterPropIsInitializedByParent()
{
// Validate prop in one of three ways.
// Fail with no notification.
if (prop == null)
{
return;
}
// Fail with an error message.
if (prop == null)
{
GD.PrintErr("'Prop' wasn't initialized");
return;
}
// Fail and terminate.
Debug.Assert(Prop, "'Prop' wasn't initialized");
}
// Use an autoload.
// Dangerous for typical nodes, but useful for true singleton nodes
// that manage their own data and don't interfere with other objects.
public void ReferenceAGlobalAutoloadedVariable()
{
Node globals = GetNode(NodePath("/root/Globals"));
GD.Print(globals);
GD.Print(globals.prop);
GD.Print(globals.my_getter());
}
};
.. _doc_accessing_data_or_logic_from_object: .. _doc_accessing_data_or_logic_from_object:
@ -288,9 +180,9 @@ accesses:
- A duck-typed property access. These will property check (as described above). - A duck-typed property access. These will property check (as described above).
If the operation isn't supported by the object, execution will halt. If the operation isn't supported by the object, execution will halt.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# All Objects have duck-typed get, set, and call wrapper methods. # All Objects have duck-typed get, set, and call wrapper methods.
get_parent().set("visible", false) get_parent().set("visible", false)
@ -304,22 +196,15 @@ accesses:
# existence, but the property isn't recognized in any _get_property_list # existence, but the property isn't recognized in any _get_property_list
# method, then the set() and get() methods will work, but the symbol # method, then the set() and get() methods will work, but the symbol
# access will claim it can't find the property. # access will claim it can't find the property.
```
.. code-tab:: csharp
// All Objects have duck-typed Get, Set, and Call wrapper methods.
GetParent().Set("visible", false);
// C# is a static language, so it has no dynamic symbol access, e.g.
// `GetParent().Visible = false` won't work.
- A method check. In the case of - A method check. In the case of
:ref:`CanvasItem.visible <class_CanvasItem_property_visible>`, one can :ref:`CanvasItem.visible <class_CanvasItem_property_visible>`, one can
access the methods, ``set_visible`` and ``is_visible`` like any other method. access the methods, ``set_visible`` and ``is_visible`` like any other method.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var child = get_child(0) var child = get_child(0)
# Dynamic lookup. # Dynamic lookup.
@ -375,89 +260,15 @@ accesses:
# defines (which means documentation! But maybe worth it?). # defines (which means documentation! But maybe worth it?).
# Any script that conforms to the documented "interface" of the name or # Any script that conforms to the documented "interface" of the name or
# group can fill in for it. # group can fill in for it.
```
.. code-tab:: csharp
Node child = GetChild(0);
// Dynamic lookup.
child.Call("SetVisible", false);
// Dynamic lookup, checks for method existence first.
if (child.HasMethod("SetVisible"))
{
child.Call("SetVisible", false);
}
// Use a group as if it were an "interface", i.e. assume it implements
// certain methods.
// Requires good documentation for the project to keep it reliable
// (unless you make editor tools to enforce it at editor time).
// Note, this is generally not as good as using an actual interface in
// C#, but you can't set C# interfaces from the editor since they are
// language-level features.
if (child.IsInGroup("Offer"))
{
child.Call("Accept");
child.Call("Reject");
}
// Cast check, followed by static lookup.
CanvasItem ci = GetParent() as CanvasItem;
if (ci != null)
{
ci.SetVisible(false);
// useful when you need to make multiple safe calls to the class
ci.ShowOnTop = true;
}
// If one does not wish to fail these checks without notifying users,
// one can use an assert instead. These will trigger runtime errors
// immediately if not true.
Debug.Assert(child.HasMethod("set_visible"));
Debug.Assert(child.IsInGroup("offer"));
Debug.Assert(CanvasItem.InstanceHas(child));
// Can also use object labels to imply an interface, i.e. assume it
// implements certain methods.
// There are two types, both of which only exist for Nodes: Names and
// Groups.
// Assuming...
// A "Quest" object exists and 1) that it can "Complete" or "Fail" and
// that it will have Text available before and after each state...
// 1. Use a name.
Node quest = GetNode("Quest");
GD.Print(quest.Get("Text"));
quest.Call("Complete"); // or "Fail".
GD.Print(quest.Get("Text")); // Implied new text content.
// 2. Use a group.
foreach (Node AChild in GetChildren())
{
if (AChild.IsInGroup("quest"))
{
GD.Print(quest.Get("Text"));
quest.Call("Complete"); // or "Fail".
GD.Print(quest.Get("Text")); // Implied new text content.
}
}
// Note that these interfaces are project-specific conventions the team
// defines (which means documentation! But maybe worth it?).
// Any script that conforms to the documented "interface" of the
// name or group can fill in for it. Also note that in C#, these methods
// will be slower than static accesses with traditional interfaces.
- Outsource the access to a :ref:`FuncRef <class_FuncRef>`. These may be useful - Outsource the access to a :ref:`FuncRef <class_FuncRef>`. These may be useful
in cases where one needs the max level of freedom from dependencies. In in cases where one needs the max level of freedom from dependencies. In
this case, one relies on an external context to setup the method. this case, one relies on an external context to setup the method.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# child.gd # child.gd
extends Node extends Node
var fn = null var fn = null
@ -477,38 +288,8 @@ accesses:
func print_me(): func print_me():
print(name) print(name)
```
.. code-tab:: csharp
// Child.cs
public class Child : Node
{
public FuncRef FN = null;
public void MyMethod()
{
Debug.Assert(FN != null);
FN.CallFunc();
}
}
// Parent.cs
public class Parent : Node
{
public Node Child;
public void _Ready()
{
Child = GetNode("Child");
Child.Set("FN", GD.FuncRef(this, "PrintMe"));
Child.MyMethod();
}
public void PrintMe() {
{
GD.Print(GetClass());
}
}
These strategies contribute to Godot's flexible design. Between them, users These strategies contribute to Godot's flexible design. Between them, users
have a breadth of tools to meet their specific needs. have a breadth of tools to meet their specific needs.

View File

@ -79,9 +79,9 @@ often execute here, but it comes down to the frequency at which one needs
the evaluations to update. If they don't need to execute every frame, then the evaluations to update. If they don't need to execute every frame, then
implementing a Timer-yield-timeout loop is another option. implementing a Timer-yield-timeout loop is another option.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Infinitely loop, but only execute whenever the Timer fires. # Infinitely loop, but only execute whenever the Timer fires.
# Allows for recurring operations that don't trigger script logic # Allows for recurring operations that don't trigger script logic
# every frame (or even every fixed frame). # every frame (or even every fixed frame).
@ -89,6 +89,7 @@ implementing a Timer-yield-timeout loop is another option.
my_method() my_method()
$Timer.start() $Timer.start()
yield($Timer, "timeout") yield($Timer, "timeout")
```
Use ``_physics_process`` when one needs a framerate-independent deltatime Use ``_physics_process`` when one needs a framerate-independent deltatime
between frames. If code needs consistent updates over time, regardless between frames. If code needs consistent updates over time, regardless
@ -105,9 +106,9 @@ One can check for input actions within the input callbacks just the same.
If one wants to use delta time, one can fetch it from the related If one wants to use delta time, one can fetch it from the related
deltatime methods as needed. deltatime methods as needed.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Called every frame, even when the engine detects no input. # Called every frame, even when the engine detects no input.
func _process(delta): func _process(delta):
if Input.is_action_just_pressed("ui_select"): if Input.is_action_just_pressed("ui_select"):
@ -119,34 +120,8 @@ deltatime methods as needed.
"InputEventKey": "InputEventKey":
if Input.is_action_just_pressed("ui_accept"): if Input.is_action_just_pressed("ui_accept"):
print(get_process_delta_time()) print(get_process_delta_time())
```
.. code-tab:: csharp
public class MyNode : Node
{
// Called every frame, even when the engine detects no input.
public void _Process(float delta)
{
if (Input.IsActionJustPressed("ui_select"))
GD.Print(delta);
}
// Called during every input event. Equally true for _input().
public void _UnhandledInput(InputEvent event)
{
switch (event)
{
case InputEventKey keyEvent:
if (Input.IsActionJustPressed("ui_accept"))
GD.Print(GetProcessDeltaTime());
break;
default:
break;
}
}
}
_init vs. initialization vs. export _init vs. initialization vs. export
----------------------------------- -----------------------------------
@ -159,9 +134,9 @@ initializations should also run here. This triggers before ``_ready`` or
Scripts have three types of property assignments that can occur during Scripts have three types of property assignments that can occur during
instantiation: instantiation:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# "one" is an "initialized value". These DO NOT trigger the setter. # "one" is an "initialized value". These DO NOT trigger the setter.
# If someone set the value as "two" from the Inspector, this would be an # If someone set the value as "two" from the Inspector, this would be an
# "exported value". These DO trigger the setter. # "exported value". These DO trigger the setter.
@ -177,31 +152,8 @@ instantiation:
func set_test(value): func set_test(value):
test = value test = value
print("Setting: ", test) print("Setting: ", test)
```
.. code-tab:: csharp
public class MyNode : Node
{
private string _test = "one";
// Changing the value from the inspector does trigger the setter in C#.
[Export]
public string Test
{
get { return _test; }
set
{
_test = value;
GD.Print("Setting: " + _test);
}
}
public MyNode()
{
// Triggers the setter as well
Test = "three";
}
}
When instantiating a scene, property values will set up according to the When instantiating a scene, property values will set up according to the
following sequence: following sequence:
@ -239,9 +191,9 @@ For example, here is a snippet that connects a node's method to
a custom signal on the parent node without failing. Useful on data-centric a custom signal on the parent node without failing. Useful on data-centric
nodes that one might create at runtime. nodes that one might create at runtime.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
var parent_cache var parent_cache
@ -261,36 +213,4 @@ nodes that one might create at runtime.
func _on_parent_interacted_with(): func _on_parent_interacted_with():
print("I'm reacting to my parent's interaction!") print("I'm reacting to my parent's interaction!")
```
.. code-tab:: csharp
public class MyNode : Node
{
public Node ParentCache = null;
public void ConnectionCheck()
{
return ParentCache.HasUserSignal("InteractedWith");
}
public void _Notification(int what)
{
switch (what)
{
case NOTIFICATION_PARENTED:
ParentCache = GetParent();
if (ConnectionCheck())
ParentCache.Connect("InteractedWith", this, "OnParentInteractedWith");
break;
case NOTIFICATION_UNPARENTED:
if (ConnectionCheck())
ParentCache.Disconnect("InteractedWith", this, "OnParentInteractedWith");
break;
}
}
public void OnParentInteractedWith()
{
GD.Print("I'm reacting to my parent's interaction!");
}
}

View File

@ -24,9 +24,9 @@ accessible to *all* scripting languages.
So, when exactly does preloading occur versus loading, and when should one use So, when exactly does preloading occur versus loading, and when should one use
either? Let's see an example: either? Let's see an example:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# my_buildings.gd # my_buildings.gd
extends Node extends Node
@ -64,26 +64,7 @@ either? Let's see an example:
# Successfully loads and only when one instantiates the script! Yay! # Successfully loads and only when one instantiates the script! Yay!
var office_scn = load("res://office.tscn") var office_scn = load("res://office.tscn")
```
.. code-tab:: csharp
using System;
using Godot;
// C# and other languages have no concept of "preloading".
public class MyBuildings : Node
{
//This is a read-only field, it can only be assigned when it's declared or during a constructor.
public readonly PackedScene Building = ResourceLoader.Load<PackedScene>("res://building.tscn");
public PackedScene ABuilding;
public override void _Ready()
{
// Can assign the value during initialization.
ABuilding = GD.Load<PackedScene>("res://office.tscn");
}
}
Preloading allows the script to handle all the loading the moment one loads the Preloading allows the script to handle all the loading the moment one loads the
script. Preloading is useful, but there are also times when one doesn't wish script. Preloading is useful, but there are also times when one doesn't wish

View File

@ -53,99 +53,64 @@ initialize it:
behavior, not start it. Note that signal names are usually past-tense verbs behavior, not start it. Note that signal names are usually past-tense verbs
like "entered", "skill_activated", or "item_collected". like "entered", "skill_activated", or "item_collected".
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Parent # Parent
$Child.connect("signal_name", object_with_method, "method_on_the_object") $Child.connect("signal_name", object_with_method, "method_on_the_object")
# Child # Child
emit_signal("signal_name") # Triggers parent-defined behavior. emit_signal("signal_name") # Triggers parent-defined behavior.
```
.. code-tab:: csharp
// Parent
GetNode("Child").Connect("SignalName", ObjectWithMethod, "MethodOnTheObject");
// Child
EmitSignal("SignalName"); // Triggers parent-defined behavior.
2. Call a method. Used to start behavior. 2. Call a method. Used to start behavior.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Parent # Parent
$Child.method_name = "do" $Child.method_name = "do"
# Child, assuming it has String property 'method_name' and method 'do'. # Child, assuming it has String property 'method_name' and method 'do'.
call(method_name) # Call parent-defined method (which child must own). call(method_name) # Call parent-defined method (which child must own).
```
.. code-tab:: csharp
// Parent
GetNode("Child").Set("MethodName", "Do");
// Child
Call(MethodName); // Call parent-defined method (which child must own).
3. Initialize a :ref:`FuncRef <class_FuncRef>` property. Safer than a method 3. Initialize a :ref:`FuncRef <class_FuncRef>` property. Safer than a method
as ownership of the method is unnecessary. Used to start behavior. as ownership of the method is unnecessary. Used to start behavior.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Parent # Parent
$Child.func_property = funcref(object_with_method, "method_on_the_object") $Child.func_property = funcref(object_with_method, "method_on_the_object")
# Child # Child
func_property.call_func() # Call parent-defined method (can come from anywhere). func_property.call_func() # Call parent-defined method (can come from anywhere).
```
.. code-tab:: csharp
// Parent
GetNode("Child").Set("FuncProperty", GD.FuncRef(ObjectWithMethod, "MethodOnTheObject"));
// Child
FuncProperty.CallFunc(); // Call parent-defined method (can come from anywhere).
4. Initialize a Node or other Object reference. 4. Initialize a Node or other Object reference.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Parent # Parent
$Child.target = self $Child.target = self
# Child # Child
print(target) # Use parent-defined node. print(target) # Use parent-defined node.
```
.. code-tab:: csharp
// Parent
GetNode("Child").Set("Target", this);
// Child
GD.Print(Target); // Use parent-defined node.
5. Initialize a NodePath. 5. Initialize a NodePath.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Parent # Parent
$Child.target_path = ".." $Child.target_path = ".."
# Child # Child
get_node(target_path) # Use parent-defined NodePath. get_node(target_path) # Use parent-defined NodePath.
```
.. code-tab:: csharp
// Parent
GetNode("Child").Set("TargetPath", NodePath(".."));
// Child
GetNode(TargetPath); // Use parent-defined NodePath.
These options hide the points of access from the child node. This in turn These options hide the points of access from the child node. This in turn
keeps the child **loosely coupled** to its environment. One can re-use it keeps the child **loosely coupled** to its environment. One can re-use it
@ -158,9 +123,9 @@ in another context without any extra changes to its API.
are siblings should only be aware of their hierarchies while an ancestor are siblings should only be aware of their hierarchies while an ancestor
mediates their communications and references. mediates their communications and references.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Parent # Parent
$Left.target = $Right.get_node("Receiver") $Left.target = $Right.get_node("Receiver")
@ -173,32 +138,7 @@ in another context without any extra changes to its API.
func _init(): func _init():
var receiver = Receiver.new() var receiver = Receiver.new()
add_child(receiver) add_child(receiver)
```
.. code-tab:: csharp
// Parent
GetNode<Left>("Left").Target = GetNode("Right/Receiver");
public class Left : Node
{
public Node Target = null;
public void Execute()
{
// Do something with 'Target'.
}
}
public class Right : Node
{
public Node Receiver = null;
public Right()
{
Receiver = ResourceLoader.Load<Script>("Receiver.cs").New();
AddChild(Receiver);
}
}
The same principles also apply to non-Node objects that maintain dependencies The same principles also apply to non-Node objects that maintain dependencies
on other objects. Whichever object actually owns the objects should manage on other objects. Whichever object actually owns the objects should manage

View File

@ -23,38 +23,16 @@ But, choosing which one to use can be a dilemma. Creating script instances
is identical to creating in-engine classes whereas handling scenes requires is identical to creating in-engine classes whereas handling scenes requires
a change in API: a change in API:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
const MyNode = preload("my_node.gd") const MyNode = preload("my_node.gd")
const MyScene = preload("my_scene.tscn") const MyScene = preload("my_scene.tscn")
var node = Node.new() var node = Node.new()
var my_node = MyNode.new() # Same method call var my_node = MyNode.new() # Same method call
var my_scene = MyScene.instance() # Different method call var my_scene = MyScene.instance() # Different method call
var my_inherited_scene = MyScene.instance(PackedScene.GEN_EDIT_STATE_MAIN) # Create scene inheriting from MyScene var my_inherited_scene = MyScene.instance(PackedScene.GEN_EDIT_STATE_MAIN) # Create scene inheriting from MyScene
```
.. code-tab:: csharp
using System;
using Godot;
public class Game : Node
{
public readonly Script MyNodeScr = (Script)ResourceLoader.Load("MyNode.cs");
public readonly PackedScene MySceneScn = (PackedScene)ResourceLoader.Load("MyScene.tscn");
public Node ANode;
public Node MyNode;
public Node MyScene;
public Node MyInheritedScene;
public Game()
{
ANode = new Node();
MyNode = new MyNode(); // Same syntax
MyScene = MySceneScn.Instance(); // Different. Instantiated from a PackedScene
MyInheritedScene = MySceneScn.Instance(PackedScene.GenEditState.Main); // Create scene inheriting from MyScene
}
}
Also, scripts will operate a little slower than scenes due to the Also, scripts will operate a little slower than scenes due to the
speed differences between engine and script code. The larger and more complex speed differences between engine and script code. The larger and more complex
@ -160,9 +138,9 @@ The code example below creates a new ``Node``, changes its name, assigns a
script to it, sets its future parent as its owner so it gets saved to disk along script to it, sets its future parent as its owner so it gets saved to disk along
with it, and finally adds it as a child of the ``Main`` node: with it, and finally adds it as a child of the ``Main`` node:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Main.gd # Main.gd
extends Node extends Node
@ -172,25 +150,7 @@ with it, and finally adds it as a child of the ``Main`` node:
child.script = preload("Child.gd") child.script = preload("Child.gd")
child.owner = self child.owner = self
add_child(child) add_child(child)
```
.. code-tab:: csharp
using System;
using Godot;
public class Main : Resource
{
public Node Child { get; set; }
public Main()
{
Child = new Node();
Child.Name = "Child";
Child.Script = ResourceLoader.Load<Script>("child.gd");
Child.Owner = this;
AddChild(Child);
}
}
Script code like this is much slower than engine-side C++ code. Each instruction Script code like this is much slower than engine-side C++ code. Each instruction
makes a call to the scripting API which leads to many "lookups" on the back-end makes a call to the scripting API which leads to many "lookups" on the back-end
@ -219,9 +179,9 @@ In the end, the best approach is to consider the following:
this in 3.1 by declaring a script class and giving it a scene as a constant. this in 3.1 by declaring a script class and giving it a scene as a constant.
The script becomes, in effect, a namespace: The script becomes, in effect, a namespace:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# game.gd # game.gd
extends Reference extends Reference
class_name Game # extends Reference, so it won't show up in the node creation dialog class_name Game # extends Reference, so it won't show up in the node creation dialog
@ -231,3 +191,4 @@ In the end, the best approach is to consider the following:
extends Node extends Node
func _ready(): func _ready():
add_child(Game.MyScene.instance()) add_child(Game.MyScene.instance())
```

View File

@ -54,9 +54,9 @@ set by the touch (or click) event.
Here is the full script for the player, with comments noting what we've Here is the full script for the player, with comments noting what we've
changed: changed:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Area2D extends Area2D
signal hit signal hit
@ -124,119 +124,7 @@ changed:
hide() hide()
emit_signal("hit") emit_signal("hit")
$CollisionShape2D.set_deferred("disabled", true) $CollisionShape2D.set_deferred("disabled", true)
```
.. code-tab:: csharp
using Godot;
using System;
public class Player : Area2D
{
[Signal]
public delegate void Hit();
[Export]
public int Speed = 400;
private Vector2 _screenSize;
// Add this variable to hold the clicked position.
private Vector2 _target;
public override void _Ready()
{
Hide();
_screenSize = GetViewport().Size;
}
public void Start(Vector2 pos)
{
Position = pos;
// Initial target us the start position.
_target = pos;
Show();
GetNode<CollisionShape2D>("CollisionShape2D").Disabled = false;
}
// Change the target whenever a touch event happens.
public override void _Input(InputEvent @event)
{
if (@event is InputEventScreenTouch eventMouseButton && eventMouseButton.Pressed)
{
_target = (@event as InputEventScreenTouch).Position;
}
}
public override void _Process(float delta)
{
var velocity = new Vector2();
// Move towards the target and stop when close.
if (Position.DistanceTo(_target) > 10)
{
velocity = _target - Position;
}
// Remove keyboard controls.
//if (Input.IsActionPressed("ui_right"))
//{
// velocity.x += 1;
//}
//if (Input.IsActionPressed("ui_left"))
//{
// velocity.x -= 1;
//}
//if (Input.IsActionPressed("ui_down"))
//{
// velocity.y += 1;
//}
//if (Input.IsActionPressed("ui_up"))
//{
// velocity.y -= 1;
//}
var animatedSprite = GetNode<AnimatedSprite>("AnimatedSprite");
if (velocity.Length() > 0)
{
velocity = velocity.Normalized() * Speed;
animatedSprite.Play();
}
else
{
animatedSprite.Stop();
}
Position += velocity * delta;
// We still need to clamp the player's position here because on devices that don't
// match your game's aspect ratio, Godot will try to maintain it as much as possible
// by creating black borders, if necessary.
// Without clamp(), the player would be able to move under those borders.
Position = new Vector2(
x: Mathf.Clamp(Position.x, 0, _screenSize.x),
y: Mathf.Clamp(Position.y, 0, _screenSize.y)
);
if (velocity.x != 0)
{
animatedSprite.Animation = "walk";
animatedSprite.FlipV = false;
animatedSprite.FlipH = velocity.x < 0;
}
else if(velocity.y != 0)
{
animatedSprite.Animation = "up";
animatedSprite.FlipV = velocity.y > 0;
}
}
public void OnPlayerBodyEntered(PhysicsBody2D body)
{
Hide(); // Player disappears after being hit.
EmitSignal("Hit");
GetNode<CollisionShape2D>("CollisionShape2D").SetDeferred("disabled", true);
}
}
Setting a main scene Setting a main scene
-------------------- --------------------

View File

@ -93,9 +93,9 @@ To import a PCK file, one uses the ProjectSettings singleton. The following
example expects a “mod.pck” file in the directory of the games executable. example expects a “mod.pck” file in the directory of the games executable.
The PCK file contains a “mod_scene.tscn” test scene in its root. The PCK file contains a “mod_scene.tscn” test scene in its root.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _your_function(): func _your_function():
# This could fail if, for example, mod.pck cannot be found. # This could fail if, for example, mod.pck cannot be found.
var success = ProjectSettings.load_resource_pack("res://mod.pck") var success = ProjectSettings.load_resource_pack("res://mod.pck")
@ -103,20 +103,7 @@ The PCK file contains a “mod_scene.tscn” test scene in its root.
if success: if success:
# Now one can use the assets as if they had them in the project from the start. # Now one can use the assets as if they had them in the project from the start.
var imported_scene = load("res://mod_scene.tscn") var imported_scene = load("res://mod_scene.tscn")
```
.. code-tab:: csharp
private void YourFunction()
{
// This could fail if, for example, mod.pck cannot be found.
var success = ProjectSettings.LoadResourcePack("res://mod.pck");
if (success)
{
// Now one can use the assets as if they had them in the project from the start.
var importedScene = (PackedScene)ResourceLoader.Load("res://mod_scene.tscn");
}
}
.. warning:: .. warning::

View File

@ -46,9 +46,9 @@ There are 3 ways to get input in an analog-aware way:
- When you have two axes (such as joystick or WASD movement) and want both - When you have two axes (such as joystick or WASD movement) and want both
axes to behave as a single input, use ``Input.get_vector()``: axes to behave as a single input, use ``Input.get_vector()``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# `velocity` will be a Vector2 between `Vector2(-1.0, -1.0)` and `Vector2(1.0, 1.0)`. # `velocity` will be a Vector2 between `Vector2(-1.0, -1.0)` and `Vector2(1.0, 1.0)`.
# This handles deadzone in a correct way for most use cases. # This handles deadzone in a correct way for most use cases.
# The resulting deadzone will have a circular shape as it generally should. # The resulting deadzone will have a circular shape as it generally should.
@ -59,70 +59,42 @@ There are 3 ways to get input in an analog-aware way:
# a square-ish shape when it should ideally have a circular shape. # a square-ish shape when it should ideally have a circular shape.
var velocity = Vector2(Input.get_action_strength("move_right") - Input.get_action_strength("move_left"), var velocity = Vector2(Input.get_action_strength("move_right") - Input.get_action_strength("move_left"),
Input.get_action_strength("move_back") - Input.get_action_strength("move_forward")).clamped(1) Input.get_action_strength("move_back") - Input.get_action_strength("move_forward")).clamped(1)
```
.. code-tab:: csharp
// `velocity` will be a Vector2 between `Vector2(-1.0, -1.0)` and `Vector2(1.0, 1.0)`.
// This handles deadzone in a correct way for most use cases.
// The resulting deadzone will have a circular shape as it generally should.
Vector2 velocity = Input.GetVector("move_left", "move_right", "move_forward", "move_back");
// The line below is similar to `get_vector()`, except that it handles
// the deadzone in a less optimal way. The resulting deadzone will have
// a square-ish shape when it should ideally have a circular shape.
Vector2 velocity = new Vector2(Input.GetActionStrength("move_right") - Input.GetActionStrength("move_left"),
Input.GetActionStrength("move_back") - Input.GetActionStrength("move_forward")).Clamped(1);
- When you have one axis that can go both ways (such as a throttle on a - When you have one axis that can go both ways (such as a throttle on a
flight stick), or when you want to handle separate axes individually, flight stick), or when you want to handle separate axes individually,
use ``Input.get_axis()``: use ``Input.get_axis()``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# `walk` will be a floating-point number between `-1.0` and `1.0`. # `walk` will be a floating-point number between `-1.0` and `1.0`.
var walk = Input.get_axis("move_left", "move_right") var walk = Input.get_axis("move_left", "move_right")
# The line above is a shorter form of: # The line above is a shorter form of:
var walk = Input.get_action_strength("move_right") - Input.get_action_strength("move_left") var walk = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
```
.. code-tab:: csharp
// `walk` will be a floating-point number between `-1.0` and `1.0`.
float walk = Input.GetAxis("move_left", "move_right");
// The line above is a shorter form of:
float walk = Input.GetActionStrength("move_right") - Input.GetActionStrength("move_left");
- For other types of analog input, such as handling a trigger or handling - For other types of analog input, such as handling a trigger or handling
one direction at a time, use ``Input.get_action_strength()``: one direction at a time, use ``Input.get_action_strength()``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# `strength` will be a floating-point number between `0.0` and `1.0`. # `strength` will be a floating-point number between `0.0` and `1.0`.
var strength = Input.get_action_strength("accelerate") var strength = Input.get_action_strength("accelerate")
```
.. code-tab:: csharp
// `strength` will be a floating-point number between `0.0` and `1.0`.
float strength = Input.GetActionStrength("accelerate");
For non-analog digital/boolean input (only "pressed" or "not pressed" values), For non-analog digital/boolean input (only "pressed" or "not pressed" values),
such as controller buttons, mouse buttons or keyboard keys, such as controller buttons, mouse buttons or keyboard keys,
use ``Input.is_action_pressed()``: use ``Input.is_action_pressed()``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# `jumping` will be a boolean with a value of `true` or `false`. # `jumping` will be a boolean with a value of `true` or `false`.
var jumping = Input.is_action_pressed("jump") var jumping = Input.is_action_pressed("jump")
```
.. tabs::
.. code-tab:: csharp
// `jumping` will be a boolean with a value of `true` or `false`.
bool jumping = Input.IsActionPressed("jump");
In Godot versions before 3.4, such as 3.3, ``Input.get_vector()`` and In Godot versions before 3.4, such as 3.3, ``Input.get_vector()`` and
``Input.get_axis()`` aren't available. Only ``Input.get_action_strength()`` ``Input.get_axis()`` aren't available. Only ``Input.get_action_strength()``
@ -205,21 +177,26 @@ in the `official Joypads demo <https://godotengine.org/asset-library/asset/140>`
Once you have a working mapping for your controller, you can test it by defining Once you have a working mapping for your controller, you can test it by defining
the ``SDL_GAMECONTROLLERCONFIG`` environment variable before running Godot: the ``SDL_GAMECONTROLLERCONFIG`` environment variable before running Godot:
.. tabs:: bash Linux/macOS
.. code-tab:: bash Linux/macOS
```
export SDL_GAMECONTROLLERCONFIG="your:mapping:here" export SDL_GAMECONTROLLERCONFIG="your:mapping:here"
./path/to/godot.x86_64 ./path/to/godot.x86_64
```
.. code-tab:: bat Windows (cmd) bat Windows (cmd)
```
set SDL_GAMECONTROLLERCONFIG=your:mapping:here set SDL_GAMECONTROLLERCONFIG=your:mapping:here
path\to\godot.exe path\to\godot.exe
```
.. code-tab:: powershell Windows (powershell) powershell Windows (powershell)
```
$env:SDL_GAMECONTROLLERCONFIG="your:mapping:here" $env:SDL_GAMECONTROLLERCONFIG="your:mapping:here"
path\to\godot.exe path\to\godot.exe
```
To test mappings on non-desktop platforms or to distribute your project with To test mappings on non-desktop platforms or to distribute your project with
additional controller mappings, you can add them by calling additional controller mappings, you can add them by calling

View File

@ -38,9 +38,9 @@ Using a script
Create a Node and attach the following script. Create a Node and attach the following script.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
@ -56,22 +56,7 @@ Create a Node and attach the following script.
# Changes a specific shape of the cursor (here, the I-beam shape). # Changes a specific shape of the cursor (here, the I-beam shape).
Input.set_custom_mouse_cursor(beam, Input.CURSOR_IBEAM) Input.set_custom_mouse_cursor(beam, Input.CURSOR_IBEAM)
```
.. code-tab:: csharp
public override void _Ready()
{
// Load the custom images for the mouse cursor.
var arrow = ResourceLoader.Load("res://arrow.png");
var beam = ResourceLoader.Load("res://beam.png");
// Changes only the arrow shape of the cursor.
// This is similar to changing it in the project settings.
Input.SetCustomMouseCursor(arrow);
// Changes a specific shape of the cursor (here, the I-beam shape).
Input.SetCustomMouseCursor(beam, Input.CursorShape.Ibeam);
}
.. note:: .. note::
Check :ref:`Input.set_custom_mouse_cursor() <class_Input_method_set_custom_mouse_cursor>`. Check :ref:`Input.set_custom_mouse_cursor() <class_Input_method_set_custom_mouse_cursor>`.

View File

@ -30,20 +30,13 @@ Pressing the Back button will exit the application if
Handling the notification is done as follows (on any node): Handling the notification is done as follows (on any node):
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _notification(what): func _notification(what):
if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST: if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST:
get_tree().quit() # default behavior get_tree().quit() # default behavior
```
.. code-tab:: csharp
public override void _Notification(int what)
{
if (what == MainLoop.NotificationWmQuitRequest)
GetTree().Quit(); // default behavior
}
When developing mobile apps, quitting is not desired unless the user is When developing mobile apps, quitting is not desired unless the user is
on the main screen, so the behavior can be changed. on the main screen, so the behavior can be changed.
@ -51,14 +44,11 @@ on the main screen, so the behavior can be changed.
It is important to note that by default, Godot apps have the built-in It is important to note that by default, Godot apps have the built-in
behavior to quit when quit is requested, this can be changed: behavior to quit when quit is requested, this can be changed:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
get_tree().set_auto_accept_quit(false) get_tree().set_auto_accept_quit(false)
```
.. code-tab:: csharp
GetTree().SetAutoAcceptQuit(false);
Sending your own quit notification Sending your own quit notification
---------------------------------- ----------------------------------
@ -72,11 +62,8 @@ to delay the line that forces the quit.
Instead, you should send a quit request: Instead, you should send a quit request:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
get_tree().notification(MainLoop.NOTIFICATION_WM_QUIT_REQUEST) get_tree().notification(MainLoop.NOTIFICATION_WM_QUIT_REQUEST)
```
.. code-tab:: csharp
GetTree().Notification(MainLoop.NotificationWmQuitRequest)

View File

@ -28,9 +28,9 @@ singleton, which you can use to query the state of an input.
Examples: Examples:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _input(event): func _input(event):
if event.is_action_pressed("jump"): if event.is_action_pressed("jump"):
jump() jump()
@ -40,25 +40,7 @@ Examples:
if Input.is_action_pressed("move_right"): if Input.is_action_pressed("move_right"):
# Move as long as the key/button is pressed. # Move as long as the key/button is pressed.
position.x += speed * delta position.x += speed * delta
```
.. code-tab:: csharp
public override void _Input(InputEvent inputEvent)
{
if (inputEvent.IsActionPressed("jump"))
{
Jump();
}
}
public override void _PhysicsProcess(float delta)
{
if (Input.IsActionPressed("move_right"))
{
// Move as long as the key/button is pressed.
position.x += speed * delta;
}
}
This gives you the flexibility to mix-and-match the type of input processing This gives you the flexibility to mix-and-match the type of input processing
you do. you do.
@ -74,27 +56,15 @@ Depending on the event type, the object will contain specific properties
related to that event. To see what events actually look like, add a Node and related to that event. To see what events actually look like, add a Node and
attach the following script: attach the following script:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
func _input(event): func _input(event):
print(event.as_text()) print(event.as_text())
```
.. code-tab:: csharp
using Godot;
using System;
public class Node : Godot.Node
{
public override void _Input(InputEvent inputEvent)
{
GD.Print(inputEvent.AsText());
}
}
As you press keys, move the mouse, and perform other inputs, you'll see each As you press keys, move the mouse, and perform other inputs, you'll see each
event scroll by in the output window. Here's an example of the output: event scroll by in the output window. Here's an example of the output:
@ -128,22 +98,13 @@ You can encounter errors if you try to access a property on an input type that
doesn't contain it - calling ``position`` on ``InputEventKey`` for example. To doesn't contain it - calling ``position`` on ``InputEventKey`` for example. To
avoid this, make sure to test the event type first: avoid this, make sure to test the event type first:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _input(event): func _input(event):
if event is InputEventMouseButton: if event is InputEventMouseButton:
print("mouse button event at ", event.position) print("mouse button event at ", event.position)
```
.. code-tab:: csharp
public override void _Input(InputEvent inputEvent)
{
if (inputEvent is InputEventMouseButton mouseEvent)
{
GD.Print("mouse button event at ", mouseEvent.Position);
}
}
InputMap InputMap
-------- --------
@ -164,22 +125,13 @@ Once you've defined your actions, you can process them in your scripts using
``is_action_pressed()`` and ``is_action_released()`` by passing the name of ``is_action_pressed()`` and ``is_action_released()`` by passing the name of
the action you're looking for: the action you're looking for:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _input(event): func _input(event):
if event.is_action_pressed("my_action"): if event.is_action_pressed("my_action"):
print("my_action occurred!") print("my_action occurred!")
```
.. code-tab:: csharp
public override void _Input(InputEvent inputEvent)
{
if (inputEvent.IsActionPressed("my_action"))
{
GD.Print("my_action occurred!");
}
}
Keyboard events Keyboard events
--------------- ---------------
@ -189,26 +141,14 @@ While it's recommended to use input actions instead, there may be cases where
you want to specifically look at key events. For this example, let's check for you want to specifically look at key events. For this example, let's check for
the :kbd:`T`: the :kbd:`T`:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _input(event): func _input(event):
if event is InputEventKey and event.pressed: if event is InputEventKey and event.pressed:
if event.scancode == KEY_T: if event.scancode == KEY_T:
print("T was pressed") print("T was pressed")
```
.. code-tab:: csharp
public override void _Input(InputEvent inputEvent)
{
if (inputEvent is InputEventKey keyEvent && keyEvent.Pressed)
{
if ((KeyList)keyEvent.Keycode == KeyList.T)
{
GD.Print("T was pressed");
}
}
}
.. tip:: See :ref:`@GlobalScope_KeyList <enum_@GlobalScope_KeyList>` for a list of scancode .. tip:: See :ref:`@GlobalScope_KeyList <enum_@GlobalScope_KeyList>` for a list of scancode
constants. constants.
@ -235,9 +175,9 @@ you to check for modifier combinations using boolean properties. Let's imagine
you want one thing to happen when the :kbd:`T` is pressed, but something you want one thing to happen when the :kbd:`T` is pressed, but something
different when it's :kbd:`Shift + T`: different when it's :kbd:`Shift + T`:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _input(event): func _input(event):
if event is InputEventKey and event.pressed: if event is InputEventKey and event.pressed:
if event.scancode == KEY_T: if event.scancode == KEY_T:
@ -245,21 +185,7 @@ different when it's :kbd:`Shift + T`:
print("Shift+T was pressed") print("Shift+T was pressed")
else: else:
print("T was pressed") print("T was pressed")
```
.. code-tab:: csharp
public override void _Input(InputEvent inputEvent)
{
if (inputEvent is InputEventKey keyEvent && keyEvent.Pressed)
{
switch ((KeyList)keyEvent.Scancode)
{
case KeyList.T:
GD.Print(keyEvent.Shift ? "Shift+T was pressed" : "T was pressed");
break;
}
}
}
.. tip:: See :ref:`@GlobalScope_KeyList <enum_@GlobalScope_KeyList>` for a list of scancode .. tip:: See :ref:`@GlobalScope_KeyList <enum_@GlobalScope_KeyList>` for a list of scancode
constants. constants.
@ -281,33 +207,16 @@ be reported in the event's ``button_index`` property. Note that the scrollwheel
also counts as a button - two buttons, to be precise, with both also counts as a button - two buttons, to be precise, with both
``BUTTON_WHEEL_UP`` and ``BUTTON_WHEEL_DOWN`` being separate events. ``BUTTON_WHEEL_UP`` and ``BUTTON_WHEEL_DOWN`` being separate events.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _input(event): func _input(event):
if event is InputEventMouseButton: if event is InputEventMouseButton:
if event.button_index == BUTTON_LEFT and event.pressed: if event.button_index == BUTTON_LEFT and event.pressed:
print("Left button was clicked at ", event.position) print("Left button was clicked at ", event.position)
if event.button_index == BUTTON_WHEEL_UP and event.pressed: if event.button_index == BUTTON_WHEEL_UP and event.pressed:
print("Wheel up") print("Wheel up")
```
.. code-tab:: csharp
public override void _Input(InputEvent inputEvent)
{
if (inputEvent is InputEventMouseButton mouseEvent && mouseEvent.Pressed)
{
switch ((ButtonList)mouseEvent.ButtonIndex)
{
case ButtonList.Left:
GD.Print("Left button was clicked at ", {mouseEvent.Position});
break;
case ButtonList.WheelUp:
GD.Print("Wheel up");
break;
}
}
}
Mouse motion Mouse motion
~~~~~~~~~~~~ ~~~~~~~~~~~~
@ -319,9 +228,9 @@ property.
Here's an example using mouse events to drag-and-drop a :ref:`Sprite <class_Sprite>` Here's an example using mouse events to drag-and-drop a :ref:`Sprite <class_Sprite>`
node: node:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
@ -342,51 +251,7 @@ node:
if event is InputEventMouseMotion and dragging: if event is InputEventMouseMotion and dragging:
# While dragging, move the sprite with the mouse. # While dragging, move the sprite with the mouse.
$Sprite.position = event.position $Sprite.position = event.position
```
.. code-tab:: csharp
using Godot;
using System;
public class Node2D : Godot.Node2D
{
private bool dragging = false;
private int clickRadius = 32; // Size of the sprite.
public override void _Input(InputEvent inputEvent)
{
Sprite sprite = GetNodeOrNull<Sprite>("Sprite");
if (sprite == null)
{
return; // No suitable node was found.
}
if (inputEvent is InputEventMouseButton mouseEvent && (ButtonList)mouseEvent.ButtonIndex == ButtonList.Left)
{
if ((mouseEvent.Position - sprite.Position).Length() < clickRadius)
{
// Start dragging if the click is on the sprite.
if (!dragging && mouseEvent.Pressed)
{
dragging = true;
}
}
// Stop dragging if the button is released.
if (dragging && !mouseEvent.Pressed)
{
dragging = false;
}
}
else
{
if (inputEvent is InputEventMouseMotion motionEvent && dragging)
{
// While dragging, move the sprite with the mouse.
sprite.Position = motionEvent.Position;
}
}
}
}
Touch events Touch events
------------ ------------

View File

@ -14,22 +14,14 @@ multiple locations, depending on the purpose.
Here is a quick example, closing your game if the escape key is hit: Here is a quick example, closing your game if the escape key is hit:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _unhandled_input(event): func _unhandled_input(event):
if event is InputEventKey: if event is InputEventKey:
if event.pressed and event.scancode == KEY_ESCAPE: if event.pressed and event.scancode == KEY_ESCAPE:
get_tree().quit() get_tree().quit()
```
.. code-tab:: csharp
public override void _UnhandledInput(InputEvent @event)
{
if (@event is InputEventKey eventKey)
if (eventKey.Pressed && eventKey.Scancode == (int)KeyList.Escape)
GetTree().Quit();
}
However, it is cleaner and more flexible to use the provided :ref:`InputMap <class_InputMap>` feature, However, it is cleaner and more flexible to use the provided :ref:`InputMap <class_InputMap>` feature,
which allows you to define input actions and assign them different keys. This way, which allows you to define input actions and assign them different keys. This way,
@ -39,22 +31,13 @@ and even build a key mapping feature on top of it to allow your game to change t
You can set up your InputMap under **Project > Project Settings > Input Map** and then use those actions like this: You can set up your InputMap under **Project > Project Settings > Input Map** and then use those actions like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _process(delta): func _process(delta):
if Input.is_action_pressed("ui_right"): if Input.is_action_pressed("ui_right"):
# Move right. # Move right.
```
.. code-tab:: csharp
public override void _Process(float delta)
{
if (Input.IsActionPressed("ui_right"))
{
// Move right.
}
}
How does it work? How does it work?
----------------- -----------------
@ -180,24 +163,16 @@ from the game code (a good example of this is detecting gestures).
The Input singleton has a method for this: The Input singleton has a method for this:
:ref:`Input.parse_input_event() <class_input_method_parse_input_event>`. You would normally use it like this: :ref:`Input.parse_input_event() <class_input_method_parse_input_event>`. You would normally use it like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var ev = InputEventAction.new() var ev = InputEventAction.new()
# Set as move_left, pressed. # Set as move_left, pressed.
ev.action = "move_left" ev.action = "move_left"
ev.pressed = true ev.pressed = true
# Feedback. # Feedback.
Input.parse_input_event(ev) Input.parse_input_event(ev)
`````
.. code-tab:: csharp
var ev = new InputEventAction();
// Set as move_left, pressed.
ev.SetAction("move_left");
ev.SetPressed(true);
// Feedback.
Input.ParseInputEvent(ev);
InputMap InputMap
-------- --------

View File

@ -25,9 +25,9 @@ several options (see :ref:`doc_multiple_resolutions` tutorial). Use, then, the
functions in nodes to obtain the mouse coordinates and viewport size, functions in nodes to obtain the mouse coordinates and viewport size,
for example: for example:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _input(event): func _input(event):
# Mouse in viewport coordinates. # Mouse in viewport coordinates.
if event is InputEventMouseButton: if event is InputEventMouseButton:
@ -37,30 +37,15 @@ for example:
# Print the size of the viewport. # Print the size of the viewport.
print("Viewport Resolution is: ", get_viewport_rect().size) print("Viewport Resolution is: ", get_viewport_rect().size)
```
.. code-tab:: csharp
public override void _Input(InputEvent @event)
{
// Mouse in viewport coordinates.
if (@event is InputEventMouseButton eventMouseButton)
GD.Print("Mouse Click/Unclick at: ", eventMouseButton.Position);
else if (@event is InputEventMouseMotion eventMouseMotion)
GD.Print("Mouse Motion at: ", eventMouseMotion.Position);
// Print the size of the viewport.
GD.Print("Viewport Resolution is: ", GetViewportRect().Size);
}
Alternatively, it's possible to ask the viewport for the mouse position: Alternatively, it's possible to ask the viewport for the mouse position:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
get_viewport().get_mouse_position() get_viewport().get_mouse_position()
```
.. code-tab:: csharp
GetViewport().GetMousePosition();
.. note:: When the mouse mode is set to ``Input.MOUSE_MODE_CAPTURED``, the ``event.position`` value from ``InputEventMouseMotion`` is the center of the screen. Use ``event.relative`` instead of ``event.position`` and ``event.speed`` to process mouse movement and position changes. .. note:: When the mouse mode is set to ``Input.MOUSE_MODE_CAPTURED``, the ``event.position`` value from ``InputEventMouseMotion`` is the center of the screen. Use ``event.relative`` instead of ``event.position`` and ``event.speed`` to process mouse movement and position changes.

View File

@ -34,21 +34,13 @@ GUI:
Once this is done, when we need to save the game, we can get all objects Once this is done, when we need to save the game, we can get all objects
to save them and then tell them all to save with this script: to save them and then tell them all to save with this script:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var save_nodes = get_tree().get_nodes_in_group("Persist") var save_nodes = get_tree().get_nodes_in_group("Persist")
for i in save_nodes: for i in save_nodes:
# Now, we can call our save function on each node. # Now, we can call our save function on each node.
```
.. code-tab:: csharp
var saveNodes = GetTree().GetNodesInGroup("Persist");
foreach (Node saveNode in saveNodes)
{
// Now, we can call our save function on each node.
}
Serializing Serializing
----------- -----------
@ -62,9 +54,9 @@ has helper functions for this, such as :ref:`to_json()
contain a save function that returns this data. The save function will look contain a save function that returns this data. The save function will look
like this: like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func save(): func save():
var save_dict = { var save_dict = {
"filename" : get_filename(), "filename" : get_filename(),
@ -87,33 +79,7 @@ like this:
"last_attack" : last_attack "last_attack" : last_attack
} }
return save_dict return save_dict
```
.. code-tab:: csharp
public Godot.Collections.Dictionary<string, object> Save()
{
return new Godot.Collections.Dictionary<string, object>()
{
{ "Filename", GetFilename() },
{ "Parent", GetParent().GetPath() },
{ "PosX", Position.x }, // Vector2 is not supported by JSON
{ "PosY", Position.y },
{ "Attack", Attack },
{ "Defense", Defense },
{ "CurrentHealth", CurrentHealth },
{ "MaxHealth", MaxHealth },
{ "Damage", Damage },
{ "Regen", Regen },
{ "Experience", Experience },
{ "Tnl", Tnl },
{ "Level", Level },
{ "AttackGrowth", AttackGrowth },
{ "DefenseGrowth", DefenseGrowth },
{ "HealthGrowth", HealthGrowth },
{ "IsAlive", IsAlive },
{ "LastAttack", LastAttack }
};
}
This gives us a dictionary with the style This gives us a dictionary with the style
@ -131,9 +97,9 @@ convert it into an easily stored string and store them in a file. Doing
it this way ensures that each line is its own object, so we have an easy it this way ensures that each line is its own object, so we have an easy
way to pull the data out of the file as well. way to pull the data out of the file as well.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Note: This can be called from anywhere inside the tree. This function is # Note: This can be called from anywhere inside the tree. This function is
# path independent. # path independent.
# Go through everything in the persist category and ask them to return a # Go through everything in the persist category and ask them to return a
@ -159,44 +125,7 @@ way to pull the data out of the file as well.
# Store the save dictionary as a new line in the save file. # Store the save dictionary as a new line in the save file.
save_game.store_line(to_json(node_data)) save_game.store_line(to_json(node_data))
save_game.close() save_game.close()
```
.. code-tab:: csharp
// Note: This can be called from anywhere inside the tree. This function is
// path independent.
// Go through everything in the persist category and ask them to return a
// dict of relevant variables.
public void SaveGame()
{
var saveGame = new File();
saveGame.Open("user://savegame.save", (int)File.ModeFlags.Write);
var saveNodes = GetTree().GetNodesInGroup("Persist");
foreach (Node saveNode in saveNodes)
{
// Check the node is an instanced scene so it can be instanced again during load.
if (saveNode.Filename.Empty())
{
GD.Print(String.Format("persistent node '{0}' is not an instanced scene, skipped", saveNode.Name));
continue;
}
// Check the node has a save function.
if (!saveNode.HasMethod("Save"))
{
GD.Print(String.Format("persistent node '{0}' is missing a Save() function, skipped", saveNode.Name));
continue;
}
// Call the node's save function.
var nodeData = saveNode.Call("Save");
// Store the save dictionary as a new line in the save file.
saveGame.StoreLine(JSON.Print(nodeData));
}
saveGame.Close();
}
Game saved! Loading is fairly simple as well. For that, we'll read each Game saved! Loading is fairly simple as well. For that, we'll read each
@ -205,9 +134,9 @@ the dict to read our values. But we'll need to first create the object
and we can use the filename and parent values to achieve that. Here is our and we can use the filename and parent values to achieve that. Here is our
load function: load function:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Note: This can be called from anywhere inside the tree. This function # Note: This can be called from anywhere inside the tree. This function
# is path independent. # is path independent.
func load_game(): func load_game():
@ -242,53 +171,7 @@ load function:
new_object.set(i, node_data[i]) new_object.set(i, node_data[i])
save_game.close() save_game.close()
```
.. code-tab:: csharp
// Note: This can be called from anywhere inside the tree. This function is
// path independent.
public void LoadGame()
{
var saveGame = new File();
if (!saveGame.FileExists("user://savegame.save"))
return; // Error! We don't have a save to load.
// We need to revert the game state so we're not cloning objects during loading.
// This will vary wildly depending on the needs of a project, so take care with
// this step.
// For our example, we will accomplish this by deleting saveable objects.
var saveNodes = GetTree().GetNodesInGroup("Persist");
foreach (Node saveNode in saveNodes)
saveNode.QueueFree();
// Load the file line by line and process that dictionary to restore the object
// it represents.
saveGame.Open("user://savegame.save", (int)File.ModeFlags.Read);
while (saveGame.GetPosition() < saveGame.GetLen())
{
// Get the saved dictionary from the next line in the save file
var nodeData = new Godot.Collections.Dictionary<string, object>((Godot.Collections.Dictionary)JSON.Parse(saveGame.GetLine()).Result);
// Firstly, we need to create the object and add it to the tree and set its position.
var newObjectScene = (PackedScene)ResourceLoader.Load(nodeData["Filename"].ToString());
var newObject = (Node)newObjectScene.Instance();
GetNode(nodeData["Parent"].ToString()).AddChild(newObject);
newObject.Set("Position", new Vector2((float)nodeData["PosX"], (float)nodeData["PosY"]));
// Now we set the remaining variables.
foreach (KeyValuePair<string, object> entry in nodeData)
{
string key = entry.Key.ToString();
if (key == "Filename" || key == "Parent" || key == "PosX" || key == "PosY")
continue;
newObject.Set(key, entry.Value);
}
}
saveGame.Close();
}
Now we can save and load an arbitrary number of objects laid out Now we can save and load an arbitrary number of objects laid out
almost anywhere across the scene tree! Each object can store different almost anywhere across the scene tree! Each object can store different

View File

@ -27,34 +27,23 @@ vertices of each of the two segments formed by the three points, using values
ranging from 0 to 1. This gives us two points that move along the segments as we ranging from 0 to 1. This gives us two points that move along the segments as we
change the value of ``t`` from 0 to 1. change the value of ``t`` from 0 to 1.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _quadratic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, t: float): func _quadratic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, t: float):
var q0 = p0.linear_interpolate(p1, t) var q0 = p0.linear_interpolate(p1, t)
var q1 = p1.linear_interpolate(p2, 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 We then interpolate ``q0`` and ``q1`` to obtain a single point ``r`` that moves
along a curve. along a curve.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var r = q0.linear_interpolate(q1, t) var r = q0.linear_interpolate(q1, t)
return r return r
```
.. code-tab:: csharp
Vector2 r = q0.LinearInterpolate(q1, t);
return r;
This type of curve is called a *Quadratic Bezier* curve. This type of curve is called a *Quadratic Bezier* curve.
@ -73,65 +62,46 @@ between four points.
We first use a function with four parameters to take four points as an input, We first use a function with four parameters to take four points as an input,
``p0``, ``p1``, ``p2`` and ``p3``: ``p0``, ``p1``, ``p2`` and ``p3``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float): 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 We apply a linear interpolation to each couple of points to reduce them to
three: three:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var q0 = p0.linear_interpolate(p1, t) var q0 = p0.linear_interpolate(p1, t)
var q1 = p1.linear_interpolate(p2, t) var q1 = p1.linear_interpolate(p2, t)
var q2 = p2.linear_interpolate(p3, 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: We then take our three points and reduce them to two:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var r0 = q0.linear_interpolate(q1, t) var r0 = q0.linear_interpolate(q1, t)
var r1 = q1.linear_interpolate(q2, 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: And to one:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var s = r0.linear_interpolate(r1, t) var s = r0.linear_interpolate(r1, t)
return s return s
```
.. code-tab:: csharp
Vector2 s = r0.LinearInterpolate(r1, t);
return s;
Here is the full function: Here is the full function:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float): func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float):
var q0 = p0.linear_interpolate(p1, t) var q0 = p0.linear_interpolate(p1, t)
var q1 = p1.linear_interpolate(p2, t) var q1 = p1.linear_interpolate(p2, t)
@ -142,21 +112,7 @@ Here is the full function:
var s = r0.linear_interpolate(r1, t) var s = r0.linear_interpolate(r1, t)
return s 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: The result will be a smooth curve interpolating between all four points:
@ -206,24 +162,15 @@ Just evaluating them may be an option, but in most cases it's not very useful. T
Let's do a simple example with the following pseudocode: Let's do a simple example with the following pseudocode:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var t = 0.0 var t = 0.0
func _process(delta): func _process(delta):
t += delta t += delta
position = _cubic_bezier(p0, p1, p2, p3, t) 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 .. image:: img/bezier_interpolation_speed.gif
@ -255,24 +202,15 @@ To make this easier, the curves need to be *baked* into equidistant points. This
Traversal at constant speed, then, can be done with the following pseudo-code: Traversal at constant speed, then, can be done with the following pseudo-code:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var t = 0.0 var t = 0.0
func _process(delta): func _process(delta):
t += delta t += delta
position = curve.interpolate_baked(t * curve.get_baked_length(), true) 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: And the output will, then, move at constant speed:

View File

@ -35,30 +35,16 @@ For cubic interpolation, there are also :ref:`Vector2.cubic_interpolate() <class
Here is simple pseudo-code for going from point A to B using interpolation: Here is simple pseudo-code for going from point A to B using interpolation:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var t = 0.0 var t = 0.0
func _physics_process(delta): func _physics_process(delta):
t += delta * 0.4 t += delta * 0.4
$Sprite.position = $A.position.linear_interpolate($B.position, t) $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: It will produce the following motion:
@ -76,30 +62,16 @@ Here is an example of transforming a monkey from Position1 to Position2:
Using the following pseudocode: Using the following pseudocode:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var t = 0.0 var t = 0.0
func _physics_process(delta): func _physics_process(delta):
t += delta t += delta
$Monkey.transform = $Position1.transform.interpolate_with($Position2.transform, t) $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: And again, it will produce the following motion:
@ -111,28 +83,16 @@ Smoothing motion
Interpolation can be used to smooth movement, rotation, etc. Here is an example of a circle following the mouse using smoothed motion: Interpolation can be used to smooth movement, rotation, etc. Here is an example of a circle following the mouse using smoothed motion:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
const FOLLOW_SPEED = 4.0 const FOLLOW_SPEED = 4.0
func _physics_process(delta): func _physics_process(delta):
var mouse_pos = get_local_mouse_position() var mouse_pos = get_local_mouse_position()
$Sprite.position = $Sprite.position.linear_interpolate(mouse_pos, delta * FOLLOW_SPEED) $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: Here is how it looks:

View File

@ -72,22 +72,15 @@ becomes 2, and 0 times 2 becomes 0, so we end up with this:
To do this in code, we can simply multiply each of the vectors: To do this in code, we can simply multiply each of the vectors:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var t = Transform2D() var t = Transform2D()
# Scale # Scale
t.x *= 2 t.x *= 2
t.y *= 2 t.y *= 2
transform = t # Change the node's transform to what we just calculated. transform = t # Change the node's transform to what we just calculated.
```
.. code-tab:: csharp
Transform2D t = Transform2D.Identity;
// Scale
t.x *= 2;
t.y *= 2;
Transform = t; // Change the node's transform to what we just calculated.
If we wanted to return it to its original scale, we can multiply If we wanted to return it to its original scale, we can multiply
each component by 0.5. That's pretty much all there is to scaling each component by 0.5. That's pretty much all there is to scaling
@ -155,9 +148,9 @@ to find what the actual values should be:
Here's how that would be done in code (place the script on a Node2D): Here's how that would be done in code (place the script on a Node2D):
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var rot = 0.5 # The rotation to apply. var rot = 0.5 # The rotation to apply.
var t = Transform2D() var t = Transform2D()
t.x.x = cos(rot) t.x.x = cos(rot)
@ -165,15 +158,7 @@ Here's how that would be done in code (place the script on a Node2D):
t.x.y = sin(rot) t.x.y = sin(rot)
t.y.x = -sin(rot) t.y.x = -sin(rot)
transform = t # Change the node's transform to what we just calculated. transform = t # Change the node's transform to what we just calculated.
```
.. code-tab:: csharp
float rot = 0.5f; // The rotation to apply.
Transform2D t = Transform2D.Identity;
t.x.x = t.y.y = Mathf.Cos(rot);
t.x.y = t.y.x = Mathf.Sin(rot);
t.y.x *= -1;
Transform = t; // Change the node's transform to what we just calculated.
To calculate the object's rotation from an existing transformation To calculate the object's rotation from an existing transformation
matrix, you can use `atan2(t.x.y, t.x.x)`, where t is the Transform2D. matrix, you can use `atan2(t.x.y, t.x.x)`, where t is the Transform2D.
@ -244,9 +229,9 @@ you to try and reproduce the screenshot without looking at the code!
.. image:: img/matrices_and_transforms/putting-all-together.png .. image:: img/matrices_and_transforms/putting-all-together.png
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var t = Transform2D() var t = Transform2D()
# Translation # Translation
t.origin = Vector2(350, 150) t.origin = Vector2(350, 150)
@ -260,21 +245,7 @@ you to try and reproduce the screenshot without looking at the code!
t.x *= 3 t.x *= 3
t.y *= 3 t.y *= 3
transform = t # Change the node's transform to what we just calculated. transform = t # Change the node's transform to what we just calculated.
```
.. code-tab:: csharp
Transform2D t = Transform2D.Identity;
// Translation
t.origin = new Vector2(350, 150);
// Rotation
float rot = -0.5f; // The rotation to apply.
t.x.x = t.y.y = Mathf.Cos(rot);
t.x.y = t.y.x = Mathf.Sin(rot);
t.y.x *= -1;
// Scale
t.x *= 3;
t.y *= 3;
Transform = t; // Change the node's transform to what we just calculated.
Shearing the transformation matrix (advanced) Shearing the transformation matrix (advanced)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -310,20 +281,14 @@ As an example, let's set Y to (1, 1):
.. image:: img/matrices_and_transforms/shear.png .. image:: img/matrices_and_transforms/shear.png
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var t = Transform2D() var t = Transform2D()
# Shear by setting Y to (1, 1) # Shear by setting Y to (1, 1)
t.y = Vector2.ONE t.y = Vector2.ONE
transform = t # Change the node's transform to what we just calculated. transform = t # Change the node's transform to what we just calculated.
```
.. code-tab:: csharp
Transform2D t = Transform2D.Identity;
// Shear by setting Y to (1, 1)
t.y = Vector2.One;
Transform = t; // Change the node's transform to what we just calculated.
.. note:: You can't set the raw values of a Transform2D in the editor, .. note:: You can't set the raw values of a Transform2D in the editor,
so you *must* use code if you want to shear the object. so you *must* use code if you want to shear the object.
@ -383,30 +348,22 @@ have a world position and want to know where it is relative to the player.
We can find what a vector relative to the player would be defined in We can find what a vector relative to the player would be defined in
world space as using the "xform" method: world space as using the "xform" method:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# World space vector 100 units below the player. # World space vector 100 units below the player.
print(transform.xform(Vector2(0, 100))) print(transform.xform(Vector2(0, 100)))
```
.. code-tab:: csharp
// World space vector 100 units below the player.
GD.Print(Transform.Xform(new Vector2(0, 100)));
And we can use the "xform_inv" method to find a what world space position And we can use the "xform_inv" method to find a what world space position
would be if it was instead defined relative to the player: would be if it was instead defined relative to the player:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Where is (0, 100) relative to the player? # Where is (0, 100) relative to the player?
print(transform.xform_inv(Vector2(0, 100))) print(transform.xform_inv(Vector2(0, 100)))
```
.. code-tab:: csharp
// Where is (0, 100) relative to the player?
GD.Print(Transform.XformInv(new Vector2(0, 100)));
.. note:: If you know in advance that the transform is positioned at .. note:: If you know in advance that the transform is positioned at
(0, 0), you can use the "basis_xform" or "basis_xform_inv" (0, 0), you can use the "basis_xform" or "basis_xform_inv"
@ -425,16 +382,11 @@ add multiples of the basis vectors to move an object relative to itself.
This code moves an object 100 units to its own right: This code moves an object 100 units to its own right:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
transform.origin += transform.x * 100 transform.origin += transform.x * 100
```
.. code-tab:: csharp
Transform2D t = Transform;
t.origin += t.x * 100;
Transform = t;
For moving in 3D, you would need to replace "x" with "basis.x". For moving in 3D, you would need to replace "x" with "basis.x".
@ -472,9 +424,9 @@ basis vectors.
To calculate a child transform's world space transform manually, this is To calculate a child transform's world space transform manually, this is
the code we would use: the code we would use:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Set up transforms just like in the image, except make positions be 100 times bigger. # Set up transforms just like in the image, except make positions be 100 times bigger.
var parent = Transform2D(Vector2(2, 0), Vector2(0, 1), Vector2(100, 200)) var parent = Transform2D(Vector2(2, 0), Vector2(0, 1), Vector2(100, 200))
var child = Transform2D(Vector2(0.5, 0), Vector2(0, 0.5), Vector2(100, 100)) var child = Transform2D(Vector2(0.5, 0), Vector2(0, 0.5), Vector2(100, 100))
@ -489,45 +441,21 @@ the code we would use:
# Change the node's transform to what we just calculated. # Change the node's transform to what we just calculated.
transform = Transform2D(basis_x, basis_y, origin) transform = Transform2D(basis_x, basis_y, origin)
```
.. code-tab:: csharp
// Set up transforms just like in the image, except make positions be 100 times bigger.
Transform2D parent = new Transform2D(2, 0, 0, 1, 100, 200);
Transform2D child = new Transform2D(0.5f, 0, 0, 0.5f, 100, 100);
// Calculate the child's world space transform
// origin = (2, 0) * 100 + (0, 1) * 100 + (100, 200)
Vector2 origin = parent.x * child.origin.x + parent.y * child.origin.y + parent.origin;
// basisX = (2, 0) * 0.5 + (0, 1) * 0 = (0.5, 0)
Vector2 basisX = parent.x * child.x.x + parent.y * child.x.y;
// basisY = (2, 0) * 0 + (0, 1) * 0.5 = (0.5, 0)
Vector2 basisY = parent.x * child.y.x + parent.y * child.y.y;
// Change the node's transform to what we just calculated.
Transform = new Transform2D(basisX, basisY, origin);
In actual projects, we can find the world transform of the child by In actual projects, we can find the world transform of the child by
applying one transform onto another using the `*` operator: applying one transform onto another using the `*` operator:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Set up transforms just like in the image, except make positions be 100 times bigger. # Set up transforms just like in the image, except make positions be 100 times bigger.
var parent = Transform2D(Vector2(2, 0), Vector2(0, 1), Vector2(100, 200)) var parent = Transform2D(Vector2(2, 0), Vector2(0, 1), Vector2(100, 200))
var child = Transform2D(Vector2(0.5, 0), Vector2(0, 0.5), Vector2(100, 100)) var child = Transform2D(Vector2(0.5, 0), Vector2(0, 0.5), Vector2(100, 100))
# Change the node's transform to what would be the child's world transform. # Change the node's transform to what would be the child's world transform.
transform = parent * child transform = parent * child
```
.. code-tab:: csharp
// Set up transforms just like in the image, except make positions be 100 times bigger.
Transform2D parent = new Transform2D(2, 0, 0, 1, 100, 200);
Transform2D child = new Transform2D(0.5f, 0, 0, 0.5f, 100, 100);
// Change the node's transform to what would be the child's world transform.
Transform = parent * child;
.. note:: When multiplying matrices, order matters! Don't mix them up. .. note:: When multiplying matrices, order matters! Don't mix them up.
@ -547,36 +475,25 @@ easier to just provide a few examples.
Multiplying an inverse transform by the normal transform undoes all Multiplying an inverse transform by the normal transform undoes all
transformations: transformations:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var ti = transform.affine_inverse() var ti = transform.affine_inverse()
var t = ti * transform var t = ti * transform
# The transform is the identity transform. # The transform is the identity transform.
```
.. code-tab:: csharp
Transform2D ti = Transform.AffineInverse();
Transform2D t = ti * Transform;
// The transform is the identity transform.
Transforming a position by a transform and its inverse results in the Transforming a position by a transform and its inverse results in the
same position (same for "xform_inv"): same position (same for "xform_inv"):
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var ti = transform.affine_inverse() var ti = transform.affine_inverse()
position = transform.xform(position) position = transform.xform(position)
position = ti.xform(position) position = ti.xform(position)
# The position is the same as before. # The position is the same as before.
```
.. code-tab:: csharp
Transform2D ti = Transform.AffineInverse();
Position = Transform.Xform(Position);
Position = ti.Xform(Position);
// The position is the same as before.
How does it all work in 3D? How does it all work in 3D?
--------------------------- ---------------------------

View File

@ -44,52 +44,36 @@ 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:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
randomize() randomize()
```
.. code-tab:: csharp
public override void _Ready()
{
GD.Randomize();
}
You can also set a fixed random seed instead using :ref:`seed() You can also set a fixed random seed instead using :ref:`seed()
<class_@GDScript_method_seed>`. Doing so will give you *deterministic* results <class_@GDScript_method_seed>`. Doing so will give you *deterministic* results
across runs: across runs:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
seed(12345) seed(12345)
# To use a string as a seed, you can hash it to a number. # To use a string as a seed, you can hash it to a number.
seed("Hello world".hash()) seed("Hello world".hash())
```
.. code-tab:: csharp
public override void _Ready()
{
GD.Seed(12345);
GD.Seed("Hello world".Hash());
}
When using the RandomNumberGenerator class, you should call ``randomize()`` on 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:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var random = RandomNumberGenerator.new() var random = RandomNumberGenerator.new()
random.randomize() random.randomize()
```
.. code-tab:: csharp
var random = new RandomNumberGenerator();
random.Randomize();
Getting a random number Getting a random number
----------------------- -----------------------
@ -102,22 +86,16 @@ 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 want to use the modulo operator (``%``) to bound the result between 0 and the
denominator: denominator:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Prints a random integer between 0 and 49. # Prints a random integer between 0 and 49.
print(randi() % 50) print(randi() % 50)
# Prints a random integer between 10 and 60. # Prints a random integer between 10 and 60.
print(randi() % 51 + 10) 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 :ref:`randf() <class_@GDScript_method_randf>` returns a random floating-point
number between 0 and 1. This is useful to implement a number between 0 and 1. This is useful to implement a
@ -130,62 +108,47 @@ floating-point number following a `normal distribution
value is more likely to be around the mean (0.0 by default), value is more likely to be around the mean (0.0 by default),
varying by the deviation (1.0 by default): varying by the deviation (1.0 by default):
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Prints a random floating-point number from a normal distribution with a mean 0.0 and deviation 1.0. # Prints a random floating-point number from a normal distribution with a mean 0.0 and deviation 1.0.
var random = RandomNumberGenerator.new() var random = RandomNumberGenerator.new()
random.randomize() random.randomize()
print(random.randfn()) 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 :ref:`rand_range() <class_@GDScript_method_rand_range>` takes two arguments
``from`` and ``to``, and returns a random floating-point number between ``from`` ``from`` and ``to``, and returns a random floating-point number between ``from``
and ``to``: and ``to``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Prints a random floating-point number between -4 and 6.5. # Prints a random floating-point number between -4 and 6.5.
print(rand_range(-4, 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() :ref:`RandomNumberGenerator.randi_range()
<class_RandomNumberGenerator_method_randi_range>` takes two arguments ``from`` <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:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Prints a random integer between -10 and 10. # Prints a random integer between -10 and 10.
var random = RandomNumberGenerator.new() var random = RandomNumberGenerator.new()
random.randomize() random.randomize()
print(random.randi_range(-10, 10)) 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 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:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var _fruits = ["apple", "orange", "pear", "banana"] var _fruits = ["apple", "orange", "pear", "banana"]
func _ready(): func _ready():
@ -201,36 +164,14 @@ We can use random integer generation to get a random element from an array:
# Returns "apple", "orange", "pear", or "banana" every time the code runs. # Returns "apple", "orange", "pear", or "banana" every time the code runs.
# We may get the same fruit multiple times in a row. # We may get the same fruit multiple times in a row.
return random_fruit return random_fruit
```
.. code-tab:: csharp
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 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:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var _fruits = ["apple", "orange", "pear", "banana"] var _fruits = ["apple", "orange", "pear", "banana"]
var _last_fruit = "" var _last_fruit = ""
@ -257,38 +198,7 @@ more logic to this method:
# Returns "apple", "orange", "pear", or "banana" every time the code runs. # 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. # The function will never return the same fruit more than once in a row.
return random_fruit 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 This approach can be useful to make random number generation feel less
repetitive. Still, it doesn't prevent results from "ping-ponging" between a repetitive. Still, it doesn't prevent results from "ping-ponging" between a
@ -300,9 +210,9 @@ 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:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var metals = { var metals = {
"copper": {"quantity": 50, "price": 50}, "copper": {"quantity": 50, "price": 50},
"silver": {"quantity": 20, "price": 150}, "silver": {"quantity": 20, "price": 150},
@ -322,6 +232,7 @@ We can apply similar logic from arrays to dictionaries as well:
# Returns a random metal value dictionary every time the code runs. # Returns a random metal value dictionary every time the code runs.
# The same metal may be selected multiple times in succession. # The same metal may be selected multiple times in succession.
return random_metal return random_metal
```
.. _doc_random_number_generation_weighted_random_probability: .. _doc_random_number_generation_weighted_random_probability:
@ -332,9 +243,9 @@ 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 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:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
randomize() randomize()
@ -354,39 +265,7 @@ floating-point number between 0.0 and 1.0. We can use this to create a
else: else:
# 5% chance of being returned. # 5% chance of being returned.
return "Rare" 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: .. _doc_random_number_generation_shuffle_bags:
@ -445,9 +324,9 @@ especially popular in procedural generation to generate realistic-looking
terrain. Godot provides :ref:`class_opensimplexnoise` for this, which supports 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:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var _noise = OpenSimplexNoise.new() var _noise = OpenSimplexNoise.new()
func _ready(): func _ready():
@ -462,22 +341,4 @@ terrain. Godot provides :ref:`class_opensimplexnoise` for this, which supports
# Prints a slowly-changing series of floating-point numbers # Prints a slowly-changing series of floating-point numbers
# between -1.0 and 1.0. # 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));
}
}

View File

@ -63,15 +63,11 @@ coordinate notation. For example, in Godot, the origin is the top-left
corner of the screen, so to place a 2D node named ``Node2D`` 400 pixels to the right and corner of the screen, so to place a 2D node named ``Node2D`` 400 pixels to the right and
300 pixels down, use the following code: 300 pixels down, use the following code:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
$Node2D.position = Vector2(400, 300) $Node2D.position = Vector2(400, 300)
```
.. code-tab:: csharp
var node2D = GetNode<Node2D>("Node2D");
node2D.Position = new Vector2(400, 300);
Godot supports both :ref:`Vector2 <class_Vector2>` and Godot supports both :ref:`Vector2 <class_Vector2>` and
:ref:`Vector3 <class_Vector3>` for 2D and 3D usage, respectively. The same :ref:`Vector3 <class_Vector3>` for 2D and 3D usage, respectively. The same
@ -82,38 +78,27 @@ Member access
The individual components of the vector can be accessed directly by name. The individual components of the vector can be accessed directly by name.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# create a vector with coordinates (2, 5) # create a vector with coordinates (2, 5)
var a = Vector2(2, 5) var a = Vector2(2, 5)
# create a vector and assign x and y manually # create a vector and assign x and y manually
var b = Vector2() var b = Vector2()
b.x = 3 b.x = 3
b.y = 1 b.y = 1
```
.. code-tab:: csharp
// create a vector with coordinates (2, 5)
var a = new Vector2(2, 5);
// create a vector and assign x and y manually
var b = new Vector2();
b.x = 3;
b.y = 1;
Adding vectors Adding vectors
-------------- --------------
When adding or subtracting two vectors, the corresponding components are added: When adding or subtracting two vectors, the corresponding components are added:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var c = a + b # (2, 5) + (3, 1) = (5, 6) var c = a + b # (2, 5) + (3, 1) = (5, 6)
```
.. code-tab:: csharp
var c = a + b; // (2, 5) + (3, 1) = (5, 6)
We can also see this visually by adding the second vector at the end of We can also see this visually by adding the second vector at the end of
the first: the first:
@ -130,16 +115,12 @@ Scalar multiplication
A vector can be multiplied by a **scalar**: A vector can be multiplied by a **scalar**:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var c = a * 2 # (2, 5) * 2 = (4, 10) var c = a * 2 # (2, 5) * 2 = (4, 10)
var d = b / 3 # (3, 6) / 3 = (1, 2) var d = b / 3 # (3, 6) / 3 = (1, 2)
```
.. code-tab:: csharp
var c = a * 2; // (2, 5) * 2 = (4, 10)
var d = b / 3; // (3, 6) / 3 = (1, 2)
.. image:: img/vector_mult1.png .. image:: img/vector_mult1.png
@ -191,14 +172,11 @@ preserving its direction. This is done by dividing each of its components
by its magnitude. Because this is such a common operation, by its magnitude. Because this is such a common operation,
``Vector2`` and ``Vector3`` provide a method for normalizing: ``Vector2`` and ``Vector3`` provide a method for normalizing:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
a = a.normalized() a = a.normalized()
```
.. code-tab:: csharp
a = a.Normalized();
.. warning:: Because normalization involves dividing by the vector's length, .. warning:: Because normalization involves dividing by the vector's length,
@ -226,26 +204,16 @@ to handle this. Here is a GDScript example of the diagram above using a
:ref:`KinematicBody2D <class_KinematicBody2D>`: :ref:`KinematicBody2D <class_KinematicBody2D>`:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# object "collision" contains information about the collision # object "collision" contains information about the collision
var collision = move_and_collide(velocity * delta) var collision = move_and_collide(velocity * delta)
if collision: if collision:
var reflect = collision.remainder.bounce(collision.normal) var reflect = collision.remainder.bounce(collision.normal)
velocity = velocity.bounce(collision.normal) velocity = velocity.bounce(collision.normal)
move_and_collide(reflect) move_and_collide(reflect)
```
.. code-tab:: csharp
// KinematicCollision2D contains information about the collision
KinematicCollision2D collision = MoveAndCollide(_velocity * delta);
if (collision != null)
{
var reflect = collision.Remainder.Bounce(collision.Normal);
_velocity = _velocity.Bounce(collision.Normal);
MoveAndCollide(reflect);
}
Dot product Dot product
~~~~~~~~~~~ ~~~~~~~~~~~
@ -266,16 +234,12 @@ and
However, in most cases it is easiest to use the built-in method. Note that However, in most cases it is easiest to use the built-in method. Note that
the order of the two vectors does not matter: the order of the two vectors does not matter:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var c = a.dot(b) var c = a.dot(b)
var d = b.dot(a) # These are equivalent. var d = b.dot(a) # These are equivalent.
```
.. code-tab:: csharp
float c = a.Dot(b);
float d = b.Dot(a); // These are equivalent.
The dot product is most useful when used with unit vectors, making the The dot product is most useful when used with unit vectors, making the
first formula reduce to just ``cosθ``. This means we can use the dot first formula reduce to just ``cosθ``. This means we can use the dot
@ -305,20 +269,13 @@ the player.
In code it would look like this: In code it would look like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var AP = A.direction_to(P) var AP = A.direction_to(P)
if AP.dot(fA) > 0: if AP.dot(fA) > 0:
print("A sees P!") print("A sees P!")
```
.. code-tab:: csharp
var AP = A.DirectionTo(P);
if (AP.Dot(fA) > 0)
{
GD.Print("A sees P!");
}
Cross product Cross product
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -334,33 +291,22 @@ If two vectors are parallel, the result of their cross product will be a null ve
The cross product is calculated like this: The cross product is calculated like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var c = Vector3() var c = Vector3()
c.x = (a.y * b.z) - (a.z * b.y) c.x = (a.y * b.z) - (a.z * b.y)
c.y = (a.z * b.x) - (a.x * b.z) c.y = (a.z * b.x) - (a.x * b.z)
c.z = (a.x * b.y) - (a.y * b.x) c.z = (a.x * b.y) - (a.y * b.x)
```
.. code-tab:: csharp
var c = new Vector3();
c.x = (a.y * b.z) - (a.z * b.y);
c.y = (a.z * b.x) - (a.x * b.z);
c.z = (a.x * b.y) - (a.y * b.x);
With Godot, you can use the built-in method: With Godot, you can use the built-in method:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var c = a.cross(b) var c = a.cross(b)
```
.. code-tab:: csharp
var c = a.Cross(b);
.. note:: In the cross product, order matters. ``a.cross(b)`` does not .. note:: In the cross product, order matters. ``a.cross(b)`` does not
give the same result as ``b.cross(a)``. The resulting vectors give the same result as ``b.cross(a)``. The resulting vectors
@ -376,26 +322,16 @@ subtraction to find two edges ``AB`` and ``AC``. Using the cross product,
Here is a function to calculate a triangle's normal: Here is a function to calculate a triangle's normal:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func get_triangle_normal(a, b, c): func get_triangle_normal(a, b, c):
# find the surface normal given 3 vertices # find the surface normal given 3 vertices
var side1 = b - a var side1 = b - a
var side2 = c - a var side2 = c - a
var normal = side1.cross(side2) var normal = side1.cross(side2)
return normal return normal
```
.. code-tab:: csharp
Vector3 GetTriangleNormal(Vector3 a, Vector3 b, Vector3 c)
{
// find the surface normal given 3 vertices
var side1 = b - a;
var side2 = c - a;
var normal = side1.Cross(side2);
return normal;
}
Pointing to a target Pointing to a target
-------------------- --------------------

View File

@ -37,14 +37,11 @@ The dot product between a **unit vector** and any **point in space**
(yes, this time we do dot product between vector and position), returns (yes, this time we do dot product between vector and position), returns
the **distance from the point to the plane**: the **distance from the point to the plane**:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var distance = normal.dot(point) var distance = normal.dot(point)
```
.. code-tab:: csharp
var distance = normal.Dot(point);
But not just the absolute distance, if the point is in the negative half But not just the absolute distance, if the point is in the negative half
space the distance will be negative, too: space the distance will be negative, too:
@ -79,39 +76,30 @@ for both. It's the same as before, but D is the distance from the origin
to the plane, travelling in N direction. As an example, imagine you want to the plane, travelling in N direction. As an example, imagine you want
to reach a point in the plane, you will just do: to reach a point in the plane, you will just do:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var point_in_plane = N*D var point_in_plane = N*D
```
.. code-tab:: csharp
var pointInPlane = N * D;
This will stretch (resize) the normal vector and make it touch the This will stretch (resize) the normal vector and make it touch the
plane. This math might seem confusing, but it's actually much simpler plane. This math might seem confusing, but it's actually much simpler
than it seems. If we want to tell, again, the distance from the point to than it seems. If we want to tell, again, the distance from the point to
the plane, we do the same but adjusting for distance: the plane, we do the same but adjusting for distance:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var distance = N.dot(point) - D var distance = N.dot(point) - D
```
.. code-tab:: csharp
var distance = N.Dot(point) - D;
The same thing, using a built-in function: The same thing, using a built-in function:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var distance = plane.distance_to(point) var distance = plane.distance_to(point)
```
.. code-tab:: csharp
var distance = plane.DistanceTo(point);
This will, again, return either a positive or negative distance. This will, again, return either a positive or negative distance.
@ -119,28 +107,21 @@ Flipping the polarity of the plane can be done by negating both
N and D. This will result in a plane in the same position, but with N and D. This will result in a plane in the same position, but with
inverted negative and positive half spaces: inverted negative and positive half spaces:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
N = -N N = -N
D = -D D = -D
```
.. code-tab:: csharp
N = -N;
D = -D;
Of course, Godot also implements this operator in :ref:`Plane <class_Plane>`, Of course, Godot also implements this operator in :ref:`Plane <class_Plane>`,
so doing: so doing:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var inverted_plane = -plane var inverted_plane = -plane
```
.. code-tab:: csharp
var invertedPlane = -plane;
Will work as expected. Will work as expected.
@ -160,16 +141,12 @@ In the case of a normal and a point, most of the work is done, as the
normal is already computed, so just calculate D from the dot product of normal is already computed, so just calculate D from the dot product of
the normal and the point. the normal and the point.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var N = normal var N = normal
var D = normal.dot(point) var D = normal.dot(point)
```
.. code-tab:: csharp
var N = normal;
var D = normal.Dot(point);
For two points in space, there are actually two planes that pass through For two points in space, there are actually two planes that pass through
them, sharing the same space but with normal pointing to the opposite them, sharing the same space but with normal pointing to the opposite
@ -177,42 +154,28 @@ directions. To compute the normal from the two points, the direction
vector must be obtained first, and then it needs to be rotated 90° vector must be obtained first, and then it needs to be rotated 90°
degrees to either side: degrees to either side:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Calculate vector from `a` to `b`. # Calculate vector from `a` to `b`.
var dvec = (point_b - point_a).normalized() var dvec = (point_b - point_a).normalized()
# Rotate 90 degrees. # Rotate 90 degrees.
var normal = Vector2(dvec.y, -dvec.x) var normal = Vector2(dvec.y, -dvec.x)
# Alternatively (depending the desired side of the normal): # Alternatively (depending the desired side of the normal):
# var normal = Vector2(-dvec.y, dvec.x) # var normal = Vector2(-dvec.y, dvec.x)
```
.. code-tab:: csharp
// Calculate vector from `a` to `b`.
var dvec = (pointB - pointA).Normalized();
// Rotate 90 degrees.
var normal = new Vector2(dvec.y, -dvec.x);
// Alternatively (depending the desired side of the normal):
// var normal = new Vector2(-dvec.y, dvec.x);
The rest is the same as the previous example, either point_a or The rest is the same as the previous example, either point_a or
point_b will work since they are in the same plane: point_b will work since they are in the same plane:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var N = normal var N = normal
var D = normal.dot(point_a) var D = normal.dot(point_a)
# this works the same # this works the same
# var D = normal.dot(point_b) # var D = normal.dot(point_b)
```
.. code-tab:: csharp
var N = normal;
var D = normal.Dot(pointA);
// this works the same
// var D = normal.Dot(pointB);
Doing the same in 3D is a little more complex and will be explained Doing the same in 3D is a little more complex and will be explained
further down. further down.
@ -237,28 +200,16 @@ can't, then the point is inside.
Code should be something like this: Code should be something like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var inside = true var inside = true
for p in planes: for p in planes:
# check if distance to plane is positive # check if distance to plane is positive
if (p.distance_to(point) > 0): if (p.distance_to(point) > 0):
inside = false inside = false
break # with one that fails, it's enough break # with one that fails, it's enough
```
.. code-tab:: csharp
var inside = true;
foreach (var p in planes)
{
// check if distance to plane is positive
if (p.DistanceTo(point) > 0)
{
inside = false;
break; // with one that fails, it's enough
}
}
Pretty cool, huh? But this gets much better! With a little more effort, Pretty cool, huh? But this gets much better! With a little more effort,
similar logic will let us know when two convex polygons are overlapping similar logic will let us know when two convex polygons are overlapping
@ -276,9 +227,9 @@ the planes of B against the points of A:
Code should be something like this: Code should be something like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var overlapping = true var overlapping = true
for p in planes_of_A: for p in planes_of_A:
@ -310,60 +261,7 @@ Code should be something like this:
if (overlapping): if (overlapping):
print("Polygons Collided!") print("Polygons Collided!")
```
.. code-tab:: csharp
var overlapping = true;
foreach (Plane plane in planesOfA)
{
var allOut = true;
foreach (Vector3 point in pointsOfB)
{
if (plane.DistanceTo(point) < 0)
{
allOut = false;
break;
}
}
if (allOut)
{
// a separating plane was found
// do not continue testing
overlapping = false;
break;
}
}
if (overlapping)
{
// only do this check if no separating plane
// was found in planes of A
foreach (Plane plane in planesOfB)
{
var allOut = true;
foreach (Vector3 point in pointsOfA)
{
if (plane.DistanceTo(point) < 0)
{
allOut = false;
break;
}
}
if (allOut)
{
overlapping = false;
break;
}
}
}
if (overlapping)
{
GD.Print("Polygons Collided!");
}
As you can see, planes are quite useful, and this is the tip of the As you can see, planes are quite useful, and this is the tip of the
iceberg. You might be wondering what happens with non convex polygons. iceberg. You might be wondering what happens with non convex polygons.
@ -407,9 +305,9 @@ edges of polygon B
So the final algorithm is something like: So the final algorithm is something like:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var overlapping = true var overlapping = true
for p in planes_of_A: for p in planes_of_A:
@ -477,113 +375,7 @@ So the final algorithm is something like:
if (overlapping): if (overlapping):
print("Polygons collided!") print("Polygons collided!")
```
.. code-tab:: csharp
var overlapping = true;
foreach (Plane plane in planesOfA)
{
var allOut = true;
foreach (Vector3 point in pointsOfB)
{
if (plane.DistanceTo(point) < 0)
{
allOut = false;
break;
}
}
if (allOut)
{
// a separating plane was found
// do not continue testing
overlapping = false;
break;
}
}
if (overlapping)
{
// only do this check if no separating plane
// was found in planes of A
foreach (Plane plane in planesOfB)
{
var allOut = true;
foreach (Vector3 point in pointsOfA)
{
if (plane.DistanceTo(point) < 0)
{
allOut = false;
break;
}
}
if (allOut)
{
overlapping = false;
break;
}
}
}
if (overlapping)
{
foreach (Vector3 edgeA in edgesOfA)
{
foreach (Vector3 edgeB in edgesOfB)
{
var normal = edgeA.Cross(edgeB);
if (normal.Length() == 0)
{
continue;
}
var maxA = float.MinValue; // tiny number
var minA = float.MaxValue; // huge number
// we are using the dot product directly
// so we can map a maximum and minimum range
// for each polygon, then check if they
// overlap.
foreach (Vector3 point in pointsOfA)
{
var distance = normal.Dot(point);
maxA = Mathf.Max(maxA, distance);
minA = Mathf.Min(minA, distance);
}
var maxB = float.MinValue; // tiny number
var minB = float.MaxValue; // huge number
foreach (Vector3 point in pointsOfB)
{
var distance = normal.Dot(point);
maxB = Mathf.Max(maxB, distance);
minB = Mathf.Min(minB, distance);
}
if (minA > maxB || minB > maxA)
{
// not overlapping!
overlapping = false;
break;
}
}
if (!overlapping)
{
break;
}
}
}
if (overlapping)
{
GD.Print("Polygons Collided!");
}
More information More information
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~

View File

@ -17,22 +17,18 @@ which has a tutorial available :ref:`here <doc_http_request_class>`.
Here's an example of using the :ref:`HTTPClient <class_HTTPClient>` Here's an example of using the :ref:`HTTPClient <class_HTTPClient>`
class. It's just a script, so it can be run by executing: class. It's just a script, so it can be run by executing:
.. tabs:: console GDScript
.. code-tab:: console GDScript
```
c:\godot> godot -s http_test.gd c:\godot> godot -s http_test.gd
```
.. code-tab:: console C#
c:\godot> godot -s HTTPTest.cs
It will connect and fetch a website. It will connect and fetch a website.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends SceneTree extends SceneTree
# HTTPClient demo # HTTPClient demo
@ -121,106 +117,4 @@ It will connect and fetch a website.
print("Text: ", text) print("Text: ", text)
quit() quit()
```
.. code-tab:: csharp
class HTTPTest : SceneTree
{
// HTTPClient demo.
// This simple class can make HTTP requests; it will not block, but it needs to be polled.
public override async void _Initialize()
{
Error err;
HTTPClient http = new HTTPClient(); // Create the client.
err = http.ConnectToHost("www.php.net", 80); // Connect to host/port.
Debug.Assert(err == Error.Ok); // Make sure the connection is OK.
// Wait until resolved and connected.
while (http.GetStatus() == HTTPClient.Status.Connecting || http.GetStatus() == HTTPClient.Status.Resolving)
{
http.Poll();
GD.Print("Connecting...");
OS.DelayMsec(500);
}
Debug.Assert(http.GetStatus() == HTTPClient.Status.Connected); // Check if the connection was made successfully.
// Some headers.
string[] headers = { "User-Agent: Pirulo/1.0 (Godot)", "Accept: */*" };
err = http.Request(HTTPClient.Method.Get, "/ChangeLog-5.php", headers); // Request a page from the site.
Debug.Assert(err == Error.Ok); // Make sure all is OK.
// Keep polling for as long as the request is being processed.
while (http.GetStatus() == HTTPClient.Status.Requesting)
{
http.Poll();
GD.Print("Requesting...");
if (OS.HasFeature("web"))
{
// Synchronous HTTP requests are not supported on the web,
// so wait for the next main loop iteration.
await ToSignal(Engine.GetMainLoop(), "idle_frame");
}
else
{
OS.DelayMsec(500);
}
}
Debug.Assert(http.GetStatus() == HTTPClient.Status.Body || http.GetStatus() == HTTPClient.Status.Connected); // Make sure the request finished well.
GD.Print("Response? ", http.HasResponse()); // The site might not have a response.
// If there is a response...
if (http.HasResponse())
{
headers = http.GetResponseHeaders(); // Get response headers.
GD.Print("Code: ", http.GetResponseCode()); // Show response code.
GD.Print("Headers:");
foreach (string header in headers)
{
// Show headers.
GD.Print(header);
}
if (http.IsResponseChunked())
{
// Does it use chunks?
GD.Print("Response is Chunked!");
}
else
{
// Or just Content-Length.
GD.Print("Response Length: ", http.GetResponseBodyLength());
}
// This method works for both anyways.
List<byte> rb = new List<byte>(); // List that will hold the data.
// While there is data left to be read...
while (http.GetStatus() == HTTPClient.Status.Body)
{
http.Poll();
byte[] chunk = http.ReadResponseBodyChunk(); // Read a chunk.
if (chunk.Length == 0)
{
// If nothing was read, wait for the buffer to fill.
OS.DelayMsec(500);
}
else
{
// Append the chunk to the read buffer.
rb.AddRange(chunk);
}
}
// Done!
GD.Print("Bytes Downloaded: ", rb.Count);
string text = Encoding.ASCII.GetString(rb.ToArray());
GD.Print(text);
}
Quit();
}
}

View File

@ -30,10 +30,9 @@ Scripting
Below is all the code we need to make it work. The URL points to an online API mocker; it returns a pre-defined JSON string, which we will then parse to get access to the data. Below is all the code we need to make it work. The URL points to an online API mocker; it returns a pre-defined JSON string, which we will then parse to get access to the data.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends CanvasLayer extends CanvasLayer
func _ready(): func _ready():
@ -45,29 +44,7 @@ Below is all the code we need to make it work. The URL points to an online API m
func _on_request_completed(result, response_code, headers, body): func _on_request_completed(result, response_code, headers, body):
var json = JSON.parse(body.get_string_from_utf8()) var json = JSON.parse(body.get_string_from_utf8())
print(json.result) print(json.result)
```
.. code-tab:: csharp
class HTTPRequestDemo : CanvasLayer
{
public override void _Ready()
{
GetNode("HTTPRequest").Connect("request_completed", this, "OnRequestCompleted");
GetNode("Button").Connect("pressed", this, "OnButtonPressed");
}
public void OnButtonPressed()
{
HTTPRequest httpRequest = GetNode<HTTPRequest>("HTTPRequest");
httpRequest.Request("http://www.mocky.io/v2/5185415ba171ea3a00704eed");
}
public void OnRequestCompleted(int result, int response_code, string[] headers, byte[] body)
{
JSONParseResult json = JSON.Parse(Encoding.UTF8.GetString(body));
GD.Print(json.Result);
}
}
With this, you should see ``(hello:world)`` printed on the console; hello being a key, and world being a value, both of them strings. With this, you should see ``(hello:world)`` printed on the console; hello being a key, and world being a value, both of them strings.
@ -78,16 +55,11 @@ Note that you may want to check whether the ``result`` equals ``RESULT_SUCCESS``
Of course, you can also set custom HTTP headers. These are given as a string array, with each string containing a header in the format ``"header: value"``. Of course, you can also set custom HTTP headers. These are given as a string array, with each string containing a header in the format ``"header: value"``.
For example, to set a custom user agent (the HTTP ``user-agent`` header) you could use the following: For example, to set a custom user agent (the HTTP ``user-agent`` header) you could use the following:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
$HTTPRequest.request("http://www.mocky.io/v2/5185415ba171ea3a00704eed", ["user-agent: YourCustomUserAgent"]) $HTTPRequest.request("http://www.mocky.io/v2/5185415ba171ea3a00704eed", ["user-agent: YourCustomUserAgent"])
```
.. code-tab:: csharp
HTTPRequest httpRequest = GetNode<HTTPRequest>("HTTPRequest");
httpRequest.Request("http://www.mocky.io/v2/5185415ba171ea3a00704eed", new string[] { "user-agent: YourCustomUserAgent" });
Please note that, for SSL/TLS encryption and thus HTTPS URLs to work, you may need to take some steps as described :ref:`here <doc_ssl_certificates>`. Please note that, for SSL/TLS encryption and thus HTTPS URLs to work, you may need to take some steps as described :ref:`here <doc_ssl_certificates>`.
@ -99,26 +71,16 @@ Sending data to server
Until now, we have limited ourselves to requesting data from a server. But what if you need to send data to the server? Here is a common way of doing it: Until now, we have limited ourselves to requesting data from a server. But what if you need to send data to the server? Here is a common way of doing it:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _make_post_request(url, data_to_send, use_ssl): func _make_post_request(url, data_to_send, use_ssl):
# Convert data to json string: # Convert data to json string:
var query = JSON.print(data_to_send) var query = JSON.print(data_to_send)
# Add 'Content-Type' header: # Add 'Content-Type' header:
var headers = ["Content-Type: application/json"] var headers = ["Content-Type: application/json"]
$HTTPRequest.request(url, headers, use_ssl, HTTPClient.METHOD_POST, query) $HTTPRequest.request(url, headers, use_ssl, HTTPClient.METHOD_POST, query)
```
.. code-tab:: csharp
public void MakePostRequest(string url, object data_to_send, bool use_ssl)
{
string query = JSON.Print(data_to_send);
HTTPRequest httpRequest = GetNode<HTTPRequest>("HTTPRequest");
string[] headers = new string[] { "Content-Type: application/json" };
httpRequest.Request(url, headers, use_ssl, HTTPClient.Method.Post, query);
}
Keep in mind that you have to wait for a request to finish before sending another one. Making multiple request at once requires you to have one node per request. Keep in mind that you have to wait for a request to finish before sending another one. Making multiple request at once requires you to have one node per request.
A common strategy is to create and delete HTTPRequest nodes at runtime as necessary. A common strategy is to create and delete HTTPRequest nodes at runtime as necessary.

View File

@ -24,9 +24,9 @@ Creating a Thread
Creating a thread is very simple, just use the following code: Creating a thread is very simple, just use the following code:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var thread var thread
# The thread will start here. # The thread will start here.
@ -47,6 +47,7 @@ Creating a thread is very simple, just use the following code:
# Thread must be disposed (or "joined"), for portability. # Thread must be disposed (or "joined"), for portability.
func _exit_tree(): func _exit_tree():
thread.wait_to_finish() thread.wait_to_finish()
```
Your function will, then, run in a separate thread until it returns. Your function will, then, run in a separate thread until it returns.
Even if the function has returned already, the thread must collect it, so call Even if the function has returned already, the thread must collect it, so call
@ -75,9 +76,9 @@ allowed to proceed with the lock (but only one at a time).
Here is an example of using a Mutex: Here is an example of using a Mutex:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var counter = 0 var counter = 0
var mutex var mutex
var thread var thread
@ -106,6 +107,7 @@ Here is an example of using a Mutex:
func _exit_tree(): func _exit_tree():
thread.wait_to_finish() thread.wait_to_finish()
print("Counter is: ", counter) # Should be 2. print("Counter is: ", counter) # Should be 2.
```
Semaphores Semaphores
---------- ----------
@ -120,9 +122,9 @@ The main thread, instead, uses
:ref:`Semaphore.post()<class_Semaphore_method_post>` to signal that data is :ref:`Semaphore.post()<class_Semaphore_method_post>` to signal that data is
ready to be processed: ready to be processed:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var counter = 0 var counter = 0
var mutex var mutex
var semaphore var semaphore
@ -183,3 +185,4 @@ ready to be processed:
# Print the counter. # Print the counter.
print("Counter is: ", counter) print("Counter is: ", counter)
```

View File

@ -50,9 +50,9 @@ Multimesh example
Here is an example of using a MultiMesh from code. Languages other than GDScript may be more Here is an example of using a MultiMesh from code. Languages other than GDScript may be more
efficient for millions of objects, but for a few thousands, GDScript should be fine. efficient for millions of objects, but for a few thousands, GDScript should be fine.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends MultiMeshInstance extends MultiMeshInstance
@ -71,31 +71,4 @@ efficient for millions of objects, but for a few thousands, GDScript should be f
# Set the transform of the instances. # Set the transform of the instances.
for i in multimesh.visible_instance_count: for i in multimesh.visible_instance_count:
multimesh.set_instance_transform(i, Transform(Basis(), Vector3(i * 20, 0, 0))) multimesh.set_instance_transform(i, Transform(Basis(), Vector3(i * 20, 0, 0)))
```
.. code-tab:: csharp C#
using Godot;
using System;
public class YourClassName : MultiMeshInstance
{
public override void _Ready()
{
// Create the multimesh.
Multimesh = new MultiMesh();
// Set the format first.
Multimesh.TransformFormat = MultiMesh.TransformFormatEnum.Transform3d;
Multimesh.ColorFormat = MultiMesh.ColorFormatEnum.None;
Multimesh.CustomDataFormat = MultiMesh.CustomDataFormatEnum.None;
// Then resize (otherwise, changing the format is not allowed)
Multimesh.InstanceCount = 1000;
// Maybe not all of them should be visible at first.
Multimesh.VisibleInstanceCount = 1000;
// Set the transform of the instances.
for (int i = 0; i < Multimesh.VisibleInstanceCount; i++)
{
Multimesh.SetInstanceTransform(i, new Transform(Basis.Identity, new Vector3(i * 20, 0, 0)));
}
}
}

View File

@ -88,9 +88,10 @@ Creating a sprite
This is a simple example of how to create a sprite from code and move it using the low-level This is a simple example of how to create a sprite from code and move it using the low-level
:ref:`CanvasItem <class_CanvasItem>` API. :ref:`CanvasItem <class_CanvasItem>` API.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node2D extends Node2D
@ -111,6 +112,7 @@ This is a simple example of how to create a sprite from code and move it using t
# Add the item, rotated 45 degrees and translated. # Add the item, rotated 45 degrees and translated.
var xform = Transform2D().rotated(deg2rad(45)).translated(Vector2(20, 30)) var xform = Transform2D().rotated(deg2rad(45)).translated(Vector2(20, 30))
VisualServer.canvas_item_set_transform(ci_rid, xform) VisualServer.canvas_item_set_transform(ci_rid, xform)
```
The Canvas Item API in the server allows you to add draw primitives to it. Once added, they can't be modified. The Canvas Item API in the server allows you to add draw primitives to it. Once added, they can't be modified.
The Item needs to be cleared and the primitives re-added (this is not the case for setting the transform, The Item needs to be cleared and the primitives re-added (this is not the case for setting the transform,
@ -118,20 +120,20 @@ which can be done as many times as desired).
Primitives are cleared this way: Primitives are cleared this way:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
VisualServer.canvas_item_clear(ci_rid) VisualServer.canvas_item_clear(ci_rid)
```
Instantiating a Mesh into 3D space Instantiating a Mesh into 3D space
---------------------------------- ----------------------------------
The 3D APIs are different from the 2D ones, so the instantiation API must be used. The 3D APIs are different from the 2D ones, so the instantiation API must be used.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Spatial extends Spatial
@ -153,6 +155,7 @@ The 3D APIs are different from the 2D ones, so the instantiation API must be use
# Move the mesh around. # Move the mesh around.
var xform = Transform(Basis(), Vector3(20, 100, 0)) var xform = Transform(Basis(), Vector3(20, 100, 0))
VisualServer.instance_set_transform(instance, xform) VisualServer.instance_set_transform(instance, xform)
```
Creating a 2D RigidBody and moving a sprite with it Creating a 2D RigidBody and moving a sprite with it
--------------------------------------------------- ---------------------------------------------------
@ -160,9 +163,9 @@ Creating a 2D RigidBody and moving a sprite with it
This creates a :ref:`RigidBody2D <class_RigidBody2D>` using the :ref:`Physics2DServer <class_Physics2DServer>` API, This creates a :ref:`RigidBody2D <class_RigidBody2D>` using the :ref:`Physics2DServer <class_Physics2DServer>` API,
and moves a :ref:`CanvasItem <class_CanvasItem>` when the body moves. and moves a :ref:`CanvasItem <class_CanvasItem>` when the body moves.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Physics2DServer expects references to be kept around. # Physics2DServer expects references to be kept around.
var body var body
var shape var shape
@ -191,6 +194,7 @@ and moves a :ref:`CanvasItem <class_CanvasItem>` when the body moves.
# The last parameter is optional, can be used as index # The last parameter is optional, can be used as index
# if you have many bodies and a single callback. # if you have many bodies and a single callback.
Physics2DServer.body_set_force_integration_callback(body, self, "_body_moved", 0) Physics2DServer.body_set_force_integration_callback(body, self, "_body_moved", 0)
```
The 3D version should be very similar, as 2D and 3D physics servers are identical (using The 3D version should be very similar, as 2D and 3D physics servers are identical (using
:ref:`RigidBody <class_RigidBody>` and :ref:`PhysicsServer <class_PhysicsServer>` respectively). :ref:`RigidBody <class_RigidBody>` and :ref:`PhysicsServer <class_PhysicsServer>` respectively).

View File

@ -54,25 +54,14 @@ per second, always. This makes physics and motion calculation work in a
more predictable way than using regular process, which might have spikes more predictable way than using regular process, which might have spikes
or lose precision if the frame rate is too high or too low. or lose precision if the frame rate is too high or too low.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
func _physics_process(delta): func _physics_process(delta):
pass pass
```
.. code-tab:: csharp
using Godot;
using System;
public class PhysicsScript : KinematicBody2D
{
public override void _PhysicsProcess(float delta)
{
}
}
Scene setup Scene setup
@ -119,27 +108,14 @@ collision happens, it stops right at the moment of the collision.
So, let's move our sprite downwards until it hits the floor: So, let's move our sprite downwards until it hits the floor:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
func _physics_process(delta): func _physics_process(delta):
move_and_collide(Vector2(0, 1)) # Move down 1 pixel per physics frame move_and_collide(Vector2(0, 1)) # Move down 1 pixel per physics frame
```
.. code-tab:: csharp
using Godot;
using System;
public class PhysicsScript : KinematicBody2D
{
public override void _PhysicsProcess(float delta)
{
// Move down 1 pixel per physics frame
MoveAndCollide(new Vector2(0, 1));
}
}
The result is that the character will move, but stop right when The result is that the character will move, but stop right when
hitting the floor. Pretty cool, huh? hitting the floor. Pretty cool, huh?
@ -147,9 +123,9 @@ hitting the floor. Pretty cool, huh?
The next step will be adding gravity to the mix, this way it behaves a The next step will be adding gravity to the mix, this way it behaves a
little more like a regular game character: little more like a regular game character:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
const GRAVITY = 200.0 const GRAVITY = 200.0
@ -160,25 +136,7 @@ little more like a regular game character:
var motion = velocity * delta var motion = velocity * delta
move_and_collide(motion) move_and_collide(motion)
```
.. code-tab:: csharp
using Godot;
using System;
public class PhysicsScript : KinematicBody2D
{
const float gravity = 200.0f;
Vector2 velocity;
public override void _PhysicsProcess(float delta)
{
velocity.y += delta * gravity;
var motion = velocity * delta;
MoveAndCollide(motion);
}
}
Now the character falls smoothly. Let's make it walk to the sides, left Now the character falls smoothly. Let's make it walk to the sides, left
and right when touching the directional keys. Remember that the values and right when touching the directional keys. Remember that the values
@ -186,9 +144,9 @@ being used (for speed at least) are pixels/second.
This adds simple walking support by pressing left and right: This adds simple walking support by pressing left and right:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
const GRAVITY = 200.0 const GRAVITY = 200.0
@ -211,43 +169,7 @@ This adds simple walking support by pressing left and right:
# The second parameter of "move_and_slide" is the normal pointing up. # The second parameter of "move_and_slide" is the normal pointing up.
# In the case of a 2D platformer, in Godot, upward is negative y, which translates to -1 as a normal. # In the case of a 2D platformer, in Godot, upward is negative y, which translates to -1 as a normal.
move_and_slide(velocity, Vector2(0, -1)) move_and_slide(velocity, Vector2(0, -1))
```
.. code-tab:: csharp
using Godot;
using System;
public class PhysicsScript : KinematicBody2D
{
const float gravity = 200.0f;
const int walkSpeed = 200;
Vector2 velocity;
public override void _PhysicsProcess(float delta)
{
velocity.y += delta * gravity;
if (Input.IsActionPressed("ui_left"))
{
velocity.x = -walkSpeed;
}
else if (Input.IsActionPressed("ui_right"))
{
velocity.x = walkSpeed;
}
else
{
velocity.x = 0;
}
// We don't need to multiply velocity by delta because "MoveAndSlide" already takes delta time into account.
// The second parameter of "MoveAndSlide" is the normal pointing up.
// In the case of a 2D platformer, in Godot, upward is negative y, which translates to -1 as a normal.
MoveAndSlide(velocity, new Vector2(0, -1));
}
}
And give it a try. And give it a try.

View File

@ -263,9 +263,9 @@ the physics engine.
For example, here is the code for an "Asteroids" style spaceship: For example, here is the code for an "Asteroids" style spaceship:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends RigidBody2D extends RigidBody2D
var thrust = Vector2(0, 250) var thrust = Vector2(0, 250)
@ -282,29 +282,7 @@ For example, here is the code for an "Asteroids" style spaceship:
if Input.is_action_pressed("ui_left"): if Input.is_action_pressed("ui_left"):
rotation_dir -= 1 rotation_dir -= 1
applied_torque = rotation_dir * torque applied_torque = rotation_dir * torque
```
.. code-tab:: csharp
class Spaceship : RigidBody2D
{
private Vector2 _thrust = new Vector2(0, 250);
private float _torque = 20000;
public override void _IntegrateForces(Physics2DDirectBodyState state)
{
if (Input.IsActionPressed("ui_up"))
AppliedForce = _thrust.Rotated(Rotation);
else
AppliedForce = new Vector2();
var rotationDir = 0;
if (Input.IsActionPressed("ui_right"))
rotationDir += 1;
if (Input.IsActionPressed("ui_left"))
rotationDir -= 1;
AppliedTorque = rotationDir * _torque;
}
}
Note that we are not setting the ``linear_velocity`` or ``angular_velocity`` Note that we are not setting the ``linear_velocity`` or ``angular_velocity``
properties directly, but rather applying forces (``thrust`` and ``torque``) to properties directly, but rather applying forces (``thrust`` and ``torque``) to
@ -362,9 +340,9 @@ information to determine the response.
For example, if you want to find the point in space where the collision For example, if you want to find the point in space where the collision
occurred: occurred:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
var velocity = Vector2(250, 250) var velocity = Vector2(250, 250)
@ -373,28 +351,13 @@ occurred:
var collision_info = move_and_collide(velocity * delta) var collision_info = move_and_collide(velocity * delta)
if collision_info: if collision_info:
var collision_point = collision_info.position var collision_point = collision_info.position
```
.. code-tab:: csharp
class Body : KinematicBody2D
{
private Vector2 _velocity = new Vector2(250, 250);
public override void _PhysicsProcess(float delta)
{
var collisionInfo = MoveAndCollide(_velocity * delta);
if (collisionInfo != null)
{
var collisionPoint = collisionInfo.GetPosition();
}
}
}
Or to bounce off of the colliding object: Or to bounce off of the colliding object:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
var velocity = Vector2(250, 250) var velocity = Vector2(250, 250)
@ -403,20 +366,7 @@ Or to bounce off of the colliding object:
var collision_info = move_and_collide(velocity * delta) var collision_info = move_and_collide(velocity * delta)
if collision_info: if collision_info:
velocity = velocity.bounce(collision_info.normal) velocity = velocity.bounce(collision_info.normal)
```
.. code-tab:: csharp
class Body : KinematicBody2D
{
private Vector2 _velocity = new Vector2(250, 250);
public override void _PhysicsProcess(float delta)
{
var collisionInfo = MoveAndCollide(_velocity * delta);
if (collisionInfo != null)
_velocity = _velocity.Bounce(collisionInfo.Normal);
}
}
:ref:`move_and_slide <class_KinematicBody2D_method_move_and_slide>` :ref:`move_and_slide <class_KinematicBody2D_method_move_and_slide>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -434,9 +384,9 @@ without writing much code.
For example, use the following code to make a character that can walk along For example, use the following code to make a character that can walk along
the ground (including slopes) and jump when standing on the ground: the ground (including slopes) and jump when standing on the ground:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
var run_speed = 350 var run_speed = 350
@ -462,40 +412,7 @@ the ground (including slopes) and jump when standing on the ground:
velocity.y += gravity * delta velocity.y += gravity * delta
get_input() get_input()
velocity = move_and_slide(velocity, Vector2(0, -1)) velocity = move_and_slide(velocity, Vector2(0, -1))
```
.. code-tab:: csharp
class Body : KinematicBody2D
{
private float _runSpeed = 350;
private float _jumpSpeed = -1000;
private float _gravity = 2500;
private Vector2 _velocity = new Vector2();
private void GetInput()
{
_velocity.x = 0;
var right = Input.IsActionPressed("ui_right");
var left = Input.IsActionPressed("ui_left");
var jump = Input.IsActionPressed("ui_select");
if (IsOnFloor() && jump)
_velocity.y = _jumpSpeed;
if (right)
_velocity.x += _runSpeed;
if (left)
_velocity.x -= _runSpeed;
}
public override void _PhysicsProcess(float delta)
{
_velocity.y += _gravity * delta;
GetInput();
_velocity = MoveAndSlide(_velocity, new Vector2(0,-1));
}
}
See :ref:`doc_kinematic_character_2d` for more details on using ``move_and_slide()``, See :ref:`doc_kinematic_character_2d` for more details on using ``move_and_slide()``,

View File

@ -62,11 +62,10 @@ Simulating the ragdoll
The ragdoll is now ready to use. To start the simulation and play the ragdoll animation, you need to call the ``physical_bones_start_simulation`` method. Attach a script to the skeleton node and call the method in the ``_ready`` method: The ragdoll is now ready to use. To start the simulation and play the ragdoll animation, you need to call the ``physical_bones_start_simulation`` method. Attach a script to the skeleton node and call the method in the ``_ready`` method:
.. tabs:: ```
.. code-tab:: gdscript GDScript
func _ready(): func _ready():
physical_bones_start_simulation() physical_bones_start_simulation()
```
To stop the simulation, call the ``physical_bones_stop_simulation()`` method. To stop the simulation, call the ``physical_bones_stop_simulation()`` method.

View File

@ -51,50 +51,31 @@ must be used.
Use the following code in 2D: Use the following code in 2D:
.. tabs:: gdscript GDscript
.. code-tab:: gdscript GDscript
```
func _physics_process(delta): func _physics_process(delta):
var space_rid = get_world_2d().space var space_rid = get_world_2d().space
var space_state = Physics2DServer.space_get_direct_state(space_rid) var space_state = Physics2DServer.space_get_direct_state(space_rid)
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
var spaceRid = GetWorld2d().Space;
var spaceState = Physics2DServer.SpaceGetDirectState(spaceRid);
}
Or more directly: Or more directly:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _physics_process(delta): func _physics_process(delta):
var space_state = get_world_2d().direct_space_state var space_state = get_world_2d().direct_space_state
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
var spaceState = GetWorld2d().DirectSpaceState;
}
And in 3D: And in 3D:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _physics_process(delta): func _physics_process(delta):
var space_state = get_world().direct_space_state var space_state = get_world().direct_space_state
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
var spaceState = GetWorld().DirectSpaceState;
}
Raycast query Raycast query
------------- -------------
@ -103,36 +84,24 @@ For performing a 2D raycast query, the method
:ref:`Physics2DDirectSpaceState.intersect_ray() <class_Physics2DDirectSpaceState_method_intersect_ray>` :ref:`Physics2DDirectSpaceState.intersect_ray() <class_Physics2DDirectSpaceState_method_intersect_ray>`
may be used. For example: may be used. For example:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _physics_process(delta): func _physics_process(delta):
var space_state = get_world_2d().direct_space_state var space_state = get_world_2d().direct_space_state
# use global coordinates, not local to node # use global coordinates, not local to node
var result = space_state.intersect_ray(Vector2(0, 0), Vector2(50, 100)) var result = space_state.intersect_ray(Vector2(0, 0), Vector2(50, 100))
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
var spaceState = GetWorld2d().DirectSpaceState;
// use global coordinates, not local to node
var result = spaceState.IntersectRay(new Vector2(), new Vector2(50, 100));
}
The result is a dictionary. If the ray didn't hit anything, the dictionary will The result is a dictionary. If the ray didn't hit anything, the dictionary will
be empty. If it did hit something, it will contain collision information: be empty. If it did hit something, it will contain collision information:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
if result: if result:
print("Hit at point: ", result.position) print("Hit at point: ", result.position)
```
.. code-tab:: csharp
if (result.Count > 0)
GD.Print("Hit at point: ", result["position"]);
The ``result`` dictionary when a collision occurs contains the following The ``result`` dictionary when a collision occurs contains the following
data: data:
@ -166,25 +135,15 @@ optional third parameter which is an array of exceptions. This is an
example of how to use it from a KinematicBody2D or any other example of how to use it from a KinematicBody2D or any other
collision object node: collision object node:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
func _physics_process(delta): func _physics_process(delta):
var space_state = get_world_2d().direct_space_state var space_state = get_world_2d().direct_space_state
var result = space_state.intersect_ray(global_position, enemy_position, [self]) var result = space_state.intersect_ray(global_position, enemy_position, [self])
```
.. code-tab:: csharp
class Body : KinematicBody2D
{
public override void _PhysicsProcess(float delta)
{
var spaceState = GetWorld2d().DirectSpaceState;
var result = spaceState.IntersectRay(globalPosition, enemyPosition, new Godot.Collections.Array { this });
}
}
The exceptions array can contain objects or RIDs. The exceptions array can contain objects or RIDs.
@ -199,27 +158,16 @@ The optional fourth argument for ``intersect_ray()`` is a collision mask. For
example, to use the same mask as the parent body, use the ``collision_mask`` example, to use the same mask as the parent body, use the ``collision_mask``
member variable: member variable:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
func _physics_process(delta): func _physics_process(delta):
var space_state = get_world().direct_space_state var space_state = get_world().direct_space_state
var result = space_state.intersect_ray(global_position, enemy_position, var result = space_state.intersect_ray(global_position, enemy_position,
[self], collision_mask) [self], collision_mask)
```
.. code-tab:: csharp
class Body : KinematicBody2D
{
public override void _PhysicsProcess(float delta)
{
var spaceState = GetWorld2d().DirectSpaceState;
var result = spaceState.IntersectRay(globalPosition, enemyPosition,
new Godot.Collections.Array { this }, CollisionMask);
}
}
See :ref:`doc_physics_introduction_collision_layer_code_example` for details on how to set the collision mask. See :ref:`doc_physics_introduction_collision_layer_code_example` for details on how to set the collision mask.
@ -242,9 +190,9 @@ obtained. This is because ``origin`` changes in orthogonal mode, while
To obtain it using a camera, the following code can be used: To obtain it using a camera, the following code can be used:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
const ray_length = 1000 const ray_length = 1000
func _input(event): func _input(event):
@ -252,20 +200,7 @@ To obtain it using a camera, the following code can be used:
var camera = $Camera var camera = $Camera
var from = camera.project_ray_origin(event.position) var from = camera.project_ray_origin(event.position)
var to = from + camera.project_ray_normal(event.position) * ray_length var to = from + camera.project_ray_normal(event.position) * ray_length
```
.. code-tab:: csharp
private const float rayLength = 1000;
public override void _Input(InputEvent @event)
{
if (@event is InputEventMouseButton eventMouseButton && eventMouseButton.Pressed && eventMouseButton.ButtonIndex == 1)
{
var camera = GetNode<Camera>("Camera");
var from = camera.ProjectRayOrigin(eventMouseButton.Position);
var to = from + camera.ProjectRayNormal(eventMouseButton.Position) * rayLength;
}
}
Remember that during ``_input()``, the space may be locked, so in practice Remember that during ``_input()``, the space may be locked, so in practice

View File

@ -31,9 +31,9 @@ The "look at" method
As described above, using the Spatial node's ``look_at()`` method can't be used each frame to follow a target. As described above, using the Spatial node's ``look_at()`` method can't be used each frame to follow a target.
Here is a custom ``look_at()`` method that will work reliably with rigid bodies: Here is a custom ``look_at()`` method that will work reliably with rigid bodies:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends RigidBody extends RigidBody
func look_follow(state, current_transform, target_position): func look_follow(state, current_transform, target_position):
@ -47,27 +47,7 @@ Here is a custom ``look_at()`` method that will work reliably with rigid bodies:
func _integrate_forces(state): func _integrate_forces(state):
var target_position = $my_target_spatial_node.get_global_transform().origin var target_position = $my_target_spatial_node.get_global_transform().origin
look_follow(state, get_global_transform(), target_position) look_follow(state, get_global_transform(), target_position)
```
.. code-tab:: csharp
class Body : RigidBody
{
private void LookFollow(PhysicsDirectBodyState state, Transform currentTransform, Vector3 targetPosition)
{
var upDir = new Vector3(0, 1, 0);
var curDir = currentTransform.basis.Xform(new Vector3(0, 0, 1));
var targetDir = (targetPosition - currentTransform.origin).Normalized();
var rotationAngle = Mathf.Acos(curDir.x) - Mathf.Acos(targetDir.x);
state.SetAngularVelocity(upDir * (rotationAngle / state.GetStep()));
}
public override void _IntegrateForces(PhysicsDirectBodyState state)
{
var targetPosition = GetNode<Spatial>("my_target_spatial_node").GetGlobalTransform().origin;
LookFollow(state, GetGlobalTransform(), targetPosition);
}
}
This method uses the rigid body's ``set_angular_velocity()`` method to rotate the body. It first calculates the difference between the current and desired angle and then adds the velocity needed to rotate by that amount in one frame's time. This method uses the rigid body's ``set_angular_velocity()`` method to rotate the body. It first calculates the difference between the current and desired angle and then adds the velocity needed to rotate by that amount in one frame's time.

View File

@ -68,24 +68,14 @@ use ``area_entered``. However, let's assume our player is a ``KinematicBody2D``
.. note:: If you're not familiar with using signals, see :ref:`doc_signals` for .. note:: If you're not familiar with using signals, see :ref:`doc_signals` for
an introduction. an introduction.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Area2D extends Area2D
func _on_Coin_body_entered(body): func _on_Coin_body_entered(body):
queue_free() queue_free()
```
.. code-tab:: csharp
public class Coin : Area2D
{
public void OnCoinBodyEntered(PhysicsBody2D body)
{
QueueFree();
}
}
Now our player can collect the coins! Now our player can collect the coins!

View File

@ -116,9 +116,9 @@ When using ``move_and_slide()`` it's possible to have multiple collisions occur,
as the slide response is calculated. To process these collisions, use ``get_slide_count()`` as the slide response is calculated. To process these collisions, use ``get_slide_count()``
and ``get_slide_collision()``: and ``get_slide_collision()``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Using move_and_collide. # Using move_and_collide.
var collision = move_and_collide(velocity * delta) var collision = move_and_collide(velocity * delta)
if collision: if collision:
@ -129,23 +129,7 @@ and ``get_slide_collision()``:
for i in get_slide_count(): for i in get_slide_count():
var collision = get_slide_collision(i) var collision = get_slide_collision(i)
print("I collided with ", collision.collider.name) print("I collided with ", collision.collider.name)
```
.. code-tab:: csharp
// Using MoveAndCollide.
var collision = MoveAndCollide(velocity * delta);
if (collision != null)
{
GD.Print("I collided with ", ((Node)collision.Collider).Name);
}
// Using MoveAndSlide.
velocity = MoveAndSlide(velocity);
for (int i = 0; i < GetSlideCount(); i++)
{
var collision = GetSlideCollision(i);
GD.Print("I collided with ", ((Node)collision.Collider).Name);
}
.. note:: `get_slide_count()` only counts times the body has collided and changed direction. .. note:: `get_slide_count()` only counts times the body has collided and changed direction.
@ -164,9 +148,9 @@ the same collision response:
.. image:: img/k2d_compare.gif .. image:: img/k2d_compare.gif
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# using move_and_collide # using move_and_collide
var collision = move_and_collide(velocity * delta) var collision = move_and_collide(velocity * delta)
if collision: if collision:
@ -174,17 +158,7 @@ the same collision response:
# using move_and_slide # using move_and_slide
velocity = move_and_slide(velocity) velocity = move_and_slide(velocity)
```
.. code-tab:: csharp
// using MoveAndCollide
var collision = MoveAndCollide(velocity * delta);
if (collision != null)
{
velocity = velocity.Slide(collision.Normal);
}
// using MoveAndSlide
velocity = MoveAndSlide(velocity);
Anything you do with ``move_and_slide()`` can also be done with ``move_and_collide()``, Anything you do with ``move_and_slide()`` can also be done with ``move_and_collide()``,
but it might take a little more code. However, as we'll see in the examples below, but it might take a little more code. However, as we'll see in the examples below,
@ -226,9 +200,9 @@ size the rectangle to fit over the sprite image.
Attach a script to the KinematicBody2D and add the following code: Attach a script to the KinematicBody2D and add the following code:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
var speed = 250 var speed = 250
@ -250,42 +224,7 @@ Attach a script to the KinematicBody2D and add the following code:
func _physics_process(delta): func _physics_process(delta):
get_input() get_input()
move_and_collide(velocity * delta) move_and_collide(velocity * delta)
```
.. code-tab:: csharp
using Godot;
using System;
public class KBExample : KinematicBody2D
{
public int Speed = 250;
private Vector2 _velocity = new Vector2();
public void GetInput()
{
// Detect up/down/left/right keystate and only move when pressed
_velocity = new Vector2();
if (Input.IsActionPressed("ui_right"))
_velocity.x += 1;
if (Input.IsActionPressed("ui_left"))
_velocity.x -= 1;
if (Input.IsActionPressed("ui_down"))
_velocity.y += 1;
if (Input.IsActionPressed("ui_up"))
_velocity.y -= 1;
_velocity = _velocity.Normalized() * Speed;
}
public override void _PhysicsProcess(float delta)
{
GetInput();
MoveAndCollide(_velocity * delta);
}
}
Run this scene and you'll see that ``move_and_collide()`` works as expected, moving Run this scene and you'll see that ``move_and_collide()`` works as expected, moving
@ -322,9 +261,9 @@ The Bullet and Wall are separate scenes so that they can be instanced.
The Player is controlled by the `w` and `s` keys for forward and back. Aiming The Player is controlled by the `w` and `s` keys for forward and back. Aiming
uses the mouse pointer. Here is the code for the Player, using ``move_and_slide()``: uses the mouse pointer. Here is the code for the Player, using ``move_and_slide()``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
var Bullet = preload("res://Bullet.tscn") var Bullet = preload("res://Bullet.tscn")
@ -354,63 +293,14 @@ uses the mouse pointer. Here is the code for the Player, using ``move_and_slide(
if dir.length() > 5: if dir.length() > 5:
rotation = dir.angle() rotation = dir.angle()
velocity = move_and_slide(velocity) velocity = move_and_slide(velocity)
```
.. code-tab:: csharp
using Godot;
using System;
public class KBExample : KinematicBody2D
{
private PackedScene _bullet = (PackedScene)GD.Load("res://Bullet.tscn");
public int Speed = 200;
private Vector2 _velocity = new Vector2();
public void GetInput()
{
// add these actions in Project Settings -> Input Map
_velocity = new Vector2();
if (Input.IsActionPressed("backward"))
{
_velocity = new Vector2(-Speed/3, 0).Rotated(Rotation);
}
if (Input.IsActionPressed("forward"))
{
_velocity = new Vector2(Speed, 0).Rotated(Rotation);
}
if (Input.IsActionPressed("mouse_click"))
{
Shoot();
}
}
public void Shoot()
{
// "Muzzle" is a Position2D placed at the barrel of the gun
var b = (Bullet)_bullet.Instance();
b.Start(GetNode<Node2D>("Muzzle").GlobalPosition, Rotation);
GetParent().AddChild(b);
}
public override void _PhysicsProcess(float delta)
{
GetInput();
var dir = GetGlobalMousePosition() - GlobalPosition;
// Don't move if too close to the mouse pointer
if (dir.Length() > 5)
{
Rotation = dir.Angle();
_velocity = MoveAndSlide(_velocity);
}
}
}
And the code for the Bullet: And the code for the Bullet:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
var speed = 750 var speed = 750
@ -430,43 +320,7 @@ And the code for the Bullet:
func _on_VisibilityNotifier2D_screen_exited(): func _on_VisibilityNotifier2D_screen_exited():
queue_free() queue_free()
```
.. code-tab:: csharp
using Godot;
using System;
public class Bullet : KinematicBody2D
{
public int Speed = 750;
private Vector2 _velocity = new Vector2();
public void Start(Vector2 pos, float dir)
{
Rotation = dir;
Position = pos;
_velocity = new Vector2(speed, 0).Rotated(Rotation);
}
public override void _PhysicsProcess(float delta)
{
var collision = MoveAndCollide(_velocity * delta);
if (collision != null)
{
_velocity = _velocity.Bounce(collision.Normal);
if (collision.Collider.HasMethod("Hit"))
{
collision.Collider.Call("Hit");
}
}
}
public void OnVisibilityNotifier2DScreenExited()
{
QueueFree();
}
}
The action happens in ``_physics_process()``. After using ``move_and_collide()``, if a The action happens in ``_physics_process()``. After using ``move_and_collide()``, if a
collision occurs, a ``KinematicCollision2D`` object is returned (otherwise, the return collision occurs, a ``KinematicCollision2D`` object is returned (otherwise, the return
@ -495,9 +349,9 @@ They can be any shape and size. In the sample project, we're using
Here's the code for the player body: Here's the code for the player body:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends KinematicBody2D extends KinematicBody2D
export (int) var run_speed = 100 export (int) var run_speed = 100
@ -527,49 +381,7 @@ Here's the code for the player body:
if jumping and is_on_floor(): if jumping and is_on_floor():
jumping = false jumping = false
velocity = move_and_slide(velocity, Vector2(0, -1)) velocity = move_and_slide(velocity, Vector2(0, -1))
```
.. code-tab:: csharp
using Godot;
using System;
public class KBExample : KinematicBody2D
{
[Export] public int RunSpeed = 100;
[Export] public int JumpSpeed = -400;
[Export] public int Gravity = 1200;
Vector2 velocity = new Vector2();
bool jumping = false;
public void GetInput()
{
velocity.x = 0;
bool right = Input.IsActionPressed("ui_right");
bool left = Input.IsActionPressed("ui_left");
bool jump = Input.IsActionPressed("ui_select");
if (jump && IsOnFloor())
{
jumping = true;
velocity.y = JumpSpeed;
}
if (right)
velocity.x += RunSpeed;
if (left)
velocity.x -= RunSpeed;
}
public override void _PhysicsProcess(float delta)
{
GetInput();
velocity.y += Gravity * delta;
if (jumping && IsOnFloor())
jumping = false;
velocity = MoveAndSlide(velocity, new Vector2(0, -1));
}
}
.. image:: img/k2d_platform.gif .. image:: img/k2d_platform.gif

View File

@ -41,9 +41,9 @@ you should remove the instance you have added by calling
.. note:: Here, you are loading a script and not a packed scene. Therefore you .. note:: Here, you are loading a script and not a packed scene. Therefore you
should use ``new()`` instead of ``instance()``. should use ``new()`` instead of ``instance()``.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# plugin.gd # plugin.gd
tool tool
extends EditorPlugin extends EditorPlugin
@ -58,30 +58,7 @@ you should remove the instance you have added by calling
func _exit_tree(): func _exit_tree():
remove_inspector_plugin(plugin) remove_inspector_plugin(plugin)
```
.. code-tab:: csharp
// Plugin.cs
#if TOOLS
using Godot;
[Tool]
public class Plugin : EditorPlugin
{
private MyInspectorPlugin _plugin;
public override void _EnterTree()
{
_plugin = new MyInspectorPlugin();
AddInspectorPlugin(_plugin);
}
public override void _ExitTree()
{
RemoveInspectorPlugin(_plugin);
}
}
#endif
Interacting with the inspector Interacting with the inspector
@ -109,9 +86,9 @@ you can call both ``add_property_editor()`` and
``add_property_editor_for_multiple_properties()``. Use these last two methods to ``add_property_editor_for_multiple_properties()``. Use these last two methods to
specifically add :ref:`class_EditorProperty`-based controls. specifically add :ref:`class_EditorProperty`-based controls.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# MyInspectorPlugin.gd # MyInspectorPlugin.gd
extends EditorInspectorPlugin extends EditorInspectorPlugin
@ -134,38 +111,8 @@ specifically add :ref:`class_EditorProperty`-based controls.
return true return true
else: else:
return false return false
```
.. code-tab:: csharp
// MyInspectorPlugin.cs
#if TOOLS
using Godot;
public class MyInspectorPlugin : EditorInspectorPlugin
{
public override bool CanHandle(Object @object)
{
// We support all objects in this example.
return true;
}
public override bool ParseProperty(Object @object, int type, string path, int hint, string hintText, int usage)
{
// We handle properties of type integer.
if (type == (int)Variant.Type.Int)
{
// Create an instance of the custom property editor and register
// it to a specific property path.
AddPropertyEditor(path, new RandomIntEditor());
// Inform the editor to remove the default property editor for
// this property type.
return true;
}
return false;
}
}
#endif
Adding an interface to edit properties Adding an interface to edit properties
-------------------------------------- --------------------------------------
@ -190,9 +137,9 @@ You can display your custom widget in two ways. Use just the default ``add_child
method to display it to the right of the property name, and use ``add_child()`` method to display it to the right of the property name, and use ``add_child()``
followed by ``set_bottom_editor()`` to position it below the name. followed by ``set_bottom_editor()`` to position it below the name.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# RandomIntEditor.gd # RandomIntEditor.gd
extends EditorProperty extends EditorProperty
@ -240,69 +187,7 @@ followed by ``set_bottom_editor()`` to position it below the name.
func refresh_control_text(): func refresh_control_text():
property_control.text = "Value: " + str(current_value) property_control.text = "Value: " + str(current_value)
```
.. code-tab:: csharp
// RandomIntEditor.cs
#if TOOLS
using Godot;
public class RandomIntEditor : EditorProperty
{
// The main control for editing the property.
private Button _propertyControl = new Button();
// An internal value of the property.
private int _currentValue = 0;
// A guard against internal changes when the property is updated.
private bool _updating = false;
public RandomIntEditor()
{
// Add the control as a direct child of EditorProperty node.
AddChild(_propertyControl);
// Make sure the control is able to retain the focus.
AddFocusable(_propertyControl);
// Setup the initial state and connect to the signal to track changes.
RefreshControlText();
_propertyControl.Connect("pressed", this, nameof(OnButtonPressed));
}
private void OnButtonPressed()
{
// Ignore the signal if the property is currently being updated.
if (_updating)
{
return;
}
// Generate a new random integer between 0 and 99.
_currentValue = (int)GD.Randi() % 100;
RefreshControlText();
EmitChanged(GetEditedProperty(), _currentValue);
}
public override void UpdateProperty()
{
// Read the current value from the property.
var newValue = (int)GetEditedObject().Get(GetEditedProperty());
if (newValue == _currentValue)
{
return;
}
// Update the control with the new value.
_updating = true;
_currentValue = newValue;
RefreshControlText();
_updating = false;
}
private void RefreshControlText()
{
_propertyControl.Text = $"Value: {_currentValue}";
}
}
#endif
Using the example code above you should be able to make a custom widget that Using the example code above you should be able to make a custom widget that
replaces the default :ref:`class_SpinBox` control for integers with a replaces the default :ref:`class_SpinBox` control for integers with a

View File

@ -43,9 +43,9 @@ creation of the files and the config file's values.
To continue with the example, use the following values: To continue with the example, use the following values:
.. tabs:: ini GDScript
.. code-tab:: ini GDScript
```
Plugin Name: My Custom Node Plugin Name: My Custom Node
Subfolder: my_custom_node Subfolder: my_custom_node
Description: A custom node made to extend the Godot Engine. Description: A custom node made to extend the Godot Engine.
@ -54,17 +54,7 @@ To continue with the example, use the following values:
Language: GDScript Language: GDScript
Script Name: custom_node.gd Script Name: custom_node.gd
Activate now: No Activate now: No
```
.. code-tab:: ini C#
Plugin Name: My Custom Node
Subfolder: my_custom_node
Description: A custom node made to extend the Godot Engine.
Author: Your Name Here
Version: 1.0.0
Language: C#
Script Name: CustomNode.cs
Activate now: No
.. warning:: .. warning::
@ -107,9 +97,10 @@ the dialog generates these callbacks for you. Your script should look something
like this: like this:
.. _doc_making_plugins_template_code: .. _doc_making_plugins_template_code:
.. tabs::
.. code-tab:: gdscript GDScript
gdscript GDScript
```
tool tool
extends EditorPlugin extends EditorPlugin
@ -122,27 +113,7 @@ like this:
func _exit_tree(): func _exit_tree():
# Clean-up of the plugin goes here. # Clean-up of the plugin goes here.
pass pass
```
.. code-tab:: csharp
#if TOOLS
using Godot;
using System;
[Tool]
public class CustomNode : EditorPlugin
{
public override void _EnterTree()
{
// Initialization of the plugin goes here.
}
public override void _ExitTree()
{
// Clean-up of the plugin goes here.
}
}
#endif
This is a good template to use when creating new plugins. This is a good template to use when creating new plugins.
@ -174,9 +145,9 @@ clicked. For that, we'll need a simple script that extends from
:ref:`class_Button`. It could also extend :ref:`class_Button`. It could also extend
:ref:`class_BaseButton` if you prefer: :ref:`class_BaseButton` if you prefer:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
tool tool
extends Button extends Button
@ -187,25 +158,7 @@ clicked. For that, we'll need a simple script that extends from
func clicked(): func clicked():
print("You clicked me!") print("You clicked me!")
```
.. code-tab:: csharp
using Godot;
using System;
[Tool]
public class MyButton : Button
{
public override void _EnterTree()
{
Connect("pressed", this, "clicked");
}
public void clicked()
{
GD.Print("You clicked me!");
}
}
That's it for our basic button. You can save this as ``my_button.gd`` inside the That's it for our basic button. You can save this as ``my_button.gd`` inside the
plugin folder. You'll also need a 16×16 icon to show in the scene tree. If you plugin folder. You'll also need a 16×16 icon to show in the scene tree. If you
@ -218,9 +171,9 @@ don't have one, you can grab the default one from the engine and save it in your
Now, we need to add it as a custom type so it shows on the **Create New Node** Now, we need to add it as a custom type so it shows on the **Create New Node**
dialog. For that, change the ``custom_node.gd`` script to the following: dialog. For that, change the ``custom_node.gd`` script to the following:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
tool tool
extends EditorPlugin extends EditorPlugin
@ -235,33 +188,7 @@ dialog. For that, change the ``custom_node.gd`` script to the following:
# Clean-up of the plugin goes here. # Clean-up of the plugin goes here.
# Always remember to remove it from the engine when deactivated. # Always remember to remove it from the engine when deactivated.
remove_custom_type("MyButton") remove_custom_type("MyButton")
```
.. code-tab:: csharp
#if TOOLS
using Godot;
using System;
[Tool]
public class CustomNode : EditorPlugin
{
public override void _EnterTree()
{
// Initialization of the plugin goes here.
// Add the new type with a name, a parent type, a script and an icon.
var script = GD.Load<Script>("MyButton.cs");
var texture = GD.Load<Texture>("icon.png");
AddCustomType("MyButton", "Button", script, texture);
}
public override void _ExitTree()
{
// Clean-up of the plugin goes here.
// Always remember to remove it from the engine when deactivated.
RemoveCustomType("MyButton");
}
}
#endif
With that done, the plugin should already be available in the plugin list in the With that done, the plugin should already be available in the plugin list in the
**Project Settings**, so activate it as explained in `Checking the results`_. **Project Settings**, so activate it as explained in `Checking the results`_.
@ -287,9 +214,9 @@ Creating a custom dock is done just like a custom node. Create a new
``plugin.cfg`` file in the ``addons/my_custom_dock`` folder, then ``plugin.cfg`` file in the ``addons/my_custom_dock`` folder, then
add the following content to it: add the following content to it:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
[plugin] [plugin]
name="My Custom Dock" name="My Custom Dock"
@ -297,16 +224,7 @@ add the following content to it:
author="Your Name Here" author="Your Name Here"
version="1.0" version="1.0"
script="custom_dock.gd" script="custom_dock.gd"
```
.. code-tab:: csharp
[plugin]
name="My Custom Dock"
description="A custom dock made so I can learn how to make plugins."
author="Your Name Here"
version="1.0"
script="CustomDock.cs"
Then create the script ``custom_dock.gd`` in the same folder. Fill it with the Then create the script ``custom_dock.gd`` in the same folder. Fill it with the
:ref:`template we've seen before <doc_making_plugins_template_code>` to get a :ref:`template we've seen before <doc_making_plugins_template_code>` to get a
@ -334,9 +252,9 @@ You need to select a dock position and define the control to add
**remove the dock** when the plugin is deactivated. **remove the dock** when the plugin is deactivated.
The script could look like this: The script could look like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
tool tool
extends EditorPlugin extends EditorPlugin
@ -361,34 +279,7 @@ The script could look like this:
remove_control_from_docks(dock) remove_control_from_docks(dock)
# Erase the control from the memory. # Erase the control from the memory.
dock.free() dock.free()
```
.. code-tab:: csharp
#if TOOLS
using Godot;
using System;
[Tool]
public class CustomDock : EditorPlugin
{
Control dock;
public override void _EnterTree()
{
dock = (Control)GD.Load<PackedScene>("addons/my_custom_dock/my_dock.tscn").Instance();
AddControlToDock(DockSlot.LeftUl, dock);
}
public override void _ExitTree()
{
// Clean-up of the plugin goes here.
// Remove the dock.
RemoveControlFromDocks(dock);
// Erase the control from the memory.
dock.Free();
}
}
#endif
Note that, while the dock will initially appear at its specified position, Note that, while the dock will initially appear at its specified position,
the user can freely change its position and save the resulting layout. the user can freely change its position and save the resulting layout.

View File

@ -33,41 +33,29 @@ To check if you are currently in the editor, use: ``Engine.editor_hint``.
For example, if you want to execute some code only in the editor, use: For example, if you want to execute some code only in the editor, use:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
if Engine.editor_hint: if Engine.editor_hint:
# Code to execute when in editor. # Code to execute when in editor.
```
.. code-tab:: csharp
if (Engine.EditorHint)
{
// Code to execute when in editor.
}
On the other hand, if you want to execute code only in game, simply negate the same statement: On the other hand, if you want to execute code only in game, simply negate the same statement:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
if not Engine.editor_hint: if not Engine.editor_hint:
# Code to execute when in game. # Code to execute when in game.
```
.. code-tab:: csharp
if (!Engine.EditorHint)
{
// Code to execute when in game.
}
Pieces of code do not have either of the 2 conditions above will run both in-editor and in-game. Pieces of code do not have either of the 2 conditions above will run both in-editor and in-game.
Here is how a ``_process()`` function might look for you: Here is how a ``_process()`` function might look for you:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _process(delta): func _process(delta):
if Engine.editor_hint: if Engine.editor_hint:
# Code to execute in editor. # Code to execute in editor.
@ -76,23 +64,7 @@ Here is how a ``_process()`` function might look for you:
# Code to execute in game. # Code to execute in game.
# Code to execute both in editor and in game. # Code to execute both in editor and in game.
```
.. code-tab:: csharp
public override void _Process(float delta)
{
if (Engine.EditorHint)
{
// Code to execute in editor.
}
if (!Engine.EditorHint)
{
// Code to execute in game.
}
// Code to execute both in editor and in game.
}
.. note:: Modifications in editor are permanent. For example, in the following case, when we remove the script, the node will keep its rotation. Be careful to avoid making unwanted modifications. .. note:: Modifications in editor are permanent. For example, in the following case, when we remove the script, the node will keep its rotation. Be careful to avoid making unwanted modifications.
@ -101,28 +73,15 @@ Try it out
Add a ``Sprite`` node to your scene and set the texture to Godot icon. Attach and open a script, and change it to this: Add a ``Sprite`` node to your scene and set the texture to Godot icon. Attach and open a script, and change it to this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
tool tool
extends Sprite extends Sprite
func _process(delta): func _process(delta):
rotation_degrees += 180 * delta rotation_degrees += 180 * delta
```
.. code-tab:: csharp
using Godot;
using System;
[Tool]
public class MySprite : Sprite
{
public override void _Process(float delta)
{
RotationDegrees += 180 * delta;
}
}
Save the script and return to the editor. You should now see your object rotate. If you run the game, it will also rotate. Save the script and return to the editor. You should now see your object rotate. If you run the game, it will also rotate.
@ -132,28 +91,15 @@ Save the script and return to the editor. You should now see your object rotate.
Now let's choose which code runs when. Modify your ``_process()`` function to look like this: Now let's choose which code runs when. Modify your ``_process()`` function to look like this:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _process(delta): func _process(delta):
if Engine.editor_hint: if Engine.editor_hint:
rotation_degrees += 180 * delta rotation_degrees += 180 * delta
else: else:
rotation_degrees -= 180 * delta rotation_degrees -= 180 * delta
```
.. code-tab:: csharp
public override void _Process(float delta)
{
if (Engine.EditorHint)
{
RotationDegrees += 180 * delta;
}
else
{
RotationDegrees -= 180 * delta;
}
}
Save the script. Now the object will spin clockwise in the editor, but if you run the game, it will spin counter-clockwise. Save the script. Now the object will spin clockwise in the editor, but if you run the game, it will spin counter-clockwise.
@ -162,9 +108,9 @@ Editing variables
Add and export a variable speed to the script. The function set_speed after "setget" is executed with your input to change the variable. Add and export a variable speed to the script. The function set_speed after "setget" is executed with your input to change the variable.
Modify ``_process()`` to include the rotation speed. Modify ``_process()`` to include the rotation speed.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
tool tool
extends Sprite extends Sprite
@ -180,35 +126,8 @@ Modify ``_process()`` to include the rotation speed.
func _process(delta): func _process(delta):
rotation_degrees += 180 * delta * speed rotation_degrees += 180 * delta * speed
```
.. code-tab:: csharp
using Godot;
using System;
[Tool]
public class MySprite : Sprite
{
private float speed = 1;
[Export]
public float Speed {
get => speed;
set => SetSpeed(value);
}
// Update speed and reset the rotation.
private void SetSpeed(float newSpeed)
{
speed = newSpeed;
RotationDegrees = 0;
}
public override void _Process(float delta)
{
RotationDegrees += 180 * delta * speed;
}
}
.. note:: Code from other nodes doesn't run in the editor. Your access to other nodes is limited. You can access the tree and nodes, and their default properties, but you can't access user variables. If you want to do so, other nodes have to run in the editor too. AutoLoad nodes cannot be accessed in the editor at all. .. note:: Code from other nodes doesn't run in the editor. Your access to other nodes is limited. You can access the tree and nodes, and their default properties, but you can't access user variables. If you want to do so, other nodes have to run in the editor too. AutoLoad nodes cannot be accessed in the editor at all.
@ -225,9 +144,9 @@ property to the currently edited scene root.
If you are using ``tool``: If you are using ``tool``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
var node = Spatial.new() var node = Spatial.new()
add_child(node) # Parent could be any node in the scene add_child(node) # Parent could be any node in the scene
@ -235,24 +154,13 @@ If you are using ``tool``:
# The line below is required to make the node visible in the Scene tree dock # The line below is required to make the node visible in the Scene tree dock
# and persist changes made by the tool script to the saved scene file. # and persist changes made by the tool script to the saved scene file.
node.set_owner(get_tree().edited_scene_root) node.set_owner(get_tree().edited_scene_root)
```
.. code-tab:: csharp
public override void _Ready()
{
var node = new Spatial();
AddChild(node); // Parent could be any node in the scene
// The line below is required to make the node visible in the Scene tree dock
// and persist changes made by the tool script to the saved scene file.
node.Owner = GetTree().EditedSceneRoot;
}
If you are using :ref:`EditorScript<class_EditorScript>`: If you are using :ref:`EditorScript<class_EditorScript>`:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _run(): func _run():
var parent = get_scene().find_node("Parent") # Parent could be any node in the scene var parent = get_scene().find_node("Parent") # Parent could be any node in the scene
var node = Spatial.new() var node = Spatial.new()
@ -261,18 +169,6 @@ If you are using :ref:`EditorScript<class_EditorScript>`:
# The line below is required to make the node visible in the Scene tree dock # The line below is required to make the node visible in the Scene tree dock
# and persist changes made by the tool script to the saved scene file. # and persist changes made by the tool script to the saved scene file.
node.set_owner(get_scene()) node.set_owner(get_scene())
```
.. code-tab:: csharp
public override void _Run()
{
var parent = GetScene().FindNode("Parent"); // Parent could be any node in the scene
var node = new Spatial();
parent.AddChild(node);
// The line below is required to make the node visible in the Scene tree dock
// and persist changes made by the tool script to the saved scene file.
node.Owner = GetScene();
}
.. warning:: Using ``tool`` improperly can yield many errors. It is advised to first write the code how you want it, and only then add the ``tool`` keyword to the top. Also, make sure to separate code that runs in-editor from code that runs in-game. This way, you can find bugs more easily. .. warning:: Using ``tool`` improperly can yield many errors. It is advised to first write the code how you want it, and only then add the ``tool`` keyword to the top. Also, make sure to separate code that runs in-editor from code that runs in-game. This way, you can find bugs more easily.

View File

@ -9,31 +9,16 @@ will render to the image it generates. This holds true even for nodes outside
of the "current" scene. Autoloads fall into this category, but so do of the "current" scene. Autoloads fall into this category, but so do
scenes which one instances and adds to the tree at runtime: scenes which one instances and adds to the tree at runtime:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var simultaneous_scene = preload("res://levels/level2.tscn").instance() var simultaneous_scene = preload("res://levels/level2.tscn").instance()
func _add_a_scene_manually(): func _add_a_scene_manually():
# This is like autoloading the scene, only # This is like autoloading the scene, only
# it happens after already loading the main scene. # it happens after already loading the main scene.
get_tree().get_root().add_child(simultaneous_scene) get_tree().get_root().add_child(simultaneous_scene)
```
.. code-tab:: csharp
public PackedScene simultaneousScene;
public MyClass()
{
simultaneousScene = (PackedScene)ResourceLoader.Load("res://levels/level2.tscn").instance();
}
public void _AddASceneManually()
{
// This is like autoloading the scene, only
// it happens after already loading the main scene.
GetTree().GetRoot().AddChild(simultaneousScene);
}
To complete the cycle and swap out the new scene with the old one, To complete the cycle and swap out the new scene with the old one,
developers have a choice to make. Many strategies exist for removing a scene developers have a choice to make. Many strategies exist for removing a scene
@ -121,14 +106,11 @@ There are also cases where one may wish to have many scenes present at the same
time. Perhaps one is adding their own singleton at runtime, or preserving a time. Perhaps one is adding their own singleton at runtime, or preserving a
a scene's data between scene changes (adding the scene to the root node). a scene's data between scene changes (adding the scene to the root node).
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
get_tree().get_root().add_child(scene) get_tree().get_root().add_child(scene)
```
.. code-tab:: csharp
GetTree().GetRoot().AddChild(scene);
Perhaps instead they wish to display multiple scenes at the same time using Perhaps instead they wish to display multiple scenes at the same time using
:ref:`ViewportContainers <class_ViewportContainer>`. This is optimal in :ref:`ViewportContainers <class_ViewportContainer>`. This is optimal in

View File

@ -66,10 +66,10 @@ The ``Default`` template is always generated dynamically per language and cannot
be configured nor overridden, but you can use these as the base for creating be configured nor overridden, but you can use these as the base for creating
other templates. other templates.
.. tabs::
.. code-tab:: gdscript GDScript gdscript GDScript
```
extends %BASE% extends %BASE%
@ -86,31 +86,7 @@ other templates.
# Called every frame. 'delta' is the elapsed time since the previous frame. # Called every frame. 'delta' is the elapsed time since the previous frame.
#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%: #func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:
# pass # pass
```
.. code-tab:: csharp
using Godot;
using System;
public class %CLASS% : %BASE%
{
// Declare member variables here. Examples:
// private int a = 2;
// private string b = "text";
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
}
// // Called every frame. 'delta' is the elapsed time since the previous frame.
// public override void _Process(float delta)
// {
//
// }
}
List of template placeholders List of template placeholders
----------------------------- -----------------------------

View File

@ -10,10 +10,9 @@ in different languages.
The following two scripts will be used as references throughout this page. The following two scripts will be used as references throughout this page.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
var str1 : String = "foo" var str1 : String = "foo"
@ -32,35 +31,7 @@ The following two scripts will be used as references throughout this page.
func print_n_times(msg : String, n : int) -> void: func print_n_times(msg : String, n : int) -> void:
for i in range(n): for i in range(n):
print(msg) print(msg)
```
.. code-tab:: csharp
public class MyCSharpNode : Node
{
public String str1 = "bar";
public String str2 { get { return "barbar"; } }
public void PrintNodeName(Node node)
{
GD.Print(node.GetName());
}
public void PrintArray(String[] arr)
{
foreach (String element in arr)
{
GD.Print(element);
}
}
public void PrintNTimes(String msg, int n)
{
for (int i = 0; i < n; ++i)
{
GD.Print(msg);
}
}
}
Instantiating nodes Instantiating nodes
------------------- -------------------

View File

@ -74,20 +74,12 @@ You can also manage groups from scripts. The following code adds the node to
which you attach the script to the ``guards`` group as soon as it enters the which you attach the script to the ``guards`` group as soon as it enters the
scene tree. scene tree.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
add_to_group("guards") add_to_group("guards")
```
.. code-tab:: csharp
public override void _Ready()
{
base._Ready();
AddToGroup("guards");
}
Imagine you're creating an infiltration game. When an Imagine you're creating an infiltration game. When an
enemy spots the player, you want all guards and robots to be on alert. enemy spots the player, you want all guards and robots to be on alert.
@ -95,18 +87,12 @@ enemy spots the player, you want all guards and robots to be on alert.
In the fictional example below, we use ``SceneTree.call_group()`` to alert all In the fictional example below, we use ``SceneTree.call_group()`` to alert all
enemies that the player was spotted. enemies that the player was spotted.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_Player_spotted(): func _on_Player_spotted():
get_tree().call_group("guards", "enter_alert_mode") get_tree().call_group("guards", "enter_alert_mode")
```
.. code-tab:: csharp
public void _OnPlayerDiscovered()
{
GetTree().CallGroup("guards", "enter_alert_mode");
}
The above code calls the function ``enter_alert_mode`` on every member of the The above code calls the function ``enter_alert_mode`` on every member of the
group ``guards``. group ``guards``.
@ -115,14 +101,11 @@ To get the full list of nodes in the ``guards`` group as an array, you can call
:ref:`SceneTree.get_nodes_in_group() :ref:`SceneTree.get_nodes_in_group()
<class_SceneTree_method_get_nodes_in_group>`: <class_SceneTree_method_get_nodes_in_group>`:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var guards = get_tree().get_nodes_in_group("guards") var guards = get_tree().get_nodes_in_group("guards")
```
.. code-tab:: csharp
var guards = GetTree().GetNodesInGroup("guards");
The :ref:`SceneTree <class_SceneTree>` class provides many more useful methods The :ref:`SceneTree <class_SceneTree>` class provides many more useful methods
to interact with scenes, their node hierarchy, and groups. It allows you to to interact with scenes, their node hierarchy, and groups. It allows you to

View File

@ -24,19 +24,13 @@ script. You can turn it off and back on by calling :ref:`Node.set_process()
The engine calls this method every time it draws a frame: The engine calls this method every time it draws a frame:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _process(delta): func _process(delta):
# Do something... # Do something...
pass pass
```
.. code-tab:: csharp
public override void _Process(float delta)
{
// Do something...
}
Keep in mind that the frequency at which the engine calls ``_process()`` depends Keep in mind that the frequency at which the engine calls ``_process()`` depends
on your application's framerate, which varies over time and across devices. on your application's framerate, which varies over time and across devices.
@ -56,19 +50,13 @@ Physics Fps. By default, it's set to run 60 times per second.
The engine calls this method every time it draws a frame: The engine calls this method every time it draws a frame:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _physics_process(delta): func _physics_process(delta):
# Do something... # Do something...
pass pass
```
.. code-tab:: csharp
public override void _PhysicsProcess(float delta)
{
// Do something...
}
The function ``_process()`` is not synchronized with physics. Its rate depends on The function ``_process()`` is not synchronized with physics. Its rate depends on
hardware and game optimization. It also runs after the physics step in hardware and game optimization. It also runs after the physics step in
@ -77,9 +65,9 @@ single-threaded games.
You can see the ``_process()`` function at work by creating a scene with a You can see the ``_process()`` function at work by creating a scene with a
single Label node, with the following script attached to it: single Label node, with the following script attached to it:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Label extends Label
var time = 0 var time = 0
@ -87,18 +75,6 @@ single Label node, with the following script attached to it:
func _process(delta): func _process(delta):
time += delta time += delta
text = str(time) # 'text' is a built-in Label property. text = str(time) # 'text' is a built-in Label property.
```
.. code-tab:: csharp
public class CustomLabel : Label
{
private float _time;
public override void _Process(float delta)
{
_time += delta;
Text = _time.ToString(); // 'Text' is a built-in Label property.
}
}
When you run the scene, you should see a counter increasing each frame. When you run the scene, you should see a counter increasing each frame.

View File

@ -25,27 +25,16 @@ player's location. See :ref:`doc_instancing` for details.
We'll use an ``Area2D`` for the bullet, which moves in a straight line at a We'll use an ``Area2D`` for the bullet, which moves in a straight line at a
given velocity: given velocity:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Area2D extends Area2D
var velocity = Vector2.ZERO var velocity = Vector2.ZERO
func _physics_process(delta): func _physics_process(delta):
position += velocity * delta position += velocity * delta
```
.. code-tab:: csharp
public class Bullet : Area2D
{
Vector2 Velocity = new Vector2();
public override void _PhysicsProcess(float delta)
{
Position += Velocity * delta;
}
}
However, if the bullets are added as children of the player, then they will However, if the bullets are added as children of the player, then they will
remain "attached" to the player as it rotates: remain "attached" to the player as it rotates:
@ -60,16 +49,12 @@ scene, which may be the player's parent or even further up the tree.
You could do this by adding the bullet to the main scene directly: You could do this by adding the bullet to the main scene directly:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var bullet_instance = Bullet.instance() var bullet_instance = Bullet.instance()
get_parent().add_child(bullet_instance) get_parent().add_child(bullet_instance)
```
.. code-tab:: csharp
Node bulletInstance = Bullet.Instance();
GetParent().AddChild(bulletInstance);
However, this will lead to a different problem. Now if you try to test your However, this will lead to a different problem. Now if you try to test your
"Player" scene independently, it will crash on shooting, because there is no "Player" scene independently, it will crash on shooting, because there is no
@ -85,9 +70,9 @@ appropriate action to spawn them.
Here is the code for the player using signals to emit the bullet: Here is the code for the player using signals to emit the bullet:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Sprite extends Sprite
signal shoot(bullet, direction, location) signal shoot(bullet, direction, location)
@ -101,56 +86,21 @@ Here is the code for the player using signals to emit the bullet:
func _process(delta): func _process(delta):
look_at(get_global_mouse_position()) look_at(get_global_mouse_position())
```
.. code-tab:: csharp
public class Player : Sprite
{
[Signal]
delegate void Shoot(PackedScene bullet, Vector2 direction, Vector2 location);
private PackedScene _bullet = GD.Load<PackedScene>("res://Bullet.tscn");
public override void _Input(InputEvent event)
{
if (input is InputEventMouseButton mouseButton)
{
if (mouseButton.ButtonIndex == (int)ButtonList.Left && mouseButton.Pressed)
{
EmitSignal(nameof(Shoot), _bullet, Rotation, Position);
}
}
}
public override _Process(float delta)
{
LookAt(GetGlobalMousePosition());
}
}
In the main scene, we then connect the player's signal (it will appear in the In the main scene, we then connect the player's signal (it will appear in the
"Node" tab). "Node" tab).
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_Player_shoot(Bullet, direction, location): func _on_Player_shoot(Bullet, direction, location):
var b = Bullet.instance() var b = Bullet.instance()
add_child(b) add_child(b)
b.rotation = direction b.rotation = direction
b.position = location b.position = location
b.velocity = b.velocity.rotated(direction) b.velocity = b.velocity.rotated(direction)
```
.. code-tab:: csharp
public void _on_Player_Shoot(PackedScene bullet, Vector2 direction, Vector2 location)
{
var bulletInstance = (Bullet)bullet.Instance();
AddChild(bulletInstance);
bulletInstance.Rotation = direction;
bulletInstance.Position = location;
bulletInstance.Velocity = bulletInstance.Velocity.Rotated(direction);
}
Now the bullets will maintain their own movement independent of the player's Now the bullets will maintain their own movement independent of the player's
rotation: rotation:

View File

@ -21,28 +21,16 @@ Sprite and Camera2D nodes to access them in your script.
To do so, you can use the following code. To do so, you can use the following code.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var sprite var sprite
var camera2d var camera2d
func _ready(): func _ready():
sprite = get_node("Sprite") sprite = get_node("Sprite")
camera2d = get_node("Camera2D") camera2d = get_node("Camera2D")
```
.. code-tab:: csharp
private Sprite _sprite;
private Camera2D _camera2d;
public override void _Ready()
{
base._Ready();
_sprite = GetNode<Sprite>("Sprite");
_camera2d = GetNode<Camera2D>("Camera2D");
}
Note that you get nodes using their name, not their type. Above, "Sprite" and Note that you get nodes using their name, not their type. Above, "Sprite" and
"Camera2D" are the nodes' names in the scene. "Camera2D" are the nodes' names in the scene.
@ -68,24 +56,14 @@ node.
To get the Tween node, you would use the following code. To get the Tween node, you would use the following code.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var tween var tween
func _ready(): func _ready():
tween = get_node("ShieldBar/Tween") tween = get_node("ShieldBar/Tween")
```
.. code-tab:: csharp
private Tween _tween;
public override void _Ready()
{
base._Ready();
_tween = GetNode<Tween>("ShieldBar/Tween");
}
.. note:: As with file paths, you can use ".." to get a parent node. The best .. note:: As with file paths, you can use ".." to get a parent node. The best
practice is to avoid doing that though not to break encapsulation. practice is to avoid doing that though not to break encapsulation.
@ -122,40 +100,26 @@ You can store the newly created node's reference in a variable and call
``add_child()`` to add it as a child of the node to which you attached the ``add_child()`` to add it as a child of the node to which you attached the
script. script.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var sprite var sprite
func _ready(): func _ready():
var sprite = Sprite.new() # Create a new Sprite. var sprite = Sprite.new() # Create a new Sprite.
add_child(sprite) # Add it as a child of this node. add_child(sprite) # Add it as a child of this node.
```
.. code-tab:: csharp
private Sprite _sprite;
public override void _Ready()
{
base._Ready();
_sprite = new Sprite(); // Create a new Sprite.
AddChild(_sprite); // Add it as a child of this node.
}
To delete a node and free it from memory, you can call its ``queue_free()`` To delete a node and free it from memory, you can call its ``queue_free()``
method. Doing so queues the node for deletion at the end of the current frame method. Doing so queues the node for deletion at the end of the current frame
after it has finished processing. At that point, the engine removes the node from after it has finished processing. At that point, the engine removes the node from
the scene and frees the object in memory. the scene and frees the object in memory.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
sprite.queue_free() sprite.queue_free()
```
.. code-tab:: csharp
_sprite.QueueFree();
Before calling ``sprite.queue_free()``, the remote scene tree looks like this. Before calling ``sprite.queue_free()``, the remote scene tree looks like this.
@ -185,39 +149,33 @@ steps:
2. Creating an instance of the loaded :ref:`PackedScene <class_PackedScene>` 2. Creating an instance of the loaded :ref:`PackedScene <class_PackedScene>`
resource. resource.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var scene = load("res://MyScene.tscn") var scene = load("res://MyScene.tscn")
```
.. code-tab:: csharp
var scene = GD.Load<PackedScene>("res://MyScene.tscn");
Preloading the scene can improve the user's experience as the load operation Preloading the scene can improve the user's experience as the load operation
happens when the compiler reads the script and not at runtime. This feature is happens when the compiler reads the script and not at runtime. This feature is
only available with GDScript. only available with GDScript.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var scene = preload("res://MyScene.tscn") var scene = preload("res://MyScene.tscn")
```
At that point, ``scene`` is a packed scene resource, not a node. To create the At that point, ``scene`` is a packed scene resource, not a node. To create the
actual node, you need to call :ref:`PackedScene.instance() actual node, you need to call :ref:`PackedScene.instance()
<class_PackedScene_method_instance>`. It returns a tree of nodes that you can <class_PackedScene_method_instance>`. It returns a tree of nodes that you can
as a child of your current node. as a child of your current node.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var instance = scene.instance() var instance = scene.instance()
add_child(instance) add_child(instance)
```
.. code-tab:: csharp
var instance = scene.Instance();
AddChild(instance);
The advantage of this two-step process is you can keep a packed scene loaded and The advantage of this two-step process is you can keep a packed scene loaded and
create new instances on the fly. For example, to quickly instance several create new instances on the fly. For example, to quickly instance several

View File

@ -31,9 +31,9 @@ Another related callback is ``_exit_tree()``, which the engine calls every time
a node exits the scene tree. This can be when you call :ref:`Node.remove_child() a node exits the scene tree. This can be when you call :ref:`Node.remove_child()
<class_Node_method_remove_child>` or when you free a node. <class_Node_method_remove_child>` or when you free a node.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Called every time the node enters the scene tree. # Called every time the node enters the scene tree.
func _enter_tree(): func _enter_tree():
pass pass
@ -46,36 +46,16 @@ a node exits the scene tree. This can be when you call :ref:`Node.remove_child()
# children received the _exit_tree() callback. # children received the _exit_tree() callback.
func _exit_tree(): func _exit_tree():
pass pass
```
.. code-tab:: csharp
// Called every time the node enters the scene tree.
public override void _EnterTree()
{
base._EnterTree();
}
// Called when both the node and its children have entered the scene tree.
public override void _Ready()
{
base._Ready();
}
// Called when the node is about to leave the scene tree, after all its
// children.
public override void _ExitTree()
{
base._ExitTree();
}
The two virtual methods ``_process()`` and ``_physics_process()`` allow you to The two virtual methods ``_process()`` and ``_physics_process()`` allow you to
update the node, every frame and every physics frame respectively. For more update the node, every frame and every physics frame respectively. For more
information, read the dedicated documentation: information, read the dedicated documentation:
:ref:`doc_idle_and_physics_processing`. :ref:`doc_idle_and_physics_processing`.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Called every frame, as often as possible. # Called every frame, as often as possible.
func _process(delta): func _process(delta):
pass pass
@ -83,20 +63,7 @@ information, read the dedicated documentation:
# Called every physics frame. # Called every physics frame.
func _physics_process(delta): func _physics_process(delta):
pass pass
```
.. code-tab:: csharp
public override void _Process(float delta)
{
// Called every frame, as often as possible.
base._Process(delta);
}
public override void _PhysicsProcess(float delta)
{
// Called every physics frame.
base._PhysicsProcess(delta);
}
Two more essential built-in node callback functions are Two more essential built-in node callback functions are
:ref:`Node._unhandled_input() <class_Node_method__unhandled_input>` and :ref:`Node._unhandled_input() <class_Node_method__unhandled_input>` and
@ -109,9 +76,9 @@ process input events before ``_unhandled_input()`` gets them.
To learn more about inputs in Godot, see the :ref:`Input section <toc-learn-features-inputs>`. To learn more about inputs in Godot, see the :ref:`Input section <toc-learn-features-inputs>`.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Called once for every event. # Called once for every event.
func _unhandled_input(event): func _unhandled_input(event):
pass pass
@ -120,21 +87,7 @@ To learn more about inputs in Godot, see the :ref:`Input section <toc-learn-feat
# consume some events. # consume some events.
func _input(event): func _input(event):
pass pass
```
.. code-tab:: csharp
// Called once for every event.
public override void _UnhandledInput(InputEvent @event)
{
base._UnhandledInput(event);
}
// Called once for every event, before _unhandled_input(), allowing you to
// consume some events.
public override void _Input(InputEvent @event)
{
base._Input(event);
}
There are some more overridable functions like There are some more overridable functions like
:ref:`Node._get_configuration_warning() :ref:`Node._get_configuration_warning()

View File

@ -18,14 +18,11 @@ How pausing works
To pause the game the pause state must be set. This is done by assigning To pause the game the pause state must be set. This is done by assigning
``true`` to the :ref:`SceneTree.paused <class_SceneTree_property_paused>` property: ``true`` to the :ref:`SceneTree.paused <class_SceneTree_property_paused>` property:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
get_tree().paused = true get_tree().paused = true
```
.. code-tab:: csharp
GetTree().Paused = true;
Doing this will cause two things. First, 2D and 3D physics will be stopped Doing this will cause two things. First, 2D and 3D physics will be stopped
for all nodes. Second, the behavior of certain nodes will stop or start for all nodes. Second, the behavior of certain nodes will stop or start
@ -44,18 +41,12 @@ be found and changed under a node's :ref:`Node <class_Node>` properties in the i
You can also alter the property with code: You can also alter the property with code:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
pause_mode = Node.PAUSE_MODE_PROCESS pause_mode = Node.PAUSE_MODE_PROCESS
```
.. code-tab:: csharp
public override void _Ready()
{
PauseMode = Node.PauseModeEnum.Process;
}
This is what each mode tells a node to do: This is what each mode tells a node to do:
@ -98,35 +89,21 @@ working when paused.
Finally, make it so when a pause button is pressed (any button will do), Finally, make it so when a pause button is pressed (any button will do),
enable the pause and show the pause screen. enable the pause and show the pause screen.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_pause_button_pressed(): func _on_pause_button_pressed():
get_tree().paused = true get_tree().paused = true
$pause_popup.show() $pause_popup.show()
```
.. code-tab:: csharp
public void _on_pause_button_pressed()
{
GetTree().Paused = true;
GetNode<Control>("pause_popup").Show();
}
To unpause, do the opposite when the pause screen is To unpause, do the opposite when the pause screen is
closed: closed:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_pause_popup_close_pressed(): func _on_pause_popup_close_pressed():
$pause_popup.hide() $pause_popup.hide()
get_tree().paused = false get_tree().paused = false
```
.. code-tab:: csharp
public void _on_pause_popup_close_pressed()
{
GetNode<Control>("pause_popup").Hide();
GetTree().Paused = false;
}

View File

@ -71,36 +71,25 @@ Loading resources from code
There are two ways to load resources from code. First, you can use the ``load()`` function anytime: There are two ways to load resources from code. First, you can use the ``load()`` function anytime:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
var res = load("res://robi.png") # Godot loads the Resource when it reads the line. var res = load("res://robi.png") # Godot loads the Resource when it reads the line.
get_node("sprite").texture = res get_node("sprite").texture = res
```
.. code-tab:: csharp
public override void _Ready()
{
var texture = (Texture)GD.Load("res://robi.png"); // Godot loads the Resource when it reads the line.
var sprite = GetNode<Sprite>("sprite");
sprite.Texture = texture;
}
You can also ``preload`` resources. Unlike ``load``, this function will read the You can also ``preload`` resources. Unlike ``load``, this function will read the
file from disk and load it at compile-time. As a result, you cannot call preload file from disk and load it at compile-time. As a result, you cannot call preload
with a variable path: you need to use a constant string. with a variable path: you need to use a constant string.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
var res = preload("res://robi.png") # Godot loads the resource at compile-time var res = preload("res://robi.png") # Godot loads the resource at compile-time
get_node("sprite").texture = res get_node("sprite").texture = res
```
.. code-tab:: csharp
// 'preload()' is unavailable in C Sharp.
Loading scenes Loading scenes
-------------- --------------
@ -112,23 +101,13 @@ scene is packed inside a resource.
To get an instance of the scene, you have to use the To get an instance of the scene, you have to use the
:ref:`PackedScene.instance() <class_PackedScene_method_instance>` method. :ref:`PackedScene.instance() <class_PackedScene_method_instance>` method.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _on_shoot(): func _on_shoot():
var bullet = preload("res://bullet.tscn").instance() var bullet = preload("res://bullet.tscn").instance()
add_child(bullet) add_child(bullet)
```
.. code-tab:: csharp
private PackedScene _bulletScene = (PackedScene)GD.Load("res://bullet.tscn");
public void OnShoot()
{
Node bullet = _bulletScene.Instance();
AddChild(bullet);
}
This method creates the nodes in the scene's hierarchy, configures them, and This method creates the nodes in the scene's hierarchy, configures them, and
returns the root node of the scene. You can then add it as a child of any other returns the root node of the scene. You can then add it as a child of any other
@ -197,9 +176,9 @@ object you create.
Let's see some examples. Let's see some examples.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# bot_stats.gd # bot_stats.gd
extends Resource extends Resource
export(int) var health export(int) var health
@ -223,54 +202,7 @@ Let's see some examples.
# Uses an implicit, duck-typed interface for any 'health'-compatible resources. # Uses an implicit, duck-typed interface for any 'health'-compatible resources.
if stats: if stats:
print(stats.health) # Prints '10'. print(stats.health) # Prints '10'.
.. code-tab:: csharp ```
// BotStats.cs
using System;
using Godot;
namespace ExampleProject {
public class BotStats : Resource
{
[Export]
public int Health { get; set; }
[Export]
public Resource SubResource { get; set; }
[Export]
public String[] Strings { get; set; }
// Make sure that every parameter has a default value.
// Otherwise, there will be problems with creating and editing
// your resource via the inspector.
public BotStats(int health = 0, Resource subResource = null, String[] strings = null)
{
Health = health;
SubResource = subResource;
Strings = strings ?? new String[0];
}
}
}
// Bot.cs
using System;
using Godot;
namespace ExampleProject {
public class Bot : KinematicBody
{
[Export]
public Resource Stats;
public override void _Ready()
{
if (Stats != null && Stats is BotStats botStats) {
GD.Print(botStats.Health); // Prints '10'.
}
}
}
}
.. note:: .. note::
@ -284,9 +216,9 @@ Let's see some examples.
Resource scripts. DataTables are a String mapped to a custom struct, similar Resource scripts. DataTables are a String mapped to a custom struct, similar
to a Dictionary mapping a String to a secondary custom Resource script. to a Dictionary mapping a String to a secondary custom Resource script.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# bot_stats_table.gd # bot_stats_table.gd
extends Resource extends Resource
@ -315,6 +247,7 @@ Let's see some examples.
GD.Print(_stats); GD.Print(_stats);
} }
} }
```
Instead of just inlining the Dictionary values, one could also, alternatively... Instead of just inlining the Dictionary values, one could also, alternatively...
@ -338,9 +271,9 @@ Let's see some examples.
extend ``Resource``, and then determine that the script failed to load for the extend ``Resource``, and then determine that the script failed to load for the
Resource object since the types are incompatible. Resource object since the types are incompatible.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
class MyResource: class MyResource:
@ -352,24 +285,4 @@ Let's see some examples.
# This will NOT serialize the 'value' property. # This will NOT serialize the 'value' property.
ResourceSaver.save("res://my_res.tres", my_res) ResourceSaver.save("res://my_res.tres", my_res)
.. code-tab:: csharp ```
using System;
using Godot;
public class MyNode : Node
{
public class MyResource : Resource
{
[Export]
public int Value { get; set; } = 5;
}
public override void _Ready()
{
var res = new MyResource();
// This will NOT serialize the 'Value' property.
ResourceSaver.Save("res://MyRes.tres", res);
}
}

View File

@ -66,16 +66,12 @@ The root :ref:`Viewport <class_Viewport>`
is always at the top of the scene. From a node, it can be obtained in is always at the top of the scene. From a node, it can be obtained in
two different ways: two different ways:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
get_tree().get_root() # Access via scene main loop. get_tree().get_root() # Access via scene main loop.
get_node("/root") # Access via absolute path. get_node("/root") # Access via absolute path.
```
.. code-tab:: csharp
GetTree().GetRoot(); // Access via scene main loop.
GetNode("/root"); // Access via absolute path.
This node contains the main viewport. Anything that is a child of a This node contains the main viewport. Anything that is a child of a
:ref:`Viewport <class_Viewport>` :ref:`Viewport <class_Viewport>`
@ -137,39 +133,26 @@ another one. The simple way to do this is to use the
:ref:`SceneTree.change_scene() <class_SceneTree_method_change_scene>` :ref:`SceneTree.change_scene() <class_SceneTree_method_change_scene>`
function: function:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _my_level_was_completed(): func _my_level_was_completed():
get_tree().change_scene("res://levels/level2.tscn") get_tree().change_scene("res://levels/level2.tscn")
```
.. code-tab:: csharp
public void _MyLevelWasCompleted()
{
GetTree().ChangeScene("res://levels/level2.tscn");
}
Rather than using file paths, one can also use ready-made Rather than using file paths, one can also use ready-made
:ref:`PackedScene <class_PackedScene>` resources using the equivalent :ref:`PackedScene <class_PackedScene>` resources using the equivalent
function function
:ref:`SceneTree.change_scene_to(PackedScene scene) <class_SceneTree_method_change_scene_to>`: :ref:`SceneTree.change_scene_to(PackedScene scene) <class_SceneTree_method_change_scene_to>`:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var next_scene = preload("res://levels/level2.tscn") var next_scene = preload("res://levels/level2.tscn")
func _my_level_was_completed(): func _my_level_was_completed():
get_tree().change_scene_to(next_scene) get_tree().change_scene_to(next_scene)
```
.. code-tab:: csharp
public void _MyLevelWasCompleted()
{
var nextScene = (PackedScene)ResourceLoader.Load("res://levels/level2.tscn");
GetTree().ChangeSceneTo(nextScene);
}
These are quick and useful ways to switch scenes but have the drawback These are quick and useful ways to switch scenes but have the drawback
that the game will stall until the new scene is loaded and running. At that the game will stall until the new scene is loaded and running. At

View File

@ -31,7 +31,8 @@ to its name in the scene tree:
To use a unique node in a script, use the ``%`` symbol and the node's To use a unique node in a script, use the ``%`` symbol and the node's
name in the path for ``get_node()``. For example: name in the path for ``get_node()``. For example:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
get_node("%RedButton").text = "Hello" get_node("%RedButton").text = "Hello"
```

View File

@ -75,29 +75,21 @@ in top-to-bottom order.
This means that any node can access a singleton named "PlayerVariables" with: This means that any node can access a singleton named "PlayerVariables" with:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
var player_vars = get_node("/root/PlayerVariables") var player_vars = get_node("/root/PlayerVariables")
player_vars.health -= 10 player_vars.health -= 10
```
.. code-tab:: csharp
var playerVariables = GetNode<PlayerVariables>("/root/PlayerVariables");
playerVariables.Health -= 10; // Instance field.
If the **Enable** column is checked (which is the default), then the singleton can If the **Enable** column is checked (which is the default), then the singleton can
be accessed directly without requiring ``get_node()``: be accessed directly without requiring ``get_node()``:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
PlayerVariables.health -= 10 PlayerVariables.health -= 10
```
.. code-tab:: csharp
// Static members can be accessed by using the class name.
PlayerVariables.Health -= 10;
Note that autoload objects (scripts and/or scenes) are accessed just like any Note that autoload objects (scripts and/or scenes) are accessed just like any
other node in the scene tree. In fact, if you look at the running scene tree, other node in the scene tree. In fact, if you look at the running scene tree,
@ -149,9 +141,9 @@ Returning to the script, it needs to fetch the current scene in the
``Global.gd`` are children of root, but autoloaded nodes are always first. This ``Global.gd`` are children of root, but autoloaded nodes are always first. This
means that the last child of root is always the loaded scene. means that the last child of root is always the loaded scene.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Node extends Node
var current_scene = null var current_scene = null
@ -159,29 +151,14 @@ means that the last child of root is always the loaded scene.
func _ready(): func _ready():
var root = get_tree().root var root = get_tree().root
current_scene = root.get_child(root.get_child_count() - 1) current_scene = root.get_child(root.get_child_count() - 1)
```
.. code-tab:: csharp
using Godot;
using System;
public class Global : Godot.Node
{
public Node CurrentScene { get; set; }
public override void _Ready()
{
Viewport root = GetTree().Root;
CurrentScene = root.GetChild(root.GetChildCount() - 1);
}
}
Now we need a function for changing the scene. This function needs to free the Now we need a function for changing the scene. This function needs to free the
current scene and replace it with the requested one. current scene and replace it with the requested one.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func goto_scene(path): func goto_scene(path):
# This function will usually be called from a signal callback, # This function will usually be called from a signal callback,
# or some other function in the current scene. # or some other function in the current scene.
@ -210,40 +187,7 @@ current scene and replace it with the requested one.
# Optionally, to make it compatible with the SceneTree.change_scene() API. # Optionally, to make it compatible with the SceneTree.change_scene() API.
get_tree().current_scene = current_scene get_tree().current_scene = current_scene
```
.. code-tab:: csharp
public void GotoScene(string path)
{
// This function will usually be called from a signal callback,
// or some other function from the current scene.
// Deleting the current scene at this point is
// a bad idea, because it may still be executing code.
// This will result in a crash or unexpected behavior.
// The solution is to defer the load to a later time, when
// we can be sure that no code from the current scene is running:
CallDeferred(nameof(DeferredGotoScene), path);
}
public void DeferredGotoScene(string path)
{
// It is now safe to remove the current scene
CurrentScene.Free();
// Load a new scene.
var nextScene = (PackedScene)GD.Load(path);
// Instance the new scene.
CurrentScene = nextScene.Instance();
// Add it to the active scene, as child of root.
GetTree().Root.AddChild(CurrentScene);
// Optionally, to make it compatible with the SceneTree.change_scene() API.
GetTree().CurrentScene = CurrentScene;
}
Using :ref:`Object.call_deferred() <class_Object_method_call_deferred>`, Using :ref:`Object.call_deferred() <class_Object_method_call_deferred>`,
the second function will only run once all code from the current scene has the second function will only run once all code from the current scene has
@ -252,43 +196,25 @@ still being used (i.e. its code is still running).
Finally, we need to fill the empty callback functions in the two scenes: Finally, we need to fill the empty callback functions in the two scenes:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Add to 'Scene1.gd'. # Add to 'Scene1.gd'.
func _on_Button_pressed(): func _on_Button_pressed():
Global.goto_scene("res://Scene2.tscn") Global.goto_scene("res://Scene2.tscn")
```
.. code-tab:: csharp
// Add to 'Scene1.cs'.
public void OnButtonPressed()
{
var global = GetNode<Global>("/root/Global");
global.GotoScene("res://Scene2.tscn");
}
and and
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
# Add to 'Scene2.gd'. # Add to 'Scene2.gd'.
func _on_Button_pressed(): func _on_Button_pressed():
Global.goto_scene("res://Scene1.tscn") Global.goto_scene("res://Scene1.tscn")
```
.. code-tab:: csharp
// Add to 'Scene2.cs'.
public void OnButtonPressed()
{
var global = GetNode<Global>("/root/Global");
global.GotoScene("res://Scene1.tscn");
}
Run the project and test that you can switch between scenes by pressing Run the project and test that you can switch between scenes by pressing
the button. the button.

View File

@ -42,28 +42,15 @@ indicate that this is the currently focused control. To check for this
status, the :ref:`Control.has_focus() <class_Control_method_has_focus>` method status, the :ref:`Control.has_focus() <class_Control_method_has_focus>` method
exists. Example exists. Example
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _draw(): func _draw():
if has_focus(): if has_focus():
draw_selected() draw_selected()
else: else:
draw_normal() draw_normal()
```
.. code-tab:: csharp
public override void _Draw()
{
if (HasFocus())
{
DrawSelected()
}
else
{
DrawNormal();
}
}
Sizing Sizing
------ ------
@ -80,33 +67,21 @@ To provide this callback, just override
:ref:`Control.get_minimum_size() <class_Control_method_get_minimum_size>`, :ref:`Control.get_minimum_size() <class_Control_method_get_minimum_size>`,
for example: for example:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func get_minimum_size(): func get_minimum_size():
return Vector2(30, 30) return Vector2(30, 30)
```
.. code-tab:: csharp
public override Vector2 _GetMinimumSize()
{
return new Vector2(20, 20);
}
Alternatively, set it using a function: Alternatively, set it using a function:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
set_custom_minimum_size(Vector2(30, 30)) set_custom_minimum_size(Vector2(30, 30))
```
.. code-tab:: csharp
public override void _Ready()
{
SetCustomMinimumSize(new Vector2(20, 20));
}
Input Input
----- -----
@ -131,24 +106,15 @@ This function is
:ref:`Control._gui_input() <class_Control_method__gui_input>`. :ref:`Control._gui_input() <class_Control_method__gui_input>`.
Simply override it in your control. No processing needs to be set. Simply override it in your control. No processing needs to be set.
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Control extends Control
func _gui_input(event): func _gui_input(event):
if event is InputEventMouseButton and event.button_index == BUTTON_LEFT and event.pressed: if event is InputEventMouseButton and event.button_index == BUTTON_LEFT and event.pressed:
print("Left mouse button was pressed!") print("Left mouse button was pressed!")
```
.. code-tab:: csharp
public override void _GuiInput(InputEvent @event)
{
if (@event is InputEventMouseButton mbe && mbe.ButtonIndex == (int)ButtonList.Left && mbe.Pressed)
{
GD.Print("Left mouse button was pressed!");
}
}
For more information about events themselves, check the :ref:`doc_inputevent` For more information about events themselves, check the :ref:`doc_inputevent`
tutorial. tutorial.
@ -159,9 +125,9 @@ Notifications
Controls also have many useful notifications for which no dedicated callback Controls also have many useful notifications for which no dedicated callback
exists, but which can be checked with the _notification callback: exists, but which can be checked with the _notification callback:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _notification(what): func _notification(what):
match what: match what:
NOTIFICATION_MOUSE_ENTER: NOTIFICATION_MOUSE_ENTER:
@ -184,45 +150,4 @@ exists, but which can be checked with the _notification callback:
NOTIFICATION_MODAL_CLOSE: NOTIFICATION_MODAL_CLOSE:
pass # For modal pop-ups, notification pass # For modal pop-ups, notification
# that the pop-up was closed. # that the pop-up was closed.
```
.. code-tab:: csharp
public override void _Notification(int what)
{
switch (what)
{
case NotificationMouseEnter:
// Mouse entered the area of this control.
break;
case NotificationMouseExit:
// Mouse exited the area of this control.
break;
case NotificationFocusEnter:
// Control gained focus.
break;
case NotificationFocusExit:
// Control lost focus.
break;
case NotificationThemeChanged:
// Theme used to draw the control changed;
// update and redraw is recommended if using a theme.
break;
case NotificationVisibilityChanged:
// Control became visible/invisible;
// check new status with is_visible().
break;
case NotificationResized:
// Control changed size; check new size with get_size().
break;
case NotificationModalClose:
// For modal pop-ups, notification that the pop-up was closed.
break;
}
}

View File

@ -164,9 +164,9 @@ Creating custom Containers
It is possible to easily create a custom container using script. Here is an example of a simple container that fits children It is possible to easily create a custom container using script. Here is an example of a simple container that fits children
to its rect size: to its rect size:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
extends Container extends Container
func _notification(what): func _notification(what):
@ -179,3 +179,4 @@ to its rect size:
func set_some_setting(): func set_some_setting():
# Some setting changed, ask for children re-sort # Some setting changed, ask for children re-sort
queue_sort() queue_sort()
```

View File

@ -62,18 +62,12 @@ For keyboard and controller navigation to work correctly, any node must be focus
using code when the scene starts. Without doing this, pressing buttons or keys won't using code when the scene starts. Without doing this, pressing buttons or keys won't
do anything. Here is a basic example of setting initial focus with code: do anything. Here is a basic example of setting initial focus with code:
.. tabs:: gdscript GDScript
.. code-tab:: gdscript GDScript
```
func _ready(): func _ready():
$StartButton.grab_focus() $StartButton.grab_focus()
```
.. code-tab:: csharp
public override void _Ready()
{
GetNode<Button>("StartButton").GrabFocus();
}
Now when the scene starts the "Start Button" node will be focused, and the keyboard Now when the scene starts the "Start Button" node will be focused, and the keyboard
or a controller can be used to navigate between it and other UI elements. or a controller can be used to navigate between it and other UI elements.

View File

@ -116,16 +116,12 @@ your custom theme types, you must utilize scripts to access those items. All con
nodes have several methods that allow to fetch theme items from the theme that nodes have several methods that allow to fetch theme items from the theme that
is applied to them. Those methods accept the theme type as one of the arguments. is applied to them. Those methods accept the theme type as one of the arguments.
.. tabs:: gdscript
.. code-tab:: gdscript
```
var accent_color = get_color("accent_color", "MyType") var accent_color = get_color("accent_color", "MyType")
label.add_color_override("font_color", accent_color) label.add_color_override("font_color", accent_color)
```
.. code-tab:: csharp
Color accentColor = GetColor("accent_color", "MyType");
label.AddColorOverride("font_color", accentColor);
To give more customization opportunities types can also be linked together as To give more customization opportunities types can also be linked together as
type variations. This is another use-case for custom theme types. For example, type variations. This is another use-case for custom theme types. For example,