mirror of
https://github.com/Relintai/pandemonium_engine_docs.git
synced 2025-02-06 16:16:00 +01:00
Removed tabs.
This commit is contained in:
parent
108cb20c13
commit
3beb0ac5d1
@ -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:
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
----------------
|
----------------
|
||||||
|
@ -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
|
||||||
|
@ -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``.
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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`.
|
||||||
|
@ -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
|
||||||
-------
|
-------
|
||||||
|
@ -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,
|
||||||
|
@ -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``
|
||||||
|
@ -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);
|
|
||||||
|
@ -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!
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
```
|
@ -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()
|
||||||
|
```
|
@ -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)
|
||||||
|
```
|
@ -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()``
|
||||||
|
@ -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
|
||||||
|
@ -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**.
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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));
|
|
||||||
}
|
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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())
|
||||||
|
```
|
@ -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
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -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::
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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>`.
|
||||||
|
@ -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)
|
|
||||||
|
@ -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
|
||||||
------------
|
------------
|
||||||
|
@ -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
|
||||||
--------
|
--------
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
|
||||||
|
@ -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?
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -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
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
|
```
|
||||||
|
@ -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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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).
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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()``,
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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!
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
@ -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
|
||||||
-------------------
|
-------------------
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
```
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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()
|
||||||
|
```
|
||||||
|
@ -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.
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user