Merge pull request #9647 from mhilbrunner/4.2-cherrypicks
4.2 cherrypicks
@@ -7,21 +7,31 @@
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url('fonts/JetBrainsMono-Regular.woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: url("fonts/JetBrainsMono-Regular.woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url('fonts/JetBrainsMono-Medium.woff2');
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: url("fonts/JetBrainsMono-Medium.woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url('fonts/JetBrainsMono-Bold.woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: url("fonts/JetBrainsMono-Bold.woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Montserrat";
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: url("fonts/Montserrat-Bold.woff2");
|
||||
}
|
||||
|
||||
/* Default (light) theme colors */
|
||||
@@ -148,6 +158,7 @@
|
||||
--footer-color: #808080;
|
||||
|
||||
--system-font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--header-font-family: "Montserrat", var(--system-font-family);
|
||||
--monospace-font-family: "JetBrains Mono", SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, Courier, monospace;
|
||||
}
|
||||
|
||||
@@ -279,25 +290,15 @@
|
||||
}
|
||||
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
input[type="text"],
|
||||
input[type="button"],
|
||||
input[type="reset"],
|
||||
input[type="submit"],
|
||||
textarea,
|
||||
legend,
|
||||
.btn,
|
||||
.rst-content .toctree-wrapper p.caption,
|
||||
.rst-versions {
|
||||
/* Use a system font stack for better performance (no Web fonts required) */
|
||||
font-family: var(--system-font-family);
|
||||
/* Some fonts that we use (namely JetBrains Mono) can come with ligatures. It's better to opt-in if needed. */
|
||||
font-variant-ligatures: none;
|
||||
}
|
||||
|
||||
h1,
|
||||
@@ -308,8 +309,8 @@ h5,
|
||||
h6,
|
||||
legend,
|
||||
.rst-content .toctree-wrapper p.caption {
|
||||
/* Use a lighter font for headers (Medium instead of Bold) */
|
||||
font-weight: 500;
|
||||
/* Use the same font as the godotengine.org website. */
|
||||
font-family: var(--header-font-family);
|
||||
}
|
||||
|
||||
/* See <https://github.com/godotengine/godot-docs/pull/5876> for context. */
|
||||
@@ -500,6 +501,7 @@ html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(
|
||||
html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple).method > dt,
|
||||
html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple).attribute > dt {
|
||||
font-family: var(--monospace-font-family);
|
||||
font-variant-ligatures: none;
|
||||
font-size: 90%;
|
||||
font-weight: normal;
|
||||
margin-bottom: 16px;
|
||||
@@ -559,6 +561,7 @@ html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(
|
||||
}
|
||||
html.writer-html5 .rst-content dl.field-list > dd strong {
|
||||
font-family: var(--monospace-font-family);
|
||||
font-variant-ligatures: none;
|
||||
}
|
||||
|
||||
footer {
|
||||
@@ -811,6 +814,7 @@ code,
|
||||
.rst-content code {
|
||||
font-size: .875em;
|
||||
font-family: var(--monospace-font-family);
|
||||
font-variant-ligatures: none;
|
||||
background-color: var(--code-background-color);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
@@ -839,6 +843,7 @@ code,
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
font-family: var(--monospace-font-family);
|
||||
font-variant-ligatures: none;
|
||||
}
|
||||
|
||||
/* Code tab display tweaks */
|
||||
@@ -1107,6 +1112,7 @@ kbd.compound > .kbd,
|
||||
.classref-descriptions-group > p.classref-enumeration-constant {
|
||||
color: var(--classref-secondary-color);
|
||||
font-family: var(--monospace-font-family);
|
||||
font-variant-ligatures: none;
|
||||
font-size: 110%;
|
||||
font-weight: 600;
|
||||
margin-bottom: 18px;
|
||||
@@ -1203,6 +1209,7 @@ p + .classref-constant {
|
||||
|
||||
.classref-property-setget p {
|
||||
font-family: var(--monospace-font-family);
|
||||
font-variant-ligatures: none;
|
||||
font-size: 100%;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
@@ -265,6 +265,7 @@ Manager.
|
||||
|
||||
Using Clang appears to be a requirement for OpenBSD, otherwise fonts
|
||||
would not build.
|
||||
For RISC-V architecture devices, use the Clang compiler instead of the GCC compiler.
|
||||
|
||||
.. note:: If you are compiling Godot for production use, then you can
|
||||
make the final executable smaller and faster by adding the
|
||||
|
||||
@@ -43,7 +43,7 @@ either ``template_release`` for a release build or ``template_debug`` for a debu
|
||||
scons platform=web target=template_release
|
||||
scons platform=web target=template_debug
|
||||
|
||||
By default, the :ref:`JavaScript singleton <doc_javascript_eval>` will be built
|
||||
By default, the :ref:`JavaScriptBridge singleton <doc_web_javascript_bridge>` will be built
|
||||
into the engine. Official export templates also have the JavaScript singleton
|
||||
enabled. Since ``eval()`` calls can be a security concern, the
|
||||
``javascript_eval`` option can be used to build without the singleton::
|
||||
|
||||
@@ -144,6 +144,11 @@ Tools must be disabled in order to use this flag, as the editor is not designed
|
||||
to operate without 3D support. Without it, the binary size can be reduced
|
||||
by about 15%.
|
||||
|
||||
.. note::
|
||||
|
||||
Disabling 3D support also disables all navigation. This includes 2D navigation,
|
||||
as it uses the 3D navigation system internally.
|
||||
|
||||
Disabling advanced GUI objects
|
||||
------------------------------
|
||||
|
||||
|
||||
@@ -275,7 +275,7 @@ movement. Let's place this code at the end of the ``_process()`` function:
|
||||
if velocity.x != 0:
|
||||
$AnimatedSprite2D.animation = "walk"
|
||||
$AnimatedSprite2D.flip_v = false
|
||||
# See the note below about boolean assignment.
|
||||
# See the note below about the following boolean assignment.
|
||||
$AnimatedSprite2D.flip_h = velocity.x < 0
|
||||
elif velocity.y != 0:
|
||||
$AnimatedSprite2D.animation = "up"
|
||||
@@ -287,7 +287,7 @@ movement. Let's place this code at the end of the ``_process()`` function:
|
||||
{
|
||||
animatedSprite2D.Animation = "walk";
|
||||
animatedSprite2D.FlipV = false;
|
||||
// See the note below about boolean assignment.
|
||||
// See the note below about the following boolean assignment.
|
||||
animatedSprite2D.FlipH = velocity.X < 0;
|
||||
}
|
||||
else if (velocity.Y != 0)
|
||||
|
||||
@@ -14,8 +14,7 @@ Click the **Instance** button (represented by a chain link icon) and select your
|
||||
|
||||
.. image:: img/instance_scene.webp
|
||||
|
||||
Now, add the following nodes as children of ``Main``, and name them as shown
|
||||
(values are in seconds):
|
||||
Now, add the following nodes as children of ``Main``, and name them as shown:
|
||||
|
||||
- :ref:`Timer <class_Timer>` (named ``MobTimer``) - to control how often mobs
|
||||
spawn
|
||||
@@ -26,7 +25,7 @@ Now, add the following nodes as children of ``Main``, and name them as shown
|
||||
- :ref:`Marker2D <class_Marker2D>` (named ``StartPosition``) - to indicate
|
||||
the player's start position
|
||||
|
||||
Set the ``Wait Time`` property of each of the ``Timer`` nodes as follows:
|
||||
Set the ``Wait Time`` property of each of the ``Timer`` nodes as follows (values are in seconds):
|
||||
|
||||
- ``MobTimer``: ``0.5``
|
||||
- ``ScoreTimer``: ``1``
|
||||
|
||||
@@ -218,9 +218,6 @@ Leaving the screen
|
||||
We still have to destroy the mobs when they leave the screen. To do so, we'll
|
||||
connect our :ref:`VisibleOnScreenNotifier3D <class_VisibleOnScreenNotifier3D>` node's ``screen_exited`` signal to the ``Mob``.
|
||||
|
||||
Head back to the 3D viewport by clicking on the *3D* label at the top of the
|
||||
editor. You can also press :kbd:`Ctrl + F2` (:kbd:`Alt + 2` on macOS).
|
||||
|
||||
|image8|
|
||||
|
||||
Select the :ref:`VisibleOnScreenNotifier3D <class_VisibleOnScreenNotifier3D>` node and on the right side of the interface,
|
||||
@@ -232,7 +229,7 @@ Connect the signal to the ``Mob``
|
||||
|
||||
|image10|
|
||||
|
||||
This will take you back to the script editor and add a new function for you,
|
||||
This will add a new function for you in your mob script,
|
||||
``_on_visible_on_screen_notifier_3d_screen_exited()``. From it, call the ``queue_free()``
|
||||
method. This function destroys the instance it's called on.
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 46 KiB |
@@ -20,6 +20,7 @@ make the most of your time learning it.
|
||||
:name: toc-learn-introduction
|
||||
|
||||
introduction_to_godot
|
||||
learn_to_code_with_gdscript
|
||||
key_concepts_overview
|
||||
first_look_at_the_editor
|
||||
learning_new_features
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
.. _doc_learn_to_code_with_gdscript:
|
||||
|
||||
Learn to code with GDScript
|
||||
===========================
|
||||
|
||||
In Godot, you can write code using the GDScript and C# programming languages.
|
||||
|
||||
If you are new to programming, we recommend starting with GDScript because we
|
||||
designed it to be simpler than all-purpose languages like C#. It will be both
|
||||
faster and easier to learn.
|
||||
|
||||
While GDScript is a language specific to Godot, the techniques you will learn
|
||||
with it will apply to other programming languages.
|
||||
|
||||
Note that it is completely normal for a programmer to learn and use multiple
|
||||
languages. Programming languages have more similarities than differences, so
|
||||
once you know one, you can learn another much faster.
|
||||
|
||||
Learn in your browser with the GDScript app
|
||||
-------------------------------------------
|
||||
|
||||
To learn GDScript, you can use the app Learn GDScript From Zero. It is a
|
||||
complete beginner course with interactive practices you can do right in your
|
||||
browser.
|
||||
|
||||
.. image:: img/learn_gdscript_app.webp
|
||||
|
||||
Click here to access the app: `Learn GDScript From Zero app`_
|
||||
|
||||
This app is an open-source project. To report bugs or contribute, head to the
|
||||
app's source code repository: `GitHub repository`_.
|
||||
|
||||
In the next part, you will get an overview of the engine's essential concepts.
|
||||
|
||||
.. _Learn GDScript From Zero app: https://gdquest.github.io/learn-gdscript/?ref=godot-docs
|
||||
.. _GitHub repository: https://github.com/GDQuest/learn-gdscript
|
||||
@@ -162,7 +162,7 @@ Close the window or press :kbd:`F8` (:kbd:`Cmd + .` on macOS) to quit the runnin
|
||||
Setting the main scene
|
||||
----------------------
|
||||
|
||||
To run our test scene, we used the Play Scene button. Another button next to it
|
||||
To run our test scene, we used the Run Current Scene button. Another button next to it
|
||||
allows you to set and run the project's main scene. You can press :kbd:`F5`
|
||||
(:kbd:`Cmd + B` on macOS) to do so.
|
||||
|
||||
|
||||
@@ -335,7 +335,7 @@ The ``visible`` property is a boolean that controls the visibility of our node.
|
||||
The line ``visible = not visible`` toggles the value. If ``visible`` is
|
||||
``true``, it becomes ``false``, and vice-versa.
|
||||
|
||||
If you run the scene now, you will see that the sprite blinks on and off, at one
|
||||
If you run the Node2D scene now, you will see that the sprite blinks on and off, at one
|
||||
second intervals.
|
||||
|
||||
Complete script
|
||||
|
||||
|
Before Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 199 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 22 KiB |
@@ -148,7 +148,7 @@ Godot has an option to unwrap meshes and visualize the UV channels. After
|
||||
selecting a MeshInstance3D node, it can be found in the **Mesh** menu at the top
|
||||
of the 3D editor viewport:
|
||||
|
||||
.. image:: img/lightmap_gi_mesh_menu.png
|
||||
.. image:: img/lightmap_gi_mesh_menu.webp
|
||||
|
||||
This will generate a second set of UV2 coordinates which can be used for baking.
|
||||
It will also set the texture size automatically.
|
||||
@@ -164,12 +164,12 @@ it unwrapped before import can be faster.
|
||||
|
||||
Simply do an unwrap on the second UV2 layer.
|
||||
|
||||
.. image:: img/lightmap_gi_blender.png
|
||||
.. image:: img/lightmap_gi_blender.webp
|
||||
|
||||
Then import the 3D scene normally. Remember you will need to set the texture
|
||||
size on the mesh after import.
|
||||
|
||||
.. image:: img/lightmap_gi_lmsize.png
|
||||
.. image:: img/lightmap_gi_lmsize.webp
|
||||
|
||||
If you use external meshes on import, the size will be kept. Be wary that most
|
||||
unwrappers in 3D modeling software are not quality-oriented, as they are meant
|
||||
@@ -208,7 +208,7 @@ Checking UV2
|
||||
In the **Mesh** menu mentioned before, the UV2 texture coordinates can be visualized.
|
||||
If something is failing, double-check that the meshes have these UV2 coordinates:
|
||||
|
||||
.. image:: img/lightmap_gi_uvchannel.png
|
||||
.. image:: img/lightmap_gi_uvchannel.webp
|
||||
|
||||
Setting up the scene
|
||||
--------------------
|
||||
@@ -217,7 +217,7 @@ Before anything is done, a **LightmapGI** node needs to be added to a scene.
|
||||
This will enable light baking on all nodes (and sub-nodes) in that scene, even
|
||||
on instanced scenes.
|
||||
|
||||
.. image:: img/lightmap_gi_scene.png
|
||||
.. image:: img/lightmap_gi_scene.webp
|
||||
|
||||
A sub-scene can be instanced several times, as this is supported by the baker.
|
||||
Each instance will be assigned a lightmap of its own. To avoid issues with
|
||||
@@ -231,7 +231,7 @@ For a **MeshInstance3D** node to take part in the baking process, it needs to ha
|
||||
its bake mode set to **Static**. Meshes that have their bake mode set to **Disabled**
|
||||
or **Dynamic** will be ignored by the lightmapper.
|
||||
|
||||
.. image:: img/lightmap_gi_use.png
|
||||
.. image:: img/lightmap_gi_use.webp
|
||||
|
||||
When auto-generating lightmaps on scene import, this is enabled automatically.
|
||||
|
||||
@@ -245,7 +245,7 @@ that light will be baked.
|
||||
Lights can be disabled (no bake) or be fully baked (direct and indirect). This
|
||||
can be controlled from the **Bake Mode** menu in lights:
|
||||
|
||||
.. image:: img/lightmap_gi_bake_mode.png
|
||||
.. image:: img/lightmap_gi_bake_mode.webp
|
||||
|
||||
The modes are:
|
||||
|
||||
@@ -308,7 +308,7 @@ Baking
|
||||
To begin the bake process, click the **Bake Lightmaps** button at the top of the
|
||||
3D editor viewport when selecting the LightmapGI node:
|
||||
|
||||
.. image:: img/lightmap_gi_bake.png
|
||||
.. image:: img/lightmap_gi_bake.webp
|
||||
|
||||
This can take from seconds to minutes (or hours) depending on scene size, bake
|
||||
method and quality selected.
|
||||
|
||||
@@ -68,8 +68,7 @@ See :ref:`Mesh.ArrayType <enum_Mesh_ArrayType>` for a full list.
|
||||
- :ref:`PackedInt32Array <class_PackedInt32Array>`
|
||||
|
||||
In most cases when creating a mesh, we define it by its vertex positions. So usually, the array of vertices (at index 0) is required, while the index array (at index 12) is optional and
|
||||
will only be used if included. It is also possible to create a mesh with only the index array and no vertex array, but that's beyond the scope of this tutorial. In fact, we won't use the
|
||||
index array at all.
|
||||
will only be used if included. It is also possible to create a mesh with only the index array and no vertex array, but that's beyond the scope of this tutorial.
|
||||
|
||||
All the other arrays carry information about the vertices. They are optional and will only be used if included. Some of these arrays (e.g. ``ARRAY_COLOR``)
|
||||
use one entry per vertex to provide extra information about vertices. They must have the same size as the vertex array. Other arrays (e.g. ``ARRAY_TANGENT``) use
|
||||
|
||||
@@ -492,6 +492,12 @@ Remember to use a transparent albedo texture (or reduce the albedo color's alpha
|
||||
channel) to make refraction visible, as refraction relies on transparency to
|
||||
have a visible effect.
|
||||
|
||||
Refraction also takes the material roughness into account. Higher roughness
|
||||
values will make the objects behind the refraction look blurrier, which
|
||||
simulates real life behavior. If you can't see behind the object when refraction
|
||||
is enabled and albedo transparency is reduced, decrease the material's
|
||||
**Roughness** value.
|
||||
|
||||
A normal map can optionally be specified in the **Refraction Texture** property
|
||||
to allow distorting the refraction's direction on a per-pixel basis.
|
||||
|
||||
|
||||
@@ -142,12 +142,13 @@ Keep going and build the whole skeleton:
|
||||
|
||||
.. image:: img/skel2d13.png
|
||||
|
||||
You will notice that all bones raise an annoying warning about a missing rest
|
||||
pose. This means that it's time to set one. Go to the *skeleton* node and create
|
||||
a rest pose. This pose is the default one, you can come back to it anytime you
|
||||
want (which is very handy for animating):
|
||||
You will notice that all bones raise a warning about a missing rest pose. A rest
|
||||
pose is the default pose for a skeleton, you can come back to it anytime you want
|
||||
(which is very handy for animating). To set one click on the *skeleton* node in
|
||||
the scene tree, then click on the ``Skeleton2D`` button in the toolbar, and select
|
||||
``Overwrite Rest Pose`` from the dropdown menu.
|
||||
|
||||
.. image:: img/skel2d14.png
|
||||
.. image:: img/skel2d14.webp
|
||||
|
||||
The warnings will go away. If you modify the skeleton (add/remove bones) you
|
||||
will need to set the rest pose again.
|
||||
|
||||
|
After Width: | Height: | Size: 988 B |
|
After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
@@ -218,6 +218,18 @@ Yay! Our animation runs:
|
||||
|
||||
The animation
|
||||
|
||||
Autoplay on load
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
You can make it so an animation plays automatically when the AnimationPlayer nodes
|
||||
scene starts, or joins another scene. To do this click the "Autoplay on load"
|
||||
button in the animation editor, it's right next to the edit button.
|
||||
|
||||
.. image:: img/autoplay_on_load.webp
|
||||
|
||||
The icon for it will also appear in front of the name of the animation, so you can
|
||||
easily identify which one is the autoplay animation.
|
||||
|
||||
Back and forth
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
@@ -240,7 +252,7 @@ in the next chapter.
|
||||
Track settings
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Each track has a settings panel at the end, where you can set its update
|
||||
Each property track has a settings panel at the end, where you can set its update
|
||||
mode, track interpolation, and loop mode.
|
||||
|
||||
.. figure:: img/animation_track_settings.webp
|
||||
@@ -361,9 +373,21 @@ If you want to reset the tracks in the editor, select the AnimationPlayer node,
|
||||
open the **Animation** bottom panel then choose **Apply Reset** in the
|
||||
animation editor's **Edit** dropdown menu.
|
||||
|
||||
When adding tracks on new animations, the editor will ask you to automatically
|
||||
create a RESET track when using the keyframe icon next to a property in the inspector.
|
||||
This does not apply on tracks created with Godot versions prior to 3.4,
|
||||
as the animation reset track feature was added in 3.4.
|
||||
when using the keyframe icon next to a property in the inspector the editor will
|
||||
ask you to automatically create a RESET track.
|
||||
|
||||
.. note:: RESET tracks is also used as a reference value for blending. See also `For better blending <../animation/animation_tree.html#for-better-blending>`__.
|
||||
|
||||
Onion Skinning
|
||||
--------------
|
||||
|
||||
Godot's animation editor allows you use onion skinning while creating an
|
||||
animation. To turn this feature on click on the onion icon in the top right
|
||||
of the animation editor. Now there will be transparent red copies of what
|
||||
is being animated in its previous positions in the animation.
|
||||
|
||||
.. image:: img/onion_skin.webp
|
||||
|
||||
The three dots button next to the onion skinning button opens a dropdown
|
||||
menu that lets you adjust how it works, including the ability to use
|
||||
onion skinning for future frames.
|
||||
|
||||
@@ -129,7 +129,7 @@ the expense of memory and some minor operational efficiency.
|
||||
|
||||
- HashMaps maintain gaps of unused memory interspersed in the table
|
||||
on purpose to reduce hash collisions and maintain the speed of
|
||||
accesses. This is why it constantly increases in size quadratically by
|
||||
accesses. This is why it constantly increases in size exponentially by
|
||||
powers of 2.
|
||||
|
||||
As one might be able to tell, Dictionaries specialize in tasks that Arrays
|
||||
|
||||
@@ -284,109 +284,10 @@ supported on your web server for further file size savings.
|
||||
be used. Instead, you should use an established web server such as Apache or
|
||||
nginx.
|
||||
|
||||
.. _doc_javascript_eval:
|
||||
Interacting with the browser and JavaScript
|
||||
-------------------------------------------
|
||||
|
||||
Calling JavaScript from script
|
||||
------------------------------
|
||||
|
||||
In web builds, the ``JavaScriptBridge`` singleton is implemented. It offers a single
|
||||
method called ``eval`` that works similarly to the JavaScript function of the
|
||||
same name. It takes a string as an argument and executes it as JavaScript code.
|
||||
This allows interacting with the browser in ways not possible with script
|
||||
languages integrated into Godot.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
func my_func():
|
||||
JavaScriptBridge.eval("alert('Calling JavaScript per GDScript!');")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private void MyFunc()
|
||||
{
|
||||
JavaScriptBridge.Eval("alert('Calling JavaScript per C#!');")
|
||||
}
|
||||
|
||||
The value of the last JavaScript statement is converted to a GDScript value and
|
||||
returned by ``eval()`` under certain circumstances:
|
||||
|
||||
* JavaScript ``number`` is returned as :ref:`class_float`
|
||||
* JavaScript ``boolean`` is returned as :ref:`class_bool`
|
||||
* JavaScript ``string`` is returned as :ref:`class_String`
|
||||
* JavaScript ``ArrayBuffer``, ``TypedArray`` and ``DataView`` are returned as :ref:`PackedByteArray<class_PackedByteArray>`
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
func my_func2():
|
||||
var js_return = JavaScriptBridge.eval("var myNumber = 1; myNumber + 2;")
|
||||
print(js_return) # prints '3.0'
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private void MyFunc2()
|
||||
{
|
||||
var jsReturn = JavaScriptBridge.Eval("var myNumber = 1; myNumber + 2;");
|
||||
GD.Print(jsReturn); // prints '3.0'
|
||||
}
|
||||
|
||||
Any other JavaScript value is returned as ``null``.
|
||||
|
||||
HTML5 export templates may be :ref:`built <doc_compiling_for_web>` without
|
||||
support for the singleton to improve security. With such templates, and on
|
||||
platforms other than HTML5, calling ``JavaScriptBridge.eval`` will also return
|
||||
``null``. The availability of the singleton can be checked with the
|
||||
``web`` :ref:`feature tag <doc_feature_tags>`:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
func my_func3():
|
||||
if OS.has_feature('web'):
|
||||
JavaScriptBridge.eval("""
|
||||
console.log('The JavaScriptBridge singleton is available')
|
||||
""")
|
||||
else:
|
||||
print("The JavaScriptBridge singleton is NOT available")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private void MyFunc3()
|
||||
{
|
||||
if (OS.HasFeature("web"))
|
||||
{
|
||||
JavaScriptBridge.Eval("console.log('The JavaScriptBridge singleton is available')");
|
||||
}
|
||||
else
|
||||
{
|
||||
GD.Print("The JavaScriptBridge singleton is NOT available");
|
||||
}
|
||||
}
|
||||
|
||||
.. tip:: GDScript's multi-line strings, surrounded by 3 quotes ``"""`` as in
|
||||
``my_func3()`` above, are useful to keep JavaScript code readable.
|
||||
|
||||
The ``eval`` method also accepts a second, optional Boolean argument, which
|
||||
specifies whether to execute the code in the global execution context,
|
||||
defaulting to ``false`` to prevent polluting the global namespace:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
func my_func4():
|
||||
# execute in global execution context,
|
||||
# thus adding a new JavaScript global variable `SomeGlobal`
|
||||
JavaScriptBridge.eval("var SomeGlobal = {};", true)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private void MyFunc4()
|
||||
{
|
||||
// execute in global execution context,
|
||||
// thus adding a new JavaScript global variable `SomeGlobal`
|
||||
JavaScriptBridge.Eval("var SomeGlobal = {};", true);
|
||||
}
|
||||
See the :ref:`dedicated page <doc_web_javascript_bridge>` on how to interact with JavaScript and access some unique Web browser features.
|
||||
|
||||
Environment variables
|
||||
---------------------
|
||||
|
||||
@@ -132,7 +132,7 @@ Here is a list of most feature tags in Godot. Keep in mind they are **case-sensi
|
||||
exported to HTML5 on a mobile device.
|
||||
|
||||
To check whether a project exported to HTML5 is running on a mobile device,
|
||||
:ref:`call JavaScript code <doc_javascript_eval>` that reads the browser's
|
||||
:ref:`call JavaScript code <doc_web_javascript_bridge>` that reads the browser's
|
||||
user agent.
|
||||
|
||||
Custom features
|
||||
|
||||
|
Before Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -316,11 +316,14 @@ Translating the project name
|
||||
|
||||
The project name becomes the app name when exporting to different
|
||||
operating systems and platforms. To specify the project name in more
|
||||
than one language, create a new setting ``application/name`` in the **Project
|
||||
Settings** and append the locale identifier to it.
|
||||
For instance, for Spanish, this would be ``application/name_es``:
|
||||
than one language go to **Project > Project Settings> Application >
|
||||
Config**. From here click on the button that says ``Localizable String
|
||||
(Size 0)``. Now there should be a button below that which says ``Add
|
||||
Translation``. Click on that and it will take you to a page where you
|
||||
can choose the language (and country if needed) for your project name
|
||||
translation. After doing that you can now type in the localized name.
|
||||
|
||||
.. image:: img/localized_name.png
|
||||
.. image:: img/localized_name.webp
|
||||
|
||||
If you are unsure about the language code to use, refer to the
|
||||
:ref:`list of locale codes <doc_locales>`.
|
||||
|
||||
@@ -141,7 +141,7 @@ way to pull the data out of the file as well.
|
||||
# Go through everything in the persist category and ask them to return a
|
||||
# dict of relevant variables.
|
||||
func save_game():
|
||||
var save_game = FileAccess.open("user://savegame.save", FileAccess.WRITE)
|
||||
var save_file = FileAccess.open("user://savegame.save", FileAccess.WRITE)
|
||||
var save_nodes = get_tree().get_nodes_in_group("Persist")
|
||||
for node in save_nodes:
|
||||
# Check the node is an instanced scene so it can be instanced again during load.
|
||||
@@ -161,7 +161,7 @@ way to pull the data out of the file as well.
|
||||
var json_string = JSON.stringify(node_data)
|
||||
|
||||
# Store the save dictionary as a new line in the save file.
|
||||
save_game.store_line(json_string)
|
||||
save_file.store_line(json_string)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
@@ -171,7 +171,7 @@ way to pull the data out of the file as well.
|
||||
// dict of relevant variables.
|
||||
public void SaveGame()
|
||||
{
|
||||
using var saveGame = FileAccess.Open("user://savegame.save", FileAccess.ModeFlags.Write);
|
||||
using var saveFile = FileAccess.Open("user://savegame.save", FileAccess.ModeFlags.Write);
|
||||
|
||||
var saveNodes = GetTree().GetNodesInGroup("Persist");
|
||||
foreach (Node saveNode in saveNodes)
|
||||
@@ -197,7 +197,7 @@ way to pull the data out of the file as well.
|
||||
var jsonString = Json.Stringify(nodeData);
|
||||
|
||||
// Store the save dictionary as a new line in the save file.
|
||||
saveGame.StoreLine(jsonString);
|
||||
saveFile.StoreLine(jsonString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,9 +228,9 @@ load function:
|
||||
|
||||
# Load the file line by line and process that dictionary to restore
|
||||
# the object it represents.
|
||||
var save_game = FileAccess.open("user://savegame.save", FileAccess.READ)
|
||||
while save_game.get_position() < save_game.get_length():
|
||||
var json_string = save_game.get_line()
|
||||
var save_file = FileAccess.open("user://savegame.save", FileAccess.READ)
|
||||
while save_file.get_position() < save_file.get_length():
|
||||
var json_string = save_file.get_line()
|
||||
|
||||
# Creates the helper class to interact with JSON
|
||||
var json = JSON.new()
|
||||
@@ -278,11 +278,11 @@ load function:
|
||||
|
||||
// Load the file line by line and process that dictionary to restore the object
|
||||
// it represents.
|
||||
using var saveGame = FileAccess.Open("user://savegame.save", FileAccess.ModeFlags.Read);
|
||||
using var saveFile = FileAccess.Open("user://savegame.save", FileAccess.ModeFlags.Read);
|
||||
|
||||
while (saveGame.GetPosition() < saveGame.GetLength())
|
||||
while (saveFile.GetPosition() < saveFile.GetLength())
|
||||
{
|
||||
var jsonString = saveGame.GetLine();
|
||||
var jsonString = saveFile.GetLine();
|
||||
|
||||
// Creates the helper class to interact with JSON
|
||||
var json = new Json();
|
||||
|
||||
@@ -44,20 +44,23 @@ The default 3D ``edge_connection_margin`` can be changed in the ProjectSettings
|
||||
The edge connection margin value of any navigation map can also be changed at runtime with the NavigationServer API.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
.. code-tab:: gdscript 2D GDScript
|
||||
|
||||
extends Node2D
|
||||
# 2D margins are designed to work with "pixel" values
|
||||
var default_2d_map_rid: RID = get_world_2d().get_navigation_map()
|
||||
NavigationServer2D.map_set_edge_connection_margin(default_2d_map_rid, 50.0)
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
func _ready() -> void:
|
||||
# 2D margins are designed to work with 2D "pixel" values.
|
||||
var default_map_rid: RID = get_world_2d().get_navigation_map()
|
||||
NavigationServer2D.map_set_edge_connection_margin(default_map_rid, 50.0)
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends Node3D
|
||||
# 3D margins are designed to work with 3D unit values
|
||||
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
|
||||
NavigationServer3D.map_set_edge_connection_margin(default_3d_map_rid, 0.5)
|
||||
|
||||
func _ready() -> void:
|
||||
# 3D margins are designed to work with 3D world unit values.
|
||||
var default_map_rid: RID = get_world_3d().get_navigation_map()
|
||||
NavigationServer3D.map_set_edge_connection_margin(default_map_rid, 0.5)
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ The ``velocity_computed`` signal of the NavigationAgent node must be connected t
|
||||
|
||||
.. image:: img/agent_safevelocity_signal.png
|
||||
|
||||
Use ``set_velocity()`` on the NavigationAgent node in ``_physics_process()`` to update the agent with the current velocity of the agent's parent node.
|
||||
Set the ``velocity`` of the NavigationAgent node in ``_physics_process()`` to update the agent with the current velocity of the agent's parent node.
|
||||
|
||||
While avoidance is enabled on the agent the ``safe_velocity`` vector will be received with the velocity_computed signal every physics frame.
|
||||
This velocity vector should be used to move the NavigationAgent's parent node in order to avoidance collision with other avoidance using agents or avoidance obstacles.
|
||||
@@ -131,6 +131,11 @@ NavigationObstacles can be used to add some environment constrains to the avoida
|
||||
|
||||
Avoidance does not affect the pathfinding. It should be seen as an additional option for constantly moving objects that cannot be (re)baked to a navigation mesh efficiently in order to move around them.
|
||||
|
||||
.. note::
|
||||
|
||||
RVO avoidance makes implicit assumptions about natural agent behavior. E.g. that agents move on reasonable passing sides that can be assigned when they encounter each other.
|
||||
This means that very clinical avoidance test scenarios will commonly fail. E.g. agents moved directly against each other with perfect opposite velocities will fail because the agents can not get their passing sides assigned.
|
||||
|
||||
Using the NavigationAgent ``enable_avoidance`` property is the preferred option
|
||||
to toggle avoidance. The following code snippets can be used to
|
||||
toggle avoidance on agents, create or delete avoidance callbacks or switch avoidance modes.
|
||||
@@ -204,7 +209,7 @@ This script adds basic navigation movement to a :ref:`Node3D <class_Node3D>` wit
|
||||
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
|
||||
var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_delta
|
||||
if navigation_agent.avoidance_enabled:
|
||||
navigation_agent.set_velocity(new_velocity)
|
||||
navigation_agent.velocity = new_velocity
|
||||
else:
|
||||
_on_velocity_computed(new_velocity)
|
||||
|
||||
@@ -237,7 +242,7 @@ This script adds basic navigation movement to a :ref:`CharacterBody3D <class_Cha
|
||||
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
|
||||
var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_speed
|
||||
if navigation_agent.avoidance_enabled:
|
||||
navigation_agent.set_velocity(new_velocity)
|
||||
navigation_agent.velocity = new_velocity
|
||||
else:
|
||||
_on_velocity_computed(new_velocity)
|
||||
|
||||
@@ -271,7 +276,7 @@ This script adds basic navigation movement to a :ref:`RigidBody3D <class_RigidBo
|
||||
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
|
||||
var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_speed
|
||||
if navigation_agent.avoidance_enabled:
|
||||
navigation_agent.set_velocity(new_velocity)
|
||||
navigation_agent.velocity = new_velocity
|
||||
else:
|
||||
_on_velocity_computed(new_velocity)
|
||||
|
||||
|
||||
@@ -23,7 +23,40 @@ without the need for more complex bitwise operations.
|
||||
In scripts the following helper functions can be used to work with the ``navigation_layers`` bitmask.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
.. code-tab:: gdscript 2D GDScript
|
||||
|
||||
func change_layers():
|
||||
var region: NavigationRegion2D = get_node("NavigationRegion2D")
|
||||
# enables 4-th layer for this region
|
||||
region.navigation_layers = enable_bitmask_inx(region.navigation_layers, 4)
|
||||
# disables 1-rst layer for this region
|
||||
region.navigation_layers = disable_bitmask_inx(region.navigation_layers, 1)
|
||||
|
||||
var agent: NavigationAgent2D = get_node("NavigationAgent2D")
|
||||
# make future path queries of this agent ignore regions with 4-th layer
|
||||
agent.navigation_layers = disable_bitmask_inx(agent.navigation_layers, 4)
|
||||
|
||||
var path_query_navigation_layers: int = 0
|
||||
path_query_navigation_layers = enable_bitmask_inx(path_query_navigation_layers, 2)
|
||||
# get a path that only considers 2-nd layer regions
|
||||
var path: PoolVector2Array = NavigationServer2D.map_get_path(
|
||||
map,
|
||||
start_position,
|
||||
target_position,
|
||||
true,
|
||||
path_query_navigation_layers
|
||||
)
|
||||
|
||||
static func is_bitmask_inx_enabled(_bitmask: int, _index: int) -> bool:
|
||||
return _bitmask & (1 << _index) != 0
|
||||
|
||||
static func enable_bitmask_inx(_bitmask: int, _index: int) -> int:
|
||||
return _bitmask | (1 << _index)
|
||||
|
||||
static func disable_bitmask_inx(_bitmask: int, _index: int) -> int:
|
||||
return _bitmask & ~(1 << _index)
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
func change_layers():
|
||||
var region: NavigationRegion3D = get_node("NavigationRegion3D")
|
||||
|
||||
@@ -44,13 +44,12 @@ If no valid polygon is found within the search radius the navigation link gets d
|
||||
The link debug visuals can be changed in the Editor :ref:`ProjectSettings<class_ProjectSettings>` under ``debug/shapes/navigation``.
|
||||
The visibility of the debug can also be controlled in the Editor 3D Viewport gizmo menu.
|
||||
|
||||
.. note::
|
||||
|
||||
NavigationLinks do not move agents between the two link positions by themselves.
|
||||
|
||||
A navigation link does not provide any automated movement through the link. Instead, when
|
||||
an agent reaches the position of a link, game code needs to react (e.g. through area triggers) and provide means for the agent
|
||||
to move through the link to end up at the links other position (e.g. through teleport or animation) to continue along the path.
|
||||
A navigation link does not provide any specialized movement through the link. Instead, when
|
||||
an agent reaches the position of a link, game code needs to react (e.g. through area triggers)
|
||||
and provide means for the agent to move through the link to end up at the links other position
|
||||
(e.g. through teleport or animation). Without that an agent will attempt to move itself along
|
||||
the path of the link. You could end up with an agent walking over a bottomless pit instead of
|
||||
waiting for a moving platform, or walking through a teleporter and proceeding through a wall.
|
||||
|
||||
Navigation link script templates
|
||||
--------------------------------
|
||||
|
||||
@@ -26,18 +26,19 @@ The 2D default navigation map RID can be obtained with ``get_world_2d().get_navi
|
||||
The 3D default navigation map RID can be obtained with ``get_world_3d().get_navigation_map()`` from any :ref:`Node3D<class_Node3D>` inheriting Node.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
.. code-tab:: gdscript 2D GDScript
|
||||
|
||||
extends Node2D
|
||||
|
||||
var default_2d_navigation_map_rid: RID = get_world_2d().get_navigation_map()
|
||||
func _ready() -> void:
|
||||
var default_navigation_map_rid: RID = get_world_2d().get_navigation_map()
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends Node3D
|
||||
|
||||
var default_3d_navigation_map_rid: RID = get_world_3d().get_navigation_map()
|
||||
func _ready() -> void:
|
||||
var default_navigation_map_rid: RID = get_world_3d().get_navigation_map()
|
||||
|
||||
Creating new navigation maps
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -57,20 +58,21 @@ Navigation regions and avoidance agents can only be part of a single navigation
|
||||
A navigation map switch will take effect only after the next NavigationServer synchronization.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
.. code-tab:: gdscript 2D GDScript
|
||||
|
||||
extends Node2D
|
||||
|
||||
var new_navigation_map: RID = NavigationServer2D.map_create()
|
||||
NavigationServer2D.map_set_active(true)
|
||||
func _ready() -> void:
|
||||
var new_navigation_map: RID = NavigationServer2D.map_create()
|
||||
NavigationServer2D.map_set_active(true)
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends Node3D
|
||||
|
||||
var new_navigation_map: RID = NavigationServer3D.map_create()
|
||||
NavigationServer3D.map_set_active(true)
|
||||
func _ready() -> void:
|
||||
var new_navigation_map: RID = NavigationServer3D.map_create()
|
||||
NavigationServer3D.map_set_active(true)
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
@@ -30,33 +30,43 @@ This reuse avoids performance implications from frequent object creation if a pr
|
||||
has a large quantity of simultaneous agents that regularly update their paths.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
.. code-tab:: gdscript 2D GDScript
|
||||
|
||||
# prepare query objects
|
||||
var query_parameters = NavigationPathQueryParameters2D.new()
|
||||
var query_result = NavigationPathQueryResult2D.new()
|
||||
# Prepare query objects.
|
||||
var query_parameters := NavigationPathQueryParameters2D.new()
|
||||
var query_result := NavigationPathQueryResult2D.new()
|
||||
|
||||
# update parameters object
|
||||
query_parameters.map = get_world_2d().get_navigation_map()
|
||||
query_parameters.start_position = agent2d_current_global_position
|
||||
query_parameters.target_position = agent2d_target_global_position
|
||||
func query_path(p_start_position: Vector2, p_target_position: Vector2, p_navigation_layers: int = 1) -> PackedVector2Array:
|
||||
if not is_inside_tree():
|
||||
return PackedVector2Array()
|
||||
|
||||
# update result object
|
||||
NavigationServer2D.query_path(query_parameters, query_result)
|
||||
var path: PackedVector2Array = query_result.get_path()
|
||||
query_parameters.map = get_world_2d().get_navigation_map()
|
||||
query_parameters.start_position = p_start_position
|
||||
query_parameters.target_position = p_target_position
|
||||
query_parameters.navigation_layers = p_navigation_layers
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
NavigationServer2D.query_path(query_parameters, query_result)
|
||||
var path: PackedVector2Array = query_result.get_path()
|
||||
|
||||
# prepare query objects
|
||||
var query_parameters = NavigationPathQueryParameters3D.new()
|
||||
var query_result = NavigationPathQueryResult3D.new()
|
||||
return path
|
||||
|
||||
# update parameters object
|
||||
query_parameters.map = get_world_3d().get_navigation_map()
|
||||
query_parameters.start_position = agent3d_current_global_position
|
||||
query_parameters.target_position = agent3d_target_global_position
|
||||
|
||||
# update result object
|
||||
NavigationServer3D.query_path(query_parameters, query_result)
|
||||
var path: PackedVector3Array = query_result.get_path()
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
# Prepare query objects.
|
||||
var query_parameters := NavigationPathQueryParameters3D.new()
|
||||
var query_result := NavigationPathQueryResult3D.new()
|
||||
|
||||
func query_path(p_start_position: Vector3, p_target_position: Vector3, p_navigation_layers: int = 1) -> PackedVector3Array:
|
||||
if not is_inside_tree():
|
||||
return PackedVector3Array()
|
||||
|
||||
query_parameters.map = get_world_3d().get_navigation_map()
|
||||
query_parameters.start_position = p_start_position
|
||||
query_parameters.target_position = p_target_position
|
||||
query_parameters.navigation_layers = p_navigation_layers
|
||||
|
||||
NavigationServer3D.query_path(query_parameters, query_result)
|
||||
var path: PackedVector3Array = query_result.get_path()
|
||||
|
||||
return path
|
||||
|
||||
@@ -33,34 +33,43 @@ Outside of grids due to polygons often covering large open areas with a single,
|
||||
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
.. code-tab:: gdscript 2D GDScript
|
||||
|
||||
extends Node2D
|
||||
# basic query for a navigation path in 2D using the default navigation map
|
||||
var default_2d_map_rid: RID = get_world_2d().get_navigation_map()
|
||||
var start_position: Vector2 = Vector2(0.0, 0.0)
|
||||
var target_position: Vector2 = Vector2(5.0, 0.0)
|
||||
var path: PackedVector2Array = NavigationServer2D.map_get_path(
|
||||
default_2d_map_rid,
|
||||
start_position,
|
||||
target_position,
|
||||
true
|
||||
)
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
# Basic query for a navigation path using the default navigation map.
|
||||
|
||||
func get_navigation_path(p_start_position: Vector2, p_target_position: Vector2) -> PackedVector2Array:
|
||||
if not is_inside_tree():
|
||||
return PackedVector2Array()
|
||||
|
||||
var default_map_rid: RID = get_world_2d().get_navigation_map()
|
||||
var path: PackedVector2Array = NavigationServer2D.map_get_path(
|
||||
default_map_rid,
|
||||
p_start_position,
|
||||
p_target_position,
|
||||
true
|
||||
)
|
||||
return path
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends Node3D
|
||||
# basic query for a navigation path in 3D using the default navigation map
|
||||
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
|
||||
var start_position: Vector3 = Vector3(0.0, 0.0, 0.0)
|
||||
var target_position: Vector3 = Vector3(5.0, 0.0, 3.0)
|
||||
var path: PackedVector3Array = NavigationServer3D.map_get_path(
|
||||
default_3d_map_rid,
|
||||
start_position,
|
||||
target_position,
|
||||
true
|
||||
)
|
||||
|
||||
# Basic query for a navigation path using the default navigation map.
|
||||
|
||||
func get_navigation_path(p_start_position: Vector3, p_target_position: Vector3) -> PackedVector3Array:
|
||||
if not is_inside_tree():
|
||||
return PackedVector3Array()
|
||||
|
||||
var default_map_rid: RID = get_world_3d().get_navigation_map()
|
||||
var path: PackedVector3Array = NavigationServer3D.map_get_path(
|
||||
default_map_rid,
|
||||
p_start_position,
|
||||
p_target_position,
|
||||
true
|
||||
)
|
||||
return path
|
||||
|
||||
A returned ``path`` by the NavigationServer will be a ``PackedVector2Array`` for 2D or a ``PackedVector3Array`` for 3D.
|
||||
These are just a memory-optimized ``Array`` of vector positions.
|
||||
|
||||
@@ -36,38 +36,45 @@ Creating new navigation regions
|
||||
|
||||
New NavigationRegion nodes will automatically register to the default world navigation map for their 2D/3D dimension.
|
||||
|
||||
The region RID can then be obtained from NavigationRegion Nodes with ``get_region_rid()``.
|
||||
The region RID can then be obtained from NavigationRegion Nodes with ``get_rid()``.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
.. code-tab:: gdscript 2D GDScript
|
||||
|
||||
extends NavigationRegion2D
|
||||
|
||||
var navigationserver_region_rid: RID = get_rid()
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends NavigationRegion3D
|
||||
|
||||
var navigationserver_region_rid: RID = get_region_rid()
|
||||
var navigationserver_region_rid: RID = get_rid()
|
||||
|
||||
New regions can also be created with the NavigationServer API and added to any existing map.
|
||||
|
||||
If regions are created with the NavigationServer API directly they need to be assigned a navigation map manually.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
.. code-tab:: gdscript 2D GDScript
|
||||
|
||||
extends Node2D
|
||||
|
||||
var new_2d_region_rid: RID = NavigationServer2D.region_create()
|
||||
var default_2d_map_rid: RID = get_world_2d().get_navigation_map()
|
||||
NavigationServer2D.region_set_map(new_2d_region_rid, default_2d_map_rid)
|
||||
func _ready() -> void:
|
||||
var new_region_rid: RID = NavigationServer2D.region_create()
|
||||
var default_map_rid: RID = get_world_2d().get_navigation_map()
|
||||
NavigationServer2D.region_set_map(new_region_rid, default_map_rid)
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends Node3D
|
||||
|
||||
var new_3d_region_rid: RID = NavigationServer3D.region_create()
|
||||
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
|
||||
NavigationServer3D.region_set_map(new_3d_region_rid, default_3d_map_rid)
|
||||
func _ready() -> void:
|
||||
var new_region_rid: RID = NavigationServer3D.region_create()
|
||||
var default_map_rid: RID = get_world_3d().get_navigation_map()
|
||||
NavigationServer3D.region_set_map(new_region_rid, default_map_rid)
|
||||
|
||||
.. note::
|
||||
|
||||
NavigationRegions can only be assigned to a single NavigationMap.
|
||||
If an existing region is assigned to a new map it will leave the old map.
|
||||
Navigation regions can only be assigned to a single navigation map.
|
||||
If an existing region is assigned to a new navigation map it will leave the old map.
|
||||
|
||||
@@ -41,30 +41,8 @@ Synchronization for the NavigationServer happens in the middle of the physics fr
|
||||
The important takeaway is that most NavigationServer changes take effect after the next physics frame and not immediately.
|
||||
This includes all changes made by navigation related nodes in the scene tree or through scripts.
|
||||
|
||||
The following functions will be executed in the synchronization phase only:
|
||||
|
||||
- ``map_set_active()``
|
||||
- ``map_set_up()``
|
||||
- ``map_set_cell_size()``
|
||||
- ``map_set_edge_connection_margin()``
|
||||
- ``region_set_map()``
|
||||
- ``region_set_transform()``
|
||||
- ``region_set_enter_cost()``
|
||||
- ``region_set_travel_cost()``
|
||||
- ``region_set_navigation_layers()``
|
||||
- ``region_set_navigation_mesh()``
|
||||
- ``agent_set_map()``
|
||||
- ``agent_set_neighbor_dist()``
|
||||
- ``agent_set_max_neighbors()``
|
||||
- ``agent_set_time_horizon()``
|
||||
- ``agent_set_radius()``
|
||||
- ``agent_set_max_speed()``
|
||||
- ``agent_set_velocity()``
|
||||
- ``agent_set_target_velocity()``
|
||||
- ``agent_set_position()``
|
||||
- ``agent_set_ignore_y()``
|
||||
- ``agent_set_callback()``
|
||||
- ``free()``
|
||||
.. note::
|
||||
All setters and delete functions require synchronization.
|
||||
|
||||
2D and 3D NavigationServer differences
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -175,7 +153,7 @@ The simplified order of execution for NavigationAgents that use avoidance:
|
||||
|
||||
- physics frame starts.
|
||||
- ``_physics_process(delta)``.
|
||||
- ``set_velocity()`` on NavigationAgent Node.
|
||||
- ``velocity`` property is set on NavigationAgent Node.
|
||||
- Agent sends velocity and position to NavigationServer.
|
||||
- NavigationServer waits for synchronization.
|
||||
- NavigationServer synchronizes and computes avoidance velocities for all registered avoidance agents.
|
||||
|
||||
@@ -429,7 +429,9 @@ without writing much code.
|
||||
|
||||
.. warning:: ``move_and_slide()`` automatically includes the timestep in its
|
||||
calculation, so you should **not** multiply the velocity vector
|
||||
by ``delta``.
|
||||
by ``delta``. This does **not** apply to ``gravity`` as it is an
|
||||
acceleration and is time dependent, and needs to be scaled by
|
||||
``delta``.
|
||||
|
||||
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:
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
:article_outdated: True
|
||||
|
||||
.. _doc_platform_html5:
|
||||
.. _doc_platform_web:
|
||||
|
||||
HTML5
|
||||
=====
|
||||
Web
|
||||
===
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-learn-features-platform-html5
|
||||
|
||||
javascript_bridge
|
||||
html5_shell_classref
|
||||
customizing_html5_shell
|
||||
|
||||
@@ -0,0 +1,325 @@
|
||||
.. _doc_web_javascript_bridge:
|
||||
|
||||
The JavaScriptBridge Singleton
|
||||
==============================
|
||||
|
||||
In web builds, the :ref:`JavaScriptBridge <class_JavaScriptBridge>` singleton
|
||||
allows interaction with JavaScript and web browsers, and can be used to implement some
|
||||
functionalities unique to the web platform.
|
||||
|
||||
Interacting with JavaScript
|
||||
---------------------------
|
||||
|
||||
Sometimes, when exporting Godot for the Web, it might be necessary to interface
|
||||
with external JavaScript code like third-party SDKs, libraries, or
|
||||
simply to access browser features that are not directly exposed by Godot.
|
||||
|
||||
The ``JavaScriptBridge`` singleton provides methods to wrap a native JavaScript object into
|
||||
a Godot :ref:`JavaScriptObject <class_JavaScriptObject>` that tries to feel
|
||||
natural in the context of Godot scripting (e.g. GDScript and C#).
|
||||
|
||||
The :ref:`JavaScriptBridge.get_interface() <class_JavaScriptBridge_method_get_interface>`
|
||||
method retrieves an object in the global scope.
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
extends Node
|
||||
|
||||
func _ready():
|
||||
# Retrieve the `window.console` object.
|
||||
var console = JavaScriptBridge.get_interface("console")
|
||||
# Call the `window.console.log()` method.
|
||||
console.log("test")
|
||||
|
||||
The :ref:`JavaScriptBridge.create_object() <class_JavaScriptBridge_method_create_object>`
|
||||
creates a new object via the JavaScript ``new`` constructor.
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
extends Node
|
||||
|
||||
func _ready():
|
||||
# Call the JavaScript `new` operator on the `window.Array` object.
|
||||
# Passing 10 as argument to the constructor:
|
||||
# JS: `new Array(10);`
|
||||
var arr = JavaScriptBridge.create_object("Array", 10)
|
||||
# Set the first element of the JavaScript array to the number 42.
|
||||
arr[0] = 42
|
||||
# Call the `pop` function on the JavaScript array.
|
||||
arr.pop()
|
||||
# Print the value of the `length` property of the array (9 after the pop).
|
||||
print(arr.length)
|
||||
|
||||
As you can see, by wrapping JavaScript objects into ``JavaScriptObject`` you can
|
||||
interact with them like they were native Godot objects, calling their methods,
|
||||
and retrieving (or even setting) their properties.
|
||||
|
||||
Base types (int, floats, strings, booleans) are automatically converted (floats
|
||||
might lose precision when converted from Godot to JavaScript). Anything else
|
||||
(i.e. objects, arrays, functions) are seen as ``JavaScriptObjects`` themselves.
|
||||
|
||||
Callbacks
|
||||
---------
|
||||
|
||||
Calling JavaScript code from Godot is nice, but sometimes you need to call a
|
||||
Godot function from JavaScript instead.
|
||||
|
||||
This case is a bit more complicated. JavaScript relies on garbage collection,
|
||||
while Godot uses reference counting for memory management. This means you have
|
||||
to explicitly create callbacks (which are returned as ``JavaScriptObjects``
|
||||
themselves) and you have to keep their reference.
|
||||
|
||||
Arguments passed by JavaScript to the callback will be passed as a single Godot
|
||||
``Array``.
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
extends Node
|
||||
|
||||
# Here we create a reference to the `_my_callback` function (below).
|
||||
# This reference will be kept until the node is freed.
|
||||
var _callback_ref = JavaScriptBridge.create_callback(_my_callback)
|
||||
|
||||
func _ready():
|
||||
# Get the JavaScript `window` object.
|
||||
var window = JavaScriptBridge.get_interface("window")
|
||||
# Set the `window.onbeforeunload` DOM event listener.
|
||||
window.onbeforeunload = _callback_ref
|
||||
|
||||
func _my_callback(args):
|
||||
# Get the first argument (the DOM event in our case).
|
||||
var js_event = args[0]
|
||||
# Call preventDefault and set the `returnValue` property of the DOM event.
|
||||
js_event.preventDefault()
|
||||
js_event.returnValue = ''
|
||||
|
||||
Here is another example that asks the user for the `Notification permission <https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API>`__
|
||||
and waits asynchronously to deliver a notification if the permission is
|
||||
granted:
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
extends Node
|
||||
|
||||
# Here we create a reference to the `_on_permissions` function (below).
|
||||
# This reference will be kept until the node is freed.
|
||||
var _permission_callback = JavaScriptBridge.create_callback(_on_permissions)
|
||||
|
||||
func _ready():
|
||||
# NOTE: This is done in `_ready` for simplicity, but SHOULD BE done in response
|
||||
# to user input instead (e.g. during `_input`, or `button_pressed` event, etc.),
|
||||
# otherwise it might not work.
|
||||
|
||||
# Get the `window.Notification` JavaScript object.
|
||||
var notification = JavaScriptBridge.get_interface("Notification")
|
||||
# Call the `window.Notification.requestPermission` method which returns a JavaScript
|
||||
# Promise, and bind our callback to it.
|
||||
notification.requestPermission().then(_permission_callback)
|
||||
|
||||
func _on_permissions(args):
|
||||
# The first argument of this callback is the string "granted" if the permission is granted.
|
||||
var permission = args[0]
|
||||
if permission == "granted":
|
||||
print("Permission granted, sending notification.")
|
||||
# Create the notification: `new Notification("Hi there!")`
|
||||
JavaScriptBridge.create_object("Notification", "Hi there!")
|
||||
else:
|
||||
print("No notification permission.")
|
||||
|
||||
Can I use my favorite library?
|
||||
------------------------------
|
||||
|
||||
You most likely can. First, you have to
|
||||
include your library in the page. You can simply customize the
|
||||
:ref:`Head Include <doc_javascript_export_options>` during export (see below),
|
||||
or even :ref:`write your own template <doc_customizing_html5_shell>`.
|
||||
|
||||
In the example below, we customize the ``Head Include`` to add an external library
|
||||
(`axios <https://axios-http.com/>`__) from a content delivery network, and a
|
||||
second ``<script>`` tag to define our own custom function:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<!-- Axios -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
<!-- Custom function -->
|
||||
<script>
|
||||
function myFunc() {
|
||||
alert("My func!");
|
||||
}
|
||||
</script>
|
||||
|
||||
We can then access both the library and the function from Godot, like we did in
|
||||
previous examples:
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
extends Node
|
||||
|
||||
# Here create a reference to the `_on_get` function (below).
|
||||
# This reference will be kept until the node is freed.
|
||||
var _callback = JavaScriptBridge.create_callback(_on_get)
|
||||
|
||||
func _ready():
|
||||
# Get the `window` object, where globally defined functions are.
|
||||
var window = JavaScriptBridge.get_interface("window")
|
||||
# Call the JavaScript `myFunc` function defined in the custom HTML head.
|
||||
window.myFunc()
|
||||
# Get the `axios` library (loaded from a CDN in the custom HTML head).
|
||||
var axios = JavaScriptBridge.get_interface("axios")
|
||||
# Make a GET request to the current location, and receive the callback when done.
|
||||
axios.get(window.location.toString()).then(_callback)
|
||||
|
||||
func _on_get(args):
|
||||
OS.alert("On Get")
|
||||
|
||||
|
||||
The eval interface
|
||||
------------------
|
||||
|
||||
The ``eval`` method works similarly to the JavaScript function of the same
|
||||
name. It takes a string as an argument and executes it as JavaScript code.
|
||||
This allows interacting with the browser in ways not possible with script
|
||||
languages integrated into Godot.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
func my_func():
|
||||
JavaScriptBridge.eval("alert('Calling JavaScript per GDScript!');")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private void MyFunc()
|
||||
{
|
||||
JavaScriptBridge.Eval("alert('Calling JavaScript per C#!');")
|
||||
}
|
||||
|
||||
The value of the last JavaScript statement is converted to a GDScript value and
|
||||
returned by ``eval()`` under certain circumstances:
|
||||
|
||||
* JavaScript ``number`` is returned as :ref:`class_float`
|
||||
* JavaScript ``boolean`` is returned as :ref:`class_bool`
|
||||
* JavaScript ``string`` is returned as :ref:`class_String`
|
||||
* JavaScript ``ArrayBuffer``, ``TypedArray``, and ``DataView`` are returned as :ref:`PackedByteArray<class_PackedByteArray>`
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
func my_func2():
|
||||
var js_return = JavaScriptBridge.eval("var myNumber = 1; myNumber + 2;")
|
||||
print(js_return) # prints '3.0'
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private void MyFunc2()
|
||||
{
|
||||
var jsReturn = JavaScriptBridge.Eval("var myNumber = 1; myNumber + 2;");
|
||||
GD.Print(jsReturn); // prints '3.0'
|
||||
}
|
||||
|
||||
Any other JavaScript value is returned as ``null``.
|
||||
|
||||
HTML5 export templates may be :ref:`built <doc_compiling_for_web>` without
|
||||
support for the singleton to improve security. With such templates, and on
|
||||
platforms other than HTML5, calling ``JavaScriptBridge.eval`` will also return
|
||||
``null``. The availability of the singleton can be checked with the
|
||||
``web`` :ref:`feature tag <doc_feature_tags>`:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
func my_func3():
|
||||
if OS.has_feature('web'):
|
||||
JavaScriptBridge.eval("""
|
||||
console.log('The JavaScriptBridge singleton is available')
|
||||
""")
|
||||
else:
|
||||
print("The JavaScriptBridge singleton is NOT available")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private void MyFunc3()
|
||||
{
|
||||
if (OS.HasFeature("web"))
|
||||
{
|
||||
JavaScriptBridge.Eval("console.log('The JavaScriptBridge singleton is available')");
|
||||
}
|
||||
else
|
||||
{
|
||||
GD.Print("The JavaScriptBridge singleton is NOT available");
|
||||
}
|
||||
}
|
||||
|
||||
.. tip:: GDScript's multi-line strings, surrounded by 3 quotes ``"""`` as in
|
||||
``my_func3()`` above, are useful to keep JavaScript code readable.
|
||||
|
||||
The ``eval`` method also accepts a second, optional Boolean argument, which
|
||||
specifies whether to execute the code in the global execution context,
|
||||
defaulting to ``false`` to prevent polluting the global namespace:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
func my_func4():
|
||||
# execute in global execution context,
|
||||
# thus adding a new JavaScript global variable `SomeGlobal`
|
||||
JavaScriptBridge.eval("var SomeGlobal = {};", true)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private void MyFunc4()
|
||||
{
|
||||
// execute in global execution context,
|
||||
// thus adding a new JavaScript global variable `SomeGlobal`
|
||||
JavaScriptBridge.Eval("var SomeGlobal = {};", true);
|
||||
}
|
||||
|
||||
|
||||
.. _doc_web_downloading_files:
|
||||
|
||||
Downloading files
|
||||
-----------------
|
||||
|
||||
Downloading files (e.g. a save game) from the Godot Web export to the user's computer can be done by directly interacting with JavaScript, but given it is a
|
||||
very common use case, Godot exposes this functionality to scripting via
|
||||
a dedicated :ref:`JavaScriptBridge.download_buffer() <class_JavaScriptBridge_method_download_buffer>`
|
||||
function which lets you download any generated buffer.
|
||||
|
||||
Here is a minimal example on how to use it:
|
||||
|
||||
extends Node
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
func _ready():
|
||||
# Asks the user download a file called "hello.txt" whose content will be the string "Hello".
|
||||
JavaScriptBridge.download_buffer("Hello".to_utf8_buffer(), "hello.txt")
|
||||
|
||||
And here is a more complete example on how to download a previously saved file:
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
extends Node
|
||||
|
||||
# Open a file for reading and download it via the JavaScript singleton.
|
||||
func _download_file(path):
|
||||
var file = FileAccess.open(path, FileAccess.READ)
|
||||
if file == null:
|
||||
push_error("Failed to load file")
|
||||
return
|
||||
# Get the file name.
|
||||
var fname = path.get_file()
|
||||
# Read the whole file to memory.
|
||||
var buffer = file.get_buffer(file.get_len())
|
||||
# Prompt the user to download the file (will have the same name as the input file).
|
||||
JavaScriptBridge.download_buffer(buffer, fname)
|
||||
|
||||
func _ready():
|
||||
# Create a temporary file.
|
||||
var config = ConfigFile.new()
|
||||
config.set_value("option", "one", false)
|
||||
config.save("/tmp/test.cfg")
|
||||
|
||||
# Download it
|
||||
_download_file("/tmp/test.cfg")
|
||||
@@ -39,7 +39,7 @@ you should remove the instance you have added by calling
|
||||
``remove_inspector_plugin()``.
|
||||
|
||||
.. 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 ``instantiate()``.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
@@ -489,10 +489,13 @@ get_string_from_utf8 StringExtensions.GetStringFromUtf8 (Consider using `S
|
||||
hex_encode StringExtensions.HexEncode (Consider using `System.Convert.ToHexString`_)
|
||||
========================= ==============================================================
|
||||
|
||||
* .NET contains many path utility methods available under the
|
||||
`System.IO.Path`_
|
||||
class that can be used when not dealing with Godot paths (paths that start
|
||||
with ``res://`` or ``user://``)
|
||||
.. note::
|
||||
|
||||
.NET provides path utility methods under the
|
||||
`System.IO.Path`_
|
||||
class. They can only be used with native OS paths, not Godot paths
|
||||
(paths that start with ``res://`` or ``user://``).
|
||||
See :ref:`doc_data_paths`.
|
||||
|
||||
.. _$ string interpolation: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated
|
||||
.. _double.ToString: https://learn.microsoft.com/en-us/dotnet/api/system.double.tostring
|
||||
|
||||
@@ -159,7 +159,7 @@ it to the ``NORMALMAP`` property. Godot will handle converting it for use in 2D
|
||||
| Built-in | Description |
|
||||
+=============================================+===============================================================+
|
||||
| in vec4 **FRAGCOORD** | Coordinate of pixel center. In screen space. ``xy`` specifies |
|
||||
| | position in window. Origin is lower-left. |
|
||||
| | position in window. Origin is upper-left. |
|
||||
+---------------------------------------------+---------------------------------------------------------------+
|
||||
| in vec2 **SCREEN_PIXEL_SIZE** | Size of individual pixels. Equal to inverse of resolution. |
|
||||
+---------------------------------------------+---------------------------------------------------------------+
|
||||
|
||||
@@ -251,7 +251,7 @@ these properties, and if you don't write to them, Godot will optimize away the c
|
||||
| in vec4 **FRAGCOORD** | Coordinate of pixel center in screen space. ``xy`` specifies position in window, ``z`` |
|
||||
| | specifies fragment depth if ``DEPTH`` is not used. Origin is lower-left. |
|
||||
+----------------------------------------+--------------------------------------------------------------------------------------------------+
|
||||
| in bool **FRONT_FACING** | ``true`` if current face if front face. |
|
||||
| in bool **FRONT_FACING** | ``true`` if current face is front facing. |
|
||||
+----------------------------------------+--------------------------------------------------------------------------------------------------+
|
||||
| in vec3 **VIEW** | Normalized vector from fragment position to camera (in view space). This is the same for both |
|
||||
| | perspective and orthogonal cameras. |
|
||||
|
||||
@@ -120,6 +120,16 @@ default values in the NVIDIA Control Panel.
|
||||
To disable this overlay on Linux, open ``nvidia-settings``, go to **X Screen 0 >
|
||||
OpenGL Settings** then uncheck **Enable Graphics API Visual Indicator**.
|
||||
|
||||
A microphone or "refresh" icon appears in the bottom-right corner of the Project Manager and editor window
|
||||
----------------------------------------------------------------------------------------------------------
|
||||
|
||||
This is caused by the NVIDIA graphics driver injecting an overlay to display
|
||||
instant replay information on ShadowPlay recording. This overlay can only be
|
||||
seen on Windows, as Linux does not have support for ShadowPlay.
|
||||
|
||||
To disable this overlay, press :kbd:`Alt + Z` (default shortcut for the NVIDIA overlay)
|
||||
and disable **Settings > HUD Layout > Status Indicator** in the NVIDIA overlay.
|
||||
|
||||
The editor or project appears overly sharp or blurry
|
||||
----------------------------------------------------
|
||||
|
||||
|
||||