Removing trailing whitespace

With `sed -i $(rg -l '[[:blank:]]*$' -g'!classes') -e 's/[[:blank:]]*$//g'`
This commit is contained in:
Rémi Verschelde
2018-11-20 11:08:14 +01:00
parent a0e32ac017
commit 4ef06a4135
69 changed files with 739 additions and 740 deletions

View File

@@ -61,12 +61,12 @@ or:
make SPHINXBUILD=~/.local/bin/sphinx-build html
```
The compilation might take some time as the `classes/` folder contains many files to parse.
The compilation might take some time as the `classes/` folder contains many files to parse.
You can then test the changes live by opening `_build/html/index.html` in your favorite browser.
### Building with Sphinx on Windows
On Windows, you need to:
On Windows, you need to:
* Download the Python installer [here](https://www.python.org/downloads/).
* Install Python. Don't forget to check the "Add Python to PATH" box.
* Use the above `pip` commands.

View File

@@ -9,6 +9,6 @@
<link rel="alternate" hreflang="pt-br" href="http://docs.godotengine.org/pt-br/" />
<link rel="alternate" hreflang="uk" href="http://docs.godotengine.org/uk/" />
<link rel="alternate" hreflang="zh-cn" href="http://docs.godotengine.org/zh-cn/" />
<link rel="alternate" hreflang="x-default" href="http://docs.godotengine.org/" />
<link rel="alternate" hreflang="x-default" href="http://docs.godotengine.org/" />
{{ super() }}
{% endblock %}

View File

@@ -56,7 +56,6 @@ Active:
::
The dog bit the man.
**Don't** use the passive voice:
@@ -400,10 +399,10 @@ and set the anisotropic filter quality to 16x in Project Settings
Screenshot size should not exceed 1920x1080.
When you need to highlight an area of the editor to show something, like a
When you need to highlight an area of the editor to show something, like a
button or option, use a 2 pixel thick outline without a bevel.
Before you add or replace any images in the documentation, they should be run through
a png compressor to save size. The built in lossless compressor in programs like Krita
or Photoshop should be done. However you should also use a lossy one, such as `pngquant <https://pngquant.org/>`_
where almost no image quality is lost during compression.
where almost no image quality is lost during compression.

View File

@@ -3,7 +3,7 @@
Contribute to the Class Reference
=================================
Godot ships with many nodes and singletons to help you develop your games in GDscript. Each is a class, documented in the :ref:`class reference <toc-class-ref>`. This reference is essential for anyone learning the engine: it is available both online and in the engine.
Godot ships with many nodes and singletons to help you develop your games in GDscript. Each is a class, documented in the :ref:`class reference <toc-class-ref>`. This reference is essential for anyone learning the engine: it is available both online and in the engine.
But it's incomplete. Some methods, variables and signals lack descriptions. Others changed with recent releases and need updates. The developers can't write the entire reference on their own. Godot needs you, and all of us, to contribute.
@@ -20,7 +20,7 @@ The class reference lies in the following XML files, in Godot's GitHub repositor
There are 5 steps to update the class reference (full guide below):
1. Fork `Godot's repository <https://github.com/godotengine/godot>`_
1. Fork `Godot's repository <https://github.com/godotengine/godot>`_
2. Clone your fork on your computer
3. Edit the class file in ``doc/classes/`` to write documentation
4. Commit your changes and push them to your fork
@@ -174,7 +174,7 @@ Our job is to add the missing text between these marks:
- <member></member>
- <signal></signal>
Write in a clear and simple language. Always follow the :ref:`writing guidelines <doc_docs_writing_guidelines>` to keep your descriptions short and easy to read. **Do not leave empty lines** in the descriptions: each line in the XML file will result in a new paragraph.
Write in a clear and simple language. Always follow the :ref:`writing guidelines <doc_docs_writing_guidelines>` to keep your descriptions short and easy to read. **Do not leave empty lines** in the descriptions: each line in the XML file will result in a new paragraph.
Here's how a class looks like in XML:

View File

@@ -12,7 +12,7 @@ The aim is to not focus on the modelling techniques (there are plenty of tutoria
Start with a tree
-----------------
I took this tree from SketchFab:
I took this tree from SketchFab:
.. image:: img/tree_base.png

View File

@@ -40,7 +40,7 @@ for a Simulator executable.
Additionally since some time Apple requires 64 bit version of application binary when you are uploading to iStore.
The best way to provide these is to create a bundle in which there are both 32bit and 64 binaries, so every device will be able to run the game.
It can be done in three steps, first compile 32 bit version, then compile 64 bit version and then use ``lipo`` to bundle them into one fat binary, all those steps can be performed with following commands:
It can be done in three steps, first compile 32 bit version, then compile 64 bit version and then use ``lipo`` to bundle them into one fat binary, all those steps can be performed with following commands:
::

View File

@@ -62,7 +62,7 @@ Distro-specific oneliners
+---------------+------------------------------------------------------------------------------------------------------------+
| **OpenBSD** | :: |
| | |
| | pkg_add python scons png llvm yasm |
| | pkg_add python scons png llvm yasm |
+---------------+------------------------------------------------------------------------------------------------------------+
| **openSUSE** | :: |
| | |

View File

@@ -46,7 +46,7 @@ The contents of the ``._sc_`` file (when not empty) are read with the ConfigFile
format as ``project.godot``, etc). So far it can contain a list of pre-loaded project in this
format:
::
::
[init_projects]
list=["demos/2d/platformer", "demos/2d/isometric"]

View File

@@ -104,7 +104,7 @@ memnew/memdelete also use a little C++ magic and notify Objects right
after they are created, and right before they are deleted.
For dynamic memory, the PoolVector<> template is provided. PoolVector is a
standard vector class, and is very similar to vector in the C++ standard library.
standard vector class, and is very similar to vector in the C++ standard library.
To create a PoolVector buffer, use this:
.. code:: cpp

View File

@@ -1,13 +1,13 @@
TSCN file format
================
A :code:`.tscn` File format is the "Text SCeNe" file format and represents
a single scene-tree inside Godot. TSCN files have the advantage of being
nearly human-readable and easy for version control systems to manage. During
import the TSCN files are compiled into binary :code:`.scn` files stored
A :code:`.tscn` File format is the "Text SCeNe" file format and represents
a single scene-tree inside Godot. TSCN files have the advantage of being
nearly human-readable and easy for version control systems to manage. During
import the TSCN files are compiled into binary :code:`.scn` files stored
inside the .import folder. This reduces the data size and speed up loading.
The :code:`.escn` file format is identical to the TSCN file format, but is used to
The :code:`.escn` file format is identical to the TSCN file format, but is used to
indicate to Godot that the file has been exported from another program and
should not be edited by the user from within Godot.
@@ -21,17 +21,17 @@ File structure
There are five main sections inside the TSCN File:
0. File Descriptor
1. External resources
1. External resources
2. Internal resources
3. Nodes
4. Connections
The file descriptor looks like :code:`[gd_scene load_steps=1 format=2]` And
should be the first entry in the file. The load_steps parameter should (in
theory) be the number of resources within the file, though in practice its
The file descriptor looks like :code:`[gd_scene load_steps=1 format=2]` And
should be the first entry in the file. The load_steps parameter should (in
theory) be the number of resources within the file, though in practice its
value seems not to matter.
These sections should appear in order, but it can be hard to distinguish
These sections should appear in order, but it can be hard to distinguish
them. The only difference between them is the first element in the heading
for all of the items in the section.
For example, the heading of all external resources should start with
@@ -48,9 +48,9 @@ Where resource_type is one of:
- sub_resource
- node
- connection
Underneath every heading comes zero or more :code:`key = value` pairs. The
values can be complex datatypes such as arrays, transformations, colors, and
Underneath every heading comes zero or more :code:`key = value` pairs. The
values can be complex datatypes such as arrays, transformations, colors, and
so on. For example, a spatial node looks like:
::
@@ -73,11 +73,11 @@ Other valid keywords include:
- index (if two nodes have the same name)
- groups
The first node in the file should not have the :code:`parent=Path/To/Node`
entry in it's heading, and it is the scene root. All scene files should have
exactly one scene root. It it does not, Godot will fail to import the file.
The parent path of other nodes should be absolute, but without the scene
root's name. If it is a direct child of the scene root, it should be
The first node in the file should not have the :code:`parent=Path/To/Node`
entry in it's heading, and it is the scene root. All scene files should have
exactly one scene root. It it does not, Godot will fail to import the file.
The parent path of other nodes should be absolute, but without the scene
root's name. If it is a direct child of the scene root, it should be
:code:`"."`. Here is an example scene tree (but without any node content).
::
@@ -86,9 +86,9 @@ root's name. If it is a direct child of the scene root, it should be
[node name="Arm" parent="." type="Spatial"] ; Parented to the scene root
[node name="Hand" parent="Arm" type="Spatial"]
[node name="Finger" parent="Arm/Hand" type="Spatial"]
Similar to the internal resource, the document for each node is currently
incomplete. Fortunately it is easy to find out because you can simply
Similar to the internal resource, the document for each node is currently
incomplete. Fortunately it is easy to find out because you can simply
save a file with that node in it. Some example nodes are:
::
@@ -154,7 +154,7 @@ Skeleton
Skeleton node inherits Spatial node, besides that it may have a list
of bones described in key, value pair in the format :code:`bones/Id/Attribute=Value`,
attributes of bone consists of
attributes of bone consists of
- name
- parent
@@ -172,7 +172,7 @@ attributes of bone consists of
4) :code:`pose` is the pose matrix use :code:`rest` as basis
5) :code:`bound_children` is a list of NodePath() points to
5) :code:`bound_children` is a list of NodePath() points to
BoneAttachments belong to this bone
An example of a skeleton node with two bones:
@@ -199,7 +199,7 @@ BoneAttachment
BoneAttachment node is an intermediate node to describe some node being parented
to a single bone in Skeleton node. The BoneAttachment has a :code:`bone_name=NameOfBone`,
and the corresponding bone being the parent has the BoneAttachment node
and the corresponding bone being the parent has the BoneAttachment node
in its :code:`bound_children` list.
An example of one MeshInstance parented to a bone in Skeleton:
@@ -253,13 +253,13 @@ node will have an accompanying ArrayMesh resource. The ArrayMesh resource
may be either internal or external to the TSCN file.
References to the resources are handled by id numbers in the resources heading.
External resources and internal resource are referred to with
:code:`ExtResource(id)` and :code:`SubResource(id)`. Because there have
External resources and internal resource are referred to with
:code:`ExtResource(id)` and :code:`SubResource(id)`. Because there have
different methods to refer to internal and external resource, you can have
the same ID for both an internal and external resource.
For example, to refer to the resource
:code:`[ext_resource id=3 type="PackedScene" path=....]` you would use
:code:`[ext_resource id=3 type="PackedScene" path=....]` you would use
:code:`ExtResource(3)`
External resources
@@ -272,10 +272,10 @@ itself. An external resource consists of:
- A type
- An ID
Godot always generates absolute paths relative to the resource directory and
thus prefixed with :code:`res://`, but paths relative to the TSCN file's
location are also valid.
Godot always generates absolute paths relative to the resource directory and
thus prefixed with :code:`res://`, but paths relative to the TSCN file's
location are also valid.
Some example external resources are:
::
@@ -286,10 +286,10 @@ Some example external resources are:
Internal resources
~~~~~~~~~~~~~~~~~~
A TSCN file can contain meshes, materials and other data, and these are
A TSCN file can contain meshes, materials and other data, and these are
contained in the internal resources section of the file. The heading
for an internal resource looks similar to those of external resources, but
does not have a path. Internal resources also have :code:`key=value` pairs
does not have a path. Internal resources also have :code:`key=value` pairs
under each heading. For example, a capsule collision shape looks like:
::
@@ -298,10 +298,10 @@ under each heading. For example, a capsule collision shape looks like:
radius = 0.5
height = 3.0
Some internal resource contain links to other internal resources (such as a
mesh having a material). In this case, the referring resource must appear
before the reference to it. Thus, in the internal resources section of the
Some internal resource contain links to other internal resources (such as a
mesh having a material). In this case, the referring resource must appear
before the reference to it. Thus, in the internal resources section of the
file, order does matter.
Unfortunately, documentation on the formats for these subresources is not
@@ -322,7 +322,7 @@ TSCN support two format of surface,
- arrays
- morph_arrays
i) :code:`primitive` is an enumerate variable, :code:`primitive=4` which is
i) :code:`primitive` is an enumerate variable, :code:`primitive=4` which is
PRIMITIVE_TRIANGLES is frequently used.
ii) :code:`arrays` as the name suggests is an array of array, it contains:
@@ -336,7 +336,7 @@ TSCN support two format of surface,
7) Bone weight array
8) Vertex index array
iii) :code:`morph_arrays` is an array of morph, each morph is exactly an
iii) :code:`morph_arrays` is an array of morph, each morph is exactly an
:code:`arrays` without vertex index array.
An example of ArrayMesh:
@@ -384,15 +384,15 @@ it includes:
- imported
- enabled
1) The :code:`type` must be put as the first attribute of each track.
1) The :code:`type` must be put as the first attribute of each track.
The value of :code:`type` can be:
- 'transform'
- 'value'
- 'method'
2) The :code:`path` has the format :code:`NodePath(Path/To/Node:Attribute)`.
It is the path from animation root node (property of AnimationPlayer) to the
2) The :code:`path` has the format :code:`NodePath(Path/To/Node:Attribute)`.
It is the path from animation root node (property of AnimationPlayer) to the
animated node or attribute.
3) The :code:`interp` is the method to interpolate frames from the keyframes.
@@ -402,12 +402,12 @@ it includes:
- 1 (linear)
- 2 (cubic)
4) The :code:`keys` is the keyframes, it appears as a PoolRealArray()
4) The :code:`keys` is the keyframes, it appears as a PoolRealArray()
but have different structure for track with different type
- A transform track use every 12 real number in the :code:`keys` to describte a keyframe.
- A transform track use every 12 real number in the :code:`keys` to describte a keyframe.
The first number is the timestamp, the second number is the transition (default 1.0
in transform track), followed by a three number translation vector, followed by
in transform track), followed by a three number translation vector, followed by
four number rotation quaternion (x,y,z,w) and finally a three number scale vector.
::

View File

@@ -5,7 +5,7 @@ Using an external text editor
While Godot has an inbuilt text editor, some developers have a tendency to
want to use a text editor they are familiar with. Godot provides this
option via the options under
option via the options under
``Editor -> Editor Settings -> Text Editor -> External``
.. image:: img/editor_settings.png

View File

@@ -140,7 +140,7 @@ It is possible to bind values when establishing a connection by passing an objec
{
var plusButton = (Button)GetNode("PlusButton");
var minusButton = (Button)GetNode("MinusButton");
plusButton.Connect("pressed", this, "ModifyValue", new object[] { 1 });
minusButton.Connect("pressed", this, "ModifyValue", new object[] { -1 });
}

View File

@@ -10,7 +10,7 @@ This tutorial aims to be a quick reference for how to use GDScript more
efficiently. It focuses on common cases specific to the language, but
also covers a lot of information on dynamically typed languages.
It's meant to be especially useful for programmers with little or no previous
It's meant to be especially useful for programmers with little or no previous
experience with dynamically typed languages.
Dynamic nature
@@ -147,7 +147,7 @@ too. Some Examples:
SomeClass instance = new SomeClass(); // Created as reference
use_class(instance); // Passed as reference
// Garbage collector will get rid of it when not in
// Garbage collector will get rid of it when not in
// use and freeze your game randomly for a second
}
@@ -280,7 +280,7 @@ easily with dictionaries. Here's a simple battleship game example:
if pos in board: # Something at that pos
if board[pos] == SHIP: # There was a ship! hit it
board[pos] = SHIP_HIT
else:
else:
print("Already hit here!") # Hey dude you already hit here
else: # Nothing, mark as water
board[pos] = WATER_HIT

View File

@@ -25,7 +25,7 @@ Examine this concrete GDScript example:
# Define a format string with placeholder '%s'
var format_string = "We're waiting for %s."
# Using the '%' operator, the placeholder is replaced with the desired value
var actual_string = format_string % "Godot"
@@ -78,7 +78,7 @@ format specifier with ``*``, see `dynamic padding`_):
var format_string = "%s was reluctant to learn %s, but now he enjoys it."
var actual_string = format_string % ["Estragon", "GDScript"]
print(actual_string)
# Output: "Estragon was reluctant to learn GDScript, but now he enjoys it."

View File

@@ -3,7 +3,7 @@
Getting started with Visual Scripting
=====================================
As with everything in Godot, we prioritize a good experience over copying or integrating third party solutions
As with everything in Godot, we prioritize a good experience over copying or integrating third party solutions
which might not fit nicely in the current workflow. This led us to write our own version of how we believe
this feature would work best with the engine.

View File

@@ -4,8 +4,8 @@ VisualScript
.. toctree::
:maxdepth: 3
:name: toc-learn-scripting-visual_script
what_is_visual_scripting
getting_started
nodes_purposes

View File

@@ -3,7 +3,7 @@
Nodes and terminology
=====================
Before continuing, it must be noted that the *Node* terminology needs to be used with care.
Before continuing, it must be noted that the *Node* terminology needs to be used with care.
When referring to *Visual Script Nodes* (or generally *Nodes*) this text will refer to the little boxes you connect with lines, which are part of a graph.
When referring to *Scene Nodes*, it is implied that the elements that make up a Scene are being referred, which are part of a tree. Their naming is similar but their function is different.
When referring to *Node* here, it will be implied that a *Visual Script Node* is referred to unless indicated otherwise.
@@ -15,7 +15,7 @@ Node properties
---------------
Like in most visual scripting implementations, each node has editable properties. In Godot, though, we try to avoid
bloating the nodes with editable controls for the sake of readability.
bloating the nodes with editable controls for the sake of readability.
Nodes still display the required information as text, but editing is done via the *Inspector*. To edit them,
select any node and edit its properties in the *Inspector*.
@@ -24,29 +24,29 @@ select any node and edit its properties in the *Inspector*.
Ports and connections
---------------------
Programming in Godot Visual Scripting is done via *Nodes* and *Port Connections* inside each function.
Programming in Godot Visual Scripting is done via *Nodes* and *Port Connections* inside each function.
Ports
~~~~~
Nodes in Godot Visual Scripting have *Ports*. These are endpoints that appear to the
Nodes in Godot Visual Scripting have *Ports*. These are endpoints that appear to the
left and right of nodes and which can be used to make *Connections*:
There are two types of *Ports*: *Sequence* and *Data*.
.. image:: img/visual_script17.png
*Sequence Ports* indicate the order in which operations are executed.
Typically when a *Node* is done processing, it will go to the next node from one of the ports at the right.
If nothing is connected the function may end, or another output *Sequence Port* might be tried (this depends on the node).
*Sequence Ports* indicate the order in which operations are executed.
Typically when a *Node* is done processing, it will go to the next node from one of the ports at the right.
If nothing is connected the function may end, or another output *Sequence Port* might be tried (this depends on the node).
Thanks to this, you can follow the logic flow within a function by following the white lines.
Not every *Node* has *Sequence Ports*. In fact, most do not.
*Data Ports* ports contain typed values. Types can be any regular Godot types,
such as a boolean, an integer, a string, a Vector3, an array, any Object or Scene Node, etc.
A *Data Port* on the right side of a node is considered an output, while,
a port on the left side is an input. Connecting them allows information to flow to the next node.
*Data Ports* ports contain typed values. Types can be any regular Godot types,
such as a boolean, an integer, a string, a Vector3, an array, any Object or Scene Node, etc.
A *Data Port* on the right side of a node is considered an output, while,
a port on the left side is an input. Connecting them allows information to flow to the next node.
Not all *Data Port* types are compatible and will allow connections, though.
Pay special attention to colors and icons, as each type has a different representation:
@@ -57,19 +57,19 @@ Pay special attention to colors and icons, as each type has a different represen
Connections
~~~~~~~~~~~
Connecting is a relatively simple process. Drag an *Output Port* towards an *Input Port*.
Connecting is a relatively simple process. Drag an *Output Port* towards an *Input Port*.
.. image:: img/visual_script_connect.gif
Disconnecting takes a bit more practice. Disconnecting in *Data Ports* happens by
Disconnecting takes a bit more practice. Disconnecting in *Data Ports* happens by
dragging the *Input* away, while for *Sequence Ports*, this happens by dragging the *Output* away.
.. image:: img/visual_script_disconnect.gif
This may seem strange at the beginning, but it happens because *Data Ports* are 1:N
(A single output port can connect to many inputs), while *Sequence Ports* are N:1
This may seem strange at the beginning, but it happens because *Data Ports* are 1:N
(A single output port can connect to many inputs), while *Sequence Ports* are N:1
(Many sequence outputs can be connected to a single input).
Connecting to empty space (drag to connect but unpress over empty space) is also context sensitive, it will supply
@@ -86,7 +86,7 @@ While, for data, a contextual set/get/call menu will open:
Adding nodes
------------
Finally! We got to the fun part! But, before explaining in more detail what each type of node does,
Finally! We got to the fun part! But, before explaining in more detail what each type of node does,
let's take a short look at how nodes are most commonly added and dealt with.
@@ -94,7 +94,7 @@ Accessing scene nodes
~~~~~~~~~~~~~~~~~~~~~
One of the most common tasks is accessing Scene Tree Nodes (again, not to mistake with *Visual Script Nodes*).
Dragging from the Scene Tree and dropping into the canvas will ask you to *call a method* (sometimes referred to as *member function*) on this node.
Dragging from the Scene Tree and dropping into the canvas will ask you to *call a method* (sometimes referred to as *member function*) on this node.
.. image:: img/visual_script19.png
@@ -210,7 +210,7 @@ a brief overview of node types follows:
Constants
~~~~~~~~~
Constant nodes are nodes that provide values that, while not changing over time, can be useful as reference values.
Constant nodes are nodes that provide values that, while not changing over time, can be useful as reference values.
Most of the time they are integer or float.
.. image:: img/visual_script36.png
@@ -273,14 +273,14 @@ As it can be seen above, there are two nodes available: A simple getter, and a s
Scene Node
^^^^^^^^^^
This is just a reference to a node in the tree, but it's easier to use this node by dragging the actual node
This is just a reference to a node in the tree, but it's easier to use this node by dragging the actual node
from the scene tree to the canvas (this will create it and configure it).
Self
^^^^
In some rare occasions, it may be desired to pass this Scene Node as argument.
In some rare occasions, it may be desired to pass this Scene Node as argument.
It can be used to call functions and set/get properties, or drag nodes (or event the node itself that has the script) from the Scene Tree to the canvas for this.
@@ -342,7 +342,7 @@ Some data types in Godot (ie, arrays, dictionaries) are iterable. This means tha
for each element that it has.
The Iterator node goes through all elements and, for each of them, it goes via the "each" sequence port,
making the element available in the "elem" data port.
making the element available in the "elem" data port.
When done, it goes via the "exit" sequence port.

View File

@@ -115,7 +115,7 @@ Notice the text [perspective] under the toolbar, it is a button that opens a lis
|image20|
.. note:: Read :ref:`doc_introduction_to_3d` for more detail about **3D workspace**.
.. note:: Read :ref:`doc_introduction_to_3d` for more detail about **3D workspace**.
The **Script** workspace is a complete code editor with a debugger, rich
auto-completion, and built-in code reference. Press F3 to access it, and

View File

@@ -70,11 +70,11 @@ F#, Boo or ClojureCLR. In practice however, C# is the only officially supported
GDNative / C++
~~~~~~~~~~~~~~
Finally, one of our brightest additions for the 3.0 release:
Finally, one of our brightest additions for the 3.0 release:
GDNative allows scripting in C++ without needing to recompile (or even
restart) Godot.
Any C++ version can be used, and mixing compiler brands and versions for the
Any C++ version can be used, and mixing compiler brands and versions for the
generated shared libraries works perfectly, thanks to our use of an internal C
API Bridge.
@@ -182,7 +182,7 @@ signals in your own scripts.
In this step, we'll connect the "pressed" signal to a custom function. Forming
connections is the first part and defining the custom function is the second part.
For the first part, Godot provides two ways to create connections: through a
For the first part, Godot provides two ways to create connections: through a
visual interface the editor provides or through code.
While we will use the code method for the remainder of this tutorial series, let's
@@ -199,7 +199,7 @@ button in the bottom right, you'll open up the connection creation dialogue.
.. image:: img/connect_dialogue.png
In the bottom-left are the key things you need to create a connection: a node
which implements the method you want to trigger (represented here as a
which implements the method you want to trigger (represented here as a
NodePath) and the name of the method to trigger.
The top-left section displays a list of your scene's nodes with the emitting
@@ -246,7 +246,7 @@ Next, write a function which will be called when the button is pressed:
.. tabs::
.. code-tab:: gdscript GDScript
func _on_Button_pressed():
func _on_Button_pressed():
get_node("Label").text = "HELLO!"
.. code-tab:: csharp
@@ -323,7 +323,7 @@ Why, hello there! Congratulations on scripting your first scene.
# Not for this case,
# but just in case.
get_node("Label/Button")
get_node("Label/Button")
.. code-tab:: csharp
@@ -335,10 +335,10 @@ Also, remember that nodes are referenced by name, not by type.
.. note::
The right-hand panel of the connect dialogue is for binding specific
values to the connected function's parameters. You can add and remove
The right-hand panel of the connect dialogue is for binding specific
values to the connected function's parameters. You can add and remove
values of different types.
The code approach also enables this with a 4th ``Array`` parameter that
is empty by default. Feel free to read up on the ``Object.connect``
is empty by default. Feel free to read up on the ``Object.connect``
method for more information.

View File

@@ -7,9 +7,9 @@ Animation supported:
- light animation
- camera animation
Multiple Actions For Single Object
Multiple Actions For Single Object
----------------------------------
In most games, one objects would have several animations to switch between.
In most games, one objects would have several animations to switch between.
This addon have a support for exporting multiple actions all at once into
a single AnimationPlayer and makes it easy to switch actions.

View File

@@ -1,7 +1,7 @@
Godot-Blender-Exporter
======================
.. note:: This chapter relates to the blender plugin called "Godot-Blender-Exporter",
.. note:: This chapter relates to the blender plugin called "Godot-Blender-Exporter",
which can be downloaded here: https://github.com/godotengine/godot-blender-exporter
Details on exporting

View File

@@ -4,7 +4,7 @@ Mesh
Modifiers
---------
There is an exporting option :code:`Apply Modifiers` to
control whether mesh modifiers are applied to the exported mesh.
control whether mesh modifiers are applied to the exported mesh.
Shape Key

View File

@@ -7,9 +7,9 @@ Rest Bone
---------
Armature object in Blender is exported as a Skeleton node along with
rest position (transform in Godot) of bones.
rest position (transform in Godot) of bones.
.. warning::
.. warning::
The three check boxes :code:`Inherit Rotation`, :code:`Inherit Scale`,
:code:`Local Location` (colored in red) must be ticked when building
armature in Blender, so that the exported bone transform would be

View File

@@ -9,7 +9,7 @@ Why importing?
Raw audio data in general is large and undesired. Godot provides two main
options to import your audio data: WAV and OGG Vorbis.
Each has different advantages.
Each has different advantages.
* Wav files use raw data or light compression, require small amount of CPU to play back (hundreds of simultaneous voices in this format are fine), but take up significant space.
* Ogg Vorbis files use a stronger compression that results in much smaller file size, but uses significantly more processor to play back.
@@ -49,10 +49,10 @@ As you can see above, sound effects become huge with reverb added.
Trimming
~~~~~~~~
One issue that happens often is that the waveform are exported with long
One issue that happens often is that the waveform are exported with long
silences at the beginning and at the end. These are inserted by
DAWs when saving to a waveform, increase their size unnecessarily and
add latency to the moment they are played back.
add latency to the moment they are played back.
Importing as WAV with the Trimming option enabled solves
this.

View File

@@ -8,7 +8,7 @@ Why importing them?
In Godot 3+, image files are no longer native resources and they must be imported.
The reason behind this is the large amount of configuration parameters that
image files can be imported with.
image files can be imported with.
This small tutorial will explain what these parameters are and how to best
make use of them.
@@ -69,7 +69,7 @@ advantages and disadvantages ( |good| = Best, |bad| =Worst ):
HDR Mode
~~~~~~~~
Godot supports high dynamic range textures (as .HDR or .EXR). These are mostly useful as high dynamic range equirectancular panorama skys (the internet
Godot supports high dynamic range textures (as .HDR or .EXR). These are mostly useful as high dynamic range equirectancular panorama skys (the internet
has plenty of if you look for them), which replace Cubemaps in Godot 2.x. Modern PCs support the BC6H VRAM format, but there are still plenty that do not.
If you want Godot to ensure full compatibility in for kind of textures, enable the "Force RGBE" option.
@@ -108,7 +108,7 @@ When pixels become smaller than the screen, mipmaps kick in. This helps reduce t
* Texture width and height must be powers of 2
* Repeat must be enabled
Keep in mind the above when making phone games and applications, want to aim for full compatibility, and need mipmaps.
Keep in mind the above when making phone games and applications, want to aim for full compatibility, and need mipmaps.
When doing 3D, mipmap should be turned on as this also improves performance (smaller versions of the texture are used for objects further away).

View File

@@ -11,7 +11,7 @@ When dealing with 3D assets, Godot has a flexible and configurable importer.
Godot works with *scenes*. This means that the entire scene being worked on in your favorite 3D DCC will be
transferred as close as possible.
Godot supports the following 3D *scene file fomats*:
Godot supports the following 3D *scene file fomats*:
* DAE (Collada), which is currently the most mature workflow.
* GLTF 2.0. Both text and binary formats are supported. Godot has full support for it, but the format is new and gaining traction.
@@ -25,7 +25,7 @@ Why not FBX?
Most game engines use the FBX format for importing 3D scenes, which is
definitely one of the most standardized in the industry. However, this
format requires the use of a closed library from Autodesk which is
distributed with a more restrictive licensing terms than Godot.
distributed with a more restrictive licensing terms than Godot.
The plan is, sometime in the future, to offer a binary plug-in using GDNative.
@@ -53,7 +53,7 @@ Exporting ESCN files from Blender
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The most powerful one, called `godot-blender-exporter
<https://github.com/godotengine/godot-blender-exporter>`__.
<https://github.com/godotengine/godot-blender-exporter>`__.
It uses .escn files which is kind of another name of .tscn file(Godot scene file),
it keeps as much information as possible from a Blender scene.
@@ -99,7 +99,7 @@ Allows setting a specific name to the generated root node.
Custom Script
^^^^^^^^^^^^^
A special script to process the whole scene after import can be provided.
A special script to process the whole scene after import can be provided.
This is great for post processing, changing materials, doing funny stuff
with the geometry etc.
@@ -123,12 +123,12 @@ Storage
By default, Godot imports a single scene. This option allows specifying
that nodes below the root will each be a separate scene and instanced
into the imported one.
into the imported one.
Of course, instancing such imported scenes in other places manually works too.
Materials
Materials
~~~~~~~~~
Location
@@ -262,7 +262,7 @@ following dialog will appear:
.. image:: img/scene_import4.png
In inherited scenes, the only limitations for modifications are:
In inherited scenes, the only limitations for modifications are:
* Nodes can't be removed (but can be added anywhere).
* Sub-Resources can't be edited (save them externally as described above for this)
@@ -295,7 +295,7 @@ Option "-col" will work only for Mesh nodes. If it is detected, a child
static collision node will be added, using the same geometry as the mesh.
However, it is often the case that the visual geometry is too complex or
too un-smooth for collisions, which ends up not working well.
too un-smooth for collisions, which ends up not working well.
To solve this, the "-colonly" modifier exists, which will remove the mesh upon
import and create a :ref:`class_staticbody` collision instead.

View File

@@ -3,8 +3,8 @@
Exporting for iOS
=================
These are the steps to load a Godot project in Xcode. This allows you to
build and deploy to an iOS device, build a release for the App Store, and
These are the steps to load a Godot project in Xcode. This allows you to
build and deploy to an iOS device, build a release for the App Store, and
do everything else you can normally do with Xcode.
Requirements
@@ -17,10 +17,10 @@ Requirements
Export a Godot project to Xcode
-------------------------------
In the Godot editor, open the **Export** window from the **Project** menu. When the
Export window opens, click **Add..** and select **iOS**.
In the Godot editor, open the **Export** window from the **Project** menu. When the
Export window opens, click **Add..** and select **iOS**.
The following export options are required. Leaving any blank with cause the
The following export options are required. Leaving any blank with cause the
exporter to throw an error:
* In the **Application** category
@@ -31,42 +31,42 @@ exporter to throw an error:
After you click **Export Project**, there are still two important options left:
* **Path** is an empty folder that will contain the exported Xcode project files.
* **File** will be the name of the Xcode project and several project specific files and directories.
* **Path** is an empty folder that will contain the exported Xcode project files.
* **File** will be the name of the Xcode project and several project specific files and directories.
.. image:: img/ios_export_file.png
.. note:: This tutorial uses **exported_xcode_project_name**, but you will use your
project's name. When you see **exported_xcode_project_name**
in the following steps, replace it with the name you used instead.
project's name. When you see **exported_xcode_project_name**
in the following steps, replace it with the name you used instead.
When the export completes, the output folder should look like this:
.. image:: img/ios_export_output.png
Opening **exported_xcode_project_name.xcodeproj** lets you build and deploy
like any other iOS app.
Opening **exported_xcode_project_name.xcodeproj** lets you build and deploy
like any other iOS app.
Active development considerations
---------------------------------
The above method creates an exported project that you can build for
release, but you have to re-export every time you make a change in Godot.
release, but you have to re-export every time you make a change in Godot.
While developing, you can speed this process up by linking your
Godot project files directly into your app.
Godot project files directly into your app.
In the following example:
* **exported_xcode_project_name** is the name of the exported iOS application (as above).
* **godot_project_to_export** is the name of the Godot project.
* **exported_xcode_project_name** is the name of the exported iOS application (as above).
* **godot_project_to_export** is the name of the Godot project.
Steps to link an Godot project folder to Xcode
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Start from an exported iOS project (follow the steps above).
2. In Finder, drag the Godot project folder into the Xcode file browser.
2. In Finder, drag the Godot project folder into the Xcode file browser.
.. image:: img/ios_export_add_dir.png
@@ -75,19 +75,19 @@ you will be able to continue to edit your Godot project in its current location.
.. image:: img/ios_export_file_ref.png
4. See the **godot_project_to_export** folder in the Xcode file browser.
4. See the **godot_project_to_export** folder in the Xcode file browser.
5. Delete **exported_xcode_project_name.pck** from the Xcode project.
.. image:: img/ios_export_delete_pck.png
6. Open **exported_xcode_project_name-Info.plist** and add a string property named
**godot_path** (this is the real key name) with a value **godot_project_to_export**
**godot_path** (this is the real key name) with a value **godot_project_to_export**
(this is the name of your project)
.. image:: img/ios_export_set_path.png
That's it! You can now edit your project in the Godot editor and build it
in Xcode when you want to run it on a device.
in Xcode when you want to run it on a device.
Services for iOS
----------------

View File

@@ -28,7 +28,7 @@ As described in `UWP documentation <https://docs.microsoft.com/en-us/windows/uwp
- available memory is 5GB
- 4 exclusive CPU cores and 2 shared CPU cores
- exclusive access to GPU power (100%)
- Exceeding these memory limitations will cause allocation failures and the application will crash.
Creating a signing certificate

View File

@@ -5,7 +5,7 @@ Export
:maxdepth: 1
:name: toc-learn-workflow-export
exporting_projects
feature_tags
exporting_for_pc

View File

@@ -6,7 +6,7 @@
Introduction
------------
This tutorial explains how the 2D lighting works in the
This tutorial explains how the 2D lighting works in the
`lights and shadows <https://github.com/godotengine/godot-demo-projects/tree/master/2d/lights_and_shadows>`_ demo project.
It begins with a brief description of the resources used final demo and then describes how
to make a scene like the demo step by step.
@@ -14,31 +14,31 @@ to make a scene like the demo step by step.
.. image:: img/light_shadow_main.png
All the resources for this tutorial can be found in the `official demo repository <https://github.com/godotengine/godot-demo-projects>`_
on github. I suggest you download it before starting. Alternatively,
it can be downloaded from the Project Manager. Launch Godot and in the top
on github. I suggest you download it before starting. Alternatively,
it can be downloaded from the Project Manager. Launch Godot and in the top
bar select "Templates" and search for "2D Lights and Shadows Demo".
Setup
-----
For this demo we use four textures: two for the lights, one for the shadow casters,
For this demo we use four textures: two for the lights, one for the shadow casters,
and one for the background. I've included links to them all here if you want to download them
separately from the demo.
The first is the background image (`bg.png <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/bg.png>`_)
The first is the background image (`bg.png <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/bg.png>`_)
used in the demo. You do not necessarily need a background, but we use one for the demo.
The second is a plain black image (`caster.png <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/caster.png>`_)
to use as our shadow caster object. For a top down game this could be a wall or any
The second is a plain black image (`caster.png <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/caster.png>`_)
to use as our shadow caster object. For a top down game this could be a wall or any
other object that casts a shadow.
Next is the light itself (`light.png <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/light.png>`_).
If you click the link you will notice how large it is. The image you use
for a light should cover the area you want your light to cover. This image is
Next is the light itself (`light.png <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/light.png>`_).
If you click the link you will notice how large it is. The image you use
for a light should cover the area you want your light to cover. This image is
1024x1024 pixels, so you should use it to cover 1024x1024 pixels in your game.
Lastly, we have the spotlight image (`spot.png <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/spot.png>`_).
The demo uses a blob to show where the light is and the larger light
Lastly, we have the spotlight image (`spot.png <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/spot.png>`_).
The demo uses a blob to show where the light is and the larger light
image to show the effect of the light upon the rest of the scene.
@@ -51,24 +51,24 @@ The demo uses four different nodes:
* :ref:`Light2D <class_Light2D>`
* :ref:`LightOccluder2D <class_LightOccluder2D>`
:ref:`CanvasModulate<class_CanvasModulate>` is used to darken the scene.
:ref:`CanvasModulate<class_CanvasModulate>` is used to darken the scene.
:ref:`Sprites <class_Sprite>` are used to display the textures for the light blobs, the
:ref:`Sprites <class_Sprite>` are used to display the textures for the light blobs, the
background, and for the shadow casters.
:ref:`Light2Ds <class_Light2D>` are used to light the scene. The way a light typically works
:ref:`Light2Ds <class_Light2D>` are used to light the scene. The way a light typically works
is by adding a selected texture over the rest of the scene to simulate lighting. But it can be
used in other ways, for example masking out parts of the scene.
:ref:`LightOccluder2Ds <class_LightOccluder2D>` are used to tell the shader which parts of
the scene cast shadows. The shadows appear only on areas covered by the :ref:`Light2D <class_Light2D>` and
:ref:`LightOccluder2Ds <class_LightOccluder2D>` are used to tell the shader which parts of
the scene cast shadows. The shadows appear only on areas covered by the :ref:`Light2D <class_Light2D>` and
their direction is based on the center of the :ref:`Light <class_Light2D>`.
Lights
------
:ref:`Lights <class_Light2D>` cover the entire extent of their respective Texture. They use additive
blending to add the color of their texture to the scene.
:ref:`Lights <class_Light2D>` cover the entire extent of their respective Texture. They use additive
blending to add the color of their texture to the scene.
.. image:: img/light_shadow_light.png
@@ -81,12 +81,12 @@ blending to add the color of their texture to the scene.
``Mix`` mixes the color of the light with the underlying scene. The resulting brightness is
halfway between the color of the light and the color underneath.
``Mask`` is used to mask out areas that are covered by the light. Masked out areas are hidden or revealed based on
``Mask`` is used to mask out areas that are covered by the light. Masked out areas are hidden or revealed based on
the color of the light.
For the demo the lights have two components, the :ref:`Light <class_Light2D>` itself (which
is the effect of the light), and a :ref:`Sprite <class_Sprite>` blob which is an image showing the
location of the light source. A child :ref:`Sprite <class_Sprite>` is not necessary to make a
For the demo the lights have two components, the :ref:`Light <class_Light2D>` itself (which
is the effect of the light), and a :ref:`Sprite <class_Sprite>` blob which is an image showing the
location of the light source. A child :ref:`Sprite <class_Sprite>` is not necessary to make a
:ref:`Light <class_Light2D>` work.
.. image:: img/light_shadow_light_blob.png
@@ -96,82 +96,82 @@ Shadows
Shadows are made by intersecting a :ref:`Light <class_Light2D>` with a :ref:`LightOccluder2D <class_LightOccluder2D>`.
By default shadows are turned off. To turn them on click on the :ref:`Light <class_Light2D>`
By default shadows are turned off. To turn them on click on the :ref:`Light <class_Light2D>`
and under the Shadows section check ``Enabled``.
In the demo we are using a :ref:`Sprite <class_Sprite>` with a Texture on it to make the "Shadow Casters",
but in reality all you need is a couple of :ref:`LightOccluder2Ds <class_LightOccluder2D>`. By itself
the :ref:`LightOccluder2D <class_LightOccluder2D>` looks like a dark spot and in this demo the :ref:`Sprite <class_Sprite>` is
In the demo we are using a :ref:`Sprite <class_Sprite>` with a Texture on it to make the "Shadow Casters",
but in reality all you need is a couple of :ref:`LightOccluder2Ds <class_LightOccluder2D>`. By itself
the :ref:`LightOccluder2D <class_LightOccluder2D>` looks like a dark spot and in this demo the :ref:`Sprite <class_Sprite>` is
just a black square.
Step by step
------------
Now that we have covered the basics of the nodes being used, we can now walk step by step through
Now that we have covered the basics of the nodes being used, we can now walk step by step through
the process of making a scene like the one found in the demo.
First add a :ref:`Sprite <class_Sprite>` and set its texture to the `background image <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/bg.png>`_. For your game this can be any
background you choose. For this style of shadow it is most likely to be a floor texture.
First add a :ref:`Sprite <class_Sprite>` and set its texture to the `background image <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/bg.png>`_. For your game this can be any
background you choose. For this style of shadow it is most likely to be a floor texture.
.. image:: img/light_shadow_background.png
Next create three :ref:`Light2D's <class_Light2D>` and set their textures to the `light image <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/light.png>`_. You can alter their
color in the top section. By default shadows are turned off and the ``mode`` is set to ``add``. This
Next create three :ref:`Light2D's <class_Light2D>` and set their textures to the `light image <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/light.png>`_. You can alter their
color in the top section. By default shadows are turned off and the ``mode`` is set to ``add``. This
means that each light adds its own color to whatever is underneath.
.. image:: img/light_shadow_all_lights_no_blob.png
Next add a child :ref:`Sprite <class_Sprite>` to each of the :ref:`Light <class_Light2D>` nodes, and set
the :ref:`Sprite's <class_Sprite>` texture to the `blob image <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/spot.png>`_. Each of these
should stay centered on the :ref:`Light <class_Light2D>` node. The blob is the image of the light
itself while the :ref:`Light <class_Light2D>` shows the effect that the light has on the scene. The
:ref:`LightOccluder2D's <class_LightOccluder2D>` will treat the position of the light as the center of the :ref:`Light <class_Light2D>`
Next add a child :ref:`Sprite <class_Sprite>` to each of the :ref:`Light <class_Light2D>` nodes, and set
the :ref:`Sprite's <class_Sprite>` texture to the `blob image <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/spot.png>`_. Each of these
should stay centered on the :ref:`Light <class_Light2D>` node. The blob is the image of the light
itself while the :ref:`Light <class_Light2D>` shows the effect that the light has on the scene. The
:ref:`LightOccluder2D's <class_LightOccluder2D>` will treat the position of the light as the center of the :ref:`Light <class_Light2D>`
node which is why we want the blob to be centered on its parent :ref:`Light <class_Light2D>`.
.. image:: img/light_shadow_all_lights.png
.. note:: At the time of writing, 3.0 is the stable release version. The 3.1 development branch contains
many changes to the animation system, so the animations in the demo will not be covered here.
.. note:: At the time of writing, 3.0 is the stable release version. The 3.1 development branch contains
many changes to the animation system, so the animations in the demo will not be covered here.
See :ref:Introduction to 2D Animation. <doc_introduction_2d> for more information.
Right now the scene should look too bright. This is because all three lights are adding color to the scene.
Right now the scene should look too bright. This is because all three lights are adding color to the scene.
This is why the demo uses a :ref:`CanvasModulate <class_CanvasModulate>` in the scene. The
:ref:`CanvasModulate <class_CanvasModulate>` multiples the entire viewport by a specific color.
Add a :ref:`CanvasModulate <class_CanvasModulate>` to the scene and set its color to ``rgb(70, 70, 70)``.
Add a :ref:`CanvasModulate <class_CanvasModulate>` to the scene and set its color to ``rgb(70, 70, 70)``.
This will make the scene sufficiently dark to see the effects of the lights distinctly.
.. image:: img/light_shadow_ambient.png
Now we add the shadow casters.
The demo uses a :ref:`Node <class_Node2D>` named "casters" to organize the shadow casters. Add a
:ref:`Node2D <class_Node2D>` to the scene. It will be used to group all the shadow casters together.
The demo uses a :ref:`Node <class_Node2D>` named "casters" to organize the shadow casters. Add a
:ref:`Node2D <class_Node2D>` to the scene. It will be used to group all the shadow casters together.
This way we can show and hide them all at the same time.
Each shadow caster is made of a :ref:`Sprite <class_Sprite>`, with a :ref:`LightOccluder2D <class_LightOccluder2D>`
child. For the demo the :ref:`Sprite <class_Sprite>` has a texture
set to the `caster image <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/caster.png>`_ and nothing else. The child :ref:`LightOccluder2D <class_LightOccluder2D>` is where all the magic happens. In a
game the :ref:`Sprite <class_Sprite>` could be more than a black box; it could be an image of whatever object is casting
Each shadow caster is made of a :ref:`Sprite <class_Sprite>`, with a :ref:`LightOccluder2D <class_LightOccluder2D>`
child. For the demo the :ref:`Sprite <class_Sprite>` has a texture
set to the `caster image <https://raw.githubusercontent.com/godotengine/godot-demo-projects/master/2d/lights_and_shadows/caster.png>`_ and nothing else. The child :ref:`LightOccluder2D <class_LightOccluder2D>` is where all the magic happens. In a
game the :ref:`Sprite <class_Sprite>` could be more than a black box; it could be an image of whatever object is casting
the shadow: a wall, a magical chest, or anything else.
.. image:: img/light_shadow_sprites.png
:ref:`LightOccluder2Ds <class_LightOccluder2D>` tell the game what shape the occluder has. They hold
an :ref:`OccluderPolygon2D <class_OccluderPolygon2D>` which is a container
for a polygon and some other information. For this demo, since our wall is a square, we
:ref:`LightOccluder2Ds <class_LightOccluder2D>` tell the game what shape the occluder has. They hold
an :ref:`OccluderPolygon2D <class_OccluderPolygon2D>` which is a container
for a polygon and some other information. For this demo, since our wall is a square, we
set ``Polygon`` to a square. The other default settings are fine.
The first setting, ``Closed`` can be either ``on`` or ``off``. A closed polygon occludes light
The first setting, ``Closed`` can be either ``on`` or ``off``. A closed polygon occludes light
coming from all directions. An open polygon only occludes light from one direction
``Cull Mode`` lets you select which direction gets culled. The default is ``Disabled``, meaning the occluder
will cast a shadow no matter which side the light is on. The other two settings ``Clockwise`` and
``Counter-Clockwise`` refer to the winding order of the vertices of the polygon. The winding order
is used to determine which side of the line is inside the polygon. Only outward facing lines cast shadows.
``Cull Mode`` lets you select which direction gets culled. The default is ``Disabled``, meaning the occluder
will cast a shadow no matter which side the light is on. The other two settings ``Clockwise`` and
``Counter-Clockwise`` refer to the winding order of the vertices of the polygon. The winding order
is used to determine which side of the line is inside the polygon. Only outward facing lines cast shadows.
To illustrate the difference, here is an image of a :ref:`LightOccluder2D <class_LightOccluder2D>` with ``Closed``
set to ``off`` in the corresponding :ref:`OccluderPolygon2D <class_OccluderPolygon2D>` so that the
set to ``off`` in the corresponding :ref:`OccluderPolygon2D <class_OccluderPolygon2D>` so that the
lines of the polygon can be seen:
.. image:: img/light_shadow_cull_disabled.png
@@ -188,18 +188,18 @@ lines of the polygon can be seen:
If ``Closed`` was set to ``on`` there would be an additional vertical line on the
left which would cast a shadow as well.
When you have added the :ref:`LightOccluder2Ds <class_LightOccluder2D>` the shadows still won't
appear. You need to go back into the :ref:`Light2Ds <class_Light2D>` and under the Shadow
When you have added the :ref:`LightOccluder2Ds <class_LightOccluder2D>` the shadows still won't
appear. You need to go back into the :ref:`Light2Ds <class_Light2D>` and under the Shadow
section set ``Enable`` to ``on``. This turns on shadows with hard edges like in the image below.
.. image:: img/light_shadow_filter0_pcf0.png
To give the shadows that nice soft edge look we set the variables ``filter``, ``filter smooth``, and
``gradient length``. Godot supports `Percentage Closer Filtering <https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch11.html>`_
(PCF) which takes multiple samples of the shadow map around a pixel and blurs them to create
a smooth shadow effect. The higher the number of samples the smoother the shadow will
look but the slower it will run. That is why Godot provides 3-13 samples by default and allows you to choose.
The demo uses PCF7.
To give the shadows that nice soft edge look we set the variables ``filter``, ``filter smooth``, and
``gradient length``. Godot supports `Percentage Closer Filtering <https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch11.html>`_
(PCF) which takes multiple samples of the shadow map around a pixel and blurs them to create
a smooth shadow effect. The higher the number of samples the smoother the shadow will
look but the slower it will run. That is why Godot provides 3-13 samples by default and allows you to choose.
The demo uses PCF7.
.. image:: img/light_shadow_normal.png
@@ -208,27 +208,27 @@ The demo uses PCF7.
.. image:: img/light_shadow_pcf13.png
.. note:: ``filter`` is set to ``PCF13``. Notice how the shadow becomes wider, this is because the
.. note:: ``filter`` is set to ``PCF13``. Notice how the shadow becomes wider, this is because the
distance between samples is based on the variable ``filter smooth``.
In order to make use of filtering you need to set the ``filter smooth`` variable.
This dictates how far apart the samples are. If you want the soft area to extend quite far you can increase
the size of ``filter smooth``. However with low samples and a large filter smooth you can see lines
In order to make use of filtering you need to set the ``filter smooth`` variable.
This dictates how far apart the samples are. If you want the soft area to extend quite far you can increase
the size of ``filter smooth``. However with low samples and a large filter smooth you can see lines
forming between the samples.
.. image:: img/light_shadow_filter30.png
.. note:: ``filter smooth` is set to ``30``.
The different :ref:`Light <class_Light2D>` nodes in the demo use different values for filter smooth.
The different :ref:`Light <class_Light2D>` nodes in the demo use different values for filter smooth.
Play around with it and see what you like.
.. image:: img/light_shadow_filter0.png
.. note:: ``filter smooth`` is set to ``0``.
Lastly there is the variable ``gradient length``. For some smooth shadows it is preferable to not have the
shadow start immediately on the object as this produces a hard edge. The gradient length variable creates
Lastly there is the variable ``gradient length``. For some smooth shadows it is preferable to not have the
shadow start immediately on the object as this produces a hard edge. The gradient length variable creates
a smooth gradient to begin the shadow to reduce the effect of the hard edge.
.. image:: img/light_shadow_grad0.png
@@ -240,5 +240,5 @@ a smooth gradient to begin the shadow to reduce the effect of the hard edge.
.. note:: ``gradient length`` is set to ``10``.
You will need to play around with settings a bit to find ones that suit your project. There is no right solution
for everyone, which is why Godot provides so much flexibility. Just keep in mind that the higher ``filter``
for everyone, which is why Godot provides so much flexibility. Just keep in mind that the higher ``filter``
set the more expensive the shadows will be.

View File

@@ -123,7 +123,7 @@ while up/down moves it forward or backward in whatever direction it's facing.
.. tabs::
.. code-tab:: gdscript GDScript
extends KinematicBody2D
export (int) var speed = 200
@@ -148,7 +148,7 @@ while up/down moves it forward or backward in whatever direction it's facing.
get_input()
rotation += rotation_dir * rotation_speed * delta
move_and_slide(velocity)
.. code-tab:: csharp
using Godot;
@@ -192,7 +192,7 @@ while up/down moves it forward or backward in whatever direction it's facing.
MoveAndSlide(velocity);
}
}
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.
The rotation is applied directly to the body's ``rotation`` property.
@@ -213,7 +213,7 @@ is set by the mouse position instead of the keyboard. The character will always
.. tabs::
.. code-tab:: gdscript GDScript
extends KinematicBody2D
export (int) var speed = 200
@@ -271,7 +271,7 @@ could get the same effect by setting the angle like this:
.. tabs::
.. code-tab:: gdscript GDScript
rotation = get_global_mouse_position().angle_to_point(position)
.. code-tab:: csharp
@@ -289,7 +289,7 @@ on the screen will cause the player to move to the target location.
.. tabs::
.. code-tab:: gdscript GDScript
extends KinematicBody2D
export (int) var speed = 200
@@ -306,7 +306,7 @@ on the screen will cause the player to move to the target location.
# rotation = velocity.angle()
if (target - position).length() > 5:
move_and_slide(velocity)
.. code-tab:: csharp
using Godot;
@@ -337,19 +337,19 @@ on the screen will cause the player to move to the target location.
}
}
}
Note the ``length()`` check we make prior to movement. Without this test,
the body would "jitter" upon reaching the target position, as it moves
slightly past the position and tries to move back, only to move too far and
repeat.
repeat.
Uncommenting the ``rotation`` line will also turn the body to point in its
direction of motion if you prefer.
.. tip:: This technique can also be used as the basis of a "following" character.
The ``target`` position can be that of any object you want to move to.
Summary
-------

View File

@@ -12,7 +12,7 @@ In this part we're going to add grenades to the player, give the player the abil
.. note:: You are assumed to have finished :ref:`doc_fps_tutorial_part_four` before moving on to this part of the tutorial.
The finished project from :ref:`doc_fps_tutorial_part_four` will be the starting project for part 5
Let's get started!
Adding grenades
@@ -34,7 +34,7 @@ coordinates instead of local coordinates, so we have ``Local Coords`` unchecked
Let's write the code needed for the grenade. Select ``Grenade`` and make a new script called ``Grenade.gd``. Add the following:
::
extends RigidBody
const GRENADE_DAMAGE = 60
@@ -55,35 +55,35 @@ Let's write the code needed for the grenade. Select ``Grenade`` and make a new s
grenade_mesh = $Grenade
blast_area = $Blast_Area
explosion_particles = $Explosion
explosion_particles.emitting = false
explosion_particles.one_shot = true
func _process(delta):
if grenade_timer < GRENADE_TIME:
grenade_timer += delta
return
else:
if explosion_wait_timer <= 0:
explosion_particles.emitting = true
grenade_mesh.visible = false
rigid_shape.disabled = true
mode = RigidBody.MODE_STATIC
var bodies = blast_area.get_overlapping_bodies()
for body in bodies:
if body.has_method("bullet_hit"):
body.bullet_hit(GRENADE_DAMAGE, body.global_transform.looking_at(global_transform.origin, Vector3(0,1,0)) )
# This would be the perfect place to play a sound!
if explosion_wait_timer < EXPLOSION_WAIT_TIME:
explosion_wait_timer += delta
if explosion_wait_timer >= EXPLOSION_WAIT_TIME:
queue_free()
@@ -93,7 +93,7 @@ Let's go over what's happening, starting with the class variables:
* ``GRENADE_TIME``: The amount of time the grenade takes (in seconds) to explode once it's created/thrown.
* ``grenade_timer``: A variable for tracking how long the grenade has been created/thrown.
* ``EXPLOSION_WAIT_TIME``: The amount of time needed (in seconds) to wait before we destroy the grenade scene after the explosion
* ``explosion_wait_timer``: A variable for tracking how much time has passed since the grenade exploded.
* ``explosion_wait_timer``: A variable for tracking how much time has passed since the grenade exploded.
* ``rigid_shape``: The :ref:`CollisionShape <class_CollisionShape>` for the grenade's :ref:`RigidBody <class_RigidBody>`.
* ``grenade_mesh``: The :ref:`MeshInstance <class_MeshInstance>` for the grenade.
* ``blast_area``: The blast :ref:`Area <class_Area>` used to damage things when the grenade explodes.
@@ -154,7 +154,7 @@ the environment and needs to stick to something.
Select ``Sticky_Grenade`` and make a new script called ``Sticky_Grenade.gd``. Add the following:
::
extends RigidBody
const GRENADE_DAMAGE = 40
@@ -180,67 +180,67 @@ Select ``Sticky_Grenade`` and make a new script called ``Sticky_Grenade.gd``. Ad
grenade_mesh = $Sticky_Grenade
blast_area = $Blast_Area
explosion_particles = $Explosion
explosion_particles.emitting = false
explosion_particles.one_shot = true
$Sticky_Area.connect("body_entered", self, "collided_with_body")
func collided_with_body(body):
if body == self:
return
if player_body != null:
if body == player_body:
return
if attached == false:
attached = true
attach_point = Spatial.new()
body.add_child(attach_point)
attach_point.global_transform.origin = global_transform.origin
rigid_shape.disabled = true
mode = RigidBody.MODE_STATIC
func _process(delta):
if attached == true:
if attach_point != null:
global_transform.origin = attach_point.global_transform.origin
if grenade_timer < GRENADE_TIME:
grenade_timer += delta
return
else:
if explosion_wait_timer <= 0:
explosion_particles.emitting = true
grenade_mesh.visible = false
rigid_shape.disabled = true
mode = RigidBody.MODE_STATIC
var bodies = blast_area.get_overlapping_bodies()
for body in bodies:
if body.has_method("bullet_hit"):
body.bullet_hit(GRENADE_DAMAGE, body.global_transform.looking_at(global_transform.origin, Vector3(0,1,0)) )
# This would be the perfect place to play a sound!
if explosion_wait_timer < EXPLOSION_WAIT_TIME:
explosion_wait_timer += delta
if explosion_wait_timer >= EXPLOSION_WAIT_TIME:
if attach_point != null:
attach_point.queue_free()
queue_free()
The code above is almost identical to the code for ``Grenade.gd``, so let's just go over what's changed.
First, we have a few more class variables:
@@ -309,7 +309,7 @@ the rotation of ``Grenade_Toss_Pos``, you can change the angle the grenades are
Okay, now let's start making the grenades work with the player. Add the following class variables to ``Player.gd``:
::
var grenade_amounts = {"Grenade":2, "Sticky Grenade":2}
var current_grenade = "Grenade"
var grenade_scene = preload("res://Grenade.tscn")
@@ -321,7 +321,7 @@ Okay, now let's start making the grenades work with the player. Add the followin
* ``grenade_scene``: The grenade scene we worked on earlier.
* ``sticky_grenade_scene``: The sticky grenade scene we worked on earlier.
* ``GRENADE_THROW_FORCE``: The force at which the player will throw the grenades at.
Most of these variables are similar to how we have our weapons set up.
.. tip:: While it's possible to make a more modular grenade system, I found it was not worth the additional complexity for just two grenades.
@@ -332,7 +332,7 @@ ______
Now we need to add some code in ``_process_input`` Add the following to ``_process_input``:
::
# ----------------------------------
# Changing and throwing grenades
@@ -345,7 +345,7 @@ Now we need to add some code in ``_process_input`` Add the following to ``_proce
if Input.is_action_just_pressed("fire_grenade"):
if grenade_amounts[current_grenade] > 0:
grenade_amounts[current_grenade] -= 1
var grenade_clone
if current_grenade == "Grenade":
grenade_clone = grenade_scene.instance()
@@ -353,12 +353,12 @@ Now we need to add some code in ``_process_input`` Add the following to ``_proce
grenade_clone = sticky_grenade_scene.instance()
# Sticky grenades will stick to the player if we do not pass ourselves
grenade_clone.player_body = self
get_tree().root.add_child(grenade_clone)
grenade_clone.global_transform = $Rotation_Helper/Grenade_Toss_Pos.global_transform
grenade_clone.apply_impulse(Vector3(0,0,0), grenade_clone.global_transform.basis.z * GRENADE_THROW_FORCE)
# ----------------------------------
Let's go over what's happening here.
First, we check to see if the ``change_grenade`` action has just been pressed. If it has, we then check to see which grenade the player is
@@ -383,7 +383,7 @@ We still need a way to show the player how many grenades are left, and we should
First, let's change some of the code in ``Player.gd`` to show how many grenades are left. Change ``process_UI`` to the following:
::
func process_UI(delta):
if current_weapon_name == "UNARMED" or current_weapon_name == "KNIFE":
# First line: Health, second line: Grenades
@@ -401,7 +401,7 @@ Now we'll show how many grenades the player has left in the UI.
While we're still in ``Player.gd``, let's add a function to add grenades to the player. Add the following function to ``Player.gd``:
::
func add_grenade(additional_grenade):
grenade_amounts[current_grenade] += additional_grenade
grenade_amounts[current_grenade] = clamp(grenade_amounts[current_grenade], 0, 4)
@@ -410,7 +410,7 @@ Now we can add a grenade using ``add_grenade``, and it will automatically be cla
.. tip:: You can change the ``4`` to a constant if you want. You'd need to make a new global constant, something like ``MAX_GRENADES``, and
then change the clamp from ``clamp(grenade_amounts[current_grenade], 0, 4)`` to ``clamp(grenade_amounts[current_grenade], 0, MAX_GRENADES)``
If you do not want to limit how many grenades the player can carry, remove the line that clamps the grenades altogether!
Now we have a function to add grenades, let's open up ``AmmoPickup.gd`` and use it!
@@ -418,13 +418,13 @@ Now we have a function to add grenades, let's open up ``AmmoPickup.gd`` and use
Open up ``AmmoPickup.gd`` and go to the ``trigger_body_entered`` function. Change it to the following:
::
func trigger_body_entered(body):
if body.has_method("add_ammo"):
body.add_ammo(AMMO_AMOUNTS[kit_size])
respawn_timer = RESPAWN_TIME
kit_size_change_values(kit_size, false)
if body.has_method("add_grenade"):
body.add_grenade(GRENADE_AMOUNTS[kit_size])
respawn_timer = RESPAWN_TIME
@@ -436,9 +436,9 @@ You may have noticed we are using a new constant we have not defined yet, ``GREN
to ``AmmoPickup.gd`` with the other class variables:
::
const GRENADE_AMOUNTS = [2, 0]
* ``GRENADE_AMOUNTS``: The amount of grenades each pick up contains.
Notice how the second element in ``GRENADE_AMOUNTS`` is ``0``. This is so the small ammo pick up does not give the player
@@ -457,7 +457,7 @@ Next let's give the player the ability to pick up and throw :ref:`RigidBody <cla
Open up ``Player.gd`` and add the following class variables:
::
var grabbed_object = null
const OBJECT_THROW_FORCE = 120
const OBJECT_GRAB_DISTANCE = 7
@@ -471,35 +471,35 @@ Open up ``Player.gd`` and add the following class variables:
With that done, all we need to do is add some code to ``process_input``:
::
# ----------------------------------
# Grabbing and throwing objects
if Input.is_action_just_pressed("fire") and current_weapon_name == "UNARMED":
if grabbed_object == null:
var state = get_world().direct_space_state
var center_position = get_viewport().size/2
var ray_from = camera.project_ray_origin(center_position)
var ray_to = ray_from + camera.project_ray_normal(center_position) * OBJECT_GRAB_RAY_DISTANCE
var ray_result = state.intersect_ray(ray_from, ray_to, [self, $Rotation_Helper/Gun_Fire_Points/Knife_Point/Area])
if ray_result != null:
if ray_result["collider"] is RigidBody:
grabbed_object = ray_result["collider"]
grabbed_object.mode = RigidBody.MODE_STATIC
grabbed_object.collision_layer = 0
grabbed_object.collision_mask = 0
else:
grabbed_object.mode = RigidBody.MODE_RIGID
grabbed_object.apply_impulse(Vector3(0,0,0), -camera.global_transform.basis.z.normalized() * OBJECT_THROW_FORCE)
grabbed_object.collision_layer = 1
grabbed_object.collision_mask = 1
grabbed_object = null
if grabbed_object != null:
@@ -545,10 +545,10 @@ We first set the :ref:`RigidBody <class_RigidBody>` we are holding mode to ``MOD
.. note:: This is making a rather large assumption that all the rigid bodies will be using ``MODE_RIGID``. While that is the case for this tutorial series,
that may not be the case in other projects.
If you have :ref:`RigidBody <class_RigidBody>`'s with different modes, you may need to store the mode of the :ref:`RigidBody <class_RigidBody>` you
have picked up into a class variable so you can change it back to the mode it was in before you picked it up.
Then we apply an impulse to send it flying forward. We send it flying in the direction the camera is facing, using the force we set in the ``OBJECT_THROW_FORCE`` variable.
We then set the grabbed :ref:`RigidBody <class_RigidBody>`'s collision layer and mask to ``1``, so it can collide with anything on layer ``1`` again.
@@ -573,7 +573,7 @@ Before we test this, we need to change something in ``_physics_process``. While
want the player to be able to change weapons or reload, so change ``_physics_process`` to the following:
::
func _physics_process(delta):
process_input(delta)
process_view_input(delta)
@@ -587,7 +587,7 @@ want the player to be able to change weapons or reload, so change ``_physics_pro
process_UI(delta)
Now the player cannot change weapons or reload while holding an object.
Now you can grab and throw RigidBody nodes while you're in the ``UNARMED`` state! Go give it a try!
Adding a turret
@@ -615,7 +615,7 @@ Now that we've looked at how the scene is set up, lets start writing the code fo
Add the following to ``Turret.gd``:
::
extends Spatial
export (bool) var use_raycast = false
@@ -656,46 +656,46 @@ Add the following to ``Turret.gd``:
var bullet_scene = preload("Bullet_Scene.tscn")
func _ready():
$Vision_Area.connect("body_entered", self, "body_entered_vision")
$Vision_Area.connect("body_exited", self, "body_exited_vision")
node_turret_head = $Head
node_raycast = $Head/Ray_Cast
node_flash_one = $Head/Flash
node_flash_two = $Head/Flash_2
node_raycast.add_exception(self)
node_raycast.add_exception($Base/Static_Body)
node_raycast.add_exception($Head/Static_Body)
node_raycast.add_exception($Vision_Area)
node_flash_one.visible = false
node_flash_two.visible = false
smoke_particles = $Smoke
smoke_particles.emitting = false
turret_health = MAX_TURRET_HEALTH
func _physics_process(delta):
if is_active == true:
if flash_timer > 0:
flash_timer -= delta
if flash_timer <= 0:
node_flash_one.visible = false
node_flash_two.visible = false
if current_target != null:
node_turret_head.look_at(current_target.global_transform.origin + Vector3(0, PLAYER_HEIGHT, 0), Vector3(0, 1, 0))
if turret_health > 0:
if ammo_in_turret > 0:
if fire_timer > 0:
fire_timer -= delta
@@ -706,7 +706,7 @@ Add the following to ``Turret.gd``:
ammo_reload_timer -= delta
else:
ammo_in_turret = AMMO_IN_FULL_TURRET
if turret_health <= 0:
if destroyed_timer > 0:
destroyed_timer -= delta
@@ -716,40 +716,40 @@ Add the following to ``Turret.gd``:
func fire_bullet():
if use_raycast == true:
node_raycast.look_at(current_target.global_transform.origin + Vector3(0, PLAYER_HEIGHT, 0), Vector3(0,1,0))
node_raycast.force_raycast_update()
if node_raycast.is_colliding():
var body = node_raycast.get_collider()
if body.has_method("bullet_hit"):
body.bullet_hit(TURRET_DAMAGE_RAYCAST, node_raycast.get_collision_point())
ammo_in_turret -= 1
else:
var clone = bullet_scene.instance()
var scene_root = get_tree().root.get_children()[0]
scene_root.add_child(clone)
clone.global_transform = $Head/Barrel_End.global_transform
clone.scale = Vector3(8, 8, 8)
clone.BULLET_DAMAGE = TURRET_DAMAGE_BULLET
clone.BULLET_SPEED = 60
ammo_in_turret -= 1
node_flash_one.visible = true
node_flash_two.visible = true
flash_timer = FLASH_TIME
fire_timer = FIRE_TIME
if ammo_in_turret <= 0:
ammo_reload_timer = AMMO_RELOAD_TIME
func body_entered_vision(body):
if current_target == null:
@@ -763,7 +763,7 @@ Add the following to ``Turret.gd``:
if body == current_target:
current_target = null
is_active = false
flash_timer = 0
fire_timer = 0
node_flash_one.visible = false
@@ -772,7 +772,7 @@ Add the following to ``Turret.gd``:
func bullet_hit(damage, bullet_hit_pos):
turret_health -= damage
if turret_health <= 0:
smoke_particles.emitting = true
destroyed_timer = DESTROYED_TIME
@@ -916,7 +916,7 @@ select one of the :ref:`StaticBody <class_StaticBody>` nodes from either ``Base`
Add the following code to ``TurretBodies.gd``:
::
extends StaticBody
export (NodePath) var path_to_turret_root
@@ -927,7 +927,7 @@ Add the following code to ``TurretBodies.gd``:
func bullet_hit(damage, bullet_hit_pos):
if path_to_turret_root != null:
get_node(path_to_turret_root).bullet_hit(damage, bullet_hit_pos)
All this code does is call ``bullet_hit`` on whatever node ``path_to_turret_root`` leads to. Go back to the editor and assign the :ref:`NodePath <class_NodePath>`
to the ``Turret`` node.
@@ -941,7 +941,7 @@ The last thing we need to do is add a way for the player to be hurt. Since all o
Open ``Player.gd`` and add the following:
::
func bullet_hit(damage, bullet_hit_pos):
health -= damage

View File

@@ -41,14 +41,14 @@ Feel free to use whatever button layout you want. Make sure that the device sele
Once you are happy with the input, close the project settings and save.
______
______
Now let's open up ``Player.gd`` and add joypad input.
First, we need to define a few new class variables. Add the following class variables to ``Player.gd``:
::
# You may need to adjust depending on the sensitivity of your joypad
var JOYPAD_SENSITIVITY = 2
const JOYPAD_DEADZONE = 0.15
@@ -60,23 +60,23 @@ Let's go over what each of these do:
.. note:: Many joypads jitter around a certain point. To counter this, we ignore any movement in a
with a radius of JOYPAD_DEADZONE. If we did not ignore said movement, the camera would jitter.
Also, we are defining ``JOYPAD_SENSITIVITY`` as a variable instead of a constant because we'll later be changing it.
Now we are ready to start handling joypad input!
Now we are ready to start handling joypad input!
______
In ``process_input`` add the following code, just before ``input_movement_vector = input_movement_vector.normalized()``:
.. tabs::
.. code-tab:: gdscript Xbox Controller
# Add joypad input, if there is a joypad
if Input.get_connected_joypads().size() > 0:
var joypad_vec = Vector2(0, 0)
if OS.get_name() == "Windows":
joypad_vec = Vector2(Input.get_joy_axis(0, 0), -Input.get_joy_axis(0, 1))
elif OS.get_name() == "X11":
@@ -95,9 +95,9 @@ In ``process_input`` add the following code, just before ``input_movement_vector
# Add joypad input, if there is a joypad
if Input.get_connected_joypads().size() > 0:
var joypad_vec = Vector2(0, 0)
if OS.get_name() == "Windows" or OS.get_name() == "X11":
joypad_vec = Vector2(Input.get_joy_axis(0, 0), -Input.get_joy_axis(0, 1))
elif OS.get_name() == "OSX":
@@ -127,7 +127,7 @@ If it is, we set ``joypad_vec`` to an empty Vector2. If it is not, we use a scal
.. note:: You can find a great article explaining all about how to handle joypad/controller dead zones here:
http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html
We're using a translated version of the scaled radial dead zone code provided in that article.
The article is a great read, and I highly suggest giving it a look!
@@ -135,16 +135,16 @@ Finally, we add ``joypad_vec`` to ``input_movement_vector``.
.. tip:: Remember how we normalize ``input_movement_vector``? This is why! If we did not normalize ``input_movement_vector``, the player could
move faster if the player pushes in the same direction with both the keyboard and the joypad!
______
Make a new function called ``process_view_input`` and add the following:
.. tabs::
.. code-tab:: gdscript Xbox Controller
func process_view_input(delta):
if Input.get_mouse_mode() != Input.MOUSE_MODE_CAPTURED:
return
@@ -179,9 +179,9 @@ Make a new function called ``process_view_input`` and add the following:
# ----------------------------------
.. code-tab:: gdscript Playstation Controller
func process_view_input(delta):
if Input.get_mouse_mode() != Input.MOUSE_MODE_CAPTURED:
return
@@ -242,10 +242,10 @@ The last thing we need to do is add ``process_view_input`` to ``_physics_process
Once ``process_view_input`` is added to ``_physics_process``, you should be able to play using a joypad!
.. note:: I decided not to use the joypad triggers for firing because we'd then have to do some more axis managing, and because I prefer to use a shoulder buttons to fire.
If you want to use the triggers for firing, you will need to change how firing works in ``process_input``. You need to get the axis values for the triggers,
and check if it's over a certain value, say ``0.8`` for example. If it is, you add the same code as when the ``fire`` action was pressed.
Adding mouse scroll wheel input
-------------------------------
@@ -254,7 +254,7 @@ Let's add one more input related feature before we start working on the pick ups
Open up ``Player.gd`` and add the following class variables:
::
var mouse_scroll_value = 0
const MOUSE_SENSITIVITY_SCROLL_WHEEL = 0.08
@@ -268,16 +268,16 @@ ______
Now let's add the following to ``_input``:
::
if event is InputEventMouseButton and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
if event.button_index == BUTTON_WHEEL_UP or event.button_index == BUTTON_WHEEL_DOWN:
if event.button_index == BUTTON_WHEEL_UP:
mouse_scroll_value += MOUSE_SENSITIVITY_SCROLL_WHEEL
elif event.button_index == BUTTON_WHEEL_DOWN:
mouse_scroll_value -= MOUSE_SENSITIVITY_SCROLL_WHEEL
mouse_scroll_value = clamp(mouse_scroll_value, 0, WEAPON_NUMBER_TO_NAME.size()-1)
if changing_weapon == false:
if reloading_weapon == false:
var round_mouse_scroll_value = int(round(mouse_scroll_value))
@@ -286,7 +286,7 @@ Now let's add the following to ``_input``:
changing_weapon = true
mouse_scroll_value = round_mouse_scroll_value
Let's go over what's happening here:
First we check if the event is a ``InputEventMouseButton`` event and that the mouse mode is ``MOUSE_MODE_CAPTURED``.
@@ -315,9 +315,9 @@ ______
One more thing we need to change is in ``process_input``. In the code for changing weapons, add the following right after the line ``changing_weapon = true``:
::
mouse_scroll_value = weapon_change_number
Now the scroll value will be changed with the keyboard input. If we did not change this, the scroll value will be out of sync. If the scroll wheel is out of
sync, scrolling forwards or backwards would not transition to the next/last weapon, but rather the next/last weapon the scroll wheel changed to.
@@ -346,7 +346,7 @@ The last thing to note is how we have a :ref:`AnimationPlayer <class_AnimationPl
Select ``Health_Pickup`` and add a new script called ``Health_Pickup.gd``. Add the following:
::
extends Spatial
export (int, "full size", "small") var kit_size = 0 setget kit_size_change
@@ -360,11 +360,11 @@ Select ``Health_Pickup`` and add a new script called ``Health_Pickup.gd``. Add t
var is_ready = false
func _ready():
$Holder/Health_Pickup_Trigger.connect("body_entered", self, "trigger_body_entered")
is_ready = true
kit_size_change_values(0, false)
kit_size_change_values(1, false)
kit_size_change_values(kit_size, true)
@@ -373,7 +373,7 @@ Select ``Health_Pickup`` and add a new script called ``Health_Pickup.gd``. Add t
func _physics_process(delta):
if respawn_timer > 0:
respawn_timer -= delta
if respawn_timer <= 0:
kit_size_change_values(kit_size, true)
@@ -457,7 +457,7 @@ We get the collision shape for the node corresponding to ``size`` and disable it
.. note:: Why are we using ``!enable`` instead of ``enable``? This is so when we say we want to enable the node, we can pass in ``true``, but since
:ref:`CollisionShape <class_CollisionShape>` uses disabled instead of enabled, we need to flip it. By flipping it, we can enable the collision shape
and make the mesh visible when ``true`` is passed in.
We then get the correct :ref:`Spatial <class_Spatial>` node holding the mesh and set its visibility to ``enable``.
This function may be a little confusing, try to think of it like this: We're enabling/disabling the proper nodes for ``size`` using ``enabled``. This is so we cannot pick up
@@ -480,15 +480,15 @@ The last thing we need to do before the player can use this health pick up is ad
Open up ``Player.gd`` and add the following class variable:
::
const MAX_HEALTH = 150
* ``MAX_HEALTH``: The maximum amount of health a player can have.
Now we need to add the ``add_health`` function to the player. Add the following to ``Player.gd``:
::
func add_health(additional_health):
health += additional_health
health = clamp(health, 0, MAX_HEALTH)
@@ -515,35 +515,35 @@ for the difference in mesh sizes.
Select ``Ammo_Pickup`` and add a new script called ``Ammo_Pickup.gd``. Add the following:
::
extends Spatial
export (int, "full size", "small") var kit_size = 0 setget kit_size_change
# 0 = full size pickup, 1 = small pickup
const AMMO_AMOUNTS = [4, 1]
const RESPAWN_TIME = 20
var respawn_timer = 0
var is_ready = false
func _ready():
$Holder/Ammo_Pickup_Trigger.connect("body_entered", self, "trigger_body_entered")
is_ready = true
kit_size_change_values(0, false)
kit_size_change_values(1, false)
kit_size_change_values(kit_size, true)
func _physics_process(delta):
if respawn_timer > 0:
respawn_timer -= delta
if respawn_timer <= 0:
kit_size_change_values(kit_size, true)
@@ -552,7 +552,7 @@ Select ``Ammo_Pickup`` and add a new script called ``Ammo_Pickup.gd``. Add the f
if is_ready:
kit_size_change_values(kit_size, false)
kit_size = value
kit_size_change_values(kit_size, true)
else:
kit_size = value
@@ -588,7 +588,7 @@ _______
All we need to do for making the ammo pick ups work is to add a new function to the player. Open ``Player.gd`` and add the following function:
::
func add_ammo(additional_ammo):
if (current_weapon_name != "UNARMED"):
if (weapons[current_weapon_name].CAN_REFILL == true):
@@ -637,7 +637,7 @@ Alright, now switch back to ``Target.tscn``, select the ``Target`` :ref:`StaticB
Add the following code to ``Target.gd``:
::
extends StaticBody
const TARGET_HEALTH = 40
@@ -662,12 +662,12 @@ Add the following code to ``Target.gd``:
func _physics_process(delta):
if target_respawn_timer > 0:
target_respawn_timer -= delta
if target_respawn_timer <= 0:
for child in broken_target_holder.get_children():
child.queue_free()
target_collision_shape.disabled = false
visible = true
current_health = TARGET_HEALTH
@@ -675,20 +675,20 @@ Add the following code to ``Target.gd``:
func bullet_hit(damage, bullet_transform):
current_health -= damage
if current_health <= 0:
var clone = destroyed_target.instance()
broken_target_holder.add_child(clone)
for rigid in clone.get_children():
if rigid is RigidBody:
var center_in_rigid_space = broken_target_holder.global_transform.origin - rigid.global_transform.origin
var direction = (rigid.transform.origin - center_in_rigid_space).normalized()
# Apply the impulse with some additional force (I find 12 works nicely)
rigid.apply_impulse(center_in_rigid_space, direction * 12 * damage)
target_respawn_timer = TARGET_RESPAWN_TIME
target_collision_shape.disabled = true
visible = false

View File

@@ -127,7 +127,7 @@ the ``X`` axis.
.. note:: If we did not use ``Rotation_helper`` then we'd likely have cases where we are rotating
both the ``X`` and ``Y`` axes at the same time. This can lead to undesirable results, as we then
could rotate on all three axes in some cases.
See :ref:`using transforms <doc_using_transforms>` for more information
_________
@@ -148,34 +148,34 @@ Add the following code to ``Player.gd``:
const ACCEL = 4.5
var dir = Vector3()
const DEACCEL= 16
const MAX_SLOPE_ANGLE = 40
var camera
var rotation_helper
var MOUSE_SENSITIVITY = 0.05
func _ready():
camera = $Rotation_Helper/Camera
rotation_helper = $Rotation_Helper
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _physics_process(delta):
process_input(delta)
process_movement(delta)
func process_input(delta):
# ----------------------------------
# Walking
dir = Vector3()
var cam_xform = camera.get_global_transform()
var input_movement_vector = Vector2()
if Input.is_action_pressed("movement_forward"):
input_movement_vector.y += 1
if Input.is_action_pressed("movement_backward"):
@@ -184,20 +184,20 @@ Add the following code to ``Player.gd``:
input_movement_vector.x -= 1
if Input.is_action_pressed("movement_right"):
input_movement_vector.x += 1
input_movement_vector = input_movement_vector.normalized()
dir += -cam_xform.basis.z.normalized() * input_movement_vector.y
dir += cam_xform.basis.x.normalized() * input_movement_vector.x
# ----------------------------------
# ----------------------------------
# Jumping
if is_on_floor():
if Input.is_action_just_pressed("movement_jump"):
vel.y = JUMP_SPEED
# ----------------------------------
# ----------------------------------
# Capturing/Freeing the cursor
if Input.is_action_just_pressed("ui_cancel"):
@@ -206,35 +206,35 @@ Add the following code to ``Player.gd``:
else:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
# ----------------------------------
func process_movement(delta):
dir.y = 0
dir = dir.normalized()
vel.y += delta*GRAVITY
var hvel = vel
hvel.y = 0
var target = dir
target *= MAX_SPEED
var accel
if dir.dot(hvel) > 0:
accel = ACCEL
else:
accel = DEACCEL
hvel = hvel.linear_interpolate(target, accel*delta)
vel.x = hvel.x
vel.z = hvel.z
vel = move_and_slide(vel,Vector3(0,1,0), 0.05, 4, deg2rad(MAX_SLOPE_ANGLE))
func _input(event):
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
rotation_helper.rotate_x(deg2rad(event.relative.y * MOUSE_SENSITIVITY))
self.rotate_y(deg2rad(event.relative.x * MOUSE_SENSITIVITY * -1))
var camera_rot = rotation_helper.rotation_degrees
camera_rot.x = clamp(camera_rot.x, -70, 70)
rotation_helper.rotation_degrees = camera_rot
@@ -243,9 +243,9 @@ This is a lot of code, so let's break it down function by function:
.. tip:: While copy and pasting code is ill advised, as you can learn a lot from manually typing the code in, you can
copy and paste the code from this page directly into the script editor.
If you do this, all of the code copied will be using spaces instead of tabs.
To convert the spaces to tabs in the script editor, click the "edit" menu and select "Convert Indent To Tabs".
This will convert all of the spaces into tabs. You can select "Convert Indent To Spaces" to convert t back into spaces.
@@ -275,7 +275,7 @@ increasing ``JUMP_SPEED`` you can get a more 'floaty' feeling character.
Feel free to experiment!
.. note:: You may have noticed that ``MOUSE_SENSITIVITY`` is written in all caps like the other constants, but ``MOUSE_SENSITIVITY`` is not a constant.
The reason behind this is we want to treat it like a constant variable (a variable that cannot change) throughout our script, but we want to be
able to change the value later when we add customizable settings. So, in an effort to remind ourselves to treat it like a constant, it's named in all caps.
@@ -543,7 +543,7 @@ First we need a few more class variables in our player script:
const MAX_SPRINT_SPEED = 30
const SPRINT_ACCEL = 18
var is_sprinting = false
var flashlight
All of the sprinting variables work exactly the same as the non sprinting variables with
@@ -555,7 +555,7 @@ we will be using to hold the player's flash light node.
Now we need to add a few lines of code, starting in ``_ready``. Add the following to ``_ready``:
::
flashlight = $Rotation_Helper/Flashlight
This gets the flash light node and assigns it to the ``flashlight`` variable.
@@ -565,7 +565,7 @@ _________
Now we need to change some of the code in ``process_input``. Add the following somewhere in ``process_input``:
::
# ----------------------------------
# Sprinting
if Input.is_action_pressed("movement_sprint"):
@@ -573,7 +573,7 @@ Now we need to change some of the code in ``process_input``. Add the following s
else:
is_sprinting = false
# ----------------------------------
# ----------------------------------
# Turning the flashlight on/off
if Input.is_action_just_pressed("flashlight"):
@@ -597,26 +597,26 @@ _________
Now we need to change a couple things in ``process_movement``. First, replace ``target *= MAX_SPEED`` with the following:
::
if is_sprinting:
target *= MAX_SPRINT_SPEED
else:
target *= MAX_SPEED
Now instead of always multiplying ``target`` by ``MAX_SPEED``, we first check to see if the player is sprinting or not.
If the player is sprinting, we instead multiply ``target`` by ``MAX_SPRINT_SPEED``.
If the player is sprinting, we instead multiply ``target`` by ``MAX_SPRINT_SPEED``.
Now all that's left is changing the acceleration when sprinting. Change ``accel = ACCEL`` to the following:
::
if is_sprinting:
accel = SPRINT_ACCEL
else:
accel = ACCEL
Now when the player is sprinting we'll use ``SPRINT_ACCEL`` instead of ``ACCEL``, which will accelerate the player faster.
Now when the player is sprinting we'll use ``SPRINT_ACCEL`` instead of ``ACCEL``, which will accelerate the player faster.
_________

View File

@@ -15,7 +15,7 @@ This is the last part of the FPS tutorial, by the end of this you will have a so
.. note:: You are assumed to have finished :ref:`doc_fps_tutorial_part_five` before moving on to this part of the tutorial.
The finished project from :ref:`doc_fps_tutorial_part_four` will be the starting project for part 6
Let's get started!
Adding the main menu
@@ -50,24 +50,24 @@ Select ``Main_Menu`` (the root node) and create a new script called ``Main_Menu.
start_menu = $Start_Menu
level_select_menu = $Level_Select_Menu
options_menu = $Options_Menu
$Start_Menu/Button_Start.connect("pressed", self, "start_menu_button_pressed", ["start"])
$Start_Menu/Button_Open_Godot.connect("pressed", self, "start_menu_button_pressed", ["open_godot"])
$Start_Menu/Button_Options.connect("pressed", self, "start_menu_button_pressed", ["options"])
$Start_Menu/Button_Quit.connect("pressed", self, "start_menu_button_pressed", ["quit"])
$Level_Select_Menu/Button_Back.connect("pressed", self, "level_select_menu_button_pressed", ["back"])
$Level_Select_Menu/Button_Level_Testing_Area.connect("pressed", self, "level_select_menu_button_pressed", ["testing_scene"])
$Level_Select_Menu/Button_Level_Space.connect("pressed", self, "level_select_menu_button_pressed", ["space_level"])
$Level_Select_Menu/Button_Level_Ruins.connect("pressed", self, "level_select_menu_button_pressed", ["ruins_level"])
$Options_Menu/Button_Back.connect("pressed", self, "options_menu_button_pressed", ["back"])
$Options_Menu/Button_Fullscreen.connect("pressed", self, "options_menu_button_pressed", ["fullscreen"])
$Options_Menu/Check_Button_VSync.connect("pressed", self, "options_menu_button_pressed", ["vsync"])
$Options_Menu/Check_Button_Debug.connect("pressed", self, "options_menu_button_pressed", ["debug"])
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
var globals = get_node("/root/Globals")
$Options_Menu/HSlider_Mouse_Sensitivity.value = globals.mouse_sensitivity
$Options_Menu/HSlider_Joypad_Sensitivity.value = globals.joypad_sensitivity
@@ -162,7 +162,7 @@ In ``level_select_menu_button_pressed``, we check to see which button is pressed
If the ``back`` button has been pressed, we change the currently visible panels to return to the main menu.
If one of the scene changing buttons are pressed, we fist call ``set_mouse_and_joypad_sensitivity`` so the singleton (``Globals.gd``) has the values from the :ref:`HSlider
If one of the scene changing buttons are pressed, we fist call ``set_mouse_and_joypad_sensitivity`` so the singleton (``Globals.gd``) has the values from the :ref:`HSlider
<class_HSlider>` nodes.
Then we tell the singleton to change nodes using its ``load_new_scene`` function, passing in the file path of the scene the player has selected.
@@ -303,12 +303,12 @@ Open up ``Globals.gd`` and add the following class variables:
# ------------------------------------
# All of the GUI/UI related variables
var canvas_layer = null
const DEBUG_DISPLAY_SCENE = preload("res://Debug_Display.tscn")
var debug_display = null
# ------------------------------------
* ``canvas_layer``: A canvas layer so the GUI/UI created in ``Globals.gd`` is always drawn on top.
@@ -336,7 +336,7 @@ This is because nodes will not be freed/destroyed when you change scene, meaning
instancing/spawning lots of nodes and you are not freeing them.
______
Now we need to add ``set_debug_display`` to ``Globals.gd``:
::
@@ -350,7 +350,7 @@ Now we need to add ``set_debug_display`` to ``Globals.gd``:
if debug_display == null:
debug_display = DEBUG_DISPLAY_SCENE.instance()
canvas_layer.add_child(debug_display)
Let's go over what's happening.
First we check to see if ``Globals.gd`` is trying to turn on the debug display, or turn it off.
@@ -409,16 +409,16 @@ Add the following to ``_process``:
if Input.is_action_just_pressed("ui_cancel"):
if popup == null:
popup = POPUP_SCENE.instance()
popup.get_node("Button_quit").connect("pressed", self, "popup_quit")
popup.connect("popup_hide", self, "popup_closed")
popup.get_node("Button_resume").connect("pressed", self, "popup_closed")
canvas_layer.add_child(popup)
popup.popup_centered()
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
get_tree().paused = true
Let's go over what's happening here.
@@ -458,9 +458,9 @@ Add the following to ``Globals.gd``:
if popup != null:
popup.queue_free()
popup = null
``popup_closed`` will resume the game and destroy the pop up if there is one.
``popup_quit`` is similar, but we're also making sure the mouse is visible and changing scenes to the title screen.
Add the following to ``Globals.gd``:
@@ -477,7 +477,7 @@ Add the following to ``Globals.gd``:
popup = null
load_new_scene(MAIN_MENU_PATH)
``popup_quit`` will resume the game, set the mouse mode to ``MOUSE_MODE_VISIBLE`` to ensure the mouse is visible in the main menu, destroy
the pop up if there is one, and change scenes to the main menu.
@@ -530,7 +530,7 @@ Open up ``Player.gd`` and add the following class variables:
const RESPAWN_TIME = 4
var dead_time = 0
var is_dead = false
var globals
* ``RESPAWN_TIME``: The amount of time (in seconds) it takes to respawn.
@@ -546,15 +546,15 @@ We now need to add a couple lines to ``_ready``, so we can use ``Globals.gd`` in
globals = get_node("/root/Globals")
global_transform.origin = globals.get_respawn_position()
Now we're getting the ``Globals.gd`` singleton and assigning it to ``globals``. We also set the player's global position
by setting the origin in the player's global :ref:`Transform <class_Transform>` to the position returned by ``globals.get_respawn_position``.
.. note:: Don't worry, we will be adding ``get_respawn_position`` further below!
______
Next we need to make a few changes to ``physics_process``. Change ``physics_processing`` to the following:
::
@@ -586,58 +586,58 @@ Let's add ``process_respawn``. Add the following to ``Player.gd``:
::
func process_respawn(delta):
# If we just died
if health <= 0 and !is_dead:
$Body_CollisionShape.disabled = true
$Feet_CollisionShape.disabled = true
changing_weapon = true
changing_weapon_name = "UNARMED"
$HUD/Death_Screen.visible = true
$HUD/Panel.visible = false
$HUD/Crosshair.visible = false
dead_time = RESPAWN_TIME
is_dead = true
if grabbed_object != null:
grabbed_object.mode = RigidBody.MODE_RIGID
grabbed_object.apply_impulse(Vector3(0,0,0), -camera.global_transform.basis.z.normalized() * OBJECT_THROW_FORCE / 2)
grabbed_object.collision_layer = 1
grabbed_object.collision_mask = 1
grabbed_object = null
if is_dead:
dead_time -= delta
var dead_time_pretty = str(dead_time).left(3)
$HUD/Death_Screen/Label.text = "You died\n" + dead_time_pretty + " seconds till respawn"
if dead_time <= 0:
global_transform.origin = globals.get_respawn_position()
$Body_CollisionShape.disabled = false
$Feet_CollisionShape.disabled = false
$HUD/Death_Screen.visible = false
$HUD/Panel.visible = true
$HUD/Crosshair.visible = true
for weapon in weapons:
var weapon_node = weapons[weapon]
if weapon_node != null:
weapon_node.reset_weapon()
health = 100
grenade_amounts = {"Grenade":2, "Sticky Grenade":2}
current_grenade = "Grenade"
is_dead = false
Let's go through what this function is doing.
@@ -792,7 +792,7 @@ Now all we need is a way to set the respawn points. Open up ``Ruins_Level.tscn``
func _ready():
var globals = get_node("/root/Globals")
globals.respawn_points = get_children()
Now when a node with ``Respawn_Point_Setter.gd`` has its ``_ready`` function called, all of the children
nodes of the node with ``Respawn_Point_Setter.gd``, ``Spawn_Points`` in the case of ``Ruins_Level.tscn``, will be added
to ``respawn_points`` in ``Globals.gd``.
@@ -806,7 +806,7 @@ Now when the player dies, they will respawn after waiting ``4`` seconds!
.. note:: No spawn points are already set up for any of the levels besides ``Ruins_Level.tscn``!
Adding spawn points to ``Space_Level.tscn`` is left as an exercise for the reader.
Writing a sound system we can use anywhere
------------------------------------------
@@ -826,7 +826,7 @@ First, open up ``SimpleAudioPlayer.gd`` and change it to the following:
audio_node = $Audio_Stream_Player
audio_node.connect("finished", self, "sound_finished")
audio_node.stop()
globals = get_node("/root/Globals")
@@ -836,13 +836,13 @@ First, open up ``SimpleAudioPlayer.gd`` and change it to the following:
globals.created_audio.remove(globals.created_audio.find(self))
queue_free()
return
audio_node.stream = audio_stream
# If you are using a AudioPlayer3D, then uncomment these lines to set the position.
#if position != null:
# audio_node.global_transform.origin = position
audio_node.play(0.0)
@@ -854,7 +854,7 @@ First, open up ``SimpleAudioPlayer.gd`` and change it to the following:
audio_node.stop()
queue_free()
There are several changes from the old version, first and foremost being we are no longer storing the sound files in ``SimpleAudioPlayer.gd`` anymore.
This is much better for performance since we're no longer loading each audio clip when we create a sound, but instead we are forcing an audio stream to be passed
in to ``play_sound``.
@@ -909,17 +909,17 @@ Lets go over these global variables.
.. note:: If you want to add additional audio, you need to add it to ``audio_clips``. No audio files are provided in this tutorial,
so you will have to provide your own.
One site I'd recommend is **GameSounds.xyz**.
I'm using the Gamemaster audio gun sound pack included in the Sonniss' GDC Game Audio bundle for 2017.
The tracks I've used (with some minor editing) are as follows:
* gun_revolver_pistol_shot_04,
* gun_semi_auto_rifle_cock_02,
* gun_submachine_auto_shot_00_automatic_preview_01
______
Now we need to add a new function called ``play_sound`` to ``Globals.gd``:
::
@@ -928,12 +928,12 @@ Now we need to add a new function called ``play_sound`` to ``Globals.gd``:
if audio_clips.has(sound_name):
var new_audio = SIMPLE_AUDIO_PLAYER_SCENE.instance()
new_audio.should_loop = loop_sound
add_child(new_audio)
created_audio.append(new_audio)
new_audio.play_sound(audio_clips[sound_name], sound_position)
else:
print ("ERROR: cannot play sound that does not exist in audio_clips!")
@@ -961,7 +961,7 @@ Add the following to ``load_new_scene``:
if (sound != null):
sound.queue_free()
created_audio.clear()
Now before ``Globals.gd`` changes scenes, it goes through each simple audio player in ``created_sounds`` and frees/destroys them. Once ``Globals.gd`` has gone through
all of the sounds in ``created_audio``, we clear ``created_audio`` so it no longer holds any references to any (noew freed/destroyed) simple audio players.
@@ -1010,11 +1010,11 @@ At this point you have a good base to build more complicated FPS games.
Other than that, the source is exactly the same, just with helpful comments explaining what
each part does.
.. tip:: The finished project source is hosted on Github as well: https://github.com/TwistedTwigleg/Godot_FPS_Tutorial
**Please note that the code in Github may or may not be in sync with the tutorial in the documentation**.
The code in the documentation is likely better managed and/or more up to date.
If you are unsure on which to use, use the project(s) provided in the documentation as they are maintained by the Godot community.
@@ -1024,7 +1024,7 @@ All assets provided in the started assets (unless otherwise noted) were **origin
All original assets provided for this tutorial are released under the ``MIT`` license.
Feel free to use these assets however you want! All original assets belong to the Godot community, with the other assets belonging to those listed below:
The skybox is created by **StumpyStrust** and can be found at OpenGameArt.org. https://opengameart.org/content/space-skyboxes-0
. The skybox is licensed under the ``CC0`` license.

View File

@@ -40,34 +40,34 @@ Follow the instructions below for either (or both) of the scenes you want to use
.. code-tab:: gdscript Space_Level.tscn
Expand "Other_Objects" and then expand "Physics_Objects".
Expand one of the "Barrel_Group" nodes and then select "Barrel_Rigid_Body" and open it using
the "Open in Editor" button.
This will bring you to the "Barrel_Rigid_Body" scene. From there select the root node and
scroll the inspector down to the bottom.
Select the drop down arrow under the "Node" tab, and then select "Load". Navigate to
"RigidBody_hit_test.gd" and select "Open".
Return back to "Space_Level.tscn".
Expand one of the "Box_Group" nodes and then select "Crate_Rigid_Body" and open it using the
"Open in Editor" button.
This will bring you to the "Crate_Rigid_Body" scene. From there select the root node and
scroll the inspector down to the bottom.
Select the drop down arrow under the "Node" tab, and then select "Load". Navigate to
"RigidBody_hit_test.gd" and select "Open".
Return back to "Space_Level.tscn".
.. code-tab:: gdscript Ruins_Level.tscn
Expand "Misc_Objects" and then expand "Physics_Objects".
Select all of the "Stone_Cube" RigidBodies and then in the inspector scroll down to the bottom.
Select the drop down arrow under the "Node" tab, and then select "Load". Navigate to
"RigidBody_hit_test.gd" and select "Open".
Return back to "Ruins_Level.tscn".
Now you can fire at all of the rigid bodies in either level and they will react to bullets hitting them!
@@ -82,7 +82,7 @@ First we need to define a few variables in each of our weapon scripts.
Open up ``Weapon_Pistol.gd`` and add the following class variables:
::
var ammo_in_weapon = 10
var spare_ammo = 20
const AMMO_IN_MAG = 10
@@ -104,14 +104,14 @@ Now we need to add ammo for both the rifle and the knife.
.. note:: You may be wondering why we are adding ammo for the knife given it does not consume any ammunition.
The reason we want to add ammo to the knife is so we have a consistent interface for all of our weapons.
If we did not add ammo variables for the knife, we would have to add checks for the knife. By adding the ammo
variables to the knife, we don't need to worry about whether or all our weapons have the same variables.
Add the following class variables to ``Weapon_Rifle.gd``:
::
var ammo_in_weapon = 50
var spare_ammo = 100
const AMMO_IN_MAG = 50
@@ -122,7 +122,7 @@ the player loses ammo regardless of whether the player hit something or not.
Now all that's left is the knife. Add the following to ``Weapon_Knife.gd``:
::
var ammo_in_weapon = 1
var spare_ammo = 1
const AMMO_IN_MAG = 1
@@ -136,7 +136,7 @@ Now all we need to do is change a one thing in ``Player.gd``.
All we need to change how we're firing the weapons in ``process_input``. Change the code for firing weapons to the following:
::
# ----------------------------------
# Firing the weapons
if Input.is_action_pressed("fire"):
@@ -147,7 +147,7 @@ All we need to change how we're firing the weapons in ``process_input``. Change
if animation_manager.current_state == current_weapon.IDLE_ANIM_NAME:
animation_manager.set_animation(current_weapon.FIRE_ANIM_NAME)
# ----------------------------------
Now the weapons have a limited amount of ammo, and will stop firing when the player runs out.
_______
@@ -159,7 +159,7 @@ First, add ``process_UI(delta)`` to ``_physics_process``.
Now add the following to ``Player.gd``:
::
func process_UI(delta):
if current_weapon_name == "UNARMED" or current_weapon_name == "KNIFE":
UI_status_label.text = "HEALTH: " + str(health)
@@ -191,10 +191,10 @@ For reloading we need to add a few more variables and a function to every weapon
Open up ``Weapon_Pistol.gd`` and add the following class variables:
::
const CAN_RELOAD = true
const CAN_REFILL = true
const RELOADING_ANIM_NAME = "Pistol_reload"
* ``CAN_RELOAD``: A boolean to track whether this weapon has the ability to reload
@@ -204,32 +204,32 @@ Open up ``Weapon_Pistol.gd`` and add the following class variables:
Now we need to add a function for handling reloading. Add the following function to ``Weapon_Pistol.gd``:
::
func reload_weapon():
var can_reload = false
if player_node.animation_manager.current_state == IDLE_ANIM_NAME:
can_reload = true
if spare_ammo <= 0 or ammo_in_weapon == AMMO_IN_MAG:
can_reload = false
if can_reload == true:
var ammo_needed = AMMO_IN_MAG - ammo_in_weapon
if spare_ammo >= ammo_needed:
spare_ammo -= ammo_needed
ammo_in_weapon = AMMO_IN_MAG
else:
ammo_in_weapon += spare_ammo
spare_ammo = 0
player_node.animation_manager.set_animation(RELOADING_ANIM_NAME)
return true
return false
Let's go over what's happening:
First we define a variable to see whether or not this weapon can reload.
@@ -255,41 +255,41 @@ _______
Now we need to add reloading to the rifle. Open up ``Weapon_Rifle.gd`` and add the following class variables:
::
const CAN_RELOAD = true
const CAN_REFILL = true
const RELOADING_ANIM_NAME = "Rifle_reload"
These variables are exactly the same as the pistol, just with ``RELOADING_ANIM_NAME`` changed to the rifle's reloading animation.
Now we need to add ``reload_weapon`` to ``Weapon_Rifle.gd``:
::
func reload_weapon():
var can_reload = false
if player_node.animation_manager.current_state == IDLE_ANIM_NAME:
can_reload = true
if spare_ammo <= 0 or ammo_in_weapon == AMMO_IN_MAG:
can_reload = false
if can_reload == true:
var ammo_needed = AMMO_IN_MAG - ammo_in_weapon
if spare_ammo >= ammo_needed:
spare_ammo -= ammo_needed
ammo_in_weapon = AMMO_IN_MAG
else:
ammo_in_weapon += spare_ammo
spare_ammo = 0
player_node.animation_manager.set_animation(RELOADING_ANIM_NAME)
return true
return false
This code is exactly the same as the pistol.
@@ -299,7 +299,7 @@ _______
The last bit we need to do for the weapons is add 'reloading' to the knife. Add the following class variables to ``Weapon_Knife.gd``:
::
const CAN_RELOAD = false
const CAN_REFILL = false
@@ -311,7 +311,7 @@ has no reloading animation.
Now we need to add ``reloading_weapon``:
::
func reload_weapon():
return false
@@ -323,9 +323,9 @@ Adding reloading to the player
Now we need to add a few things to ``Player.gd``. First we need to define a new class variable:
::
var reloading_weapon = false
* ``reloading_weapon``: A variable to track whether or not the player is currently trying to reload.
@@ -334,7 +334,7 @@ Next we need to add another function call to ``_physics_process``.
Add ``process_reloading(delta)`` to ``_physics_process``. Now ``_physics_process`` should look something like this:
::
func _physics_process(delta):
process_input(delta)
process_movement(delta)
@@ -345,7 +345,7 @@ Add ``process_reloading(delta)`` to ``_physics_process``. Now ``_physics_process
Now we need to add ``process_reloading``. Add the following function to ``Player.gd``:
::
func process_reloading(delta):
if reloading_weapon == true:
var current_weapon = weapons[current_weapon_name]
@@ -371,7 +371,7 @@ Before we can let the player reload, we need to change a few things in ``process
The first thing we need to change is in the code for changing weapons. We need to add a additional check (``if reloading_weapon == false:``) to see if the player is reloading:
::
if changing_weapon == false:
# New line of code here!
if reloading_weapon == false:
@@ -384,7 +384,7 @@ This makes it so the player cannot change weapons if the player is reloading.
Now we need to add the code to trigger a reload when the player pushes the ``reload`` action. Add the following code to ``process_input``:
::
# ----------------------------------
# Reloading
if reloading_weapon == false:
@@ -429,7 +429,7 @@ reloading.
Let's change our firing code in ``process_input`` so it reloads when trying to fire an empty weapon:
::
# ----------------------------------
# Firing the weapons
if Input.is_action_pressed("fire"):
@@ -448,11 +448,11 @@ Now we check to make sure the player is not reloading before we fire the weapon,
we set ``reloading_weapon`` to ``true`` if the player tries to fire.
This will make it where the player will try to reload when the player tries to fire a empty weapon.
_______
With that done, the player can now reload! Give it a try! Now you can fire all of the spare ammo for each weapon.
Adding sounds
-------------
@@ -497,12 +497,12 @@ and insert the following code:
func play_sound(sound_name, position=null):
if audio_pistol_shot == null or audio_rifle_shot == null or audio_gun_cock == null:
print ("Audio not set!")
queue_free()
return
if sound_name == "Pistol_shot":
audio_node.stream = audio_pistol_shot
elif sound_name == "Rifle_shot":
@@ -566,7 +566,7 @@ to save on resources.
Because we cannot play looping sounds, certain effects, like footstep sounds, are harder to accomplish
because we then have to keep track of whether or not there is a sound effect and whether or not we
need to continue playing it.
One of the biggest flaws with this system is we can only play sounds from ``Player.gd``.
Ideally we'd like to be able to play sounds from any script at any time.
@@ -625,7 +625,7 @@ Open up ``Weapon_Pistol.gd``.
Now, we want to make a noise when the player fires the pistol, so add the following to the end of the ``fire_weapon`` function:
::
player_node.create_sound("Pistol_shot", self.global_transform.origin)
Now when the player fires the pistol, we'll play the ``Pistol_shot`` sound.
@@ -634,7 +634,7 @@ To make a sound when the player reloads, we need to add the following right unde
``reload_weapon`` function:
::
player_node.create_sound("Gun_cock", player_node.camera.global_transform.origin)
Now when the player reloads, we'll play the ``Gun_cock`` sound.
@@ -647,7 +647,7 @@ Open up ``Weapon_Rifle.gd``.
To play sounds when the rifle is fired, add the following to the end of the ``fire_weapon`` function:
::
player_node.create_sound("Rifle_shot", ray.global_transform.origin)
Now when the player fires the rifle, we'll play the ``Rifle_shot`` sound.
@@ -656,7 +656,7 @@ To make a sound when the player reloads, we need to add the following right unde
``reload_weapon`` function:
::
player_node.create_sound("Gun_cock", player_node.camera.global_transform.origin)
Now when the player reloads, we'll play the ``Gun_cock`` sound.

View File

@@ -1205,7 +1205,7 @@ Now we need to add this code:
func bullet_hit(damage, bullet_global_trans):
var direction_vect = bullet_global_trans.basis.z.normalized() * BASE_BULLET_BOOST;
apply_impulse((bullet_global_trans.origin - global_transform.origin).normalized(), direction_vect * damage)

View File

@@ -173,7 +173,7 @@ You can hide the gizmos in the 3D view of the editor through this menu:
.. image:: img/tuto_3d6_1.png
To hide a specific type of gizmos, you can toggle them off in the "View" menu.
.. image:: img/tuto_3d6_2.png
Default environment

View File

@@ -8,7 +8,7 @@ Introduction
:ref:`Gridmaps <class_GridMap>` are a tool for creating 3D
game levels, similar to the way :ref:`TileMap <doc_using_tilemaps>`
works in 2D. You start with a predefined collection of 3D meshes (a
works in 2D. You start with a predefined collection of 3D meshes (a
:ref:`class_MeshLibrary`) that can be placed on a grid,
as if you were building a level with an unlimited amount of Lego blocks.
@@ -34,7 +34,7 @@ scene to see an example of how to set up the mesh library.
.. image:: img/gridmap_meshlibrary1.png
As you can see, this scene has a :ref:`class_Spatial` node as its root, and
a number of :ref:`class_MeshInstance` node children.
a number of :ref:`class_MeshInstance` node children.
If you don't need any physics in your scene, then you're done. However, in most
cases you'll want to assign collision bodies to the meshes.
@@ -42,7 +42,7 @@ cases you'll want to assign collision bodies to the meshes.
Collisions
----------
You can manually assign a :ref:`class_StaticBody` and
You can manually assign a :ref:`class_StaticBody` and
:ref:`class_CollisionShape` to each mesh. Alternatively, you can use the "Mesh" menu
to automatically create the collision body based on the mesh data.

View File

@@ -116,7 +116,7 @@ A default basis (unmodified) is akin to:
GD.Print(identityBasis.y); // prints: (0, 1, 0)
GD.Print(identityBasis.z); // prints: (0, 0, 1)
// The Identity basis is equivalent to:
// 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))
@@ -189,7 +189,7 @@ To rotate relative to object space (the node's own transform) use the following:
rotate_object_local(Vector3(1, 0, 0), PI)
.. code-tab:: csharp
// Rotate locally
RotateObjectLocal(Vector3.Right, Mathf.Pi);
@@ -341,7 +341,7 @@ Example of looking around, FPS style:
// modify accumulated mouse rotation
_rotationX += mouseMotion.Relative.x * LookAroundSpeed;
_rotationY += mouseMotion.Relative.y * LookAroundSpeed;
// reset rotation
Transform transform = Transform;
transform.basis = Basis.Identity;

View File

@@ -6,13 +6,13 @@ Vertex displacement with shaders
Introduction
------------
This tutorial will teach you how to displace the vertices of
a :ref:`Plane Mesh<class_PlaneMesh>` inside a shader. Vertex displacement can be used
This tutorial will teach you how to displace the vertices of
a :ref:`Plane Mesh<class_PlaneMesh>` inside a shader. Vertex displacement can be used
for a wide variety of effects, but most commonly it is used
as a quick way to turn a flat plane into a simple terrain. Typically
this is done using a heightmap, but in order to keep everything self
contained, in this tutorial we will use noise in a shader. At the end
of this tutorial we will have a deformed plane that looks like a
of this tutorial we will have a deformed plane that looks like a
miniature terrain complete with dynamic lighting.
By reading this tutorial you should gain a basic understanding of:
@@ -28,18 +28,18 @@ The plane mesh
--------------
First, add a :ref:`Spatial<class_Spatial>` node to the scene to act as the root. Next, add a :ref:`MeshInstance<class_MeshInstance>`
as a child.
as a child.
.. image:: img/vertex_displacement_new_mesh.png
Select the newly created :ref:`MeshInstance<class_MeshInstance>`. Then click on the button that says "null"
Select the newly created :ref:`MeshInstance<class_MeshInstance>`. Then click on the button that says "null"
next to the :ref:`Mesh<class_MeshInstance>` in the Inspector. This will bring up a list of :ref:`PrimitiveMeshes<class_PrimitiveMesh>`.
Select "New PlaneMesh".
.. image:: img/vertex_displacement_planemesh.png
The button will change into a small image of a plane. Click on it to enter into
the Inspector for the :ref:`Plane Mesh<class_MeshInstance>`.
the Inspector for the :ref:`Plane Mesh<class_MeshInstance>`.
Then, in the viewport, click in the upper left corner where it says [Perspective].
A menu will appear. In the middle of the menu are options for how to display the scene.
@@ -96,7 +96,7 @@ the ``render_mode`` the plane would appear blue because it would pick up the sky
Next we will define a vertex shader. The vertex shader determines where the vertices of your
:ref:`Mesh<class_MeshInstance>` appear in the final scene. We will be using it to offset the height of each vertex and
make our flat plane appear like a little terrain.
make our flat plane appear like a little terrain.
We define the vertex shader like so:
@@ -106,7 +106,7 @@ We define the vertex shader like so:
}
With nothing in the ``vertex`` function Godot will use its default vertex shader. We can easily
With nothing in the ``vertex`` function Godot will use its default vertex shader. We can easily
start to make changes by adding a single line:
::
@@ -114,14 +114,14 @@ start to make changes by adding a single line:
void vertex() {
VERTEX.y += cos(VERTEX.x) * sin(VERTEX.z);
}
Adding this line you should get an image like the one below.
.. image:: img/vertex_displacement_cos.png
Okay, lets unpack this. The ``y`` value of the ``VERTEX`` is being increased. And we are passing
the ``x`` and ``z`` components of the ``VERTEX`` as arguments to ``cos`` and ``sin`` this gives us
a wave like appearance across the ``x`` and ``z`` axis.
a wave like appearance across the ``x`` and ``z`` axis.
What we want to achieve is the look of little hills, after all ``cos`` and ``sin`` already look kind of like
hills. We do so by scaling the inputs to the ``cos`` and ``sin`` functions.
@@ -134,7 +134,7 @@ hills. We do so by scaling the inputs to the ``cos`` and ``sin`` functions.
.. image:: img/vertex_displacement_cos_scaled.png
This looks better, but it is still too spiky. This is because ``cos`` and ``sin`` output values between ``-1`` and ``1``,
This looks better, but it is still too spiky. This is because ``cos`` and ``sin`` output values between ``-1`` and ``1``,
so the range of the output is much too high. We correct this by multiplying the result by ``0.5`` to reduce the size.
::
@@ -151,12 +151,12 @@ Noise
-----
Noise is a very popular tool for procedural generation. Think of it as similar to the cosine function
where you have repeating hills except with noise each hill has a different height. Understanding
where you have repeating hills except with noise each hill has a different height. Understanding
noise is not necessary for this tutorial. There is nothing wrong with simply copying and pasting
the code below.
The first function we use to generate the noise is the ``hash`` function. It gives the random height
for each of the hill tops.
for each of the hill tops.
::
@@ -165,8 +165,8 @@ for each of the hill tops.
}
You will find similar functions to this all over the internet. It is lovingly referred to as the
'one-liner hash function'. It works well for simple noise, but there are many better alternatives
floating around as well. For this tutorial it will work fine.
'one-liner hash function'. It works well for simple noise, but there are many better alternatives
floating around as well. For this tutorial it will work fine.
Next we define the ``noise`` function. It smoothly interpolates between the random heights.
Again, if this code seems daunting, do not worry, just copy paste and move on with the tutorial.
@@ -178,7 +178,7 @@ Again, if this code seems daunting, do not worry, just copy paste and move on wi
vec2 f = fract(x);
f = f * f * (3.0 - 2.0 * f);
vec2 a = vec2(1.0, 0.0);
return mix(mix(hash(p + a.yy), hash(p + a.xy), f.x),
return mix(mix(hash(p + a.yy), hash(p + a.xy), f.x),
mix(hash(p + a.yx), hash(p + a.xx), f.x), f.y);
}
@@ -201,7 +201,7 @@ we increase the frequency each level, decrease the amplitude, and calculate a ne
return height;
}
We can now use this noise function in place of ``cos`` and ``sin`` in the previous section.
We can now use this noise function in place of ``cos`` and ``sin`` in the previous section.
::
@@ -211,7 +211,7 @@ We can now use this noise function in place of ``cos`` and ``sin`` in the previo
.. image:: img/vertex_displacement_noise1.png
With the noise function in place we already have something that looks kind of cool.
There is a lot of detail, it kind of looks hilly or mountainous.
There is a lot of detail, it kind of looks hilly or mountainous.
Fragment shader
---------------
@@ -220,14 +220,14 @@ The difference between a vertex shader and a fragment shader is that the vertex
runs per vertex and sets properties such as ``VERTEX`` (position) and ``NORMAL``, while
the fragment shader runs per pixel and, most importantly, sets the ``ALBEDO`` color of the :ref:`Mesh<class_MeshInstance>`.
Now lets look at the :ref:`Mesh<class_MeshInstance>` with a regular shader instead of the wireframe. Set the
Now lets look at the :ref:`Mesh<class_MeshInstance>` with a regular shader instead of the wireframe. Set the
viewport back to 'Display Normal'.
.. image:: img/vertex_displacement_noise2.png
The :ref:`Mesh<class_MeshInstance>` appears completely white because the fragment shader is coloring each pixel white,
but if every pixel is white we lose detail on the :ref:`Mesh<class_MeshInstance>`. So lets color each pixel based
on the height calculated in the vertex shader. We do so by setting the ``COLOR`` variable
on the height calculated in the vertex shader. We do so by setting the ``COLOR`` variable
in the vertex shader. And by setting the ``ALBEDO`` in the fragment shader to the calculated
``COLOR`` variable.
@@ -250,7 +250,7 @@ Uniforms
--------
Uniform variables allow you to pass data from the game into the shader. They can
be very useful for controlling shader effects. Uniforms can be almost any
be very useful for controlling shader effects. Uniforms can be almost any
datatype that can be used in the shader. To use a uniform you declare it in
your :ref:`Shader<class_Shader>` using the keyword ``uniform``.
@@ -262,16 +262,16 @@ Lets make a uniform that changes the height of the terrain.
Godot lets you initialize a uniform with a value, here ``height_scale`` is set to
``0.5``. You can set uniforms from gdscript by calling the function ``set_shader_param``
``0.5``. You can set uniforms from gdscript by calling the function ``set_shader_param``
on the material corresponding to the shader. The value passed from gdscript takes
precedence over the value used to initialize it in the shader.
::
::
material.set_shader_param("height_scale", 0.5)
Remember that the string passed into ``set_shader_param`` must match the name
of the uniform variable in the :ref:`Shader<class_Shader>`. You can use the uniform variable anywhere
of the uniform variable in the :ref:`Shader<class_Shader>`. You can use the uniform variable anywhere
inside your :ref:`Shader<class_Shader>`. Here, we will use it to set the height value instead
of arbitrarily multiplying by ``0.5``.
@@ -313,15 +313,15 @@ at the beginning of this tutorial, lets remove that.
It looks slightly better now, you can see the light affecting the terrain, and it has
turned blue as a result of the sky. The problem is the light is affecting the terrain
as if it were a flat plane. This is because the light shader uses the normals of the
:ref:`Mesh<class_MeshInstance>` to calculate light. The normals are stored in the :ref:`Mesh<class_MeshInstance>`, but we are changing
:ref:`Mesh<class_MeshInstance>` to calculate light. The normals are stored in the :ref:`Mesh<class_MeshInstance>`, but we are changing
the shape of the :ref:`Mesh<class_MeshInstance>` in the shader so the normals are no longer correct. To fix this
we need to recalculate the normals in the shader. Godot makes this easy for us, all we
have to do is calculate the new normal and set ``NORMAL`` to that value in the vertex shader.
With ``NORMAL`` set Godot will do all the difficult lighting calculations for us.
To calculate the normal from noise we are going to use a technique called 'central differences'.
This is used a lot, especially in places like shadertoy, to calculate normals in shaders.
What we will do is calculate the noise at four points surrounding the vertex in the ``x`` and ``z`` directions and then calculate
This is used a lot, especially in places like shadertoy, to calculate normals in shaders.
What we will do is calculate the noise at four points surrounding the vertex in the ``x`` and ``z`` directions and then calculate
the slope at the vertex from that. After all a normal is just an indicator of the slope of the
noise.
@@ -333,15 +333,15 @@ We calculate the normal with one line in the vertex shader.
vec3 normal = normalize(vec3(fbm(VERTEX.xz - e) - fbm(VERTEX.xz + e), 2.0 * e.x, fbm(VERTEX.xz - e.yx) - fbm(VERTEX.xz + e.yx)));
NORMAL = normal;
The variable ``e`` just makes it easier to add and subtract the right value from the ``VERTEX``.
Setting ``e`` to a lower number will increase the level of detail of the normal.
The variable ``e`` just makes it easier to add and subtract the right value from the ``VERTEX``.
Setting ``e`` to a lower number will increase the level of detail of the normal.
With ``NORMAL`` calculated the terrain now looks like:
.. image:: img/vertex_displacement_normal.png
This still does not look how we want it to. The issue here is that the noise changes
faster than the vertices do. So when we calculate the normal at the point of the
This still does not look how we want it to. The issue here is that the noise changes
faster than the vertices do. So when we calculate the normal at the point of the
``VERTEX`` it does not align with what we see in the final :ref:`Mesh<class_MeshInstance>`. In order to fix
this we add more vertices. The below image is made with a :ref:`Mesh<class_MeshInstance>` with ``subdivision`` set
to ``100``.
@@ -363,6 +363,6 @@ shaders in Godot. As a further exercise try changing the ``height_scale`` from g
try using different :ref:`Primitive Meshes<class_PrimitiveMesh>`, and try making your
own functions to calculate ``height``.
For further information on how to use shaders in Godot
For further information on how to use shaders in Godot
you should check out the :ref:`doc_shading_language` page.

View File

@@ -81,7 +81,7 @@ create a child sprite and load the torso, later accommodate it properly:
.. image:: img/tuto_cutout3.png
This looks good. Let's see if our hierarchy works as a skeleton by
rotating the torso. We can do this be pressing ``E`` to enter rotate mode,
rotating the torso. We can do this be pressing ``E`` to enter rotate mode,
and dragging with the left mouse button. To exit rotate mode hit ``ESC``.
.. image:: img/tutovec_torso1.gif
@@ -121,7 +121,7 @@ simple! Or maybe not:
.. image:: img/tuto_cutout7.png
Right. In 2D, parent nodes appear below children nodes. Well, this sucks.
Right. In 2D, parent nodes appear below children nodes. Well, this sucks.
But how can this problem be solved? We want the left arm to appear behind
the hip and the torso. For this, we can move the nodes behind the hip
(note that you can bypass this by setting the Node2D Z property, but then you
@@ -229,8 +229,8 @@ can be selected and animated.
Finally, create endpoints in all meaningful extremities and connect the
whole skeleton with bones up to the hip.
You may notice when connecting the hip and torso, that an extra bone is created.
To fix this, select the root and hip node, open the Skeleton menu, click ``clear bones``.
You may notice when connecting the hip and torso, that an extra bone is created.
To fix this, select the root and hip node, open the Skeleton menu, click ``clear bones``.
.. image:: img/tuto_cutout15_2.png
@@ -354,9 +354,9 @@ Batch setting transition curves
When creating complex animations and inserting many keyframes,
editing the individual keyframe curves for each can become an endless
task. For this, the Animation Editor has a small menu where changing
animation curves is easy. First select the appropriate keys. Next click on the
pencil icon in the bottom right of the animation panel, this will open the
transition editor. Now click on one of the curve options most appropriate for
animation curves is easy. First select the appropriate keys. Next click on the
pencil icon in the bottom right of the animation panel, this will open the
transition editor. Now click on one of the curve options most appropriate for
your animation.
.. image:: img/tuto_cutout24.png

View File

@@ -25,37 +25,37 @@ While it may look like a lot (and there is more as you scroll down), each field
described in terms of what you should put in. We will nonetheless go over what
is required in the submission form here as well.
* **Asset Name**:
The name of your asset. Should be a unique, descriptive title of
* **Asset Name**:
The name of your asset. Should be a unique, descriptive title of
what your asset is.
* **Category**:
* **Category**:
The category that your asset belongs to, and will be shown in
search results. The category is split into **Addons** and **Projects**.
search results. The category is split into **Addons** and **Projects**.
In-editor, assets of the Project type (Templates, Demos, Projects) only show
up when viewing the AssetLib from the Project Manager, while assets of the
Addon type will only be visible from inside a project.
* **Godot version**:
Addon type will only be visible from inside a project.
* **Godot version**:
The version of the engine that the asset works with.
Currently it's not possible to have a single asset entry contain downloads for
multiple engine versions, so you may need to re-submit the asset multiple times,
with an entry for each Godot version it supports. This is particularly important
when dealing with major versions of the engine, such as Godot 2.x and Godot 3.x.
* **Version**:
* **Version**:
The version number of the asset. While you are free to choose
and use any versioning scheme that you like, you may want to look into
something such as `SemVer <https://semver.org>`_ if you want your asset's
versioning scheme to be clear and consistent. Note that there is also an
internal version number, incremented every time the asset download URL is
changed or updated.
* **Repository host**:
changed or updated.
* **Repository host**:
Assets uploaded to the AssetLib are not hosted on it
directly. Instead, they point to repositories hosted on third-party Git providers
such as GitHub, GitLab or Bitbucket. This is where you choose which provider
your asset uses, so the site can compute the final download link.
* **Repository URL**:
* **Repository URL**:
The URL to your asset's files/webpage. This will vary
based on your choice of provider, but it should look similar to `https://github.com/<user>/<project>`.
* **Issues URL**:
* **Issues URL**:
The URL to your asset's issue tracker. Again, this will differ
from repository host to repository host, but will likely look similar to
`https://github.com/<user>/<project>/issues`. You may leave this field empty
@@ -71,7 +71,7 @@ is required in the submission form here as well.
* **License**:
The license under which you are distributing the asset. The list
includes a variety of free and open-source software licenses, such as GPL
(v2 and v3), MIT, BSD and Boost Software License. You can visit `OpenSource.org <https://opensource.org>`_
(v2 and v3), MIT, BSD and Boost Software License. You can visit `OpenSource.org <https://opensource.org>`_
for a detailed description of each of the listed licenses.
* **Description**:
Finally, you can use the Description field for a textual
@@ -83,7 +83,7 @@ You may also include up to three video and/or image previews, which will be show
at the bottom of the asset page. Use the "Enable" checkbox on each of the preview
submission boxes to enable them.
* **Type**:
* **Type**:
Either an image, or a video.
* **Image/YouTube URL**:
Either a link to the image, or to a video, hosted on YouTube.
@@ -97,7 +97,7 @@ which you can visit on the AssetLib `here <https://godotengine.org/asset-library
take up to a few days for your addon to be accepted (or rejected), so please
be patient! You will be informed when your asset is reviewed. If it was rejected,
you will be told why that may have been, and you will be able to submit it again
with the appropriate changes.
with the appropriate changes.
You may have some luck accelerating the approval process by messaging the
moderators/assetlib reviewers on IRC (the #godotengine-atelier channel on Freenode),
or the official Discord server.

View File

@@ -73,7 +73,7 @@ Now let's take a look at what an asset's page looks like and what it contains.
Registering and logging in
~~~~~~~~~~~~~~~~~~~~~~~~~~
In order to upload assets to the AssetLib, you need to be logged in, and to do
In order to upload assets to the AssetLib, you need to be logged in, and to do
that, you need a registered user account. In the future, this may also give you
access to other features, such as commenting on or rating the existing assets.
You do *not* need to be logged in to browse and download the assets.
@@ -119,13 +119,13 @@ like, with some differences:
|image8|
Similarly to the web version of the AssetLib, here you can search
Similarly to the web version of the AssetLib, here you can search
for assets by category, name, and sort them by factors such as name or edit date.
Notably, you can only fetch assets for the current version of Godot you are running.
Also, you can only download Projects, Demos and Templates from the Project Manager
view of the AssetLib, while Addons (tools, scripts, materials etc.) can only be
downloaded from the in-project AssetLib.
downloaded from the in-project AssetLib.
In addition, unlike when using the web frontend, the search results are updated
in real-time (you do not have to press Search after every change to your search
query for the changes to take place).
@@ -155,7 +155,7 @@ Here you can see a list of all the files that will be installed. You can tick of
any of the files that you do not wish to install, and Godot will also inform you
about any problems with files that it cannot install. These files will be shown
in red, and hovering over them will show you a message stating why it cannot be
installed.
installed.
|image12|

View File

@@ -7,4 +7,4 @@ Audio
audio_buses
audio_streams

View File

@@ -90,13 +90,13 @@ Listing the video memory usage of the running game.
Misc
++++
Miscellaneous options for debug.
Miscellaneous options for debug.
Remote in Scene dock
--------------------
When running your game, a bar will occur at the top of the ``Scene`` dock. You can switch to ``Remote`` and inspect or change the nodes' parameters in the running game.
When running your game, a bar will occur at the top of the ``Scene`` dock. You can switch to ``Remote`` and inspect or change the nodes' parameters in the running game.
.. image:: img/overview_remote.png

View File

@@ -59,7 +59,7 @@ exists. Example
{
DrawSelected()
}
else
else
{
DrawNormal();
}
@@ -83,7 +83,7 @@ for example:
.. tabs::
.. code-tab:: gdscript GDScript
func get_minimum_size():
func get_minimum_size():
return Vector2(30, 30)
.. code-tab:: csharp
@@ -207,7 +207,7 @@ exists, but can be checked with the _notification callback:
{
case NotificationMouseEnter:
// mouse entered the area of this control
break;
break;
case NotificationMouseExit:
// mouse exited the area of this control

View File

@@ -80,7 +80,7 @@ An example usage:
var t = new Theme();
t.SetColor("fontColor", "Label", new Color(1.0f, 1.0f, 1.0f));
var l = new Label();
l.SetTheme(t);

View File

@@ -8,7 +8,7 @@ You might want to change the appearance of the mouse cursor in your game in orde
1. Using project settings
2. Using a script
Using project settings is a simpler but more limited way to customize the mouse cursor. The second way is more customizable but involves scripting.
Using project settings is a simpler but more limited way to customize the mouse cursor. The second way is more customizable but involves scripting.
Using project settings
----------------------
@@ -35,12 +35,12 @@ Create a Node and attach the following script.
# Load the custom images for the mouse cursor
var arrow = load("res://arrow.png")
var beam = load("res://beam.png")
func _ready():
# Changes only the arrow shape of the cursor
# This is similar to changing it in the project settings
Input.set_custom_mouse_cursor(arrow)
# Changes a specific shape of the cursor (here the IBeam shape)
Input.set_custom_mouse_cursor(beam, Input.CURSOR_IBEAM)

View File

@@ -77,7 +77,7 @@ received input, in order:
1. First of all, the standard :ref:`Node._input() <class_Node__input>` function
will be called in any node that overrides it (and hasn't disabled input processing with :ref:`Node.set_process_input() <class_Node_set_process_input>`).
If any function consumes the event, it can call :ref:`SceneTree.set_input_as_handled() <class_SceneTree_set_input_as_handled>`, and the event will
not spread any more. This ensures that you can filter all events of interest, even before the GUI.
not spread any more. This ensures that you can filter all events of interest, even before the GUI.
For gameplay input, :ref:`Node._unhandled_input() <class_Node__unhandled_input>` is generally a better fit, because it allows the GUI to intercept the events.
2. Second, it will try to feed the input to the GUI, and see if any
control can receive it. If so, the :ref:`Control <class_Control>` will be called via the
@@ -90,7 +90,7 @@ received input, in order:
of mouse events via :ref:`Control._gui_input() <class_Control__gui_input>`
callback, and whether these events are propagated further.
3. If so far no one consumed the event, the unhandled input callback
will be called if overridden (and not disabled with
will be called if overridden (and not disabled with
:ref:`Node.set_process_unhandled_input() <class_Node_set_process_unhandled_input>`).
If any function consumes the event, it can call :ref:`SceneTree.set_input_as_handled() <class_SceneTree_set_input_as_handled>`, and the
event will not spread any more. The unhandled input callback is ideal for full-screen gameplay events, so they are not received when a GUI is active.
@@ -150,7 +150,7 @@ There are several specialised types of InputEvent, described in the table below:
| | | information. (only available on mobile |
| | | devices) |
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
| :ref:`InputEventScreenDrag <class_InputEventScreenDrag>` | SCREEN_DRAG | Contains multi-touch drag information. |
| :ref:`InputEventScreenDrag <class_InputEventScreenDrag>` | SCREEN_DRAG | Contains multi-touch drag information. |
| | | (only available on mobile devices) |
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
| :ref:`InputEventAction <class_InputEventAction>` | SCREEN_ACTION | Contains a generic action. These events |
@@ -185,7 +185,7 @@ The Input singleton has a method for this:
var ev = InputEventAction.new()
# set as move_left, pressed
ev.set_as_action("move_left", true)
ev.set_as_action("move_left", true)
# feedback
Input.parse_input_event(ev)

View File

@@ -123,7 +123,7 @@ progress bar or loading screen, etc.
# start your "loading..." animation
get_node("animation").play("loading")
wait_frames = 1
wait_frames = 1
``_process`` is where the loader is polled. ``poll`` is called, and then
we deal with the return value from that call. ``OK`` means keep polling,

View File

@@ -5,7 +5,7 @@ Encrypting save games
Why?
----
Because the world today is not the world of yesterday. A capitalist
oligarchy runs the world and forces us to consume in order to keep the
gears of this rotten society on track. As such, the biggest market for
@@ -33,7 +33,7 @@ How?
----
The class :ref:`File <class_File>` can open a file at a
location and read/write data (integers, strings and variants).
location and read/write data (integers, strings and variants).
It also supports encryption.
To create an encrypted file, a passphrase must be provided, like this:

View File

@@ -201,7 +201,7 @@ that hasn't been translated, rotated or scaled.
var m = Transform2D()
print(m)
# prints: ((1, 0), (0, 1), (0, 0))
.. code-tab:: csharp
@@ -442,7 +442,7 @@ If the matrix is orthonormal, then:
.. tabs::
.. code-tab:: gdscript GDScript
# if m is orthonormal, then
pos = mi.xform(pos)
# is the same is

View File

@@ -158,7 +158,7 @@ the velocity to the current position.
.. image:: img/vector_movement1.png
.. tip:: Velocity measures the **change** in position per unit of time. The
.. tip:: Velocity measures the **change** in position per unit of time. The
new position is found by adding velocity to the previous position.
- Pointing toward a target
@@ -189,14 +189,14 @@ by its magnitude:
.. code-tab:: gdscript GDScript
var a = Vector2(2, 4)
var m = sqrt(a.x*a.x + a.y*a.y) # get magnitude "m" using the Pythagorean theorem
var m = sqrt(a.x*a.x + a.y*a.y) # get magnitude "m" using the Pythagorean theorem
a.x /= m
a.y /= m
.. code-tab:: csharp
var a = new Vector2(2, 4);
var m = Mathf.Sqrt(a.x*a.x + a.y*a.y); // get magnitude "m" using the Pythagorean theorem
var m = Mathf.Sqrt(a.x*a.x + a.y*a.y); // get magnitude "m" using the Pythagorean theorem
a.x /= m;
a.y /= m;
@@ -249,7 +249,7 @@ to handle this. Here is a GDScript example of the diagram above using a
move_and_collide(reflect)
.. code-tab:: csharp
// KinematicCollision2D contains information about the collision
KinematicCollision2D collision = MoveAndCollide(_velocity * delta);
if (collision != null)
@@ -270,13 +270,13 @@ direction, a scalar value has only magnitude.
The formula for dot product takes two common forms:
.. math::
A \cdot B = \left \| A \right \|\left \| B \right \|\cos \Theta
and
.. math::
A \cdot B = A_{x}B_{x} + A_{y}B_{y}
However, in most cases it is easiest to use the built-in method. Note that

View File

@@ -261,7 +261,7 @@ Code should be something like this:
break; // with one that fails, it's enough
}
}
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
too. This is called the Separating Axis Theorem (or SAT) and most
@@ -328,7 +328,7 @@ Code should be something like this:
break;
}
}
if (allOut)
{
// a separating plane was found
@@ -353,7 +353,7 @@ Code should be something like this:
break;
}
}
if (allOut)
{
overlapping = false;
@@ -481,7 +481,7 @@ So the final algorithm is something like:
print("Polygons collided!")
.. code-tab:: csharp
var overlapping = true;
foreach (Plane plane in planesOfA)
@@ -520,7 +520,7 @@ So the final algorithm is something like:
break;
}
}
if (allOut)
{
overlapping = false;

View File

@@ -47,10 +47,10 @@ Here is how a ``_process()`` function might look like for you:
func _process(delta):
if Engine.editor_hint:
# Code to execute in editor.
if not Engine.editor_hint:
# Code to execute in game.
# Code to execute both in editor and in game.
.. note:: Modifications in editor are permanent. In our case, when we remove the script, the node will keep its rotation direction. Be careful so that you don't make unwanted modifications.

View File

@@ -63,10 +63,10 @@ This object is not meant to be created directly, but is designed so that several
This object extends from :ref:`PacketPeer <class_PacketPeer>`, so it inherits all the useful methods for serializing, sending and receiving data. On top of that, it adds methods to set a peer, transfer mode, etc. It also includes signals that will let you know when peers connect or disconnect.
This class interface can abstract most types of network layers, topologies and libraries. By default Godot
This class interface can abstract most types of network layers, topologies and libraries. By default Godot
provides an implementation based on ENet (:ref:`NetworkedMultiplayerEnet <class_NetworkedMultiplayerENet>`), but this could be used to implement mobile APIs (for adhoc WiFi, Bluetooth) or custom device/console-specific networking APIs.
For most common cases, using this object directly is discouraged, as Godot provides even higher level networking facilities.
For most common cases, using this object directly is discouraged, as Godot provides even higher level networking facilities.
Yet it is made available in case a game has specific needs for a lower level API.
Initializing the network
@@ -118,7 +118,7 @@ Terminating the networking feature:
Managing connections
--------------------
Some games accept connections at any time, others during the lobby phase. Godot can be requested to no longer accept
Some games accept connections at any time, others during the lobby phase. Godot can be requested to no longer accept
connections at any point (see `set_refuse_new_network_connections(bool)` and related methods on :ref:`SceneTree <class_SceneTree>`). To manage who connects, Godot provides the following signals in SceneTree:
Server and Clients:
@@ -138,7 +138,7 @@ Clients:
- `connection_failed`
- `server_disconnected`
Again, all these functions are mainly useful for lobby management or for adding/removing players on the fly.
Again, all these functions are mainly useful for lobby management or for adding/removing players on the fly.
For these tasks the server clearly has to work as a server and you have do tasks manually such as sending a newly connected
player information about other already connected players (e.g. their names, stats, etc).
@@ -168,7 +168,7 @@ Functions can be called in two fashions:
- Reliable: the function call will arrive no matter what, but may take longer because it will be re-transmitted in case of failure.
- Unreliable: if the function call does not arrive, it will not be re-transmitted, but if it arrives it will do it quickly.
In most cases, reliable is desired. Unreliable is mostly useful when synchronizing object positions (sync must happen constantly,
In most cases, reliable is desired. Unreliable is mostly useful when synchronizing object positions (sync must happen constantly,
and if a packet is lost, it's not that bad because a new one will eventually arrive and it would likely be outdated because the object moved further in the meantime, even if it was resent reliably).
There is also the `get_rpc_sender_id` function in `SceneTree` which can be used to check which peer (or peer ID) sent a RPC call.
@@ -232,7 +232,7 @@ You might have noticed already something different, which is the usage of the `r
::
remote func register_player(id, info):
This keyword has two main uses. The first is to let Godot know that this function can be called from RPC. If no keywords are added
Godot will block any attempts to call functions for security. This makes security work a lot easier (so a client can't call a function
to delete a file on another client's system).
@@ -252,7 +252,7 @@ The others will be explained further down.
Note that you could also use the `get_rpc_sender_id` function on `SceneTree` to check which peer actually made the RPC call to `register_player`.
With this, lobby management should be more or less explained. Once you have your game going you will most likely want to add some
extra security to make sure clients don't do anything funny (just validate the info they send from time to time, or before
extra security to make sure clients don't do anything funny (just validate the info they send from time to time, or before
game start). For the sake of simplicity and because each game will share different information, this is not shown here.
Starting the game
@@ -264,13 +264,13 @@ special in itself, but we'll explain a few nice tricks that can be done at this
Player scenes
^^^^^^^^^^^^^
In most games, each player will likely have its own scene. Remember that this is a multiplayer game, so in every peer
In most games, each player will likely have its own scene. Remember that this is a multiplayer game, so in every peer
you need to instance **one scene for each player connected to it**. For a 4 player game, each peer needs to instance 4 player nodes.
So, how to name such nodes? In Godot nodes need to have an unique name. It must also be relatively easy for a player to tell which
nodes represent each player ID.
The solution is to simply name the *root nodes of the instanced player scenes as their network ID*. This way, they will be the same in
The solution is to simply name the *root nodes of the instanced player scenes as their network ID*. This way, they will be the same in
every peer and RPC will work great! Here is an example:
::

View File

@@ -69,7 +69,7 @@ or lose precision if the frame rate is too high or too low.
public class PhysicsScript : KinematicBody2D
{
public override void _PhysicsProcess(float delta)
{
}
@@ -85,9 +85,9 @@ for the character. Use the robot sprite and create a scene like this:
.. image:: img/kbscene.png
You'll notice that there's a warning icon next to our CollisionShape2D node,
that's because we haven't defined a shape for it. Create a new CircleShape2D
in the shape property of CollisionShape2D. Click on <CircleShape2D> to go to the
You'll notice that there's a warning icon next to our CollisionShape2D node,
that's because we haven't defined a shape for it. Create a new CircleShape2D
in the shape property of CollisionShape2D. Click on <CircleShape2D> to go to the
options for it, and set the radius to 30:
.. image:: img/kbradius.png
@@ -121,21 +121,21 @@ collision happens, it stops right at the moment of the collision.
So, let's move our sprite downwards until it hits the floor:
.. tabs::
.. code-tab:: gdscript GDScript
.. code-tab:: gdscript GDScript
extends KinematicBody2D
func _physics_process(delta):
move_and_collide(Vector2(0, 1)) # Move down 1 pixel per physics frame
.. code-tab:: csharp
.. code-tab:: csharp
using Godot;
using System;
public class PhysicsScript : KinematicBody2D
{
public override void _PhysicsProcess(float delta)
{
// Move down 1 pixel per physics frame
@@ -150,7 +150,7 @@ The next step will be adding gravity to the mix, this way it behaves a
little more like an actual game character:
.. tabs::
.. code-tab:: gdscript GDScript
.. code-tab:: gdscript GDScript
extends KinematicBody2D
@@ -163,7 +163,7 @@ little more like an actual game character:
var motion = velocity * delta
move_and_collide(motion)
.. code-tab:: csharp
.. code-tab:: csharp
using Godot;
using System;
@@ -207,14 +207,14 @@ This adds simple walking support by pressing left and right:
velocity.x = WALK_SPEED
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 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.
move_and_slide(velocity, Vector2(0, -1))
.. code-tab:: csharp
.. code-tab:: csharp
using Godot;
using System;
@@ -246,7 +246,7 @@ This adds simple walking support by pressing left and right:
// 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.
// 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));
}
}

View File

@@ -60,7 +60,7 @@ and to detect contact with other objects.
.. note:: In order to detect collisions, at least one ``Shape2D`` must be
assigned to the object.
The most common way to assign a shape is by adding a :ref:`CollisionShape2D <class_CollisionShape2D>`
or :ref:`CollisionPolygon2D <class_CollisionPolygon2D>` as a child of the object.
These nodes allow you to draw the shape directly in the editor workspace.
@@ -85,7 +85,7 @@ In order to avoid this inaccuracy, any code that needs to access a body's proper
be run in the :ref:`Node._physics_process() <class_Node__physics_process>`
callback, which is called before each physics step at a constant frame rate
(60 times per second by default).
Collision Layers and Masks
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -183,7 +183,7 @@ that is overriding the global physics properties.
When a rigid body is at rest and hasn't moved for a time, it goes to sleep.
A sleeping body acts like a static body, and its forces are not calculated by
the physics engine. The body will wake up when forces are applied, either by
a collision or via code.
a collision or via code.
Rigid body modes
~~~~~~~~~~~~~~~~
@@ -318,9 +318,9 @@ occurred:
.. code-tab:: gdscript GDScript
extends KinematicBody2D
var velocity = Vector2(250, 250)
func _physics_process(delta):
var collision_info = move_and_collide(velocity * delta)
if collision_info:
@@ -348,9 +348,9 @@ Or to bounce off of the colliding object:
.. code-tab:: gdscript GDScript
extends KinematicBody2D
var velocity = Vector2(250, 250)
func _physics_process(delta):
var collision_info = move_and_collide(velocity * delta)
if collision_info:

View File

@@ -29,16 +29,16 @@ When starting our game, we will need to get the item details from Google such as
#First listen to the sku details update callback
IAP.connect("sku_details_complete",self,"sku_details_complete")
#Then ask google the details for these items
IAP.sku_details_query(["pid1","pid2"]) #pid1 and pid2 are our product ids entered in Googleplay dashboard
#This will be called when sku details are retrieved successfully
func sku_details_complete():
print(IAP.sku_details) #This will print the details as JSON format, refer the format in iap.gd
print(IAP.sku_details["pid1"].price) #print formatted localized price
We can use the IAP details to display the title, price and/or description on our shop scene.
Check if user purchased an item
@@ -89,7 +89,7 @@ We can also implement other signals for the purchase flow and improve the user e
Consumables and Non-Consumables
-------------------------------
There are two types of products - consumables and non-consumables.
There are two types of products - consumables and non-consumables.
**Consumables** are purchased and used, for eg: healing potions which can be purchased again and again.
**Non-consumables** are one time purchases, for eg: Level packs.
@@ -108,7 +108,7 @@ If our game has only consumables, we don't have to do this. We can set it to con
::
IAP.set_auto_consume(true)
If our game has only non-consumables, we can
::

View File

@@ -311,7 +311,7 @@ that the import wasn't successful.
return ERR_PARSE_ERROR
var color
if options.use_red_anyway:
if options.use_red_anyway:
color = Color8(255, 0, 0)
else:
color = Color8(int(channels[0]), int(channels[1]), int(channels[2]))

View File

@@ -7,7 +7,7 @@ bits = ARGUMENTS.get("bits", 64)
final_lib_path = 'demo/bin/'
# This makes sure to keep the session environment variables on windows,
# This makes sure to keep the session environment variables on windows,
# that way you can run scons in a vs 2017 prompt and it will find all the required tools
env = Environment()
if platform == "windows":

View File

@@ -38,7 +38,7 @@ See also :ref:`Compiling <toc-devel-compiling>` as the build tools are identical
to the ones you need to compile Godot from source.
You can download these repositories from GitHub or let Git
do the work for you.
do the work for you.
Note that these repositories now have different branches for different versions of Godot. GDNative modules written for an earlier version of Godot will work in newer versions (with the exception of one breaking change in ARVR interfaces between 3.0 and 3.1) but not vise versa so make sure you download the correct branch.
If you are versioning your project using Git,
@@ -69,7 +69,7 @@ Do make sure you clone recursive to pull in both repositories:
.. note:: The ``-b 3.0`` I've added as an example to show how to select a specific branch for a specific version of Godot.
Also ``godot-cpp`` now includes ``godot_headers`` as a nested submodule, if you've manually downloaded them please make sure to place ``godot_headers`` inside of the ``godot-cpp`` folder.
You don't have to do it this way but I've found it easiest to manage. If you decide to just download the repositories or just clone them into your folder, makes sure to keep the folder layout the same as I've setup here as much of the code we'll be showcasing here assumes the project has this layout.
If you cloned the example from the link specified in

View File

@@ -22,18 +22,18 @@ Accordingly, most features available in GLSL are available in Godot's shading la
Shader Programs
^^^^^^^^^^^^^^^
In GLSL each shader uses a separate program. You have one program for the vertex shader and one
for the fragment shader. In Godot you have a single shader that contains a ``vertex`` and/or a
``fragment`` function. If you only choose to write one, Godot will supply the other.
In GLSL each shader uses a separate program. You have one program for the vertex shader and one
for the fragment shader. In Godot you have a single shader that contains a ``vertex`` and/or a
``fragment`` function. If you only choose to write one, Godot will supply the other.
Godot allows uniform variables and functions to be shared by defining the fragment and vertex
shaders in one file. In GLSL the vertex and fragment programs cannot share variables except
Godot allows uniform variables and functions to be shared by defining the fragment and vertex
shaders in one file. In GLSL the vertex and fragment programs cannot share variables except
when varyings are used.
Vertex attributes
^^^^^^^^^^^^^^^^^
In GLSL you can pass in per-vertex information using attributes. In GLSL you have the flexibility to
In GLSL you can pass in per-vertex information using attributes. In GLSL you have the flexibility to
pass in as much or as little as you want. In Godot you have a set number of input attributes
including, ``VERTEX`` (position), ``COLOR``, ``UV``, ``UV2``, ``NORMAL``. For a complete list
see the :ref:`Shading language reference <doc_shading_language>`.
@@ -41,17 +41,17 @@ see the :ref:`Shading language reference <doc_shading_language>`.
gl_Position
^^^^^^^^^^^
``gl_Position`` receives the final position of a vertex specified in the vertex shader.
It is specified by the user in clip space. Typically in GLSL the model space vertex position
is passed in using a vertex attribute called ``position`` and you handle the
conversion from model space to clip space manually.
``gl_Position`` receives the final position of a vertex specified in the vertex shader.
It is specified by the user in clip space. Typically in GLSL the model space vertex position
is passed in using a vertex attribute called ``position`` and you handle the
conversion from model space to clip space manually.
In Godot ``VERTEX`` specifies the vertex position in model space at the beginning of the ``vertex``
function. Godot also handles the final conversion to clip space after the user-defined ``vertex``
function is run. If you want to skip the conversion from model to view space, you can set the
``render_mode`` to ``skip_vertex_transform``. If you want to skip all transforms, set
``render_mode`` to ``skip_vertex_transform`` and set the ``PROJECTION_MATRIX`` to ``mat4(1.0)``
in order to nullify the final transform from view space to clip space.
function. Godot also handles the final conversion to clip space after the user-defined ``vertex``
function is run. If you want to skip the conversion from model to view space, you can set the
``render_mode`` to ``skip_vertex_transform``. If you want to skip all transforms, set
``render_mode`` to ``skip_vertex_transform`` and set the ``PROJECTION_MATRIX`` to ``mat4(1.0)``
in order to nullify the final transform from view space to clip space.
Varyings
^^^^^^^^
@@ -63,15 +63,15 @@ out of the vertex shader is defined with ``out`` in the vertex shader and ``in``
Main
^^^^
In GLSL each shader program looks like a self-contained C-style program. Accordingly, the main entry point
is ``main``. If you are copying a vertex shader, rename ``main`` to ``vertex`` and if you are copying a
In GLSL each shader program looks like a self-contained C-style program. Accordingly, the main entry point
is ``main``. If you are copying a vertex shader, rename ``main`` to ``vertex`` and if you are copying a
fragment shader, rename ``main`` to ``fragment``.
Constants
^^^^^^^^^
Godot currently does not support constants. You can fake the functionality by using a uniform initialized
to the value, but you will not benefit from the increased speed from using a constant.
to the value, but you will not benefit from the increased speed from using a constant.
Macros
^^^^^^
@@ -79,14 +79,14 @@ Macros
In keeping with its similarity to C, GLSL lets you use macros. Commonly ``#define`` is used to define
constants or small functions. There is no straightforward way to translate defines to Godot's shading language.
If it is a function that is defined, then replace with a function, and if it is a constant then replace with
a uniform. For other macros (``#if``, ``#ifdef``, etc.) there is no equivalent because they run during the
a uniform. For other macros (``#if``, ``#ifdef``, etc.) there is no equivalent because they run during the
pre-processing stage of compilation.
Variables
^^^^^^^^^
GLSL has many built in variables that are hard-coded. These variables are not uniforms, so they
are not editable from the main program.
are not editable from the main program.
+---------------------+---------+------------------------+-----------------------------------------------------+
|Variable |Type |Equivalent |Description |
@@ -102,20 +102,20 @@ are not editable from the main program.
|gl_PointCoord |vec2 |POINT_COORD |Position on point when drawing Point primitives. |
+---------------------+---------+------------------------+-----------------------------------------------------+
|gl_FrontFacing |bool |FRONT_FACING |True if front face of primitive. |
+---------------------+---------+------------------------+-----------------------------------------------------+
+---------------------+---------+------------------------+-----------------------------------------------------+
.. _glsl_coordinates:
Coordinates
^^^^^^^^^^^
``gl_FragCoord`` in GLSL and ``FRAGCOORD`` in the Godot shading language use the same coordinate system.
``gl_FragCoord`` in GLSL and ``FRAGCOORD`` in the Godot shading language use the same coordinate system.
If using UV in Godot, the y-coordinate will be flipped upside down.
Precision
^^^^^^^^^
In GLSL you can define the precision of a given type (float or int) at the top of the shader with the
In GLSL you can define the precision of a given type (float or int) at the top of the shader with the
``precision`` keyword. In Godot you can set the precision of individual variables as you need by placing
precision qualifiers ``lowp``, ``mediump``, and ``highp`` before the type when defining the variable. For
more information see the :ref:`Shading Language <doc_shading_language>` reference.
@@ -123,7 +123,7 @@ more information see the :ref:`Shading Language <doc_shading_language>` referenc
Shadertoy
---------
`Shadertoy <https://www.shadertoy.com>`_ is a website that makes it easy to write fragment shaders and
`Shadertoy <https://www.shadertoy.com>`_ is a website that makes it easy to write fragment shaders and
create `pure magic <https://www.shadertoy.com/view/4tjGRh>`_.
Shadertoy does not give the user full control over the shader. It only allows the user to write a
@@ -139,18 +139,18 @@ has the regular types, including `Constants`_ and macros.
mainImage
^^^^^^^^^
The main point of entry to a Shadertoy shader is the ``mainImage`` function. ``mainImage`` has two
parameters, ``fragColor`` and ``fragCoord`` which correspond to ``COLOR`` and ``FRAGCOORD`` in Godot
parameters, ``fragColor`` and ``fragCoord`` which correspond to ``COLOR`` and ``FRAGCOORD`` in Godot
respectively. These parameters are handled automatically in Godot, so you do not need to include them
as parameters yourself. Anything in the ``mainImage`` function should be copied into the ``fragment``
function when porting to Godot.
Variables
^^^^^^^^^
In order to make writing fragment shaders straightforward and easy, Shadertoy handles passing a lot
of helpful information from the main program into the fragment shader for you. A few of these
have no equivalents in Godot because Godot has chosen not to make them available by default.
This is okay because Godot gives you the ability to make your own uniforms. For variables whose
equivalents are listed as "Provide with Uniform", the user is responsible for creating that
In order to make writing fragment shaders straightforward and easy, Shadertoy handles passing a lot
of helpful information from the main program into the fragment shader for you. A few of these
have no equivalents in Godot because Godot has chosen not to make them available by default.
This is okay because Godot gives you the ability to make your own uniforms. For variables whose
equivalents are listed as "Provide with Uniform", the user is responsible for creating that
uniform themself. The description gives the reader a hint about what they can pass in as a substitute.
+---------------------+---------+------------------------+-----------------------------------------------------+
@@ -176,7 +176,7 @@ uniform themself. The description gives the reader a hint about what they can pa
+---------------------+---------+------------------------+-----------------------------------------------------+
|iChannelResolution[4]|vec3 |1.0 / TEXTURE_PIXEL_SIZE|Resolution of particular texture. |
+---------------------+---------+------------------------+-----------------------------------------------------+
|iChanneli |Sampler2D|TEXTURE |Godot provides only one built in, user can make more.|
|iChanneli |Sampler2D|TEXTURE |Godot provides only one built in, user can make more.|
+---------------------+---------+------------------------+-----------------------------------------------------+
Coordinates
@@ -188,11 +188,11 @@ Coordinates
The Book of Shaders
-------------------
Similar to Shadertoy, `The Book of Shaders <https://thebookofshaders.com>`_ provides access to a fragment
shader in the web browser for the user to interact with. The user is restricted to writing fragment
shader code with a set list of uniforms passed in and with no ability to add additional uniforms.
Similar to Shadertoy, `The Book of Shaders <https://thebookofshaders.com>`_ provides access to a fragment
shader in the web browser for the user to interact with. The user is restricted to writing fragment
shader code with a set list of uniforms passed in and with no ability to add additional uniforms.
For further help on porting shaders to various frameworks generally, The Book of Shaders provides
For further help on porting shaders to various frameworks generally, The Book of Shaders provides
a `page <https://thebookofshaders.com/04>`_ on running shaders in various frameworks.
Types
@@ -210,8 +210,8 @@ a Book of Shaders ``main`` function should be copied into Godot's ``fragment`` f
Variables
^^^^^^^^^
The Book of Shaders sticks closer to plain GLSL than Shadertoy does. It also implements fewer uniforms than
Shadertoy.
The Book of Shaders sticks closer to plain GLSL than Shadertoy does. It also implements fewer uniforms than
Shadertoy.
+---------------------+---------+------------------------+-----------------------------------------------------+
|Variable |Type |Equivalent |Description |

View File

@@ -9,7 +9,7 @@ Introduction
For the most common cases, Godot provides ready to use materials for
most types of shaders, such as SpatialMaterial, CanvasItemMaterial and
ParticlesMaterial (@TODO link to tutorials/classes). They are flexible implementations that cover most
use cases.
use cases.
Shader materials allow writing a custom shader directly, for maximum flexibility.
Examples of this are:

View File

@@ -81,7 +81,7 @@ demonstrate the effect of different stretch modes. A single sprite, also
directly at the target resolution. 3D will be largely unaffected,
while in 2D there is no longer a 1:1 correspondence between sprite
pixels and screen pixels, which may result in scaling artifacts.
This is a good option if your 2D artwork has a sufficiently high
resolution and does not require pixel-perfect rendering. Consider
enabling texture filtering and mipmapping on your 2D textures and
@@ -138,7 +138,7 @@ to the region outside the blue frame you see in the 2D editor.
taller than the base resolution, the viewport will be grown in the
vertical direction (and more content will be visible to the bottom).
You can also think of this as "Expand Vertically".
This is usually the best option for creating GUIs or HUDs that scale,
so some controls can be anchored to the bottom
(:ref:`doc_size_and_anchors`).
@@ -151,7 +151,7 @@ to the region outside the blue frame you see in the 2D editor.
screen is wider than the base resolution, the viewport will be grown
in the horizontal direction (and more content will be visible to the
right). You can also think of this as "Expand Horizontally".
This is usually the best option for 2D games that scroll horizontally
(like runners or platformers).

View File

@@ -15,9 +15,9 @@ of making a procedural planet like the one below:
.. note:: This tutorial does not cover how to code a dynamic atmosphere like the one this planet has.
This tutorial assumes you are familiar with how to set up a basic scene including:
a :ref:`Camera <class_Camera>`, a :ref:`light source <class_OmniLight>`, a
:ref:`Mesh Instance <class_MeshInstance>` with a :ref:`Primitive Mesh <class_PrimitiveMesh>`,
and applying a :ref:`Spatial Material <class_SpatialMaterial>` to the mesh. The focus will be on using
a :ref:`Camera <class_Camera>`, a :ref:`light source <class_OmniLight>`, a
:ref:`Mesh Instance <class_MeshInstance>` with a :ref:`Primitive Mesh <class_PrimitiveMesh>`,
and applying a :ref:`Spatial Material <class_SpatialMaterial>` to the mesh. The focus will be on using
the :ref:`Viewport <class_Viewport>` to dynamically create textures that can be applied to the mesh.
During the course of this tutorial will cover the following topics:
@@ -32,15 +32,15 @@ Setting up the Viewport
First, add a :ref:`Viewport <class_Viewport>` to the scene.
Next, set the size of the :ref:`Viewport <class_Viewport>` to ``(1024, 512)``. The
:ref:`Viewport <class_Viewport>` can actually be any size so long as the width is double the height.
The width needs to be double the height so that the image will accurately map onto the
Next, set the size of the :ref:`Viewport <class_Viewport>` to ``(1024, 512)``. The
:ref:`Viewport <class_Viewport>` can actually be any size so long as the width is double the height.
The width needs to be double the height so that the image will accurately map onto the
sphere as we will be using equirectangular projection, but more on that later.
.. image:: img/planet_new_viewport.png
Next, disable HDR and disable 3D. We don't need HDR because our planets surface will not be especially
bright so values between ``0`` and ``1`` will be fine. And we will be using a :ref:`ColorRect <class_ColorRect>`
bright so values between ``0`` and ``1`` will be fine. And we will be using a :ref:`ColorRect <class_ColorRect>`
to render the surface, so we don't need 3D either.
Select the Viewport and add a :ref:`ColorRect <class_ColorRect>` as a child.
@@ -73,12 +73,12 @@ apply to the sphere.
Applying the texture
--------------------
Now we go into the :ref:`Mesh Instance <class_MeshInstance>` and add a :ref:`Spatial Material <class_SpatialMaterial>`
to it. No need for a special :ref:`Shader Material <class_ShaderMaterial>` (although that would be a good idea
Now we go into the :ref:`Mesh Instance <class_MeshInstance>` and add a :ref:`Spatial Material <class_SpatialMaterial>`
to it. No need for a special :ref:`Shader Material <class_ShaderMaterial>` (although that would be a good idea
for more advanced effects, like the atmosphere in the example above).
Open the newly created :ref:`Spatial Material <class_SpatialMaterial>` and scroll down to the "Albedo" section
and click beside the "Texture" property to add an Albedo Texture. Here we will apply the texture we made.
and click beside the "Texture" property to add an Albedo Texture. Here we will apply the texture we made.
Choose "New ViewportTexture"
.. image:: img/planet_new_viewport_texture.png
@@ -92,16 +92,16 @@ Your sphere should now be colored in with the colors we rendered to the Viewport
.. image:: img/planet_seam.png
Notice the ugly seam that forms where the texture wraps around? This is because we are picking
a color based on UV coordinates and UV coordinates do not wrap around the texture. This is a classic
problem in 2D map projection. Gamedevs often have a 2-dimensional map they want to project
onto a sphere but when it wraps around it has large seams. There is an elegant work around for this
a color based on UV coordinates and UV coordinates do not wrap around the texture. This is a classic
problem in 2D map projection. Gamedevs often have a 2-dimensional map they want to project
onto a sphere but when it wraps around it has large seams. There is an elegant work around for this
problem that we will illustrate in the next section.
Making the planet texture
-------------------------
So now when we render to our :ref:`Viewport <class_Viewport>` it appears magically on the sphere. But there is an ugly
seam created by our texture coordinates. So how do we get a range of coordinates that wrap around
seam created by our texture coordinates. So how do we get a range of coordinates that wrap around
the sphere in a nice way? One solution is to use a function that repeats on the domain of our texture.
``sin`` and ``cos`` are two such functions. Lets apply them to the texture and see what happens
@@ -111,10 +111,10 @@ the sphere in a nice way? One solution is to use a function that repeats on the
.. image:: img/planet_sincos.png
Not too bad. If you look around you can see that the seam has now disappeared, but in its place we
Not too bad. If you look around you can see that the seam has now disappeared, but in its place we
have pinching at the poles. This pinching is due to the way Godot maps textures to spheres in its
:ref:`Spatial Material <class_SpatialMaterial>`. It uses a projection technique called equirectangular
projection. Which translates a spherical map onto a 2D plane.
:ref:`Spatial Material <class_SpatialMaterial>`. It uses a projection technique called equirectangular
projection. Which translates a spherical map onto a 2D plane.
.. note:: If you are interested in a little extra information on the technique, we will be converting from
spherical coordinates into Cartesian coordinates. Spherical coordinates map the longitude and
@@ -123,8 +123,8 @@ projection. Which translates a spherical map onto a 2D plane.
For each pixel we will calculate its 3D position on the sphere. From that we will use
3D noise to determine a color value. By calculating the noise in 3D we solve the problem
of the pinching at the poles. To understand why, picture the noise being calculated across the
surface of the sphere instead of across the 2D plane. When you calculate across the
of the pinching at the poles. To understand why, picture the noise being calculated across the
surface of the sphere instead of across the 2D plane. When you calculate across the
surface of the sphere you never hit an edge, and hence you never create a seam or
a pinch point on the pole. The following code converts the ``UVs`` into Cartesion
coordinates.
@@ -216,13 +216,13 @@ Coloring the planet
Now to make the planet colors. There are many ways to do this, if you look on `Shadertoy <https://www.shadertoy.com>`_
you will find all kinds of ways of mapping colors to procedural planet terrain. For now
we will stick with a simple gradient between water and land.
we will stick with a simple gradient between water and land.
To make a gradient in glsl we use the ``mix`` function. ``mix`` takes two values to interpolate
between and a third parameter to choose how much to interpolate between them, in essence
it *mixes* the two values together. In other APIs this function is often called ``lerp``.
Although, ``lerp`` is typically reserved for mixing two floats together, ``mix`` can take any
values whether it be floats or vector types.
values whether it be floats or vector types.
::
@@ -231,7 +231,7 @@ values whether it be floats or vector types.
The first color is blue for the ocean. The second color is a kind of reddish color (because
all alien planets need red terrain). And finally they are mixed together by ``n.x * 0.5 + 0.5``.
``n.x`` smoothly varies between ``-1`` and ``1``. So we map it into the ``0-1`` range that ``mix`` expects.
Now you can see that the colors change between blue and red.
Now you can see that the colors change between blue and red.
.. image:: img/planet_noise_color.png
@@ -252,7 +252,7 @@ and it returns ``1`` whenever ``n.x`` is above ``0``.
One more thing to make this a little more planet-y. The land shouldn't be so blobby lets make the edges
a little rougher. A trick that is often used in shaders to make rough looking terrain with noise is
to layer levels of noise over one another at various frequencies. We use one layer to make the
to layer levels of noise over one another at various frequencies. We use one layer to make the
overall blobby structure of the continents. Then another layer breaks up the edges a bit, and then
another, and so on. What we will do is calculate ``n`` with four lines of shader code
instead of just one. ``n`` becomes:
@@ -277,7 +277,7 @@ Making an ocean
One final thing to make this look more like a planet. The ocean and the land reflect light differently.
So we want the ocean to shine a little more than the land. We can do this by passing a fourth value
into the ``alpha`` channel of our output ``COLOR`` and using it as a Roughness map.
into the ``alpha`` channel of our output ``COLOR`` and using it as a Roughness map.
::
@@ -286,24 +286,24 @@ into the ``alpha`` channel of our output ``COLOR`` and using it as a Roughness m
This line returns ``0.3`` for water and ``1.0`` for land. This means that the land is going to be quite
rough while the water will be quite smooth.
And then in the material under the "Metallic" section make sure ``Metallic`` is set to ``0`` and
``Specular`` is set to ``1``. The reason for this is the water reflects light really well, but
And then in the material under the "Metallic" section make sure ``Metallic`` is set to ``0`` and
``Specular`` is set to ``1``. The reason for this is the water reflects light really well, but
isn't metallic. These values are not physically accurate, but they are good enough for this demo.
Next under the "Roughness" section set ``Roughness`` to ``1`` and set the roughness texture to a
:ref:`Viewport Texture <class_ViewportTexture>` pointing to our planet texture :ref:`Viewport <class_Viewport>`.
Finally set the ``Texture Channel`` to ``Alpha``. This instructs the renderer to use the ``alpha``
Next under the "Roughness" section set ``Roughness`` to ``1`` and set the roughness texture to a
:ref:`Viewport Texture <class_ViewportTexture>` pointing to our planet texture :ref:`Viewport <class_Viewport>`.
Finally set the ``Texture Channel`` to ``Alpha``. This instructs the renderer to use the ``alpha``
channel of our output ``COLOR`` as the ``Roughness`` value.
.. image:: img/planet_ocean.png
You'll notice that very little changes except that the planet is no longer reflecting the sky.
You'll notice that very little changes except that the planet is no longer reflecting the sky.
This is happening because by default when something is rendered with an
alpha value it gets drawn as a transparent object over the background. And since the default background
of the :ref:`Viewport <class_Viewport>` is opaque, the ``alpha`` channel of the
:ref:`Viewport Texture <class_ViewportTexture>` is ``1`` resulting in the planet texture being
drawn with slightly fainter colors and a ``Roughness`` value of ``1`` everywhere. To correct this we
go into the :ref:`Viewport <class_Viewport>` and set "Transparent Bg" to on. Since we are now
of the :ref:`Viewport <class_Viewport>` is opaque, the ``alpha`` channel of the
:ref:`Viewport Texture <class_ViewportTexture>` is ``1`` resulting in the planet texture being
drawn with slightly fainter colors and a ``Roughness`` value of ``1`` everywhere. To correct this we
go into the :ref:`Viewport <class_Viewport>` and set "Transparent Bg" to on. Since we are now
rendering one transparent object on top of another we want to enable ``blend_premul_alpha``:
::
@@ -311,7 +311,7 @@ rendering one transparent object on top of another we want to enable ``blend_pre
render_mode blend_premul_alpha;
This pre-multiplies the colors by the ``alpha`` value and then blends them correctly together. Typically
when blending one transparent color on top of another, even if the background has an ``alpha`` of ``0`` (as it
when blending one transparent color on top of another, even if the background has an ``alpha`` of ``0`` (as it
does in this case), you end up with weird color bleed issues. Setting ``blend_premul_alpha`` fixes that.
Now the planet should look like it is reflecting light on the ocean but not the land. If you haven't done

View File

@@ -8,30 +8,30 @@ Introduction
Think of :ref:`Viewports <class_Viewport>` as a screen that the game is projected onto. In order
to see the game we need to have a surface to draw it on, this surface is
the Root :ref:`Viewport <class_Viewport>`.
the Root :ref:`Viewport <class_Viewport>`.
.. image:: img/viewportnode.png
:ref:`Viewports <class_Viewport>` can also be added to the scene so that there
are multiple surfaces to draw on. When we are drawing to a :ref:`Viewport <class_Viewport>`
that is not the Root we call it a render target. We can access the contents
of a render target by accessing its corresponding :ref:`texture <class_ViewportTexture>`.
are multiple surfaces to draw on. When we are drawing to a :ref:`Viewport <class_Viewport>`
that is not the Root we call it a render target. We can access the contents
of a render target by accessing its corresponding :ref:`texture <class_ViewportTexture>`.
By using a :ref:`Viewport <class_Viewport>` as a render target
we can either render multiple scenes simultaneously or we can render to
a :ref:`texture <class_ViewportTexture>` which is applied to an object in the scene, for example a dynamic
a :ref:`texture <class_ViewportTexture>` which is applied to an object in the scene, for example a dynamic
skybox.
:ref:`Viewports <class_Viewport>` have a variety of use cases including:
:ref:`Viewports <class_Viewport>` have a variety of use cases including:
- Rendering 3D objects within a 2D game
- Rendering 2D elements in a 3D game
- Rendering dynamic textures
- Generating procedural textures at runtime
- Rendering multiple cameras in the same scene
What all these use cases have in common is that you are given the ability to
draw objects to a texture as if it were another screen and then you can choose
- Rendering multiple cameras in the same scene
What all these use cases have in common is that you are given the ability to
draw objects to a texture as if it were another screen and then you can choose
what to do with the resulting texture.
Input
@@ -40,8 +40,8 @@ Input
:ref:`Viewports <class_Viewport>` are also responsible for delivering properly adjusted and
scaled input events to all their children nodes. Typically input is received by the
nearest :ref:`Viewport <class_Viewport>` in the tree, but you can set :ref:`Viewports <class_Viewport>` to not receive input by checking
'Disable Input' to 'on', this will allow the next nearest :ref:`Viewport <class_Viewport>` in the tree to capture
the input.
'Disable Input' to 'on', this will allow the next nearest :ref:`Viewport <class_Viewport>` in the tree to capture
the input.
.. image:: img/input.png
@@ -67,7 +67,7 @@ following hierarchy:
.. image:: img/cameras.png
CameraA will display on the Root :ref:`Viewport <class_Viewport>` and it will draw MeshA. CameraB
will be captured by the :ref:`Viewport <class_Viewport>` Node along with MeshB. Even though MeshB is in the scene
will be captured by the :ref:`Viewport <class_Viewport>` Node along with MeshB. Even though MeshB is in the scene
hierarchy, it will still not be drawn to the Root :ref:`Viewport <class_Viewport>`. Similarly MeshA will not
be visible from the :ref:`Viewport <class_Viewport>` node because :ref:`Viewport <class_Viewport>` nodes only
capture nodes below them in the hierarchy.
@@ -83,11 +83,11 @@ or make it the current camera by calling:
Scale & stretching
------------------
:ref:`Viewports <class_Viewport>` have a "size" property which represents the size of the :ref:`Viewport <class_Viewport>`
in pixels. For :ref:`Viewports <class_Viewport>` which are children of :ref:`ViewportContainers <class_viewportcontainer>`,
:ref:`Viewports <class_Viewport>` have a "size" property which represents the size of the :ref:`Viewport <class_Viewport>`
in pixels. For :ref:`Viewports <class_Viewport>` which are children of :ref:`ViewportContainers <class_viewportcontainer>`,
these values are overridden, but for all others this sets their resolution.
It is also possible to scale the 2D content and make the :ref:`Viewport <class_Viewport>` resolution
It is also possible to scale the 2D content and make the :ref:`Viewport <class_Viewport>` resolution
different than the one specified in size, by calling:
::
@@ -199,7 +199,7 @@ their albedo.
Overdraw draws the meshes semi-transparent with an additive blend so you can see how the meshes overlap.
.. image:: img/overdraw.png
.. image:: img/overdraw.png
*The same scene with Debug Draw set to Overdraw*
@@ -227,12 +227,12 @@ and then selecting the :ref:`Viewport <class_Viewport>` you want to use.
.. image:: img/texturepath.png
Every frame the :ref:`Viewport <class_Viewport>`'s texture is cleared away with the default clear color (or a transparent
Every frame the :ref:`Viewport <class_Viewport>`'s texture is cleared away with the default clear color (or a transparent
color if Transparent BG is set to true). This can be changed by setting Clear Mode to Never or Next Frame.
As the name implies, Never means the texture will never be cleared while next frame will
clear the texture on the next frame and then set itself to Never.
By default, re-rendering of the :ref:`Viewport <class_Viewport>` happens when the
By default, re-rendering of the :ref:`Viewport <class_Viewport>` happens when the
:ref:`Viewport <class_Viewport>`'s :ref:`ViewportTexture <class_ViewportTexture>` has been drawn in a frame. If visible, it will be
rendered, otherwise it will not. This behavior can be changed to manual
rendering (once), or always render, no matter if visible or not. This flexibility

View File

@@ -18,7 +18,7 @@ You can use the function :ref:`get_interfaces <class_ARVRServer_get_interfaces>`
To enable an interface you execute the following code:
.. tabs::
.. code-tab:: gdscript GDScript
.. code-tab:: gdscript GDScript
var arvr_interface = ARVRServer.find_interface("Native mobile")
if arvr_interface and arvr_interface.initialize():