diff --git a/.gitignore b/.gitignore index 417714958..e3642b897 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,38 @@ _build/ env/ -extensions/__pycache__/ __pycache__ *.pyc *~ .directory +.vs/ .vscode/ *.mo +# Vim temp files +*.swo +*.swp + +# Geany/geany-plugins files +*.geany +.geanyprj + # Finder (macOS) makes these automatically. .DS_Store +__MACOSX -# And Windows keeps creating these. -Thumbs.db +# Windows image file caches +[Tt]humbs.db +[Tt]humbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Windows shortcuts +*.lnk + +# Windows folder config file +[Dd]esktop.ini + +# Windows Recycle Bin used on file shares +$RECYCLE.BIN/ +logo.h +*.autosave diff --git a/README.md b/README.md index 813b21817..e7504903e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,9 @@ Though arguably less convenient to edit than a wiki, this git repository is mean ### Editing existing pages -To edit an existing page, locate its .rst source file and open it in your favorite text editor. You can then commit the changes, push them to your fork and make a pull request. **Note that the pages in `classes/` should not be edited here, they are automatically generated from Godot's [XML class references](https://github.com/godotengine/godot/tree/master/doc/classes).** +To edit an existing page, locate its .rst source file and open it in your favorite text editor. You can then commit the changes, push them to your fork and make a pull request. +**Note that the pages in `classes/` should not be edited here, they are automatically generated from Godot's [XML class references](https://github.com/godotengine/godot/tree/master/doc/classes).** +See [Contribute to the Class Reference](https://docs.godotengine.org/en/latest/community/contributing/updating_the_class_reference.html) for details. ### Adding new pages diff --git a/about/docs_changelog.rst b/about/docs_changelog.rst index adf7a16ed..593611d09 100644 --- a/about/docs_changelog.rst +++ b/about/docs_changelog.rst @@ -63,6 +63,11 @@ Animation - :ref:`2D skeletons ` - :ref:`AnimationTree ` +GUI +^^^ + +- :ref:`Containers ` + Viewports ^^^^^^^^^ @@ -74,9 +79,14 @@ Shading - :ref:`Intro to shaders: 2D and 3D water (7 video tutorials) ` - :ref:`Migrating to Godot’s shading language ` -- :ref:`Vertex displacement with shaders ` - :ref:`Advanced post-processing ` +Your First Shader Series: + - :ref:`What are shaders? ` + - :ref:`Your first CanvasItem shader ` + - :ref:`Your first Spatial shader ` + - :ref:`Your first Spatial shader: Part 2 ` + Shading Reference: - :ref:`Shaders ` - :ref:`Shading language ` @@ -99,6 +109,12 @@ Multi-threading - :ref:`Thread safe APIs ` +Optimization +^^^^^^^^^^^^ + +- :ref:`Using MultiMesh ` +- :ref:`Using servers ` + Miscellaneous ^^^^^^^^^^^^^ @@ -107,6 +123,11 @@ Miscellaneous - :ref:`Change scenes manually ` - :ref:`Differences between GLES2 and GLES3 ` +Legal +^^^^^ + +- :ref:`Complying with Licenses ` + Compiling ^^^^^^^^^ diff --git a/about/faq.rst b/about/faq.rst index e6e28f5e8..b5ba36395 100644 --- a/about/faq.rst +++ b/about/faq.rst @@ -10,7 +10,7 @@ Godot is `Free and Open-Source Software `_, that is incompatible with the `open license `_ provided by Godot. That being said, FBX support could still be provided by third parties -as a plugin. (See Plugins question above.) +as a plugin. (See Plugins question below.) Will [insert closed SDK such as FMOD, GameWorks, etc.] be supported in Godot? ----------------------------------------------------------------------------- @@ -168,7 +168,7 @@ anyone else interested in adding those libraries as a module and shipping your game with them--as either open- or closed-source. To see how support for your SDK of choice could still be provided, look at the -Plugins question above. +Plugins question below. If you know of a third-party SDK that is not supported by Godot but that offers free and open-source integration, consider starting the integration work yourself. @@ -198,14 +198,14 @@ This is mostly needed for 2D, as in 3D it's just a matter of Camera XFov or YFov resolution, the larger your assets, the more memory they will take and the longer the time it will take for loading. -2. Use the stretch options in Godot, 2D stretching with keeping aspect - works best. Check the :ref:`doc_multiple_resolutions` tutorial +2. Use the stretch options in Godot; 2D stretching while keeping aspect + ratios works best. Check the :ref:`doc_multiple_resolutions` tutorial on how to achieve this. 3. Determine a minimum resolution and then decide if you want your game to stretch vertically or horizontally for different aspect ratios, or - whether there is a minimum one and you want black bars to appear - instead. This is also explained in the previous step. + if there is one aspect ratio and you want black bars to appear + instead. This is also explained in :ref:`doc_multiple_resolutions`. 4. For user interfaces, use the :ref:`anchoring ` to determine where controls should stay and move. If UIs are more diff --git a/about/introduction.rst b/about/introduction.rst index 9b113d6ff..623ea77c5 100644 --- a/about/introduction.rst +++ b/about/introduction.rst @@ -17,6 +17,17 @@ This page gives a broad presentation of the engine and of the contents of this documentation, so that you know where to start if you are a beginner or where to look if you need info on a specific feature. +Before you start +---------------- + +The :ref:`Tutorials and resources ` page lists +video tutorials contributed by the community. If you prefer video to text, +those may be worth a look. + +In case you have trouble with one of the tutorials or your project, +you can find help on the various :ref:`Community channels `, +especially the Godot Discord community, Q&A, and IRC. + About Godot Engine ------------------ diff --git a/community/channels.rst b/community/channels.rst index 44d288b1f..652286d63 100644 --- a/community/channels.rst +++ b/community/channels.rst @@ -7,7 +7,7 @@ So, where is the Godot community and where can you ask questions and get help? Note that some of these channels are run and moderated by members of the Godot community or third parties. -A brief overview over these channels is also available on the `website `_. +A brief overview over these and other channels is also available on the `Godot website `_. Q&A --- @@ -20,8 +20,9 @@ IRC on Freenode - `General: #godotengine `_ - `Engine development: #godotengine-devel `_ - `Documentation: #godotengine-doc `_ +- `Pull request meetings: #godotengine-meeting `_ - `GDNative: #godotengine-gdnative `_ -- `Webseite/PR: #godotengine-atelier `_ +- `Website and public relations: #godotengine-atelier `_ - `IRC logs `_ Other chats @@ -43,4 +44,4 @@ Social networks Forum ----- -- `Forum (godotdevelopers.org) `_ +- `Forum (godotforums.org) `_ diff --git a/community/contributing/code_style_guidelines.rst b/community/contributing/code_style_guidelines.rst index cf9cca868..ab6f19e76 100644 --- a/community/contributing/code_style_guidelines.rst +++ b/community/contributing/code_style_guidelines.rst @@ -52,7 +52,7 @@ Using clang-format locally ~~~~~~~~~~~~~~~~~~~~~~~~~~ First of all, you will need to install clang-format. As of now, you need to use -**clang-format 6.x** to be compatible with Godot's format. Later versions might +**clang-format 8.x** to be compatible with Godot's format. Later versions might be suitable, but previous versions had bugs that will cause formatting changes to the current code base. diff --git a/community/contributing/documentation_guidelines.rst b/community/contributing/documentation_guidelines.rst index 5fb8a8648..7172778e2 100644 --- a/community/contributing/documentation_guidelines.rst +++ b/community/contributing/documentation_guidelines.rst @@ -35,6 +35,19 @@ reference documentation about the reStructuredText markup language. main repository. These files are then later used to generate the in-editor documentation as well as the API reference of the online docs. Read more here: :ref:`doc_updating_the_class_reference`. + +The 'Edit on Github' link +------------------------- + +If you're reading documentation on ``docs.godotengine.org`` you'll see an 'Edit on GitHub' hyperlink at the top right of the page. Once you've created a GitHub account you can propose changes to a page you're reading as follows: + +1. Copy the URL that the GitHub link points to. Part of the URL refers to a version name such as '3.1' or 'latest'. Swap this part for the word 'master' so that the result looks something like this: ``https://github.com/godotengine/godot-docs/blob/master/community/contributing/docs_writing_guidelines.rst`` +2. Load that URL in your browser. +3. On the GitHub page you're taken to, click the pencil icon. It has the tooltip "Edit the file in a fork of this project". +4. Complete all the edits you want to make for that page. +5. Summarise the changes you made in the form at the bottom of the page and click the button labelled 'Propose file change' when done. +6. On the following screens, click the 'Create pull request' buttons until you see a message like ``Open. yourGitHubUsername wants to merge 1 commit into godotengine:master from yourGitHubUsername:patch-6`` +7. A reviewer will evaluate your changes and incorporate them into the docs if they're judged to improve them. You might also be asked to make modifications to your changes before they're included. What makes good documentation? ------------------------------ diff --git a/community/contributing/updating_the_class_reference.rst b/community/contributing/updating_the_class_reference.rst index 2ccb996ad..937498b9a 100644 --- a/community/contributing/updating_the_class_reference.rst +++ b/community/contributing/updating_the_class_reference.rst @@ -3,9 +3,11 @@ 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 `. 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. Each is a class, documented in the :ref:`class reference `. +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. +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. **Important:** If you are planning to make larger changes or a more substantial contribution, it is usually a good idea to create an issue (or a comment in an existing one) to let others know so they don't start working on the same thing too. @@ -160,6 +162,8 @@ Edit the file for your chosen class in ``doc/classes/`` to update the class refe Edit it using your favourite text editor. If you use a code editor, make sure that it doesn't change the indent style: tabs for the XML, and 4 spaces inside BBcode-style blocks. More on that below. +If you need to check that the modifications you've made are correct in the generated documentation, build Godot as described :ref:`here `, run the editor and open the help for the page you modified. + How to write the class reference ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/community/tutorials.rst b/community/tutorials.rst index 2335249dc..ce571d561 100644 --- a/community/tutorials.rst +++ b/community/tutorials.rst @@ -3,22 +3,45 @@ Tutorials and resources ======================= -This is a list of third-party tutorials and other resources created by the community -that may be of interest. +This is a list of third-party tutorials and resources created by the Godot community. For resources, remember that there is the official `Godot Asset Library `_ full of official and community resources too! Also have a look at this `huge list over at Reddit `_. + +Think there is something missing here? Feel free to submit a `Pull Request `_ as always. + +Where to start +-------------- + +The Godot video tutorials by `GDQuest `_, `Game from Scratch `_ and `KidsCanCode `_ are well-regarded in the community and often recommended as a gentle introduction to beginners. + +If you're interested in Visual Scripting, `Emilio's tutorials `_ may be worth a look. + +Some of the others mentioned below provide more advanced tutorials, e.g. on 3D or shaders. Video tutorials --------------- +- `Emilio `_ - `GDQuest `_ -- `KidsCanCode `_ -- `Game from Scratch: Godot 3 Tutorial Series `_ +- `Game Endeavor `_ +- `Game from Scratch `_ - `HeartBeast `_ +- `Jeremy Bullock `_ +- `KidsCanCode `_ +- `Mister Taft Creates `_ +- `Miziziziz `_ +- `P1X / Krzysztof Jankowski `_ +- `Pigdev `_ - `Steincodes `_ Text tutorials -------------- -- `KidsCanCode: An ongoing tutorial series `_ +- `KidsCanCode `_ +- `Steincodes `_ + +Devlogs +------- +- `Andrea Catania (Physics & AI) `_ +- `Bastiaan Olij (AR & VR) `_ Resources --------- diff --git a/development/cpp/custom_modules_in_cpp.rst b/development/cpp/custom_modules_in_cpp.rst index 43aa22f99..6c1b80a70 100644 --- a/development/cpp/custom_modules_in_cpp.rst +++ b/development/cpp/custom_modules_in_cpp.rst @@ -303,7 +303,7 @@ during runtime with the ``LD_LIBRARY_PATH`` environ variable: user@host:~/godot$ ./bin/godot* **note**: Pay attention you have to ``export`` the environ variable otherwise -you won't be able to play you project from within the editor. +you won't be able to play your project from within the editor. On top of that, it would be nice to be able to select whether to compile our module as shared library (for development) or as a part of the Godot binary diff --git a/development/file_formats/tscn.rst b/development/file_formats/tscn.rst index 7560175d6..21a2526ea 100644 --- a/development/file_formats/tscn.rst +++ b/development/file_formats/tscn.rst @@ -12,7 +12,7 @@ indicate to Godot that the file has been exported from another program and should not be edited by the user from within Godot. For those looking for a complete description, the parsing is handled in the -file `scene_format_text.cpp `_ +file `resource_format_text.cpp `_ in the class :code:`ResourceFormatLoaderText` File structure diff --git a/getting_started/editor/external_editor.rst b/getting_started/editor/external_editor.rst index 74955297f..a98b1e121 100644 --- a/getting_started/editor/external_editor.rst +++ b/getting_started/editor/external_editor.rst @@ -3,9 +3,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 +Godot can be used with an external text editor, such as Sublime Text or Visual Studio Code. To select an external text editor via the Godot editor menu: ``Editor -> Editor Settings -> Text Editor -> External`` .. image:: img/editor_settings.png @@ -31,11 +29,15 @@ Some example Exec Flags for various editors include: +---------------------+-----------------------------------------------------+ | Editor | Exec Flags | +=====================+=====================================================+ -| Geany/Kate | {file} --line {line} --column {col} | +| Geany/Kate | {file} -\-line {line} -\-column {col} | +---------------------+-----------------------------------------------------+ | Atom/Sublime Text | {file}:{line} | +---------------------+-----------------------------------------------------+ -| JetBrains Rider | --line {line} {file} | +| JetBrains Rider | -\-line {line} {file} | +---------------------+-----------------------------------------------------+ -| Visual Studio Code | {project} --goto {file}:{line}:{col} | +| Visual Studio Code | {project} -\-goto {file}:{line}:{col} | +---------------------+-----------------------------------------------------+ +| Vim (gVim) | "+call cursor({line}, {col})" {file} | ++---------------------+-----------------------------------------------------+ + +.. note:: For Visual Studio Code you will have to point to the "code.cmd" file. diff --git a/getting_started/scripting/gdscript/gdscript_advanced.rst b/getting_started/scripting/gdscript/gdscript_advanced.rst index e89dec624..f3ab20284 100644 --- a/getting_started/scripting/gdscript/gdscript_advanced.rst +++ b/getting_started/scripting/gdscript/gdscript_advanced.rst @@ -425,37 +425,37 @@ functions in your script. An example implementation of a forward iterator follow :: - class FwdIterator: - var start, curr, end, increment + class ForwardIterator: + var start + var current + var end + var increment - func _init(start, stop, inc): + func _init(start, stop, increment): self.start = start - self.curr = start + self.current = start self.end = stop - self.increment = inc + self.increment = increment - func is_done(): - return (curr < end) - - func do_step(): - curr += increment - return is_done() + func should_continue(): + return (current < end) func _iter_init(arg): - curr = start - return is_done() + current = start + return should_continue() func _iter_next(arg): - return do_step() + current += increment + return should_continue() func _iter_get(arg): - return curr + return current And it can be used like any other iterator: :: - var itr = FwdIterator.new(0, 6, 2) + var itr = ForwardIterator.new(0, 6, 2) for i in itr: print(i) # Will print 0, 2, and 4 diff --git a/getting_started/scripting/gdscript/gdscript_basics.rst b/getting_started/scripting/gdscript/gdscript_basics.rst index d1f4816dc..64bb1ac17 100644 --- a/getting_started/scripting/gdscript/gdscript_basics.rst +++ b/getting_started/scripting/gdscript/gdscript_basics.rst @@ -531,7 +531,7 @@ To add a key to an existing dictionary, access it like an existing key and assign to it:: var d = {} # Create an empty Dictionary. - d.waiting = 14 # Add String "Waiting" as a key and assign the value 14 to it. + d.waiting = 14 # Add String "waiting" as a key and assign the value 14 to it. d[4] = "hello" # Add integer 4 as a key and assign the String "hello" as its value. d["Godot"] = 3.01 # Add String "Godot" as a key and assign the value 3.01 to it. diff --git a/getting_started/scripting/gdscript/static_typing.rst b/getting_started/scripting/gdscript/static_typing.rst index 77b56c100..e769c0a98 100644 --- a/getting_started/scripting/gdscript/static_typing.rst +++ b/getting_started/scripting/gdscript/static_typing.rst @@ -288,7 +288,7 @@ And the same callback, with type hints: func _on_area_entered(area: CollisionObject2D) -> void: pass -You’re free to replace, e.g. the ``PhysicsBody2D``, with your own type, +You’re free to replace, e.g. the ``CollisionObject2D``, with your own type, to cast parameters automatically: :: diff --git a/getting_started/step_by_step/animations.rst b/getting_started/step_by_step/animations.rst index ac4bac773..73e561595 100644 --- a/getting_started/step_by_step/animations.rst +++ b/getting_started/step_by_step/animations.rst @@ -51,7 +51,7 @@ The logo will appear from the top of the screen. With the animation editor panel open, select the "logo" node and set the "Rect / Position" property to ``(118, -400)`` and press the key button next -to the property: +to the property to add a keyframe: .. image:: img/robisplash_anim_logo_inspector_key.png @@ -61,12 +61,12 @@ The keyframe will be added in the animation player editor: .. image:: img/robisplash_anim_editor_keyframe.png -Move the editor cursor to the end by clicking here: +Move the editor cursor forward in time by clicking here: .. image:: img/robisplash_anim_editor_track_cursor.png Change the logo position to ``(118, 0)`` and add a keyframe again. With two -keyframes, the animation happens. +keyframes with different values, the animation happens. .. image:: img/robisplash_anim_editor_keyframe_2.png diff --git a/getting_started/step_by_step/exporting.rst b/getting_started/step_by_step/exporting.rst index 24bd75617..8bce4fdb9 100644 --- a/getting_started/step_by_step/exporting.rst +++ b/getting_started/step_by_step/exporting.rst @@ -117,7 +117,7 @@ changed: func _on_Player_body_entered( body ): hide() emit_signal("hit") - $CollisionShape2D.call_deferred("set_disabled", true) + $CollisionShape2D.set_deferred("disabled", true) Export templates ---------------- diff --git a/getting_started/step_by_step/files/dodge_assets.zip b/getting_started/step_by_step/files/dodge_assets.zip index 567d4e55e..eb9c5a9df 100644 Binary files a/getting_started/step_by_step/files/dodge_assets.zip and b/getting_started/step_by_step/files/dodge_assets.zip differ diff --git a/getting_started/step_by_step/filesystem.rst b/getting_started/step_by_step/filesystem.rst index 02817629d..c3a1b03fb 100644 --- a/getting_started/step_by_step/filesystem.rst +++ b/getting_started/step_by_step/filesystem.rst @@ -6,16 +6,10 @@ File system Introduction ------------ -File systems are yet another hot topic in engine development. The -file system manages how the assets are stored and how they are accessed. +A file system manages how assets are stored and how they are accessed. A well-designed file system also allows multiple developers to edit the -same source files and assets while collaborating. - -Initial versions of the Godot engine (and previous iterations before it was -named Godot) used a database. Assets were stored in it and assigned an -ID. Other approaches were tried as well, such as local databases, files with -metadata, etc. In the end, the simple approach won and now Godot stores -all assets as files in the file system. +same source files and assets while collaborating. Godot stores +all assets as files in its file system. Implementation -------------- @@ -27,12 +21,11 @@ included. If a resource has sub-resources that are built-in, the resource is saved in a single file together with all the bundled sub-resources. For example, a font resource is often bundled together with the font textures. -In general, the Godot file system avoids using metadata files. The reason for -this is simple, existing asset managers and VCSs are simply much better than +The Godot file system avoids using metadata files. Existing asset managers and VCSs are better than anything we can implement, so Godot tries its best to play along with SVN, Git, Mercurial, Perforce, etc. -Example of a file system contents: +Example of file system contents: :: @@ -69,7 +62,7 @@ cumbersome and non-portable. To solve this problem, the special path ``res://`` was created. The path ``res://`` will always point at the project root (where -project.godot is located, so in fact ``res://project.godot`` is always +project.godot is located, so ``res://project.godot`` is always valid). This file system is read-write only when running the project locally from @@ -80,7 +73,7 @@ read-only and writing will no longer be permitted. User path --------- -Writing to disk is often still needed for various tasks such as saving game +Writing to disk is still needed for tasks such as saving game state or downloading content packs. To this end, the engine ensures that there is a special path ``user://`` that is always writable. @@ -90,7 +83,7 @@ Host file system Alternatively host file system paths can also be used, but this is not recommended for a released product as these paths are not guaranteed to work on all platforms. However, using host file system paths can be useful when writing development -tools in Godot! +tools in Godot. Drawbacks --------- @@ -112,5 +105,5 @@ on other platforms, such as Linux, Android, etc. This may also apply to exported which use a compressed package to store all files. It is recommended that your team clearly define a naming convention for files when -working with Godot! One simple fool-proof convention is to only allow lowercase +working with Godot. One simple fool-proof convention is to only allow lowercase file and path names. diff --git a/getting_started/step_by_step/instancing.rst b/getting_started/step_by_step/instancing.rst index 681be6845..29b569fd5 100644 --- a/getting_started/step_by_step/instancing.rst +++ b/getting_started/step_by_step/instancing.rst @@ -104,7 +104,7 @@ Press "Play" and notice that all of the instanced balls are now much more bouncy. Because the instanced balls are based on the saved scene, changes to that scene will affect all instances. -You can also adjust individual instances. Set the bounce value back to ``0.5`` +You can also adjust individual instances. Set the bounce value back to ``0`` and then in the ``Main`` scene, select one of the instanced balls. Set its ``Bounce`` to ``1`` and press "Play". diff --git a/getting_started/step_by_step/instancing_continued.rst b/getting_started/step_by_step/instancing_continued.rst index cff7ec4f9..f3f9b57df 100644 --- a/getting_started/step_by_step/instancing_continued.rst +++ b/getting_started/step_by_step/instancing_continued.rst @@ -17,7 +17,7 @@ Design language --------------- But the greatest strength that comes with instancing scenes is that it works -as an excellent design language. This is pretty much what distinguishes Godot +as an excellent design language. This distinguishes Godot from all the other engines out there. Godot was designed from the ground up around this concept. diff --git a/getting_started/step_by_step/resources.rst b/getting_started/step_by_step/resources.rst index 1143b85d7..9fab30667 100644 --- a/getting_started/step_by_step/resources.rst +++ b/getting_started/step_by_step/resources.rst @@ -135,7 +135,7 @@ returns the root node of the scene. You can then add it as a child of any other node. The approach has several advantages. As the :ref:`PackedScene.instance() -` function is pretty fast, you can create new +` function is fast, you can create new enemies, bullets, effects, etc. without having to load them again from disk each time. Remember that, as always, images, meshes, etc. are all shared between the scene instances. diff --git a/getting_started/step_by_step/scene_tree.rst b/getting_started/step_by_step/scene_tree.rst index 79498254e..e63db2731 100644 --- a/getting_started/step_by_step/scene_tree.rst +++ b/getting_started/step_by_step/scene_tree.rst @@ -6,18 +6,10 @@ SceneTree Introduction ------------ -This is where things start getting abstract, but don't panic. There's -not much more depth than this. - In previous tutorials, everything revolved around the concept of -nodes. Scenes are simply a collection of nodes. They become active once +nodes. Scenes are collections of nodes. They become active once they enter the *scene tree*. -This concept deserves going into a little more detail. In fact, the -scene system is not even a core component of Godot as it is possible to -skip it and write a script (or C++ code) that talks directly to the -servers, but making a game that way would be a lot of work. - MainLoop -------- @@ -46,7 +38,7 @@ game engine over a low level middleware. The scene system is the game engine, while the :ref:`OS ` and servers are the low level API. -In any case, the scene system provides its own main loop to OS, +The scene system provides its own main loop to OS, :ref:`SceneTree `. This is automatically instanced and set when running a scene, no need to do any extra work. @@ -64,7 +56,7 @@ important uses: When a node is part of the Scene Tree, the :ref:`SceneTree ` -singleton can be obtained by simply calling +singleton can be obtained by calling :ref:`Node.get_tree() `. Root viewport @@ -85,10 +77,10 @@ two different ways: GetTree().GetRoot(); // Access via scene main loop. GetNode("/root"); // Access via absolute path. -This node contains the main viewport, anything that is a child of a +This node contains the main viewport. Anything that is a child of a :ref:`Viewport ` is drawn inside of it by default, so it makes sense that the top of all -nodes is always a node of this type otherwise nothing would be seen! +nodes is always a node of this type otherwise nothing would be seen. While other viewports can be created in the scene (for split-screen effects and such), this one is the only one that is never created by the @@ -106,16 +98,16 @@ _enter_tree() and _ready() callbacks (as well as _exit_tree()). .. image:: img/activescene.png When nodes enter the *Scene Tree*, they become active. They get access -to everything they need to process, get input, display 2D and 3D, -notifications, play sound, groups, etc. When they are removed from the -*scene tree*, they lose access. +to everything they need to process, get input, display 2D and 3D visuals, +receive and send notifications, play sounds, etc. When they are removed from the +*scene tree*, they lose these abilities. Tree order ---------- Most node operations in Godot, such as drawing 2D, processing, or getting notifications are done in tree order. This means that parents and -siblings with a smaller rank in the tree order will get notified before +siblings with a lower rank in the tree order will get notified before the current node. .. image:: img/toptobottom.png @@ -181,7 +173,7 @@ function These are quick and useful ways to switch scenes but have the drawback that the game will stall until the new scene is loaded and running. At -some point in your game, it may be desired to create proper loading +some point in the development of your game, it may be preferable to create proper loading screens with progress bar, animated indicators or thread (background) -loading. This must be done manually using autoloads (see next chapter!) -and :ref:`doc_background_loading`. \ No newline at end of file +loading. This must be done manually using autoloads (see next chapter) +and :ref:`doc_background_loading`. diff --git a/getting_started/step_by_step/scenes_and_nodes.rst b/getting_started/step_by_step/scenes_and_nodes.rst index 2bd4b92f1..aa2ef167a 100644 --- a/getting_started/step_by_step/scenes_and_nodes.rst +++ b/getting_started/step_by_step/scenes_and_nodes.rst @@ -162,6 +162,13 @@ demo should finally execute: Success! +.. note:: + + If this doesn't immediately work and you have a hiDPI display on + at least one of your monitors, go to + **Project → Project Settings → Display → Window** then enable + **Allow Hidpi** under **Dpi**. + .. _doc_scenes_and_nodes-configuring_the_project: Configuring the project diff --git a/getting_started/step_by_step/scripting_continued.rst b/getting_started/step_by_step/scripting_continued.rst index 15ca4c804..d214acd57 100644 --- a/getting_started/step_by_step/scripting_continued.rst +++ b/getting_started/step_by_step/scripting_continued.rst @@ -17,8 +17,7 @@ Idle processing is activated when the method :ref:`Node._process() ` function. -This method will be called every time a frame is drawn, so it's fully dependent on -how many frames per second (FPS) the application is running at: +This method will be called every time a frame is drawn: .. tabs:: .. code-tab:: gdscript GDScript @@ -34,8 +33,12 @@ how many frames per second (FPS) the application is running at: // Do something... } -The delta parameter contains the time elapsed in seconds, as a -floating point, since the previous call to ``_process()``. +It's important to bear in mind that the frequecy with which ``_process()`` +will be called depends on how many frames per second (FPS) your application +is running at. This rate can vary over time and devices. + +To help manage this variability the ``delta`` parameter contains the time +elapsed in seconds, as a floating point, since the previous call to ``_process()``. This parameter can be used to make sure things always take the same amount of time, regardless of the game's FPS. @@ -52,7 +55,7 @@ Physics -> Common -> Physics Fps. The function ``_process()``, however, is not synced with physics. Its frame rate is not constant and is dependent on hardware and game optimization. Its execution is done after the physics step on single-threaded games. -A simple way to test this is to create a scene with a single Label node, +A simple way to see the ``_process()`` function at work is to create a scene with a single Label node, with the following script: .. tabs:: @@ -84,13 +87,15 @@ Which will show a counter increasing each frame. Groups ------ -Nodes can be added to groups, as many as desired per node, and is a useful feature for organizing large scenes. -There are two ways to do this. The first is from the UI, from the Groups button under the Node panel: +Groups in Godot work like tags you might have come across in other software. +A node can be added to as many groups as desired. This is a useful feature for +organizing large scenes. There are two ways to do add nodes to groups. The +first is from the UI, using the Groups button under the Node panel: .. image:: img/groups_in_nodes.png -And the second way is from code. One example would be to tag nodes -which are enemies: +And the second way is from code. The following script would add the current +node to the ``enemies`` group as soon as it appeared in the scene tree. .. tabs:: .. code-tab:: gdscript GDScript @@ -145,7 +150,7 @@ like interacting with scenes, their node hierarchy and groups of nodes. It allows you to easily switch scenes or reload them, to quit the game or pause and unpause it. It even comes with interesting signals. -So check it out if you got some time! +So check it out if you have some time! Notifications ------------- @@ -310,7 +315,7 @@ used: _sprite.Free(); // Immediately removes the node from the scene and frees it. } -When a node is freed, it also frees all its children nodes. Because of +When a node is freed, it also frees all its child nodes. Because of this, manually deleting nodes is much simpler than it appears. Free the base node and everything else in the subtree goes away with it. diff --git a/getting_started/step_by_step/signals.rst b/getting_started/step_by_step/signals.rst index 708eeda49..7592bc62a 100644 --- a/getting_started/step_by_step/signals.rst +++ b/getting_started/step_by_step/signals.rst @@ -15,8 +15,8 @@ button can emit a signal when it's pressed. Signals are a way to *decouple* your game objects, which leads to better organized and more manageable code. Instead of forcing game objects to expect other objects -to always be present, they can instead emit signals that any interested objects can -subscribe to and respond. +to always be present, they can instead emit signals that all interested objects can +subscribe to and respond to. Below you can see some examples of how you can use signals in your own projects. @@ -25,8 +25,11 @@ Timer example To see how signals work, let's try using a :ref:`Timer ` node. Create a new scene with a Node and two children: a Timer and a :ref:`Sprite `. -You can use the Godot icon for the Sprite's texture, or any other image you -like. Attach a script to the root node, but don't add any code to it yet. +In the Scene dock, rename Node to TimerExample. + +For the Sprite's texture, you can use the Godot icon, or any other image you +like. Do so by selecting ``Load`` in the Sprite's Texture attribute drop-down menu. +Attach a script to the root node, but don't add any code to it yet. Your scene tree should look like this: @@ -90,6 +93,8 @@ the signal is received. Let's make the Sprite blink: extends Node func _on_Timer_timeout(): + # Note: the `$` operator is a shorthand for `get_node()`, + # so `$Sprite` is equivalent to `get_node("Sprite")`. $Sprite.visible = !$Sprite.visible .. code-tab:: csharp @@ -174,7 +179,7 @@ You can also declare your own custom signals in Godot: Once declared, your custom signals will appear in the Inspector and can be connected in the same way as a node's built-in signals. -To emit a signal via code, use the ``emit`` function: +To emit a signal via code, use the ``emit_signal`` function: .. tabs:: .. code-tab:: gdscript GDScript @@ -199,126 +204,6 @@ To emit a signal via code, use the ``emit`` function: } } -Shooting example ----------------- - -As another example of signal usage, let's consider a player character that can -rotate and shoot towards the mouse. Every time the mouse button is clicked, -we create an instance of the bullet at the player's location. See :ref:`doc_instancing` -for details. - -However, if the bullets are added as children of the player, then they will -remain "attached" to the player as it rotates: - -.. image:: img/signals_shoot1.gif - -Instead, we need the bullets to be independent of the player's movement - once -fired, they should continue traveling in a straight line and the player can no -longer affect them. Instead of being added to the scene tree as a child of the -player, it makes more sense to add the bullet as a child of the "main" game -scene, which may be the player's parent or even further up the tree. - -You could do this by adding the bullet directly: - -.. tabs:: - .. code-tab:: gdscript GDScript - - var bullet_instance = Bullet.instance() - get_parent().add_child(bullet_instance) - - .. code-tab:: csharp - - Node bulletInstance = Bullet.Instance(); - GetParent().AddChild(bulletInstance); - -However, this will lead to a different problem. Now if you try and test your -"Player" scene independently, it will crash on shooting, because there is no -parent node to access. This makes it a lot harder to test your player code -independently and also means that if you decide to change your main scene's -node structure, the player's parent may no longer be the appropriate node to -receive the bullets. - -The solution to this is to use a signal to "emit" the bullets from the player. -The player then has no need to "know" what happens to the bullets after that - -whatever node is connected to the signal can "receive" the bullets and take the -appropriate action to spawn them. - - -Here is the code for the player using signals to emit the bullet: - -.. tabs:: - .. code-tab:: gdscript GDScript - - extends Sprite - - signal shoot(bullet, direction, location) - - var Bullet = preload("res://Bullet.tscn") - - func _input(event): - if event is InputEventMouseButton: - if event.button_index == BUTTON_LEFT and event.pressed: - emit_signal("shoot", Bullet, rotation, position) - - func _process(delta): - look_at(get_global_mouse_position()) - - .. code-tab:: csharp - - public class Player : Sprite - { - [Signal] - delegate void Shoot(PackedScene bullet, Vector2 direction, Vector2 location); - - private PackedScene _bullet = GD.Load("res://Bullet.tscn"); - - public override void _Input(InputEvent event) - { - if (input is InputEventMouseButton mouseButton) - { - if (mouseButton.ButtonIndex == (int)ButtonList.Left && mouseButton.Pressed) - { - EmitSignal(nameof(Shoot), _bullet, Rotation, Position); - } - } - } - - public override _Process(float delta) - { - LookAt(GetGlobalMousePosition()); - } - } - - -In the main scene, we then connect the player's signal (it will appear in the -"Node" tab). - -.. tabs:: - .. code-tab:: gdscript GDScript - - func _on_Player_shoot(Bullet, direction, location): - var b = Bullet.instance() - add_child(b) - b.rotation = direction - b.position = location - b.velocity = b.velocity.rotated(direction) - - .. code-tab:: csharp - - public void _on_Player_Shoot(PackedScene bullet, Vector2 direction, Vector2 location) - { - var bulletInstance = (Bullet)bullet.Instance(); - AddChild(bulletInstance); - bulletInstance.Rotation = direction; - bulletInstance.Position = location; - bulletInstance.Velocity = bulletInstance.Velocity.Rotated(direction); - } - -Now the bullets will maintain their own movement independent of the player's -rotation: - -.. image:: img/signals_shoot2.gif - Conclusion ---------- diff --git a/getting_started/step_by_step/singletons_autoload.rst b/getting_started/step_by_step/singletons_autoload.rst index 26e98a5bc..df5b5b0a6 100644 --- a/getting_started/step_by_step/singletons_autoload.rst +++ b/getting_started/step_by_step/singletons_autoload.rst @@ -33,7 +33,7 @@ Using this concept, you can create objects that: - Can handle switching scenes and between-scene transitions - Act like a singleton, since GDScript does not support global variables by design -Autoloading nodes and scripts caters to this need. +Autoloading nodes and scripts can give us these characteristics. AutoLoad -------- @@ -270,7 +270,7 @@ and } Run the project and test that you can switch between scenes by pressing -the button! +the button. Note: When scenes are small, the transition is instantaneous. However, if your scenes are more complex, they may take a noticeable amount of time to appear. To diff --git a/getting_started/step_by_step/ui_introduction_to_the_ui_system.rst b/getting_started/step_by_step/ui_introduction_to_the_ui_system.rst index 48e2297af..957d11399 100644 --- a/getting_started/step_by_step/ui_introduction_to_the_ui_system.rst +++ b/getting_started/step_by_step/ui_introduction_to_the_ui_system.rst @@ -7,10 +7,7 @@ Computer displays, mobile phones, and TV screen come in all shapes and sizes. To ship a game, you'll need to support different screen ratios and resolutions. It can be hard to build responsive interfaces that adapt to all platforms. Thankfully, Godot comes with robust tools to -design and manage a responsive User Interface. To design your UI, you'll -use the Control nodes. These are the nodes with green icons in the -editor. There are dozens of them, to create anything from life bars to -complex applications. Godot's entire editor and plugins use these nodes. +design and manage a responsive User Interface. .. figure:: img/godot_editor_ui.png @@ -18,21 +15,23 @@ complex applications. Godot's entire editor and plugins use these nodes. This guide will get you started with UI design. You will learn: -- The five most useful control nodes to build your games’ interface +- The five most useful control nodes to build your games' interface - How to work with the anchor of UI elements - How to efficiently place and arrange your user interface using containers -- The five most common containers +- The five most common containers (at a later time, you can learn more about containers in + the :ref:`GUI Containers ` documentation page). To learn how to control the interface and connect it to other scripts, read :ref:`Build your first game UI in Godot `. -Only use Control nodes when you design your interfaces. They have unique -properties that allow them to work with one another. Other nodes, like -Node2D, Sprite, etc. will not work. You can still use some nodes that -work with others, like the AnimationPlayer, Tween or the StreamPlayer. -Control nodes are CanvasItems like Node2D, so you can apply shaders to -them. +To design your UI, you'll use the Control nodes. These are the nodes with green icons in the +editor. There are dozens of them, for creating anything from life bars to +complex applications. Godot's editor itself is built using Control nodes. + +Control nodes have unique properties that allow them to work well with one another. +Other visual nodes, like Node2D and Sprite don't have these capabilities. So to +make your life easier use Control nodes wherever possible when building your UIs. All control nodes share the same main properties: @@ -75,14 +74,18 @@ TextureRect It seems similar to the Sprite node, but it offers multiple scaling modes. Set the Stretch Mode property to change its behavior: -- ``Scale On Expand (compat)`` scales the texture to fit the node’s bounding rectangle, only if ``expand`` property is ``true``; otherwise, it behaves like ``Keep`` mode. Default mode for backwards compatibility. -- ``Scale`` scales the texture to fit the node’s bounding rectangle -- ``Tile`` makes the texture repeat, but it won't scale +- ``Scale On Expand (compat)`` scales the texture to fit the node's bounding rectangle, + only if ``expand`` property is ``true``; otherwise, it behaves like ``Keep`` mode. + Default mode for backwards compatibility. +- ``Scale`` scales the texture to fit the node's bounding rectangle. +- ``Tile`` makes the texture repeat, but it won't scale. - ``Keep`` and ``Keep Centered`` force the texture to remain at its original size, in the top left corner or the center of the frame - respectively -- ``Keep Aspect`` and ``Keep Aspect Centered`` scales the texture but force it to remain its original aspect ratio, in the top left corner or the center of the frame respectively -- ``Keep Aspect Covered`` works just like ``Keep Aspect Centered`` but the shorter side fits the bounding rectangle and the other one clips to the node’s limits + respectively. +- ``Keep Aspect`` and ``Keep Aspect Centered`` scales the texture but force it to remain + its original aspect ratio, in the top left corner or the center of the frame respectively. +- ``Keep Aspect Covered`` works just like ``Keep Aspect Centered`` but the shorter side + fits the bounding rectangle and the other one clips to the node's limits. As with Sprite nodes, you can modulate the TextureRect's color. Click the ``Modulate`` property and use the color picker. @@ -195,7 +198,7 @@ Like any properties, you can edit the 4 anchor points in the Inspector, but this is not the most convenient way. When you select a control node, the layout menu appears above the viewport, in the toolbar. It gives you a list of icons to set all 4 anchors with a single click, instead of -using the inspector’s 4 properties. The layout menu will only show up +using the inspector's 4 properties. The layout menu will only show up when you select a control node. .. figure:: img/layout_menu.png diff --git a/getting_started/step_by_step/your_first_game.rst b/getting_started/step_by_step/your_first_game.rst index 3f31494bb..1694b6884 100644 --- a/getting_started/step_by_step/your_first_game.rst +++ b/getting_started/step_by_step/your_first_game.rst @@ -459,7 +459,7 @@ Add this code to the function: func _on_Player_body_entered(body): hide() # Player disappears after being hit. emit_signal("hit") - $CollisionShape2D.call_deferred("set_disabled", true) + $CollisionShape2D.set_deferred("disabled", true) .. code-tab:: csharp @@ -475,7 +475,7 @@ to disable the player's collision so that we don't trigger the ``hit`` signal more than once. .. Note:: Disabling the area's collision shape can cause an error if it happens - in the middle of the engine's collision processing. Using ``call_deferred()`` + in the middle of the engine's collision processing. Using ``set_deferred()`` allows us to have Godot wait to disable the shape until it's safe to do so. @@ -1125,6 +1125,39 @@ sync with the changing score: Now you're ready to play! Click the "Play the Project" button. You will be asked to select a main scene, so choose ``Main.tscn``. +Removing old creeps +~~~~~~~~~~~~~~~~~~~ + +If you play until "Game Over" and then start a new game the creeps from the +previous game are still on screen. It would be better if they all disappeared +at the start of a new game. + +We'll use the ``start_game`` signal that's already being emitted by the ``HUD`` +node to remove the remaining creeps. We can't use the editor to connect the +signal to the mobs in the way we need because there are no ``Mob`` nodes in the +``Main`` scene tree until we run the game. Instead we'll use code. + +Start by adding a new function to ``Mob.gd``. ``queue_free()`` will delete the +current node at the end of the current frame. + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _on_start_game(): + queue_free() + +Then in ``Main.gd`` add a new line inside the ``_on_MobTimer_timeout()`` function, +at the end. + +.. tabs:: + .. code-tab:: gdscript GDScript + + $HUD.connect("start_game", mob, "_on_start_game") + +This line tells the new Mob node (referenced by the ``mob`` variable) to respond +to any ``start_game`` signal emitted by the ``HUD`` node by running its +``_on_start_game()`` function. + Finishing up ------------ diff --git a/getting_started/workflow/assets/escn_exporter/skeleton.rst b/getting_started/workflow/assets/escn_exporter/skeleton.rst index 026794751..ea4cb5177 100644 --- a/getting_started/workflow/assets/escn_exporter/skeleton.rst +++ b/getting_started/workflow/assets/escn_exporter/skeleton.rst @@ -15,6 +15,9 @@ rest position (transform in Godot) of bones. armature in Blender, so that the exported bone transform be consistent between Blender and Godot +It is important that the mesh is not deformed by bones when exporting in Blender. Make sure +that the skeleton is reset to its T-pose or default rest pose. + Bone Weights ------------ diff --git a/getting_started/workflow/assets/importing_scenes.rst b/getting_started/workflow/assets/importing_scenes.rst index f56b16e2d..59fb33977 100644 --- a/getting_started/workflow/assets/importing_scenes.rst +++ b/getting_started/workflow/assets/importing_scenes.rst @@ -19,6 +19,9 @@ Godot supports the following 3D *scene file fomats*: Just copy the scene file together with the texture to the project repository, and Godot will do a full import. +It is important that the mesh is not deformed by bones when exporting. Make sure that the skeleton is reset to its T-pose +or default rest pose before exporting with your favorite 3D editor. + Why not FBX? ~~~~~~~~~~~~ diff --git a/getting_started/workflow/best_practices/data_preferences.rst b/getting_started/workflow/best_practices/data_preferences.rst index f8385ddc5..fdc1f6f97 100644 --- a/getting_started/workflow/best_practices/data_preferences.rst +++ b/getting_started/workflow/best_practices/data_preferences.rst @@ -240,7 +240,7 @@ tree structures. func _notification(p_what): match p_what: NOTIFICATION_PREDELETE: - # destructor + # Destructor. for a_child in _children: a_child.free() diff --git a/getting_started/workflow/best_practices/godot_interfaces.rst b/getting_started/workflow/best_practices/godot_interfaces.rst index 20338c38b..d51697569 100644 --- a/getting_started/workflow/best_practices/godot_interfaces.rst +++ b/getting_started/workflow/best_practices/godot_interfaces.rst @@ -21,13 +21,13 @@ is to get a reference to an existing object from another acquired instance. .. tabs:: .. code-tab:: gdscript GDScript - var obj = node.object # Property access - var obj = node.get_object() # Method access + var obj = node.object # Property access. + var obj = node.get_object() # Method access. .. code-tab:: csharp - Object obj = node.Object; // Property access - Object obj = node.GetObject(); // Method access + Object obj = node.Object; // Property access. + Object obj = node.GetObject(); // Method access. The same principle applies for :ref:`Reference ` objects. While users often access :ref:`Node ` and @@ -207,22 +207,22 @@ Nodes likewise have an alternative access point: the SceneTree. public object Prop; public void CallMeAfterPropIsInitializedByParent() { - // Validate prop in one of three ways + // Validate prop in one of three ways. - // Fail with no notification + // Fail with no notification. if (prop == null) { return; } - // Fail with an error message + // Fail with an error message. if (prop == null) { GD.PrintErr("'Prop' wasn't initialized"); return; } - // Fail and terminate + // Fail and terminate. Debug.Assert(Prop, "'Prop' wasn't initialized"); } @@ -288,7 +288,7 @@ accesses: .. tabs:: .. code-tab:: gdscript GDScript - # All Objects have duck-typed get, set, and call wrapper methods + # All Objects have duck-typed get, set, and call wrapper methods. get_parent().set("visible", false) # Using a symbol accessor, rather than a string in the method call, @@ -317,7 +317,7 @@ accesses: .. tabs:: .. code-tab:: gdscript GDScript - var child = GetChild(0) + var child = get_child(0) # Dynamic lookup. child.call("set_visible", false) @@ -353,13 +353,13 @@ accesses: # A "Quest" object exists and 1) that it can "complete" or "fail" and # that it will have text available before and after each state... - # 1. Use a name + # 1. Use a name. var quest = $Quest print(quest.text) quest.complete() # or quest.fail() print(quest.text) # implied new text content - # 2. Use a group + # 2. Use a group. for a_child in get_children(): if a_child.is_in_group("quest"): print(quest.text) @@ -374,10 +374,10 @@ accesses: Node child = GetChild(0); - // Dynamic lookup + // Dynamic lookup. child.Call("SetVisible", false); - // Dynamic lookup, checks for method existence first + // Dynamic lookup, checks for method existence first. if (child.HasMethod("SetVisible")) { child.Call("SetVisible", false); @@ -395,7 +395,7 @@ accesses: child.Call("Reject"); } - // Cast check, followed by static lookup + // Cast check, followed by static lookup. CanvasItem ci = GetParent() as CanvasItem; if (ci != null) { @@ -419,13 +419,13 @@ accesses: // A "Quest" object exists and 1) that it can "Complete" or "Fail" and // that it will have Text available before and after each state... - // 1. Use a name + // 1. Use a name. Node quest = GetNode("Quest"); GD.Print(quest.Get("Text")); quest.Call("Complete"); // or "Fail". GD.Print(quest.Get("Text")); // Implied new text content. - // 2. Use a group + // 2. Use a group. foreach (Node AChild in GetChildren()) { if (AChild.IsInGroup("quest")) diff --git a/getting_started/workflow/best_practices/logic_preferences.rst b/getting_started/workflow/best_practices/logic_preferences.rst index 154ef37da..162f1cbcb 100644 --- a/getting_started/workflow/best_practices/logic_preferences.rst +++ b/getting_started/workflow/best_practices/logic_preferences.rst @@ -30,8 +30,8 @@ either? Let's see an example: # my_buildings.gd extends Node - # (note how constant scripts/scenes have a different naming scheme than - # their property variants). + # Note how constant scripts/scenes have a different naming scheme than + # their property variants. # This value is a constant, so it spawns when the Script object loads. # The script is preloading the value. The advantage here is that the editor @@ -73,14 +73,14 @@ either? Let's see an example: // C# and other languages have no concept of "preloading". public class MyBuildings : Node { - //This is a read-only field, it can only be assigned when it's declared or during a constructor + //This is a read-only field, it can only be assigned when it's declared or during a constructor. public readonly PackedScene Building = ResourceLoader.Load("res://building.tscn"); public PackedScene ABuilding; public override void _Ready() { - // Can assign the value during initialization + // Can assign the value during initialization. ABuilding = GD.Load("res://office.tscn"); } } diff --git a/index.rst b/index.rst index 89f5ec4e3..c73cbfd4f 100644 --- a/index.rst +++ b/index.rst @@ -104,8 +104,10 @@ The main documentation for the site is organized into the following sections: tutorials/platform/index tutorials/threads/index tutorials/content/index + tutorials/optimization/index tutorials/misc/index tutorials/debug/index + tutorials/legal/index .. toctree:: diff --git a/tutorials/2d/2d_movement.rst b/tutorials/2d/2d_movement.rst index 2d465dffd..5bc7dae7f 100644 --- a/tutorials/2d/2d_movement.rst +++ b/tutorials/2d/2d_movement.rst @@ -59,7 +59,7 @@ Add a script to the kinematic body and add the following code: func _physics_process(delta): get_input() - move_and_slide(velocity) + velocity = move_and_slide(velocity) .. code-tab:: csharp @@ -94,7 +94,7 @@ Add a script to the kinematic body and add the following code: public override void _PhysicsProcess(float delta) { GetInput(); - MoveAndSlide(velocity); + velocity = MoveAndSlide(velocity); } } @@ -144,7 +144,7 @@ while up/down moves it forward or backward in whatever direction it's facing. func _physics_process(delta): get_input() rotation += rotation_dir * rotation_speed * delta - move_and_slide(velocity) + velocity = move_and_slide(velocity) .. code-tab:: csharp @@ -183,7 +183,7 @@ while up/down moves it forward or backward in whatever direction it's facing. { GetInput(); Rotation += rotationDir * RotationSpeed * delta; - MoveAndSlide(velocity); + velocity = MoveAndSlide(velocity); } } @@ -224,7 +224,7 @@ is set by the mouse position instead of the keyboard. The character will always func _physics_process(delta): get_input() - move_and_slide(velocity) + velocity = move_and_slide(velocity) .. code-tab:: csharp @@ -254,7 +254,7 @@ is set by the mouse position instead of the keyboard. The character will always public override void _PhysicsProcess(float delta) { GetInput(); - MoveAndSlide(velocity); + velocity = MoveAndSlide(velocity); } } @@ -298,7 +298,7 @@ on the screen will cause the player to move to the target location. velocity = (target - position).normalized() * speed # rotation = velocity.angle() if (target - position).length() > 5: - move_and_slide(velocity) + velocity = move_and_slide(velocity) .. code-tab:: csharp @@ -326,7 +326,7 @@ on the screen will cause the player to move to the target location. // Rotation = velocity.Angle(); if ((target - Position).Length() > 5) { - MoveAndSlide(velocity); + velocity = MoveAndSlide(velocity); } } } diff --git a/tutorials/2d/2d_sprite_animation.rst b/tutorials/2d/2d_sprite_animation.rst new file mode 100644 index 000000000..c8ea29532 --- /dev/null +++ b/tutorials/2d/2d_sprite_animation.rst @@ -0,0 +1,155 @@ +.. _doc_2d_sprite_animation: + +2D Sprite animation +=================== + +Introduction +------------ + +In this tutorial, you'll learn two different ways to create 2D animated +characters. Typically, when you create or download an animated character, it +will come in one of two ways: as individual images or as a single sprite sheet +containing all the animation's frames. Depending on which type of assets you +have, you can choose one of the following solutions. + +First, we'll use :ref:`AnimatedSprite ` to +animate a collection of individual images. Then, to use a sprite sheet, we'll +use :ref:`AnimationPlayer ` along with the *Animation* +property of :ref:`Sprite `. + +.. note:: Art for the following examples by https://opengameart.org/users/ansimuz + +Individual images with AnimatedSprite +------------------------------------- + +In this scenario, you have a collection of images, each containing one of your +character's animation frames. For this example, we'll use the following +animation: + +.. image:: img/2d_animation_run_preview.gif + +You can download the images here: +:download:`run_animation.zip ` + +Unzip the images and place them in your project folder. Set up your scene tree +with the following nodes: + +.. image:: img/2d_animation_tree1.png + +.. note:: The root node could also be :ref:`Area2D ` or + :ref:`RigidBody2D `. The animation will still be + made in the same way. Once the animation is completed, you can + assign a shape to the CollisionShape2D. See + :ref:`Physics Introduction ` for more + information. + +Now select the ``AnimatedSprite`` and in its *SpriteFrames* property, select +"New SpriteFrames". + +.. image:: img/2d_animation_new_spriteframes.png + +Click on the new SpriteFrames resource and you'll see a new panel appear at the +bottom of the editor window: + +.. image:: img/2d_animation_spriteframes.png + +From the FileSystem dock on the left side, drag the 8 individual images into +the center part of the SpriteFrames panel. On the left side, change the name +of the animation from "default" to "run". + +.. image:: img/2d_animation_spriteframes_done.png + +Back in the Inspector, check the box for the *Playing* property. You should +now see the animation playing in the viewport. However, it is a bit slow. To +fix this, change the *Speed (FPS)* setting in the SpriteFrames panel. + +You can add additional animations by clicking the "New Animation" button and +adding additional images. + +Controlling the animation +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once the animation is complete, you can control the animation via code using +the ``play()`` and ``stop()`` methods. Here is a brief example to play the +animation while the right arrow key is held, and stop it when the key is +released. + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends KinematicBody2D + + func _process(delta): + if Input.is_action_pressed("ui_right"): + $AnimatedSprite.play("run") + else: + $AnimatedSprite.stop() + + +Sprite sheet with AnimationPlayer +--------------------------------- + +In the event you have a sprite sheet containing all of your animation frames, +you can't easily use ``AnimatedSprite``. Instead, you can use a standard +:ref:`Sprite ` node to display the texture, and then animate the +change from texture to texture with :ref:`AnimationPlayer `. + +Consider this sprite sheet, which contains 6 frames of animation: + +.. image:: img/2d_animation_player-run.png + +Right-click the image and choose "Save Image As" to download, then copy the +image into your project folder. + +Our goal is to display these images one after another in a loop. Start by +setting up your scene tree: + +.. image:: img/2d_animation_tree2.png + +.. note:: The root node could also be :ref:`Area2D ` or + :ref:`RigidBody2D `. The animation will still be + made in the same way. Once the animation is completed, you can + assign a shape to the CollisionShape2D. See + :ref:`Physics Introduction ` for more + information. + +Drag the spritesheet into the Sprite's *Texture* property, and you'll see the +whole sheet displayed on the screen. To slice it up into individual frames, +expand the *Animation* section in the Inspector and set the *Hframes* to ``6``. +*Hframes* and *Vframes* are the number of horizontal and vertical frames in +your sprite sheet. + +.. image:: img/2d_animation_setframes.png + +Now try changing the value of the *Frame* property. You'll see that it ranges +from ``0`` to ``5`` and the image displayed by the Sprite changes accordingly. +This is the property we'll be animating. + +Select the ``AnimationPlayer`` and click the "Animation" button followed by +"New". Name the new animation "walk". Set the animation length to ``0.6`` and +click the "Loop" button so that our animation will repeat. + +.. image:: img/2d_animation_new_animation.png + +Now select the ``Sprite`` node and click the key icon to add a new track. + +.. image:: img/2d_animation_new_track.png + +Continue adding frames at each point in the timeline (``0.1`` seconds by +default), until you have all the frames from 0 to 5. You'll see the frames +actually appearing in the animation track: + +.. image:: img/2d_animation_full_animation.png + +Press "Play" on the animation to see how it looks. + +.. image:: img/2d_animation_running.gif + +Summary +------- + +These examples illustrate the two most common situations you'll encounter in +2D animation. Each has its benefits. Working with ``AnimationPlayer`` is +a bit more complex, but provides additional functionality, since you can also +animate other properties like position or scale. Experiment and see which +works best for your needs. diff --git a/tutorials/2d/canvas_layers.rst b/tutorials/2d/canvas_layers.rst index dd6f2e140..2729c25a2 100644 --- a/tutorials/2d/canvas_layers.rst +++ b/tutorials/2d/canvas_layers.rst @@ -9,37 +9,29 @@ Viewport and Canvas items Regular 2D nodes, such as :ref:`Node2D ` or :ref:`Control ` both inherit from :ref:`CanvasItem `, which is the base for all 2D -nodes. CanvasItems can be arranged in trees and they will inherit -their transform. This means that when moving the parent, the children -will be moved too. +nodes. CanvasItems can be arranged in trees. Each item will inherit +its parent's transform. This means that when the parent is moved, the children +will move too. -These nodes are placed as direct or indirect children of a +CanvasItem nodes, and nodes inheriting from them, are direct or indirect children of a :ref:`Viewport `, and will be displayed through it. -Viewport has the property +A Viewport has the property :ref:`Viewport.canvas_transform `, -which allows to transform all the CanvasItem hierarchy by a custom -:ref:`Transform2D ` transform. Nodes such as +which allows applying a custom +:ref:`Transform2D ` transform to the CanvasItem hierarchy it contains. Nodes such as :ref:`Camera2D ` work by changing that transform. -Changing the canvas transform is useful because it is a lot more +Effects like scrolling are best achieved by manipulating the canvas transform property. This approach is more efficient than moving the root canvas item (and hence the whole scene). -Canvas transform is a simple matrix that offsets the whole 2D drawing, -so it's the most efficient way to do scrolling. -Not enough... -------------- - -But this is not enough. There are often situations where the game or -application may not want *everything* transformed by the canvas +Usually though, we don't want *everything* in the game or app to be subject to the canvas transform. Examples of this are: - **Parallax Backgrounds**: Backgrounds that move slower than the rest of the stage. -- **HUD**: Heads-up display, or user interface. If the world moves, - the life counter, score, etc. must stay static. -- **Transitions**: Effects used for transitions (fades, blends) may - also want it to remain at a fixed location. +- **UI**: Think of a user interface (UI) or Heads-up display (HUD) superimposed on our view of the game world. We want a life counter, score display and other elements to retain their screen positions even when our view of the game world is changing. +- **Transitions**: We may want visual effects used for transitions (fades, blends) to remain at a fixed screen location. How can these problems be solved in a single scene tree? @@ -52,8 +44,8 @@ children and grand-children. Viewport children will draw by default at layer "0", while a CanvasLayer will draw at any numeric layer. Layers with a greater number will be drawn above those with a smaller number. CanvasLayers also have their own transform and do not depend on the -transform of other layers. This allows the UI to be fixed in-place -while the world moves. +transform of other layers. This allows the UI to be fixed in screen-space +while our view on the game world changes. An example of this is creating a parallax background. This can be done with a CanvasLayer at layer "-1". The screen with the points, life @@ -66,11 +58,8 @@ Here's a diagram of how it looks: CanvasLayers are independent of tree order, and they only depend on their layer number, so they can be instantiated when needed. -Performance ------------ - -Even though there shouldn't be any performance limitation, it is not -advised to use excessive amount of layers to arrange drawing order of -nodes. The most optimal way will always be arranging them by tree order. -2d nodes also have a property for controlling their drawing order -(see :ref:`Node2D.z_index `). +.. note:: CanvasLayers aren't necessary to control the drawing order of nodes. + The standard way to ensuring that a node is correctly drawn 'in front' or 'behind' others is to manipulate the + order of the nodes in the scene panel. Perhaps counterintuitively, the topmost nodes in the scene panel are drawn + on *behind* lower ones in the viewport. 2d nodes also have a property for controlling their drawing order + (see :ref:`Node2D.z_index `). diff --git a/tutorials/2d/custom_drawing_in_2d.rst b/tutorials/2d/custom_drawing_in_2d.rst index 869f74ff6..e934e3911 100644 --- a/tutorials/2d/custom_drawing_in_2d.rst +++ b/tutorials/2d/custom_drawing_in_2d.rst @@ -434,8 +434,8 @@ smaller value, which directly depends on the rendering speed. # We only wrap angles when both of them are bigger than 360. if angle_from > 360 and angle_to > 360: - angle_from = wrap(angle_from, 0, 360) - angle_to = wrap(angle_to, 0, 360) + angle_from = wrapf(angle_from, 0, 360) + angle_to = wrapf(angle_to, 0, 360) update() .. code-tab:: csharp diff --git a/tutorials/2d/files/run_animation.zip b/tutorials/2d/files/run_animation.zip new file mode 100644 index 000000000..3fc00f582 Binary files /dev/null and b/tutorials/2d/files/run_animation.zip differ diff --git a/tutorials/2d/img/2d_animation_full_animation.png b/tutorials/2d/img/2d_animation_full_animation.png new file mode 100644 index 000000000..cd1a52ab7 Binary files /dev/null and b/tutorials/2d/img/2d_animation_full_animation.png differ diff --git a/tutorials/2d/img/2d_animation_new_animation.png b/tutorials/2d/img/2d_animation_new_animation.png new file mode 100644 index 000000000..4c4e2929b Binary files /dev/null and b/tutorials/2d/img/2d_animation_new_animation.png differ diff --git a/tutorials/2d/img/2d_animation_new_spriteframes.png b/tutorials/2d/img/2d_animation_new_spriteframes.png new file mode 100644 index 000000000..59b1cdd3d Binary files /dev/null and b/tutorials/2d/img/2d_animation_new_spriteframes.png differ diff --git a/tutorials/2d/img/2d_animation_new_track.png b/tutorials/2d/img/2d_animation_new_track.png new file mode 100644 index 000000000..a6ac3c58f Binary files /dev/null and b/tutorials/2d/img/2d_animation_new_track.png differ diff --git a/tutorials/2d/img/2d_animation_player-run.png b/tutorials/2d/img/2d_animation_player-run.png new file mode 100644 index 000000000..cc022b1d3 Binary files /dev/null and b/tutorials/2d/img/2d_animation_player-run.png differ diff --git a/tutorials/2d/img/2d_animation_run_preview.gif b/tutorials/2d/img/2d_animation_run_preview.gif new file mode 100644 index 000000000..96e1bb17e Binary files /dev/null and b/tutorials/2d/img/2d_animation_run_preview.gif differ diff --git a/tutorials/2d/img/2d_animation_running.gif b/tutorials/2d/img/2d_animation_running.gif new file mode 100644 index 000000000..dc35b31a8 Binary files /dev/null and b/tutorials/2d/img/2d_animation_running.gif differ diff --git a/tutorials/2d/img/2d_animation_setframes.png b/tutorials/2d/img/2d_animation_setframes.png new file mode 100644 index 000000000..8923170f0 Binary files /dev/null and b/tutorials/2d/img/2d_animation_setframes.png differ diff --git a/tutorials/2d/img/2d_animation_spriteframes.png b/tutorials/2d/img/2d_animation_spriteframes.png new file mode 100644 index 000000000..a8d28ef45 Binary files /dev/null and b/tutorials/2d/img/2d_animation_spriteframes.png differ diff --git a/tutorials/2d/img/2d_animation_spriteframes_done.png b/tutorials/2d/img/2d_animation_spriteframes_done.png new file mode 100644 index 000000000..bbd148589 Binary files /dev/null and b/tutorials/2d/img/2d_animation_spriteframes_done.png differ diff --git a/tutorials/2d/img/2d_animation_tree1.png b/tutorials/2d/img/2d_animation_tree1.png new file mode 100644 index 000000000..8b831b8bb Binary files /dev/null and b/tutorials/2d/img/2d_animation_tree1.png differ diff --git a/tutorials/2d/img/2d_animation_tree2.png b/tutorials/2d/img/2d_animation_tree2.png new file mode 100644 index 000000000..aca61c247 Binary files /dev/null and b/tutorials/2d/img/2d_animation_tree2.png differ diff --git a/tutorials/2d/img/tilemap_add_tile.png b/tutorials/2d/img/tilemap_add_tile.png new file mode 100644 index 000000000..bc22128ee Binary files /dev/null and b/tutorials/2d/img/tilemap_add_tile.png differ diff --git a/tutorials/2d/img/tilemap_add_tileset.png b/tutorials/2d/img/tilemap_add_tileset.png new file mode 100644 index 000000000..54479d80a Binary files /dev/null and b/tutorials/2d/img/tilemap_add_tileset.png differ diff --git a/tutorials/2d/img/tilemap_draw.png b/tutorials/2d/img/tilemap_draw.png new file mode 100644 index 000000000..445ad5f54 Binary files /dev/null and b/tutorials/2d/img/tilemap_draw.png differ diff --git a/tutorials/2d/img/tilemap_menu.png b/tutorials/2d/img/tilemap_menu.png new file mode 100644 index 000000000..b166ff011 Binary files /dev/null and b/tutorials/2d/img/tilemap_menu.png differ diff --git a/tutorials/2d/img/tilemap_mode.png b/tutorials/2d/img/tilemap_mode.png new file mode 100644 index 000000000..9bea02056 Binary files /dev/null and b/tutorials/2d/img/tilemap_mode.png differ diff --git a/tutorials/2d/img/tilemap_size.png b/tutorials/2d/img/tilemap_size.png new file mode 100644 index 000000000..32713aff2 Binary files /dev/null and b/tutorials/2d/img/tilemap_size.png differ diff --git a/tutorials/2d/img/tilemap_tool.png b/tutorials/2d/img/tilemap_tool.png new file mode 100644 index 000000000..b41f0bc0f Binary files /dev/null and b/tutorials/2d/img/tilemap_tool.png differ diff --git a/tutorials/2d/img/tileset_add_collision.png b/tutorials/2d/img/tileset_add_collision.png new file mode 100644 index 000000000..065621215 Binary files /dev/null and b/tutorials/2d/img/tileset_add_collision.png differ diff --git a/tutorials/2d/img/tileset_atlas.png b/tutorials/2d/img/tileset_atlas.png new file mode 100644 index 000000000..9eba838d5 Binary files /dev/null and b/tutorials/2d/img/tileset_atlas.png differ diff --git a/tutorials/2d/img/tileset_draw_atlas.png b/tutorials/2d/img/tileset_draw_atlas.png new file mode 100644 index 000000000..cf609ffd9 Binary files /dev/null and b/tutorials/2d/img/tileset_draw_atlas.png differ diff --git a/tutorials/2d/img/tileset_snap.png b/tutorials/2d/img/tileset_snap.png new file mode 100644 index 000000000..bcf5074ae Binary files /dev/null and b/tutorials/2d/img/tileset_snap.png differ diff --git a/tutorials/2d/img/tilesheet.png b/tutorials/2d/img/tilesheet.png new file mode 100755 index 000000000..642333df8 Binary files /dev/null and b/tutorials/2d/img/tilesheet.png differ diff --git a/tutorials/2d/index.rst b/tutorials/2d/index.rst index 202ed164d..a280f7eb1 100644 --- a/tutorials/2d/index.rst +++ b/tutorials/2d/index.rst @@ -13,3 +13,4 @@ 2d_lights_and_shadows 2d_meshes custom_drawing_in_2d + 2d_sprite_animation diff --git a/tutorials/2d/using_tilemaps.rst b/tutorials/2d/using_tilemaps.rst index e8f6f0fdb..0485c7b3e 100644 --- a/tutorials/2d/using_tilemaps.rst +++ b/tutorials/2d/using_tilemaps.rst @@ -6,178 +6,162 @@ Using tilemaps Introduction ------------ -Tilemaps are a simple and quick way to make 2D game levels. Basically, -you start with a bunch of reference tiles (or pieces) that can be put on a -grid, as many times each as desired - think of it like a map editor: +A tilemap is a grid of tiles used to create a game's layout. There are several +benefits to using :ref:`TileMap ` nodes to design your levels. +First, they make it possible to draw the layout by "painting' the tiles onto a +grid, which is much faster than placing individual :ref:`Sprite ` +nodes one by one. Second, they allow for much larger levels because they are +optimized for drawing large numbers of tiles. Finally, you can add collision, +occlusion, and navigation shapes to tiles, adding additional functionality to +the TileMap. -.. image:: img/tilemap.png +.. image:: img/tileset_draw_atlas.png -Collisions can also be added to the tiles, allowing for both 2D side -scrolling and top down games. - -Making a tileset ----------------- - -To begin, a tileset needs to be made. Here are some tiles for it. -They are all in the same image for optimization reasons. -There are so-called *texture packers* that will generate these spritesheets -out of your separate texture files. -Having them as separate images also works. - -.. image:: img/tileset.png - -Create a new project and move the above PNG image into the directory. Next, -go into the image's import settings and turn off ``Filter``, keeping it on will cause -issues later. ``Mipmaps`` should already be disabled; if not, disable this too. - -We will be creating a :ref:`TileSet ` -resource. While this resource exports properties, it's pretty difficult -to get complex data into it and maintain it. Here is what it would look like to -manually edit the resource: - -.. image:: img/tileset_edit_resource.png - -There are enough properties to get by. With some effort, editing this -way can work. But the easiest way to edit and maintain a tileset is exporting -it from a specially-crafted scene! - -TileSet scene +Project setup ------------- -Create a new scene with a regular Node or Node2D as root. For each tile you want to define, -add a sprite node as a child. Since tiles here are 50x50, you should turn on the grid -(``View -> Show Grid`` or ``G`` key) and enable snap (``Use Snap`` icon or ``Shift + S`` keys). -Moving tiles with the mouse might still be a bit inaccurate, -so use your arrow keys as well. +This demo we'll use the following tiles taken from Kenney's "Abstract Platformer" +art pack. You can find the complete set `here `_ +but for this demo we'll stick to this small set. -If more than one tile is present in the source image, make sure to use -the region property of the sprite to adjust which part of the texture is being -used. +.. image:: img/tilesheet.png -Finally, make sure to name your sprite node correctly. This will ensure -that, in subsequent edits to the tileset (for example, if you've added -collision, changed the region, etc), the tile will still be **identified -correctly and updated**. This name should be unique. +Create a new project and place the above image in the project folder. -Sounds like quite a few requirements, so here's a screenshot that shows -where everything of relevance is: +When using a tileset, it's important that adjacent tiles match up. Godot's default +is to import 2D images using an interpolated "filter" mode, which will result in +ugly borders between the tiles. Select the image and click the Import tab. Turn +off ``Filter`` and click "Reimport". See :ref:`doc_import_images` for details. -.. image:: img/tile_example.png +TileMap node +------------ -Continue adding all the tiles, adjusting the offsets if needed (that is, if you have -multiple tiles in a single source image). Again, *remember that their names must -be unique*. +Add a new :ref:`TileMap ` node to the scene. By default, a TileMap +uses a square grid of tiles. You can also use a perspective-based "Isometric" mode +or define your own custom tile shape. -.. image:: img/tile_example2.png +.. image:: img/tilemap_mode.png -Collision ---------- +Under the "Cell" section in the Inspector are many properties you can adjust to +customize your tilemap's behavior: -To add collision to a tile, create a StaticBody2D child for each sprite. -This is a static collision node. Then create a CollisionShape2D or -CollisionPolygon as a child of the StaticBody2D. The CollisionPolygon is -recommended because it is easier to edit. +.. image:: img/tilemap_size.png -.. image:: img/tile_example3.png +- ``Cell Size`` + This defines the size of the grid. This should match the pixel size + of your tiles. The default value is ``(64, 64)``. -Finally, edit the polygon; this will give the tile a collision and fix -the warning icon next to the CollisionPolygon node. **Remember to use snap!** -Using snap will make sure collision polygons are aligned properly, allowing -a character to walk seamlessly from tile to tile. Also **do not scale or move** -the collision and/or collision polygon nodes. Leave them at offset 0,0, with -scale 1,1 and rotation 0 with respect to the parent sprite. +- ``YSort`` + This causes tiles to be drawn in order of their ``Y`` position, so that + "lower" tiles are drawn on top of "higher" ones. -.. image:: img/tile_example4.png +- ``Half Offset`` and ``Tile Origin`` + These properties affect the position of the tile relative to the grid position. -Keep adding collisions to the tiles until we are done. Note that BG is just -a background, so it should not have a collision. +- ``Quadrant`` + Defines the chunk size used for batched drawing. This can negatively + affect performance. Don't change it unless you know what you're doing. -.. image:: img/tile_example5.png +- ``Custom Transform`` + Used to alter the tile's shape. Use this if you have non-square tiles. -OK! We're done! Remember to save this scene for future edit. Name it -"tileset_edit.scn" or something like that. +All of these options can be left at their defaults for this demo. -Exporting a TileSet -------------------- +Creating a TileSet +------------------ -With the scene created and opened in the editor, the next step will be to -create a tileset. Use Scene > Convert To > Tile Set from the Scene Menu: +Once you've configured your tilemap, it's time to add a +:ref:`TileSet `. A TileSet is a +:ref:`Resource ` that contains the data about your +tiles - their textures, collision shapes, and other properties. When the game +runs, the TileMap combines the individual tiles into a single object. -.. image:: img/tileset_export.png +To add a new TileSet, click on the "Tile Set" property and select "New +TileSet". -Then choose a filename, like "mytiles.tres". Make sure the "Merge With -Existing" option is toggled on. This way, every time the tileset -resource file is overwritten, existing tiles are merged and updated -(they are referenced by their unique name, so again, **name your tiles -properly**). +.. image:: img/tilemap_add_tileset.png -.. image:: img/tileset_merge.png +When you do this, the "TileSet" panel will open at the bottom of the editor +window: -Using the TileSet in a TileMap ------------------------------- +.. image:: img/tilemap_tool.png -Create a new scene, using any node or node2d as root, and then create a -:ref:`TileMap ` as -a child. +First, you need to add the texture(s) that you'll use for the tiles. Click the +"Add Texture(s) to TileSet" button and select the ``tilesheet.png`` image. -.. image:: img/tilemap_scene.png +Next, click "New Single Tile" and drag in the image to select the tile you want. +Click the "Enable Snap" button to make it easier to select the entire tile. A +yellow rectangle appears around the selected tile. -Go to the TileSet property of this node and assign the one created in -previous steps: +.. image:: img/tilemap_add_tile.png -.. image:: img/tileset_property.png +Click on the TileMap in the scene tree, and you'll see that the newly created +tile now appears on the right side. Click in the viewport and you can place +tiles. Right-click to remove them. -Also set the cell size to '50', since that is the size used by the -tiles. Quadrant size is a tuning value, which means that the engine will -draw and cull the tilemap in blocks of 16x16 tiles. This value is -usually fine and does not need to be changed, but can be used to fine tune -performance in specific cases (if you know what you are doing). +.. image:: img/tilemap_draw.png -Painting your world -------------------- - -With all set, make sure the TileMap node is selected. A red grid will -appear on the screen, allowing you to paint on it with the selected tile on the -left palette. - -.. image:: img/tile_example6.png - -To avoid accidentally moving and selecting the tilemap node (something -common, given it's a huge node), it is recommended that you lock it, -using the lock button: +It's easy to accidentally select and move the tilemap node. To avoid this, use +the node's lock button: .. image:: img/tile_lock.png -If you accidentally place a tile somewhere you don't want it to be, you -can delete it with ``RMB`` (the right mouse button) while in the tilemap editor. +Collision Shapes +---------------- -You can also flip and rotate sprites in the TileMap editor (note: -flipping the sprite in the TileSet will have no effect). Icons at the -top right of the editor allow flipping and rotating of the currently -selected sprite - you can also use the A and S keys to flip the sprite -horizontally and vertically. With a brick pattern like this tutorial uses, -flipping the sprites would create unpleasant discontinuities unless you're -flipping an entire region of bricks. But for some kinds of tiles, flipping -can be a convenient and space-saving feature. +If you're making a map that needs collisions - walls, floor, or other obstacles, +for example - then you'll need to add collision shapes to any tiles that you +want to be considered "solid". -Offset and scaling artifacts ----------------------------- +Click "TileSet" at the bottom of the editor window to return to the tileset +tool. Click the tile you previously defined (outlined in yellow). Select the +"Collision" tab and click the "Create a new rectangle" button. Make sure you +still have grid snap enabled, then click and drag in the tile. A square +collision shape appears in light blue: -When using a single texture for all the tiles, scaling the tileset (or -even moving to a non pixel-aligned location) will most likely result in -filtering artifacts like so: +.. image:: img/tileset_add_collision.png -.. image:: img/tileset_filter.png +You can add occlusion and navigation shapes to the tile in the same way. -This is unavoidable, as it is the way the hardware bilinear filter -works. To avoid this situation, there are a few workarounds. Try the -one that looks better for you: +Atlas tiles +----------- +Rather than adding individual tiles one at a time, you can define a group of +tiles all at once using an atlas. Click "New Atlas" and drag to select the +entire tile sheet. -- Disable filtering and mipmaps for either the tileset texture or all tile textures if using separate images (see the :ref:`doc_import_images` asset pipeline tutorial). -- Enable pixel snap (Set ``Project > Project Settings > - Rendering > Quality > 2d > Use Pixel Snap`` to true; you can also search for ``Pixel Snap``). -- Viewport Scaling can often help with shrinking the map (see the - :ref:`doc_viewports` tutorial). Simply adding a camera, setting it to ``Current`` and playing around with its ``Zoom`` may be a good starting point. -- You can use a single, separate image for each tile. This will remove all artifacts, but - can be more cumbersome to implement and is less optimized. +.. image:: img/tileset_atlas.png + +If you haven't already, make sure to change the "Step" in the snap settings to +`(64, 64)`, or your tiles may be chopped into smaller pieces. You can find +this in the Inspector: + +.. image:: img/tileset_snap.png + +Once you've defined the atlas you can add collision shapes to the individual +tiles as before. You can also click "Icon" to select one of the tiles to represent +the atlas. + +Back in the TileMap, you can select the atlas tile and you'll see all of the +tiles it contains: + +.. image:: img/tileset_draw_atlas.png + +In addition to saving time when defining the tiles, this can help by grouping +similar tiles together when you're working with a large number of tiles. + +Tips and tricks +--------------- + +- If you're using a :ref:`Camera2D ` to scroll your level, you + may notice lines appearing between your tiles. To fix this, open Project + Settings and enable "Use Pixel Snap" in the "Rendering/Quality" section. + +- You can flip and rotate tiles using the icons at the top right of the editor. + +- To draw straight lines, hold while clicking and dragging a tile. + +- Tools such as copy, paste, and bucket fill, can be found in the "TileMap" + menu in the upper-right. + +.. image:: img/tilemap_menu.png \ No newline at end of file diff --git a/tutorials/3d/files/gridmap_demo.zip b/tutorials/3d/files/gridmap_demo.zip index 19cad440d..e95301a7f 100644 Binary files a/tutorials/3d/files/gridmap_demo.zip and b/tutorials/3d/files/gridmap_demo.zip differ diff --git a/tutorials/3d/fps_tutorial/img/AnimationPlayerAddPoint.png b/tutorials/3d/fps_tutorial/img/AnimationPlayerAddPoint.png deleted file mode 100644 index 47fce3b7f..000000000 Binary files a/tutorials/3d/fps_tutorial/img/AnimationPlayerAddPoint.png and /dev/null differ diff --git a/tutorials/3d/fps_tutorial/img/AnimationPlayerAddTrack.png b/tutorials/3d/fps_tutorial/img/AnimationPlayerAddTrack.png index 3ac6ae425..d0824e339 100644 Binary files a/tutorials/3d/fps_tutorial/img/AnimationPlayerAddTrack.png and b/tutorials/3d/fps_tutorial/img/AnimationPlayerAddTrack.png differ diff --git a/tutorials/3d/fps_tutorial/img/AnimationPlayerCallFuncTrack.png b/tutorials/3d/fps_tutorial/img/AnimationPlayerCallFuncTrack.png index 815c0c752..7c28a93d7 100644 Binary files a/tutorials/3d/fps_tutorial/img/AnimationPlayerCallFuncTrack.png and b/tutorials/3d/fps_tutorial/img/AnimationPlayerCallFuncTrack.png differ diff --git a/tutorials/3d/fps_tutorial/img/AnimationPlayerEditPoints.png b/tutorials/3d/fps_tutorial/img/AnimationPlayerEditPoints.png deleted file mode 100644 index e208a2215..000000000 Binary files a/tutorials/3d/fps_tutorial/img/AnimationPlayerEditPoints.png and /dev/null differ diff --git a/tutorials/3d/fps_tutorial/img/AnimationPlayerInsertKey.png b/tutorials/3d/fps_tutorial/img/AnimationPlayerInsertKey.png new file mode 100644 index 000000000..edbae6dc3 Binary files /dev/null and b/tutorials/3d/fps_tutorial/img/AnimationPlayerInsertKey.png differ diff --git a/tutorials/3d/fps_tutorial/part_one.rst b/tutorials/3d/fps_tutorial/part_one.rst index 56ca7314b..38a318ed6 100644 --- a/tutorials/3d/fps_tutorial/part_one.rst +++ b/tutorials/3d/fps_tutorial/part_one.rst @@ -138,7 +138,8 @@ Attach a new script to the ``Player`` node and call it ``Player.gd``. Let's program our player by adding the ability to move around, look around with the mouse, and jump. Add the following code to ``Player.gd``: -:: +.. tabs:: + .. code-tab:: gdscript GDScript extends KinematicBody @@ -240,6 +241,135 @@ Add the following code to ``Player.gd``: camera_rot.x = clamp(camera_rot.x, -70, 70) rotation_helper.rotation_degrees = camera_rot + .. code-tab:: csharp + + using Godot; + using System; + + public class Player : KinematicBody + { + [Export] + public float Gravity = -24.8f; + [Export] + public float MaxSpeed = 20.0f; + [Export] + public float JumpSpeed = 18.0f; + [Export] + public float Accel = 4.5f; + [Export] + public float Deaccel = 16.0f; + [Export] + public float MaxSlopeAngle = 40.0f; + [Export] + public float MouseSensitivity = 0.05f; + + private Vector3 _vel = new Vector3(); + private Vector3 _dir = new Vector3(); + + private Camera _camera; + private Spatial _rotationHelper; + + // Called when the node enters the scene tree for the first time. + public override void _Ready() + { + _camera = GetNode("Rotation_Helper/Camera"); + _rotationHelper = GetNode("Rotation_Helper"); + + Input.SetMouseMode(Input.MouseMode.Captured); + } + + public override void _PhysicsProcess(float delta) + { + ProcessInput(delta); + ProcessMovement(delta); + } + + private void ProcessInput(float delta) + { + // ------------------------------------------------------------------- + // Walking + _dir = new Vector3(); + Transform camXform = _camera.GetGlobalTransform(); + + Vector2 inputMovementVector = new Vector2(); + + if (Input.IsActionPressed("movement_forward")) + inputMovementVector.y += 1; + if (Input.IsActionPressed("movement_backward")) + inputMovementVector.y -= 1; + if (Input.IsActionPressed("movement_left")) + inputMovementVector.x -= 1; + if (Input.IsActionPressed("movement_right")) + inputMovementVector.x += 1; + + inputMovementVector = inputMovementVector.Normalized(); + + _dir += -camXform.basis.z.Normalized() * inputMovementVector.y; + _dir += camXform.basis.x.Normalized() * inputMovementVector.x; + // ------------------------------------------------------------------- + + // ------------------------------------------------------------------- + // Jumping + if (IsOnFloor()) + { + if (Input.IsActionJustPressed("movement_jump")) + _vel.y = JumpSpeed; + } + // ------------------------------------------------------------------- + + // ------------------------------------------------------------------- + // Capturing/Freeing the cursor + if (Input.IsActionJustPressed("ui_cancel")) + { + if (Input.GetMouseMode() == Input.MouseMode.Visible) + Input.SetMouseMode(Input.MouseMode.Captured); + else + Input.SetMouseMode(Input.MouseMode.Visible); + } + // ------------------------------------------------------------------- + } + + private void ProcessMovement(float delta) + { + _dir.y = 0; + _dir = _dir.Normalized(); + + _vel.y += delta * Gravity; + + Vector3 hvel = _vel; + hvel.y = 0; + + Vector3 target = _dir; + + target *= MaxSpeed; + + float accel; + if (_dir.Dot(hvel) > 0) + accel = Accel; + else + accel = Deaccel; + + hvel = hvel.LinearInterpolate(target, accel * delta); + _vel.x = hvel.x; + _vel.z = hvel.z; + _vel = MoveAndSlide(_vel, new Vector3(0, 1, 0), false, 4, Mathf.Deg2Rad(MaxSlopeAngle)); + } + + public override void _Input(InputEvent @event) + { + if (@event is InputEventMouseMotion && Input.GetMouseMode() == Input.MouseMode.Captured) + { + InputEventMouseMotion mouseEvent = @event as InputEventMouseMotion; + _rotationHelper.RotateX(Mathf.Deg2Rad(mouseEvent.Relative.y * MouseSensitivity)); + RotateY(Mathf.Deg2Rad(-mouseEvent.Relative.x * MouseSensitivity)); + + Vector3 cameraRot = _rotationHelper.RotationDegrees; + cameraRot.x = Mathf.Clamp(cameraRot.x, -70, 70); + _rotationHelper.RotationDegrees = cameraRot; + } + } + } + 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 @@ -339,7 +469,8 @@ In Godot, the origin is at position ``(0, 0, 0)`` with a rotation of ``(0, 0, 0) If you want to move using the world space directional vectors, you'd do something like this: -:: +.. tabs:: + .. code-tab:: gdscript GDScript if Input.is_action_pressed("movement_forward"): node.translate(Vector3(0, 0, 1)) @@ -349,6 +480,17 @@ If you want to move using the world space directional vectors, you'd do somethin node.translate(Vector3(1, 0, 0)) if Input.is_action_pressed("movement_right"): node.translate(Vector3(-1, 0, 0)) + + .. code-tab:: csharp + + if (Input.IsActionPressed("movement_forward")) + node.Translate(new Vector3(0, 0, 1)); + if (Input.IsActionPressed("movement_backward")) + node.Translate(new Vector3(0, 0, -1)); + if (Input.IsActionPressed("movement_left")) + node.Translate(new Vector3(1, 0, 0)); + if (Input.IsActionPressed("movement_right")) + node.Translate(new Vector3(-1, 0, 0)); .. note:: Notice how we do not need to do any calculations to get world space directional vectors. We can define a few :ref:`Vector3 ` variables and input the values pointing in each direction. @@ -387,7 +529,8 @@ Each of those vectors point towards each of the local space vectors coming from To use the :ref:`Spatial ` node's local directional vectors, we use this code: -:: +.. tabs:: + .. code-tab:: gdscript GDScript if Input.is_action_pressed("movement_forward"): node.translate(node.global_transform.basis.z.normalized()) @@ -397,6 +540,17 @@ To use the :ref:`Spatial ` node's local directional vectors, we u node.translate(node.global_transform.basis.x.normalized()) if Input.is_action_pressed("movement_right"): node.translate(-node.global_transform.basis.x.normalized()) + + .. code-tab:: csharp + + if (Input.IsActionPressed("movement_forward")) + node.Translate(node.GlobalTransform.basis.z.Normalized()); + if (Input.IsActionPressed("movement_backward")) + node.Translate(-node.GlobalTransform.basis.z.Normalized()); + if (Input.IsActionPressed("movement_left")) + node.Translate(node.GlobalTransform.basis.x.Normalized()); + if (Input.IsActionPressed("movement_right")) + node.Translate(-node.GlobalTransform.basis.x.Normalized()); Here is what local space looks like in 2D: @@ -539,7 +693,8 @@ so let's do that! First we need a few more class variables in our player script: -:: +.. tabs:: + .. code-tab:: gdscript GDScript const MAX_SPRINT_SPEED = 30 const SPRINT_ACCEL = 18 @@ -547,6 +702,16 @@ First we need a few more class variables in our player script: var flashlight + .. code-tab:: csharp + + [Export] + public float MaxSprintSpeed = 30.0f; + [Export] + public float SprintAccel = 18.0f; + private bool _isSprinting = false; + + private SpotLight _flashlight; + All the sprinting variables work exactly the same as the non sprinting variables with similar names. @@ -555,17 +720,23 @@ 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``: -:: +.. tabs:: + .. code-tab:: gdscript GDScript flashlight = $Rotation_Helper/Flashlight + .. code-tab:: csharp + + _flashlight = GetNode("Rotation_Helper/Flashlight"); + This gets the ``Flashlight`` node and assigns it to the ``flashlight`` variable. _________ Now we need to change some of the code in ``process_input``. Add the following somewhere in ``process_input``: -:: +.. tabs:: + .. code-tab:: gdscript GDScript # ---------------------------------- # Sprinting @@ -584,6 +755,26 @@ Now we need to change some of the code in ``process_input``. Add the following s flashlight.show() # ---------------------------------- + .. code-tab:: csharp + + // ------------------------------------------------------------------- + // Sprinting + if (Input.IsActionPressed("movement_sprint")) + _isSprinting = true; + else + _isSprinting = false; + // ------------------------------------------------------------------- + + // ------------------------------------------------------------------- + // Turning the flashlight on/off + if (Input.IsActionJustPressed("flashlight")) + { + if (_flashlight.IsVisibleInTree()) + _flashlight.Hide(); + else + _flashlight.Show(); + } + Let's go over the additions: We set ``is_sprinting`` to true when the player is holding down the ``movement_sprint`` action, and false @@ -597,25 +788,40 @@ _________ Now we need to change a couple things in ``process_movement``. First, replace ``target *= MAX_SPEED`` with the following: -:: +.. tabs:: + .. code-tab:: gdscript GDScript if is_sprinting: target *= MAX_SPRINT_SPEED else: target *= MAX_SPEED + .. code-tab:: csharp + + if (_isSprinting) + target *= MaxSprintSpeed; + else + target *= MaxSpeed; + 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``. Now all that's left is to change the acceleration when sprinting. Change ``accel = ACCEL`` to the following: -:: +.. tabs:: + .. code-tab:: gdscript GDScript if is_sprinting: accel = SPRINT_ACCEL else: accel = ACCEL + .. code-tab:: csharp + + if (_isSprinting) + accel = SprintAccel; + else + accel = Accel; Now, when the player is sprinting, we'll use ``SPRINT_ACCEL`` instead of ``ACCEL``, which will accelerate the player faster. diff --git a/tutorials/3d/fps_tutorial/part_two.rst b/tutorials/3d/fps_tutorial/part_two.rst index 7ff4c264c..50b622aa9 100644 --- a/tutorials/3d/fps_tutorial/part_two.rst +++ b/tutorials/3d/fps_tutorial/part_two.rst @@ -255,7 +255,7 @@ check for every possible animation state. If we need to, we will transition into _________ -Finally, there is ``animation_callback``. This function will be called by a function track in our animations. +Finally, there is ``animation_callback``. This function will be called by a call method track in our animations. If we have a :ref:`FuncRef ` assigned to ``callback_function``, then we call that passed in function. If we do not have a :ref:`FuncRef ` assigned to ``callback_function``, we print out a warning to the console. @@ -271,17 +271,16 @@ Before that, though, we need to set some animation callback tracks in our firing Open up ``Player.tscn`` if you don't have it open and navigate to the :ref:`AnimationPlayer ` node (``Player`` -> ``Rotation_Helper`` -> ``Model`` -> ``Animation_Player``). -We need to attach a function track to three of our animations: The firing animation for the pistol, rifle, and knife. +We need to attach a call method track to three of our animations: The firing animation for the pistol, rifle, and knife. Let's start with the pistol. Click the animation drop down list and select "Pistol_fire". Now scroll down to the bottom of the list of animation tracks. The final item in the list should read -``Armature/Skeleton:Left_UpperPointer``. Now at the bottom of the list, click the plus icon on the bottom -bar of animation window, right next to the loop button and the up arrow. +``Armature/Skeleton:Left_UpperPointer``. Now above the list, click the "Add track" button, to the left of the time line .. image:: img/AnimationPlayerAddTrack.png -This will bring up a window with three choices. We want to add a function callback track, so click the -option that reads "Add Call Func Track". This will open a window showing the entire node tree. Navigate to the +This will bring up a window with a few choices. We want to add a call method track, so click the +option that reads "Call Method Track". This will open a window showing the entire node tree. Navigate to the :ref:`AnimationPlayer ` node, select it, and press OK. .. image:: img/AnimationPlayerCallFuncTrack.png @@ -306,25 +305,13 @@ reach the point where the muzzle starts to flash. You can also change how the timeline scrubbing snaps by changing the value in ``Step (s)`` to a lower/higher value. -Once you get to a point you like, press the little green plus symbol on the far right side of the -``AnimationPlayer`` track. This will place a little green point at the position you are currently -at in the animation on your ``AnimationPlayer`` track. +Once you get to a point you like, right click on the row for "Animation Player" and press insert key. +In the empty name field, enter ``animation_callback`` and press ``enter``. -.. image:: img/AnimationPlayerAddPoint.png +.. image:: img/AnimationPlayerInsertKey.png -Now we have one more step before we are done with the pistol. Select the "enable editing of individual keys" -button on the far right corner of the animation window. It looks like a pencil with a little point beside it. -.. image:: img/AnimationPlayerEditPoints.png - -Once you've clicked that, a new window will open on the right side. Now click the green point on the ``AnimationPlayer`` -track. This will bring up the information associated with that point in the timeline. In the empty name field, enter -``animation_callback`` and press ``enter``. - -Now when we are playing this animation the callback function will be triggered at that specific point of the animation. - -.. warning:: Be sure to press the "enable editing of individual keys" button again to turn off the ability to edit individual keys - so you cannot change one of the transform tracks by accident! +Now when we are playing this animation the call method track will be triggered at that specific point of the animation. _________ @@ -333,28 +320,21 @@ Let's repeat the process for the rifle and knife firing animations! .. note:: Because the process is exactly the same as the pistol, the process is going to explained in a little less depth. Follow the steps from above if you get lost! It is exactly the same, just on a different animation. -Go to the "Rifle_fire" animation from the animation drop down. Add the function callback track once you reach the bottom of the -animation track list by clicking the little plus icon at the bottom of the screen. Find the point where the muzzle starts -to flash and click the little green plus symbol to add a function callback point at that position on the track. +Go to the "Rifle_fire" animation from the animation drop down. Add the call method track once you reach the bottom of the +animation track list by clicking the "Add Track" button above the list. Find the point where the muzzle starts +to flash and right click and press ``Insert Key`` to add a call method track point at that position on the track. -Next, click the "enable editing of individual keys" button. -Select the newly created function callback point, put "animation_callback" into the name field and press ``enter``. -Click the "enable editing of individual keys" button again to turn off individual key editing. -so we cannot change one of the transform tracks by accident. +Type "animation_callback" into the name field of the pop up which opened and press ``enter``. -Now we need to apply the callback function track to the knife animation. Select the "Knife_fire" animation and scroll to the bottom of the -animation tracks. Click the plus symbol at the bottom of the animation window and add a function callback track. -Next find a point around the first third of the animation to place the animation callback function point at. +Now we need to apply the callback method track to the knife animation. Select the "Knife_fire" animation and scroll to the bottom of the +animation tracks. Click the "Add Track" button above the list and add a method track. +Next find a point around the first third of the animation to place the animation callback method point at. .. note:: We will not actually be firing the knife, and the animation is a stabbing animation rather than a firing one. For this tutorial we are reusing the gun firing logic for our knife, so the animation has been named in a style that is consistent with the other animations. -From there click the little green plus to add a function callback point at the current position. Then click the "enable editing of individual keys" -button, the button with a plus at the bottom right side of the animation window. -Select the newly created function callback point, put "animation_callback" into the name field and press ``enter``. -Click the "enable editing of individual keys" button again to turn off individual key editing, -so we cannot change one of the transform tracks by accident. +From there right click on the timeline and click "Insert Key". Put "animation_callback" into the name field and press ``enter``. .. tip:: Be sure to save your work! diff --git a/tutorials/3d/img/multimesh_scene_tree.png b/tutorials/3d/img/multimesh_scene_tree.png index 34edcf9ff..3d5ac5ff9 100644 Binary files a/tutorials/3d/img/multimesh_scene_tree.png and b/tutorials/3d/img/multimesh_scene_tree.png differ diff --git a/tutorials/3d/img/multimesh_toolbar.png b/tutorials/3d/img/multimesh_toolbar.png index c8a7de390..8ab4698b9 100644 Binary files a/tutorials/3d/img/multimesh_toolbar.png and b/tutorials/3d/img/multimesh_toolbar.png differ diff --git a/tutorials/3d/using_multi_mesh_instance.rst b/tutorials/3d/using_multi_mesh_instance.rst index 81a0d4c98..f97d85c60 100644 --- a/tutorials/3d/using_multi_mesh_instance.rst +++ b/tutorials/3d/using_multi_mesh_instance.rst @@ -31,8 +31,8 @@ on. In the tree example, this would be the landscape. The other node is used as the source, the mesh that you want to have duplicated. In the tree case, this would be the tree itself. -In our example, we would use a :ref:`Node ` as the root node of the -scene. Your scene tree would look like this: +In our example, we would use a :ref:`Spatial ` node as the root node of +the scene. Your scene tree would look like this: .. image:: img/multimesh_scene_tree.png diff --git a/tutorials/3d/using_transforms.rst b/tutorials/3d/using_transforms.rst index c97573605..d890e3213 100644 --- a/tutorials/3d/using_transforms.rst +++ b/tutorials/3d/using_transforms.rst @@ -334,7 +334,7 @@ Example of looking around, FPS style: public override void _Input(InputEvent @event) { - if (mouseMotion is InputEventMouseMotion mouseMotion) + if (@event is InputEventMouseMotion mouseMotion) { // modify accumulated mouse rotation _rotationX += mouseMotion.Relative.x * LookAroundSpeed; @@ -374,7 +374,7 @@ Converting a rotation to quaternion is straightforward. // Convert basis to quaternion, keep in mind scale is lost var a = transform.basis.Quat(); - var b = transform.basis.Quat(); + var b = transform2.basis.Quat(); // Interpolate using spherical-linear interpolation (SLERP). var c = a.Slerp(b, 0.5f); // find halfway point between a and b // Apply back diff --git a/tutorials/3d/vertex_animation/animating_thousands_of_fish.rst b/tutorials/3d/vertex_animation/animating_thousands_of_fish.rst index 190060540..3215400ce 100644 --- a/tutorials/3d/vertex_animation/animating_thousands_of_fish.rst +++ b/tutorials/3d/vertex_animation/animating_thousands_of_fish.rst @@ -80,13 +80,13 @@ We construct a rotation matrix like so: //angle is scaled by 0.1 so that the fish only pivots and doesn't rotate all the way around //pivot is a uniform float float pivot_angle = cos(time) * 0.1 * pivot; - mat2 rotation_matrix = mat2(vec2(cos(pivot_angle), -sin(pivot_angle)), vec2(sin(pivot_angle), cos(pivot_angle))); + mat2 rotation_matrix = mat2(vec2(cos(pivot_angle), -sin(pivot_angle)), vec2(sin(pivot_angle), cos(pivot_angle))); And then we apply it in the ``x`` and ``z`` axes by multiplying it by ``VERTEX.xz``. .. code-block:: glsl - VERTEX.xz = rotation_matrix * VERTEX.xz; + VERTEX.xz = rotation_matrix * VERTEX.xz; With only the pivot applied you should see something like this: @@ -121,14 +121,14 @@ we first construct a rotation matrix. //twist is a uniform float float twist_angle = cos(time + body) * 0.3 * twist; - mat2 twist_matrix = mat2(vec2(cos(twist_angle), -sin(twist_angle)), vec2(sin(twist_angle), cos(twist_angle))); + mat2 twist_matrix = mat2(vec2(cos(twist_angle), -sin(twist_angle)), vec2(sin(twist_angle), cos(twist_angle))); We apply the rotation in the ``xy`` axes so that the fish appears to roll around its spine. For this to work, the fishes spine needs to be centered on the ``z`` axis. .. code-block:: glsl - VERTEX.xy = twist_matrix * VERTEX.xy; + VERTEX.xy = twist_matrix * VERTEX.xy; Here is the fish with twist applied: @@ -219,10 +219,10 @@ to loop over all the instances and set their transform to a random position. :: - for i in range($School.multimesh.instance_count): - var position = Transform() - position = position.translated(Vector3(randf() * 100 - 50, randf() * 50 - 25, randf() * 50 - 25)) - $School.multimesh.set_instance_transform(i, position) + for i in range($School.multimesh.instance_count): + var position = Transform() + position = position.translated(Vector3(randf() * 100 - 50, randf() * 50 - 25, randf() * 50 - 25)) + $School.multimesh.set_instance_transform(i, position) Running this script will place the fish in random positions in a box around the position of the MultiMeshInstance. @@ -244,7 +244,7 @@ We do that by adding the per-instance custom value ``INSTANCE_CUSTOM`` to ``time .. code-block:: glsl - float time = (TIME * time_scale) + (6.28318 * INSTANCE_CUSTOM.x); + float time = (TIME * time_scale) + (6.28318 * INSTANCE_CUSTOM.x); Next, we need to pass a value into ``INSTANCE_CUSTOM``. We do that by adding one line into the ``for`` loop from above. In the ``for`` loop we assign each instance a set of four diff --git a/tutorials/3d/vertex_animation/controlling_thousands_of_fish.rst b/tutorials/3d/vertex_animation/controlling_thousands_of_fish.rst index 3203c66d7..83c916ae0 100644 --- a/tutorials/3d/vertex_animation/controlling_thousands_of_fish.rst +++ b/tutorials/3d/vertex_animation/controlling_thousands_of_fish.rst @@ -92,16 +92,16 @@ Then, use those seeds to generate random numbers using ``rand_from_seed``: .. code-block:: glsl CUSTOM.x = rand_from_seed(alt_seed1); - vec3 position = vec3(rand_from_seed(alt_seed2)*2.0-1.0, - rand_from_seed(alt_seed3)*2.0-1.0, - rand_from_seed(alt_seed4)*2.0-1.0); + vec3 position = vec3(rand_from_seed(alt_seed2) * 2.0 - 1.0, + rand_from_seed(alt_seed3) * 2.0 - 1.0, + rand_from_seed(alt_seed4) * 2.0 - 1.0); Finally, assign ``position`` to ``TRANSFORM[3].xyz``, which is the part of the transform that holds the position information. .. code-block:: glsl - TRANSFORM[3].xyz = CUSTOM.xyz*20.0; + TRANSFORM[3].xyz = position * 20.0; Remember, all this code so far goes inside the ``RESTART`` block. diff --git a/tutorials/animation/cutout_animation.rst b/tutorials/animation/cutout_animation.rst index cedef3b73..ad1584cf4 100644 --- a/tutorials/animation/cutout_animation.rst +++ b/tutorials/animation/cutout_animation.rst @@ -6,47 +6,46 @@ Cutout animation What is it? ~~~~~~~~~~~ -Cut-out is a technique of animating in 2D where pieces of paper (or -similar material) are cut in special shapes and laid one over the other. -The papers are animated and photographed, frame by frame using a stop -motion technique (more info -`here `__). +Traditionally, `cutout animation `__ +is a type of `stop motion animation `__ +in which pieces of paper (or other thin material) are cut into special shapes +and arranged in two-dimensional representations of characters and objects. +Characters' bodies are usually made out of several pieces. The pieces are +arranged and photographed once for each frame of the film. The animator moves +and rotates the parts in small increments between each shot to create the +illusion of movement when the images are played back quickly in sequence. -With the advent of the digital age, this technique became possible using -computers, which resulted in an increased amount of animation TV shows -using digital Cut-out. Notable examples are `South -Park `__ or `Jake and the Never -Land -Pirates `__ -. +Simulations of cutout animation can now be created using software as seen in +`South Park `__ and `Jake and the Never +Land Pirates `__. In video games, this technique has also become popular. Examples of -this are `Paper -Mario `__ or `Rayman -Origins `__ . +this are `Paper Mario `__ or +`Rayman Origins `__ . -Cutout in Godot -~~~~~~~~~~~~~~~ +Cutout animation in Godot +~~~~~~~~~~~~~~~~~~~~~~~~~ -Godot provides a few tools for working with these kind of assets, but -its overall design makes it ideal for the workflow. The reason is that, -unlike other tools meant for this, Godot has the following advantages: +Godot provides tools for working with cutout rigs, and is ideal for the workflow: - **The animation system is fully integrated with the engine**: This - means, animations can control much more than just motion of objects, - such as textures, sprite sizes, pivots, opacity, color modulation, - etc. Everything can be animated and blended. -- **Mix with Traditional**: AnimatedSprite allows traditional animation - to be mixed, useful for complex objects, such as shape of hands - and foot, changing facial expression, etc. -- **Custom Shaped Elements**: Can be created with + means animations can control much more than just motion of objects. Textures, + sprite sizes, pivots, opacity, color modulation, and more, can all be animated + and blended. +- **Combine animation styles**: AnimatedSprite allows traditional cel animation + to be used alongside cutout animation. In cel animation different animation + frames use entirely different drawings rather than the same pieces positioned + differently. In an otherwise cutout-based animation, cel animation can be used + selectively for complex parts such as hands, feet, changing facial expressions, + etc. +- **Custom Shaped Elements**: Custom shapes can be created with :ref:`Polygon2D ` - allowing the mixing of UV animation, deformations, etc. -- **Particle Systems**: Can also be mixed with the traditional - animation hierarchy, useful for magic effects, jetpacks, etc. + allowing UV animation, deformations, etc. +- **Particle Systems**: A cutout animation rig can be combined with particle + systems. This can useful for magic effects, jetpacks, etc. - **Custom Colliders**: Set colliders and influence areas in different - parts of the skeletons, great for bosses, fighting games, etc. -- **Animation Tree**: Allows complex combinations and blendings of + parts of the skeletons, great for bosses and fighting games. +- **Animation Tree**: Allows complex combinations and blending between several animations, the same way it works in 3D. And much more! @@ -69,14 +68,14 @@ Create an empty Node2D as root of the scene, we will work under it: .. image:: img/tuto_cutout1.png -OK, the first node of the model that we will create will be the hip. +The first node of the model is the hip. Generally, both in 2D and 3D, the hip is the root of the skeleton. This makes it easier to animate: .. image:: img/tuto_cutout2.png Next will be the torso. The torso needs to be a child of the hip, so -create a child sprite and load the torso, later accommodate it properly: +create a child sprite and load the torso texture, later accommodate it properly: .. image:: img/tuto_cutout3.png @@ -86,11 +85,9 @@ and dragging with the left mouse button. To exit rotate mode hit ``ESC``. .. image:: img/tutovec_torso1.gif -Ouch, that doesn't look good! The rotation pivot is wrong, this means -it needs to be adjusted. +The rotation pivot is wrong and needs to be adjusted. -This small little cross in the middle of the -:ref:`Sprite ` is +This small cross in the middle of the :ref:`Sprite ` is the rotation pivot: .. image:: img/tuto_cutout4.png @@ -103,52 +100,52 @@ Sprite: .. image:: img/tuto_cutout5.png -However, there is a way to do it more *visually*. While hovering over the -desired pivot point, simply press the "v" key to move the pivot there for the -selected Sprite. Alternately, there is a tool in the tool bar that has a +The pivot can also be adjusted *visually*. While hovering over the +desired pivot point, press the "v" key to move the pivot there for the +selected Sprite. There is also a tool in the tool bar that has a similar function. .. image:: img/tutovec_torso2.gif -Now it looks good! Let's continue adding body pieces, starting by the -right arm. Make sure to put the sprites in hierarchy, so their rotations -and translations are relative to the parent: +Continue adding body pieces, starting with the +right arm. Make sure to put each sprite in its correct place in the hierarchy, +so its rotations and translations are relative to its parent: .. image:: img/tuto_cutout6.png -This seems easy, so continue with the left arm. The rest should be -simple! Or maybe not: +With the left arm there's a problem. In 2D, child nodes appear in front of +their parents: .. image:: img/tuto_cutout7.png -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 -won't learn about all this!): +We want the left arm to appear *behind* +the hip and the torso. We could move the left arm nodes behind the hip (above +the hip node in the scene hierarchy), but then the left arm is no longer in its +proper place in the hierarchy. This means it wouldn't be affected the movement +of the torso. We'll fix this problem with ``RemoteTransform2D`` nodes. -.. image:: img/tuto_cutout8.png - -But then, we lose the hierarchy layout, which allows to control the -skeleton like.. a skeleton. Is there any hope?.. Of Course! +.. note:: You can also fix depth ordering problems by adjusting the Z property + of any node inheriting from Node2D. RemoteTransform2D node ~~~~~~~~~~~~~~~~~~~~~~ -Godot provides a special node, :ref:`RemoteTransform2D `. -This node will transform nodes that are sitting somewhere else in the -hierarchy, by applying the transform to the remote nodes. +The :ref:`RemoteTransform2D ` node transforms nodes +somewhere else in the hierarchy. This node applies its own transform (including +any transformation it inherits from its parents) to the remote node it targets. -This enables to have a visibility order independent from the -hierarchy. +This allows us to correct the visibility order of our elements independent from +the locations of those parts in the cutout hierarchy. -Simply create two more nodes as children from torso, remote_arm_l and -remote_hand_l and link them to the actual sprites: +Create a ``RemoteTransform2D`` node as a child of the torso. Call it ``remote_arm_l``. +Create another RemoteTransform2D node inside the first and call it ``remote_hand_l``. +Use the ``Remote Path`` property of the two new nodes to target the ``arm_l`` and +``hand_l`` sprites respectively: .. image:: img/tuto_cutout9.png -Moving the remote transform nodes will move the sprites, allowing you to -easily animate and pose the character: +Moving the ``RemoteTransform2D`` nodes now moves the sprites. So we can create +animations by adjusting the ``RemoteTransform2D`` transforms: .. image:: img/tutovec_torso4.gif @@ -163,27 +160,23 @@ parts. The resulting scene should look similar to this: The resulting rig will be easy to animate. By selecting the nodes and rotating them you can animate forward kinematics (FK) efficiently. -For simple objects and rigs this is fine, however the following problems -are common: +For simple objects and rigs this is fine, but there are limitations: -- Selecting sprites can become difficult for complex rigs, and the - scene tree ends being used due to the difficulty of clicking over the - proper sprite. -- Inverse Kinematics is often desired for extremities. +- Selecting sprites in the main viewport can become difficult in complex rigs. + The scene tree ends up being used to select parts instead, which can be slower. +- Inverse Kinematics (IK) is useful for animating extremities like hands and + feet, and can't be used with our rig in its current state. -To solve these problems, Godot supports a simple method of skeletons. +To solve these problems we'll use Godot's skeletons. Skeletons ~~~~~~~~~ -Godot doesn't actually support *true* Skeletons, but it does feature a -helper to create "bones" between nodes. This is enough for most cases, -but the way it works is not completely obvious. - - +In Godot there is a helper to create "bones" between nodes. The bone-linked +nodes are called skeletons. As an example, let's turn the right arm into a skeleton. To create -skeletons, a chain of nodes must be selected from top to bottom: +a skeleton, a chain of nodes must be selected from top to bottom: .. image:: img/tuto_cutout11.png @@ -191,25 +184,13 @@ Then, click on the Skeleton menu and select ``Make Bones``. .. image:: img/tuto_cutout12.png -This will add bones covering the arm, but the result is not quite what -is expected. +This will add bones covering the arm, but the result may be surprising. .. image:: img/tuto_cutout13.png -It looks like the bones are shifted up in the hierarchy. The hand -connects to the arm, and the arm to the body. So the question is: - -- Why does the hand lack a bone? -- Why does the arm connect to the body? - -This might seem strange at first, but will make sense later on. In -traditional skeleton systems, bones have a position, an orientation and -a length. In Godot, bones are mostly helpers so they connect the current -node with the parent. Because of this, **toggling a node as a bone will -just connect it to the parent**. - -So, with this knowledge. Let's do the same again so we have an actual, -useful skeleton. +Why does the hand lack a bone? In Godot, a bone connects a +node with its parent. And there's currently no child of the hand node. +With this knowledge let's try again. The first step is creating an endpoint node. Any kind of node will do, but :ref:`Position2D ` is preferred because it's @@ -226,36 +207,40 @@ bones: The result resembles a skeleton a lot more, and now the arm and forearm can be selected and animated. -Finally, create endpoints in all meaningful extremities and connect the -whole skeleton with bones up to the hip. +Create endpoints for all important extremities. Generate bones for all +articulable parts of the cutout, with the hip as the ultimate connection +between all of them. -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 that an extra bone is created when connecting the hip and torso. +Godot has connected the hip node to the scene root with a bone, and we don't +want that. To fix this, select the root and hip node, open the Skeleton menu, +click ``clear bones``. .. image:: img/tuto_cutout15_2.png - -After fixing that your final skeleton should look something like this: +Your final skeleton should look something like this: .. image:: img/tuto_cutout16.png -Finally! the whole skeleton is rigged! On close look, it is noticeable -that there is a second set of endpoints in the hands. This will make +You might have noticed a second set of endpoints in the hands. This will make sense soon. -Now that a whole skeleton is rigged, the next step is setting up the IK +Now that the whole figure is rigged, the next step is setting up the IK chains. IK chains allow for more natural control of extremities. IK chains ~~~~~~~~~ -IK chains are a powerful animation tool. Imagine you want to pose a character's foot in a specific position on the ground. Without IK chains, each motion of the foot would require rotating and positioning several other bones. This would be quite complex and lead to imprecise results. +IK stands for inverse kinematics. It's a convenient technique for animating the +position of hands, feet and other extremities of rigs like the one we've made. +Imagine you want to pose a character's foot in a specific position on the ground. +Without IK chains, each motion of the foot would require rotating and positioning +several other bones (the shin and the thigh at least). This would be quite +complex and lead to imprecise results. -What if we could move the foot and let the rest of the leg self-adjust? +IK allows us to move directly the foot while the shin and thigh self-adjust. -This type of posing is called IK (Inverse Kinematic). - -To create an IK chain, simply select a chain of bones from endpoint to +To create an IK chain, select a chain of bones from endpoint to the base for the chain. For example, to create an IK chain for the right leg, select the following: @@ -269,103 +254,119 @@ As a result, the base of the chain will turn *Yellow*. .. image:: img/tuto_cutout19.png -Once the IK chain is set-up, simply grab any of the bones in the -extremity, any child or grand-child of the base of the chain and try to -grab it and move it. Result will be pleasant, satisfaction warranted! +Once the IK chain is set-up grab any child or grand-child of the base of the +chain (e.g. a foot) and move it. You'll see the rest of the chain adjust as you +adjust its position. .. image:: img/tutovec_torso5.gif -Animation -~~~~~~~~~ +Animation tips +~~~~~~~~~~~~~~ -The following section will be a collection of tips for creating -animation for your rigs. If unsure about how the animation system in -Godot works, refresh it by checking again the :ref:`doc_animations`. +The following section will be a collection of tips for creating animation for +your cutout rigs. For more information on how the animation system in Godot +works, see :ref:`doc_animations`. -2D animation ------------- +Setting keyframes and excluding properties +------------------------------------------ -When doing animation in 2D, a helper will be present in the top menu. -This helper only appears when the animation editor window is opened: +Special contextual elements appear in the top toolbar when the animation editor +window is open: .. image:: img/tuto_cutout20.png -The key button will insert location/rotation/scale keyframes to the -selected objects or bones. This depends on the mask enabled. Green items -will insert keys while red ones will not, so modify the key insertion -mask to your preference. +The key button inserts location, rotation, and scale keyframes for the +selected objects or bones at the current playhead position. -Rest pose -~~~~~~~~~ +The "loc", "rot", and "scl" toggle buttons to the left of the key button modify +its function, allowing you to specify which of the three properties keyframes +will be created for. -These kind of rigs do not have a "rest" pose, so it's recommended to -create a reference rest pose in one of the animations. +Here's an illustration of how this can be useful: Imagine you have a node which +already has two keyframes animating its scale only. You want to add an +overlapping rotation movement to the same node. The rotation movement should +begin and end at different times from the scale change that's already set up. +You can use the toggle buttons to have only rotation information added when you +add a new keyframe. This way, you can avoid adding unwanted scale keyframes +which would disrupt the existing scale animation. -Simply do the following steps: +Creating a rest pose +~~~~~~~~~~~~~~~~~~~~ -1. Make sure the rig is in "rest" (not doing any specific pose). +Think of a rest pose as a default pose that your cutout rig should be set to +when no other pose is active in your game. Create a rest pose as follows: -2. Create a new animation, rename it to "rest". +1. Make sure the rig parts are positioned in what looks like a "resting" +arrangement. -3. Select all nodes (box selection should work fine). +2. Create a new animation, rename it "rest". -4. Select "loc" and "rot" on the top menu. +3. Select all nodes in your rig (box selection should work fine). -5. Push the key button. Keys will be inserted for everything, creating - a default pose. +4. Make sure the "loc", "rot", and "scl" toggle buttons are all active in the +toolbar. + +5. Press the key button. Keys will be inserted for all selected parts storing +their current arrangement. This pose can now be recalled when necessary in +your game by playing the "rest" animation you've created. .. image:: img/tuto_cutout21.png -Rotation -~~~~~~~~ +Modifying rotation only +~~~~~~~~~~~~~~~~~~~~~~~ -Animating these models means only modifying the rotation of the nodes. -Location and scale are rarely used, with the only exception of moving -the entire rig from the hip (which is the root node). +When animating a cutout rig, often it's only the rotation of the nodes that +needs to change. +Location and scale are rarely used. -As a result, when inserting keys, only the "rot" button needs to be -pressed most of the time: +So when inserting keys, you might find it convenient to have only the "rot" +toggle active most of the time: .. image:: img/tuto_cutout22.png -This will avoid the creation of extra animation tracks for the position -that will remain unused. +This will avoid the creation of unwanted animation tracks for position +and scale. -Keyframing IK -~~~~~~~~~~~~~ +Keyframing IK chains +~~~~~~~~~~~~~~~~~~~~ -When editing IK chains, it is not necessary to select the whole chain to +When editing IK chains, it's not necessary to select the whole chain to add keyframes. Selecting the endpoint of the chain and inserting a -keyframe will automatically insert keyframes until the chain base too. -This makes the task of animating extremities much simpler. +keyframe will automatically insert keyframes for all other parts of the chain too. -Moving sprites above and behind others -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Visually move a sprite behind its parent +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -RemoteTransform2D works in most cases, but sometimes it is -necessary to have a node above and below others during an animation. To -aid on this the "Behind Parent" property exists on any Node2D: +Sometimes it is necessary to have a node change its visual depth relative to +its parent node during an animation. Think of a character facing the camera, +who pulls something out from behind his back and holds it out in front of him. +During this animation the whole arm and the object in his hand would need to +change their visual depth relative to the body of the character. + +To help with this there's a keyframable "Behind Parent" property on all +Node2D-inheriting nodes. When planning your rig, think about the movements it +will need to perform and give some thought to how you'll use "Behind Parent" +and/or RemoteTransform2D nodes. They provide overlapping functionality. .. image:: img/tuto_cutout23.png -Batch setting transition curves -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Setting easing curves for multiple keys +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -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 -your animation. +To apply the same the easing curve to multiple keyframes at once: + +1. Select the relevant keys. +2. Click on the pencil icon in the bottom right of the animation panel, this + will open the transition editor. +3. In the transition editor click on the desired curve to apply it. .. image:: img/tuto_cutout24.png 2D Skeletal deform ~~~~~~~~~~~~~~~~~~ -Starting with Godot 3.1, 2D skeletal deform is supported, which can be used to enhance this workflow and add more flexibility -(single pieces can deform more organically instead of having to use many separate ones). +Skeletal deform can be used to augment a cutout rig, allowing single pieces to +deform organically (e.g. antennae that wobble as an insect character walks). This process is described in a :ref:`separate tutorial `. diff --git a/tutorials/audio/audio_buses.rst b/tutorials/audio/audio_buses.rst index 5adc63aa7..b772e7f0b 100644 --- a/tutorials/audio/audio_buses.rst +++ b/tutorials/audio/audio_buses.rst @@ -6,34 +6,23 @@ Audio buses Introduction ------------ -Beginning with Godot 3.0, the audio engine has been rewritten from scratch. -The aim now is to present an interface much friendlier to sound design -professionals. To achieve this, the audio engine contains a virtual rack -where unlimited audio buses can be created and, on each of it, unlimited -amount of effect processors can be added (or more like, as long as your -CPU holds up!) +Godot's audio processing code has been written with games in mind, with the aim of achieving an optimal balance between performance and sound quality. -The implementation in Godot is pretty efficient and has been written -entirely from the ground up, without relying on any existing audio libraries. - -Even the effect processors were written exclusively for Godot (save for -the pitch shifting library), with games in mind. This allows -an efficient tradeoff between performance and sound quality. +Godot's audio engine allows any number of audio buses to be created and any number of effect processors can be added to each bus. Only the hardware of the device running your game will limit the number of buses and effects that can be used before performance starts to suffer. Decibel scale ------------- -The new audio engine works primarily using the decibel scale. We have -chosen this over linear representation of amplitude because it's -more intuitive for audio professionals. +Godot's sound interface is designed to meet the expectations of sound design +professionals. To this end it primarily uses the decibel scale. For those unfamiliar with it, it can be explained with a few facts: * The decibel (dB) scale is a relative scale. It represents the ratio of sound power by using 10 times the base 10 logarithm of the ratio (10×log\ :sub:`10`\ (P/P\ :sub:`0`\ )). -* For every 3dB, sound doubles or halves. 6dB represents a factor 4, 9dB a factor 8, 10dB a factor 10, 20dB a factor 100, etc. +* For every 3dB, sound amplitude doubles or halves. 6dB represents a factor of 4, 9dB a factor of 8, 10dB a factor of 10, 20dB a factor of 100, etc. * Since the scale is logarithmic, true zero (no audio) can't be represented. -* 0dB is considered the maximum audible volume without *clipping*. This limit is not the human limit, but a limit from the sound hardware. Your sound output simply can't output any sound louder than 0dB without distorting it (clipping it). -* Because of the above, your sound mix should work in a way where the sound output of the *Master Bus* (more on that later), should never be above 0dB. +* 0dB is the maximum amplitude possible in a digital audio system. This limit is not the human limit, but a limit from the sound hardware. Audio with amplitudes that are too high to be represented properly below 0dB create a kind of distortion called clipping. +* To avoid clipping, your sound mix be arranged so that the output of the *Master Bus* (more on that later) never exceeds 0dB. * Every 3dB below the 0dB limit, sound energy is *halved*. It means the sound volume at -3dB is half as loud as 0dB. -6dB is half as loud as -3dB and so on. * When working with decibels, sound is considered no longer audible between -60dB and -80dB. This makes your working range generally between -60dB and 0dB. @@ -46,20 +35,20 @@ Audio buses can be found in the bottom panel of the Godot Editor: .. image:: img/audio_buses1.png -An *Audio Bus* bus (often called *Audio Channel* too) is a device where audio is channeled. Audio data passes through it and can be *modified* and *re-routed*. A VU meter (the bars that go up and down when sound is played) can measure the loudness of the sound in Decibel scale. +An *Audio Bus* (also called an *Audio Channel*) can be considered a place that audio is channeled through on the way to playback through a device's speakers. Audio data can be *modified* and *re-routed* by an *Audio Bus*. An *Audio Bus* has a VU meter (the bars that light up when sound is played) which indicates the amplitude of the signal passing through. -The leftmost bus is the *Master Bus*. This bus outputs the mix to your speakers so, as mentioned in the item above (Decibel Scale), make sure that your mix rarely or never goes above 0dB in this bus. -The rest of the audio buses are used for *routing*. This means that, after modifying the sound, they must send it to another bus to the left. Routing is always from right to left without exception as this -avoids creating infinite routing loops! +The leftmost bus is the *Master Bus*. This bus outputs the mix to your speakers so, as mentioned in the item above (Decibel Scale), make sure that your mix level doesn't reach 0dB in this bus. +The rest of the audio buses can be flexibly routed. After modifying the sound, they send it to another bus to the left. The destination bus can be specified for each of the non-master audio buses. Routing always passes audio from buses on the right to buses further to the left. This +avoids infinite routing loops. .. image:: img/audio_buses2.png -In the above image, *Bus 2* is routing its output to the *Master* bus. +In the above image, the output of *Bus 2* has been routed to the *Master* bus. -Playback of audio to a bus --------------------------- +Playback of audio through a bus +------------------------------- -To test playback to a bus, create an AudioStreamPlayer node, load an AudioStream and select a target bus for playback: +To test passing audio to a bus, create an AudioStreamPlayer node, load an AudioStream and select a target bus for playback: .. image:: img/audio_buses3.png @@ -74,37 +63,40 @@ Audio buses can contain all sorts of effects. These effects modify the sound in .. image:: img/audio_buses4.png -Following is a short description of available effects: +Try them all out to get a sense of how they alter sound. Here follows a short description of available effects: Amplify ~~~~~~~ -It's the most basic effect. It changes the sound volume. Amplifying too much can make the sound clip, so be wary of that. +Amplify changes the amplitude of the signal. Some care needs to be taken. Setting the level too high can make the sound clip, which is usually undesirable. BandLimit and BandPass ~~~~~~~~~~~~~~~~~~~~~~ -These are resonant filters which block frequencies around the *Cutoff* point. BandPass is resonant, while BandLimit stretches to the sides. +These are resonant filters which block frequencies around the *Cutoff* point. BandPass can be used to simulate sound passing through an old telephone line or megaphone. Modulating the BandPass frequency can simulate the sound of a wah-wah guitar pedal, think of the guitar in Jimi Hendrix's *Voodoo Child (Slight Return)*. Chorus ~~~~~~ -This effect adds extra voices, detuned by LFO and with a small delay, to add more richness to the sound harmonics and stereo width. +The Chorus effect duplicates the incoming audio, delays the duplicate slightly and uses an LFO to continuously modulate the pitch of the duplicated signal before mixing the duplicatated signal(s) and the original together again. This creates a shimmering effect and adds stereo width to the sound. Compressor ~~~~~~~~~~ -The aim of a dynamic range compressor is to reduce the level of the sound when the amplitude goes over a certain threshold in Decibels. -One of the main uses of a compressor is to increase the dynamic range while clipping the least possible (when sound goes over 0dB). +A dynamic range compressor automatically attenuates the level of the incoming signal when its amplitude exceeds a certain threshold. The level of attenuation applied is proportional to how far the incoming audio exceeds the threshold. The compressor's Ratio parameter controls the degree of attenuation. +One of the main uses of a compressor is to reduce the dynamic range of signals with very loud and quiet parts. Reducing the dynamic range of a signal can make it easier to mix. -The compressor has many uses in the mix, for example: +The compressor has many uses. For example: -* It can be used in the Master bus to compress the whole output (Although a Limiter would probably be better) +* It can be used in the Master bus to compress the whole output. * It can be used in voice channels to ensure they sound as even as possible. -* It can be *Sidechained*. This means it can reduce the sound level using another audio bus for threshold detection. This technique is very common in video game mixing to download the level of Music/SFX while voices are being heard. -* It can accentuate transients by using a bit wider attack, meaning it can make sound effects sound more punchy. +* It can be *Sidechained*. This means it can reduce the sound level of one signal using the level of another audio bus for threshold detection. This technique is very common in video game mixing to 'duck' the level of music or sound effects when voices need to be heard. +* It can accentuate transients by using a slower attack. This can make sound effects more punchy. + +.. note:: + + If your goal is to prevent a signal from exceeding a given amplitude altogether, rather that to reduce the dynamic range of the signal, a limiter is likely a better choice than a compressor. See below. -There is a lot of bibliography written about compressors, and the Godot implementation is rather standard. Delay ~~~~~ @@ -114,53 +106,58 @@ Adds an "Echo" effect with a feedback loop. It can be used, together with Reverb Distortion ~~~~~~~~~~ -Adds classical effects to modify the sound and make it dirty. Godot supports effects like overdrive, tan, or bit crushing. -For games, it can simulate sound coming from some saturated device or speaker efficiently. +Distortion effects make the sound 'dirty'. Godot offers several types of distortion: Overdrive, tan and bit crushing. +Distortion can be used simulate sound coming through a low-quality speaker or device. -EQ6, EQ10, EQ21 -~~~~~~~~~~~~~~~ +EQ, EQ6, EQ10, EQ21 +~~~~~~~~~~~~~~~~~~~ -Godot provides three models of equalizers with different band counts. Equalizers are useful on the Master Bus to completely master a mix and give it character. They are -also useful when a game is run on a mobile device, to adjust the mix to that kind of speakers (it can be added but disabled when headphones are plugged). +Godot provides four equalizers with different numbers of bands. An equalizer on the master bus can be useful to cut frequencies that the device's speakers can't reproduce well (e.g. a mobile phone's speakers won't reproduce bass content well). The equalizer effect can be disabled when headphones are plugged in. + +Filter +~~~~~~ + +Filter is what all other effects processors inherit from and should not be used directly. HighPassFilter, HighShelfFilter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These are filters that cut frequencies below a specific *Cutoff*. A common use of high pass filters is to add it to effects (or voice) that were recorded too close to a mic and need -to sound more realistic. It is commonly used for some types of environment, like space. +These are filters that cut frequencies below a specific *Cutoff* frequency. HighPassFilter and HighShelfFilter are used to reduce the bass content of a signal. Limiter ~~~~~~~ -A limiter is similar to a compressor, but it's less flexible and designed to disallow sound going over a given dB threshold. Adding one in the *Master Bus* is always recommended -to reduce the effects of clipping. +A limiter is similar to a compressor, but it's less flexible and designed to prevent a signal's amplitude exceeding a given dB threshold. Adding a limiter to the *Master Bus* is a safeguard against clipping. LowPassFilter, LowShelfFilter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These are the most common filters, they cut frequencies above a specific *Cutoff* and can also resonate. They can be used for a wide amount of effects, from underwater sound to simulating -a sound coming from far away. +These are the most common filters, they cut frequencies above a specific *Cutoff* frequency and can also resonate (boost frequencies close to the *Cutoff* frequency). Low pass filters can be used to simulate 'muffled' sound. For instance underwater sound, sound blocked by walls, or distant sound. NotchFilter ~~~~~~~~~~~ -The opposite to the BandPassFilter, it removes a band of sound from the frequency spectrum at a given *Cutoff*. +The opposite to the BandPassFilter, it removes a band of sound from the frequency spectrum at a given *Cutoff* frequency. Panner ~~~~~~ -This is a simple helper to pan sound left or right. +The Panner allows the stereo balance of a signal to be adjusted between left and right channels (wear headphones to audition this effect). Phaser ~~~~~~ -It probably does not make much sense to explain that this effect is formed by two signals being dephased and cancelling each other out. -It will be sufficient to note that you can make a Darth Vader voice with it, or jet-like sounds. +It probably does not make much sense to explain that this effect is formed by two signals being dephased and cancelling each other out. You can make a Darth Vader voice with it, or jet-like sounds. PitchShift ~~~~~~~~~~ -This effect allows for modulating pitch independently of tempo. All frequencies can be increased/decreased with minimal effect on transients. Can be used for effects such as voice modulation. +This effect allows the adjustment of the signal's pitch independently of its speed. All frequencies can be increased/decreased with minimal effect on transients. PitchShift can be useful to create unusually high or deep voices. + +Record +~~~~~~ + +The Record effect allows audio passing through the bus to be written to a file. Reverb ~~~~~~ @@ -171,7 +168,7 @@ to apply chamber feel to all sounds. StereoEnhance ~~~~~~~~~~~~~ -This effect has a few algorithms available to enhance the stereo spectrum, in case this is needed. +This effect has a few algorithms for enhancing a signal's stereo spectrum. Automatic bus disabling ----------------------- @@ -191,5 +188,4 @@ If a bus is renamed, however, the reference will be lost and the Stream Player w Default bus layout ------------------ -The default bus layout is automatically saved to the ``res://default_bus_layout.tres`` file. Other bus layouts can be saved to/retrieved from files in case of having -to change snapshots, but in most cases, this is not necessary. +The default bus layout is automatically saved to the ``res://default_bus_layout.tres`` file. Custom bus arrangements can saved and loaded from disk. diff --git a/tutorials/audio/audio_streams.rst b/tutorials/audio/audio_streams.rst index 26ee5bb5b..2d739a5fd 100644 --- a/tutorials/audio/audio_streams.rst +++ b/tutorials/audio/audio_streams.rst @@ -6,43 +6,41 @@ Audio streams Introduction ------------ -As you might have already read in the :ref:`Audio Buses Tutorial` , +As you might have already read in the :ref:`Audio Buses Tutorial`, sound is sent to each bus via an AudioStreamPlayer. -There are many types of AudioStreamPlayer, which will be explained in detail. Each one loads +There are different kinds of AudioStreamPlayer. Each one loads an AudioStream and plays it back. AudioStream ----------- -An audio stream is an abstract object that emits sound. It can come from many places, but most commonly +An audio stream is an abstract object that emits sound. The sound can come from many places, but most commonly from the filesystem. Audio files, such as .wav or .ogg, can be loaded as AudioStreams and placed inside an AudioStreamPlayer. -Here is a comparison of the two, to help you choose which one fits your specific use case best: +Here is a comparison of the two types of file to help you choose the one that fits your use case best: -* Audio files of type *.wav* are quite large, but use little CPU power to play back. Hundreds of them can be played simultaneously with little impact to performance. This format is usually best for short sound effects, as the importer will trim and convert them to IMA-ADPCM. -* Audio files of type *.ogg* are much smaller, but use considerably more CPU power to play back, so only a few can be played back (especially on mobile!). This format is usually best for music or long sound effect sequences. It also works well for voice at relatively low bitrates. +* Audio files of type *.wav* are quite large, but use little CPU power to play back. Hundreds of them can be played simultaneously with little impact to performance. This format is usually best for short sound effects. +* Audio files of type *.ogg* are much smaller, but use considerably more CPU power to play back, so only a few can be played back (especially on mobile!). This format works well for music, long sound effect sequences, and voice at relatively low bitrates. -Keep in mind neither .wav nor .ogg usually contains looping information, so this information must be set on the import options of each: +Keep in mind neither .wav nor .ogg usually contains looping information. If looping is desired it must be set up using the import options of each file type: .. image:: img/audio_stream_import.png -There are other types of AudioStream, such as AudioStreamRandomPitch, which takes an existing AudioStream and modulates the pitch every time it's played back randomly (great for some sound effects), -and more will keep appearing in the future. +There are other types of AudioStreamPlayer, such as AudioStreamRandomPitch. This one makes a random adjustment to the sound's pitch every time it's played back. This can be helpful for adding interest to sounds that are played back often. AudioStreamPlayer ----------------- -This is the standard stream player; it can play to any given bus. In 5.1 sound, it can send to stereo mix or front speakers. +This is the standard stream player; it can play to any bus. In 5.1 sound, it can send to stereo mix or front speakers. AudioStreamPlayer2D ------------------- This is a variant of AudioStreamPlayer, but emits sound in a 2D positional environment. When close to the left of the screen, the panning will go left. When close to the right side, it will go right. -While it's possible to send these effects to specific audio buses, one of the best strategies is to use an Area2D to divert sound to a specific bus. This allows you to create buses with different -reverb or sound qualities and make it so the emitter will automatically send to them when entering the Area2D shapes. +.. note:: Area2Ds can be used to divert sound from any AudioStreamPlayer2Ds they contain to specific buses. This makes it possible to create buses with different reverb or sound qualities to handle action happening in a particular parts of your game world. .. image:: img/audio_stream_2d_area.png @@ -66,19 +64,19 @@ This is done by enabling this type of reverb in the *Reverb Bus* section of *Are .. image:: img/audio_stream_reverb_bus.png -At the same time, a special bus layout is created where each area receives the reverb info from each area. Of course, an actual Reverb effect must be created in that bus for anything to happen: +At the same time, a special bus layout is created where each area receives the reverb info from each area. A Reverb effect needs to be created and configured in each reverb bus to complete the setup for the desired effect: .. image:: img/audio_stream_reverb_bus2.png -The Area Reverb Bus section also has a specific parameter named "Uniformity". Some types of rooms bounce sounds more than others (like for example, a typical warehouse), so reverberation can be heard -almost uniformly across the room even though the source is far away. Playing around with this parameter can simulate that effect. +The Area Reverb Bus section also has a parameter named "Uniformity". Some types of rooms bounce sounds more than others (like a warehouse), so reverberation can be heard +almost uniformly across the room even though the source may be far away. Playing around with this parameter can simulate that effect. Doppler ~~~~~~~ -When the relative velocity between an emitter and listener changes, this is perceived as an increase or decrease of the pitch shift. Godot can track changes in velocities of *AudioStreamPlayer3D* or *Camera*. +When the relative velocity between an emitter and listener changes, this is perceived as an increase or decrease in the pitch of the emitted sound. Godot can track changes in velocities of *AudioStreamPlayer3D* or *Camera*. Both have this property, which must be enabled manually: .. image:: img/audio_stream_doppler.png -Simply enable it by setting it depending on how objects will be moved (whether on regular *process* or *physics_process* step) and the tracking will happen automatically! +Enable it by setting it depending on how objects will be moved (whether on regular *process* or *physics_process* step) and the tracking will happen automatically. diff --git a/tutorials/audio/img/audio_buses1.png b/tutorials/audio/img/audio_buses1.png index 6671a37ab..0a584130d 100644 Binary files a/tutorials/audio/img/audio_buses1.png and b/tutorials/audio/img/audio_buses1.png differ diff --git a/tutorials/audio/img/audio_buses2.png b/tutorials/audio/img/audio_buses2.png index f40726c8e..889c6b025 100644 Binary files a/tutorials/audio/img/audio_buses2.png and b/tutorials/audio/img/audio_buses2.png differ diff --git a/tutorials/audio/img/audio_buses3.png b/tutorials/audio/img/audio_buses3.png index b72d4469a..ed6539d57 100644 Binary files a/tutorials/audio/img/audio_buses3.png and b/tutorials/audio/img/audio_buses3.png differ diff --git a/tutorials/audio/img/audio_buses4.png b/tutorials/audio/img/audio_buses4.png index 29ebf8f19..a533e2a52 100644 Binary files a/tutorials/audio/img/audio_buses4.png and b/tutorials/audio/img/audio_buses4.png differ diff --git a/tutorials/audio/img/audio_buses5.png b/tutorials/audio/img/audio_buses5.png index 556763eab..f9c95df8c 100644 Binary files a/tutorials/audio/img/audio_buses5.png and b/tutorials/audio/img/audio_buses5.png differ diff --git a/tutorials/content/index.rst b/tutorials/content/index.rst index 58c356b47..07219503a 100644 --- a/tutorials/content/index.rst +++ b/tutorials/content/index.rst @@ -5,4 +5,6 @@ Creating content :maxdepth: 1 :name: toc-tutorials-content + procedural_geometry making_trees + diff --git a/tutorials/content/procedural_geometry.rst b/tutorials/content/procedural_geometry.rst new file mode 100644 index 000000000..389ecac23 --- /dev/null +++ b/tutorials/content/procedural_geometry.rst @@ -0,0 +1,144 @@ +.. _doc_procedural_geometry: + +Procedural geometry generation +============================== + +Users often ask how to generate geometry from code. This is not very complicated, but it's not obvious. +Godot provides a few classes entirely dedicated to make it this easy. Still, the best tool for the job depends +entirely on the use case. + +SurfaceTool +----------- + +This is the most common helper. :ref:`SurfaceTool` is a class you can instantiate to generate :ref:`Meshes`, specifically *Mesh Surfaces*. + +It has a similar API to OpenGL 1.x, and it's meant for static content. This means, the mesh is generated once and then used. + +Here is a simple example of how to use it to add a single triangle. + +.. tabs:: + .. code-tab:: gdscript GDScript + + var st = SurfaceTool.new() + + st.begin(Mesh.PRIMITIVE_TRIANGLES) + + # Prepare attributes for add_vertex. + st.add_normal(Vector3(0, 0, 1)) + st.add_uv(Vector2(0, 0)) + # Call last for each vertex, adds the above attributes. + st.add_vertex(Vector3(-1, -1, 0)) + + st.add_normal(Vector3(0, 0, 1)) + st.add_uv(Vector2(0, 1)) + st.add_vertex(Vector3(-1, 1, 0)) + + st.add_normal(Vector3(0, 0, 1)) + st.add_uv(Vector2(1, 1)) + st.add_vertex(Vector3(1, 1, 0)) + + # Create indices, indices are optional. + st.index() + + # Commit to a mesh. + var mesh = st.commit() + +Just explore the APIs and the possibilities. + +ImmediateGeometry +----------------- + +Unlike *SurfaceTool*, :ref:`ImmediateGeometry` is an actual node. It's similar in the "OpenGL 1.x" style API, +but it's actually designed to create content on the fly and modify it every frame efficiently. + +Generating complex geometry (several thousand vertices) with this node is inefficient, even if it's done only once. Instead, *ImmediateGeometry* is designed to generate simple geometry that changes every frame. + +It's used similar to *SurfaceTool*. + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends ImmediateGeometry + + void _process(delta): + # Clean up before drawing. + clear() + + # Begin draw. + begin(Mesh.PRIMITIVE_TRIANGLES) + + # Prepare attributes for add_vertex. + set_normal( Vector3(0, 0, 1)) + set_uv(Vector2(0, 0)) + # Call last for each vertex, adds the above attributes. + add_vertex(Vector3(-1, -1, 0)) + + set_normal(Vector3(0, 0, 1)) + set_uv(Vector2(0, 1)) + add_vertex(Vector3(-1, 1, 0)) + + set_normal(Vector3(0, 0, 1)) + set_uv(Vector2(1, 1)) + add_vertex(Vector3(1, 1, 0)) + + # End drawing. + end() + +Arrays +------ + +Lastly, the final way to do this is to create arrays themselves. This is the most efficient way to create static geometry, and is only +recommended when SurfaceTool is not fast enough. + +Similar code as before, but draw a square using indices: + + +.. tabs:: + .. code-tab:: gdscript GDScript + + var arrays = [] + arrays.resize(Mesh.ARRAY_MAX) + + var normal_array = [] + var uv_array = [] + var vertex_array = [] + var index_array = [] + + normal_array.resize(4) + uv_array.resize(4) + vertex_array.resize(4) + index_array.resize(6) + + normal_array[0] = Vector3(0, 0, 1) + uv_array[0] = Vector2(0, 0) + vertex_array[0] = Vector3(-1, -1) + + normal_array[1] = Vector3(0, 0, 1) + uv_array[1] = Vector2(0,1) + vertex_array[1] = Vector3(-1, 1) + + normal_array[2] = Vector3(0, 0, 1) + uv_array[2] = Vector2(1, 1) + vertex_array[2] = Vector3(1, 1) + + normal_array[3] = Vector3(0, 0, 1) + uv_array[3] = Vector2(1, 0) + vertex_array[3] = Vector3(1, -1) + + # Indices are optional in Godot, but if they exist they are used. + index_array[0] = 0 + index_array[1] = 1 + index_array[2] = 2 + + index_array[3] = 2 + index_array[4] = 3 + index_array[5] = 0 + + arrays[Mesh.ARRAY_VERTEX] = vertex_array + arrays[Mesh.ARRAY_NORMAL] = normal_array + arrays[Mesh.ARRAY_TEX_UV] = uv_array + arrays[Mesh.ARRAY_INDEX] = index_array + + var mesh = ArrayMesh.new() + + mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES,arrays) diff --git a/tutorials/gui/gui_containers.rst b/tutorials/gui/gui_containers.rst new file mode 100644 index 000000000..ecafe430b --- /dev/null +++ b/tutorials/gui/gui_containers.rst @@ -0,0 +1,181 @@ +.. _doc_gui_containers: + +Containers +========== + +:ref:`Anchors ` are an efficient way to handle +different aspect ratios for basic multiple resolution handling in GUIs, + +For more complex user interfaces, they can become difficult to use. + +This is often the case of games, such as RPGs, online chats, tycoons or simulations. Another +common case where more advanced layout features may be required is in-game tools (or simply just tools). + +All these situations require a more capable OS-like user interface, with advanced layout and formatting. +For that, :ref:`Containers ` are more useful. + +Container layout +---------------- + +Containers provide a huge amount of layout power (as an example, the Godot editor user interface is entirely done using them): + + .. image:: img/godot_containers.png + +When a :ref:`Container `-derived node is used, all children :ref:`Control ` nodes give up their +own positioning ability. This means the *Container* will control their positioning and any attempt to manually alter these +nodes will be either ignored or invalidated the next time their parent is resized. + +Likewise, when a *Container* derived node is resized, all it's children will be re-positioned according to it, +with a behavior based on the type of container used: + + .. image:: img/container_example.gif + +Example of *HBoxContainer* resizing children buttons. + +The real strength of containers is that they can be nested (as nodes), allowing the creation of very complex layouts that resize effortlessly. + +Size flags +---------- + +When adding a node to a container, the way the container treats each child depends mainly on their *size flags*. These flags +can be found by inspecting any control that is a child of a *Container*. + + .. image:: img/container_size_flags.png + +Size flags are independent for vertical and horizontal sizing and not all containers make use of them (but most do): + +* **Fill**: Ensures the control *fills* the designated area within the container. No matter if + a control *expands* or not (see below), it will only *fill* the designated area when this is toggled on (it is by default). +* **Expand**: Attempts to use as most space as possible in the parent container (in this each axis). + Controls that don't expand will be pushed away by those that do. Between those expanding, the + amount of space they take from each other is determined by the *Ratio* (see below). +* **Shrink Center** When expanding (and if not filling), try to remain at the center of the expanded + area (by default it remains at the left or top). +* **Ratio** Simple ratio of how much expanded controls take up the available space in relation to each + other. A control with "2", will take up twice as much available space as one with "1". + +Experimenting with these flags and different containers is recommended to get a better grasp on how they work. + +Container types +--------------- + +Godot provides several container types out of the box as they serve different purposes: + +Box Containers +^^^^^^^^^^^^^^ + +Arrange child controls vertically or horizontally (via :ref:`HBoxContainer ` and +:ref:`VBoxContainer `). In the opposite of the designated direction +(as in, vertical for an horizontal container), it just expands the children. + + .. image:: img/containers_box.png + +These containers make use of the *Ratio* property for children with the *Expand* flag set. + +Grid Container +^^^^^^^^^^^^^^ + +Arranges child controls in a grid layout (via :ref:`GridContainer `, amount +of columns must be specified). Uses both the vertical and horizontal expand flags. + + .. image:: img/containers_grid.png + +Margin Container +^^^^^^^^^^^^^^^^ + +Child controls are expanded towards the bounds of this control (via +:ref:`MarginContainer `). Padding will be added on the margins +depending on the theme configuration. + + .. image:: img/containers_margin.png + +Again, keep in mind that the margins are a *Theme* value, so they need to be edited at the +constants overrides section if desired for a single control: + + .. image:: img/containers_margin_constants.png + +Tab Container +^^^^^^^^^^^^^ + +Allows you to place several child controls stacked on top of each other (via +:ref:`TabContainer `), with only the *current* one visible. + + .. image:: img/containers_tab.png + +Changing the *current* one is done via tabs located at the top of the container, via clicking: + + .. image:: img/containers_tab_click.gif + +The titles are generated from the node names by default (although they can be overriden via *TabContainer* API). + +Settings such as tab placement and *StyleBox* can be modified in the *TabContainer* theme overrides. + +Split Container +^^^^^^^^^^^^^^^ + +Accepts only one or two children controls, then places them side to side with a divisor +(via :ref:`HSplitContainer ` and :ref:`VSplitContainer `). +Respects both horizontal and vertical flags, as well as *Ratio*. + + .. image:: img/containers_split.png + +The divisor can be dragged around to change the size relation between both children: + + .. image:: img/containers_split_drag.gif + + +PanelContainer +^^^^^^^^^^^^^^ + +Simple container that draws a *StyleBox*, then expands children to cover its whole area +(via :ref:`PanelContainer `, respecting the *StyleBox* margins). +It respects both the horizontal and vertical size flags. + + .. image:: img/containers_panel.png + +This container is useful as top-level, or just to add custom backgrounds to sections of a layout. + +ScrollContainer +^^^^^^^^^^^^^^^ + +Accepts a single child node. If this node is bigger than the container, scrollbars will be added +to allow panning the node around (via :ref:`ScrollContainer `). Both +vertical and horizontal size flags are respected, and the behavior can be turned on or off +per axis in the properties. + + .. image:: img/containers_scroll.png + +Mouse wheel and touch drag (when touch is available) are also valid ways to pan the child control around. + + .. image:: img/containers_center_pan.gif + +As in the example above, one of the most common ways to use this container is together with a *VBoxContainer* as child. + + +ViewportContainer +^^^^^^^^^^^^^^^^^ + +This is a special control that will only accept a single *Viewport* node as child, and it will display +it as if it was an image (via :ref:`ViewportContainer `). + +Creating custom Containers +-------------------------- + +It is possible to easily create a custom container using script. Here is an example of a simple container that fits children +to its rect size: + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends Container + + func _notification(what): + if (what==NOTIFICATION_SORT_CHILDREN): + # Must re-sort the children + for c in get_children(): + # Fit to own size + fit_child_in_rect( c, Rect2( Vector2(), rect_size ) ) + + func set_some_setting(): + # Some setting changed, ask for childre re-sort + queue_sort() diff --git a/tutorials/gui/img/container_example.gif b/tutorials/gui/img/container_example.gif new file mode 100644 index 000000000..866967ab7 Binary files /dev/null and b/tutorials/gui/img/container_example.gif differ diff --git a/tutorials/gui/img/container_size_flags.png b/tutorials/gui/img/container_size_flags.png new file mode 100644 index 000000000..e760e17e0 Binary files /dev/null and b/tutorials/gui/img/container_size_flags.png differ diff --git a/tutorials/gui/img/containers_box.png b/tutorials/gui/img/containers_box.png new file mode 100644 index 000000000..91cbd78ef Binary files /dev/null and b/tutorials/gui/img/containers_box.png differ diff --git a/tutorials/gui/img/containers_center.png b/tutorials/gui/img/containers_center.png new file mode 100644 index 000000000..378d6c88d Binary files /dev/null and b/tutorials/gui/img/containers_center.png differ diff --git a/tutorials/gui/img/containers_center_pan.gif b/tutorials/gui/img/containers_center_pan.gif new file mode 100644 index 000000000..fe6393976 Binary files /dev/null and b/tutorials/gui/img/containers_center_pan.gif differ diff --git a/tutorials/gui/img/containers_grid.png b/tutorials/gui/img/containers_grid.png new file mode 100644 index 000000000..b996bc82a Binary files /dev/null and b/tutorials/gui/img/containers_grid.png differ diff --git a/tutorials/gui/img/containers_margin.png b/tutorials/gui/img/containers_margin.png new file mode 100644 index 000000000..eabaf1a7a Binary files /dev/null and b/tutorials/gui/img/containers_margin.png differ diff --git a/tutorials/gui/img/containers_margin_constants.png b/tutorials/gui/img/containers_margin_constants.png new file mode 100644 index 000000000..648a540d6 Binary files /dev/null and b/tutorials/gui/img/containers_margin_constants.png differ diff --git a/tutorials/gui/img/containers_panel.png b/tutorials/gui/img/containers_panel.png new file mode 100644 index 000000000..94e50eae4 Binary files /dev/null and b/tutorials/gui/img/containers_panel.png differ diff --git a/tutorials/gui/img/containers_scroll.png b/tutorials/gui/img/containers_scroll.png new file mode 100644 index 000000000..a61307927 Binary files /dev/null and b/tutorials/gui/img/containers_scroll.png differ diff --git a/tutorials/gui/img/containers_split.png b/tutorials/gui/img/containers_split.png new file mode 100644 index 000000000..d99172c22 Binary files /dev/null and b/tutorials/gui/img/containers_split.png differ diff --git a/tutorials/gui/img/containers_split_drag.gif b/tutorials/gui/img/containers_split_drag.gif new file mode 100644 index 000000000..b54a2c3ed Binary files /dev/null and b/tutorials/gui/img/containers_split_drag.gif differ diff --git a/tutorials/gui/img/containers_tab.png b/tutorials/gui/img/containers_tab.png new file mode 100644 index 000000000..a605541c8 Binary files /dev/null and b/tutorials/gui/img/containers_tab.png differ diff --git a/tutorials/gui/img/containers_tab_click.gif b/tutorials/gui/img/containers_tab_click.gif new file mode 100644 index 000000000..326e48c30 Binary files /dev/null and b/tutorials/gui/img/containers_tab_click.gif differ diff --git a/tutorials/gui/img/godot_containers.png b/tutorials/gui/img/godot_containers.png new file mode 100644 index 000000000..3ef8f5fe4 Binary files /dev/null and b/tutorials/gui/img/godot_containers.png differ diff --git a/tutorials/gui/index.rst b/tutorials/gui/index.rst index 0fc0c8f81..f4643c74c 100644 --- a/tutorials/gui/index.rst +++ b/tutorials/gui/index.rst @@ -8,6 +8,7 @@ GUI gui_skinning custom_gui_controls size_and_anchors + gui_containers bbcode_in_richtextlabel -.. gui_containers + diff --git a/tutorials/gui/size_and_anchors.rst b/tutorials/gui/size_and_anchors.rst index 6ba098449..67f6bd418 100644 --- a/tutorials/gui/size_and_anchors.rst +++ b/tutorials/gui/size_and_anchors.rst @@ -1,7 +1,7 @@ .. _doc_size_and_anchors: Size and anchors ----------------- +================ If a game was always going to be run on the same device and at the same resolution, positioning controls would be a simple matter of setting the @@ -38,3 +38,28 @@ it, leaving a 20 pixel margin: .. image:: img/marginaround.png +Centering a control +------------------- + +To center a control in its parent, set its anchors to 0.5 and each margin +to half of its relevant dimension. For example, the code below shows how +a TextureRect can be centered in its parent: + +:: + + var rect = TextureRect.new() + rect.texture = load("res://icon.png") + rect.anchor_left = 0.5 + rect.anchor_right = 0.5 + rect.anchor_top = 0.5 + rect.anchor_bottom = 0.5 + var texture_size = rect.texture.get_size() + rect.margin_left = -texture_size.x / 2 + rect.margin_right = -texture_size.x / 2 + rect.margin_top = -texture_size.y / 2 + rect.margin_bottom = -texture_size.y / 2 + add_child(rect) + +Setting each anchor to 0.5 moves the reference point for the margins to +the center of its parent. From there, we set negative margins so that +the control gets its natural size. diff --git a/tutorials/inputs/img/inputs_inputmap.png b/tutorials/inputs/img/inputs_inputmap.png new file mode 100644 index 000000000..63b504845 Binary files /dev/null and b/tutorials/inputs/img/inputs_inputmap.png differ diff --git a/tutorials/inputs/index.rst b/tutorials/inputs/index.rst index 9c3695802..545114055 100644 --- a/tutorials/inputs/index.rst +++ b/tutorials/inputs/index.rst @@ -6,5 +6,6 @@ Inputs :name: toc-learn-features-inputs inputevent + input_examples mouse_and_input_coordinates custom_mouse_cursor diff --git a/tutorials/inputs/input_examples.rst b/tutorials/inputs/input_examples.rst new file mode 100644 index 000000000..234be2735 --- /dev/null +++ b/tutorials/inputs/input_examples.rst @@ -0,0 +1,244 @@ +.. _doc_input_examples: + +Input examples +============== + +Introduction +------------ + +In this tutorial, you'll learn how to use Godot's :ref:`InputEvent ` +system to capture player input. There are many different types of input your +game may use - keyboard, gamepad, mouse, etc. - and many different ways to +turn those inputs into actions in your game. This document will show you some +of the most common scenarios, which you can use as starting points for your +own projects. + +.. note:: For a detailed overview of how Godot's input event system works, + see :ref:`doc_inputevent`. + +Events versus polling +--------------------- + +Sometimes you want your game to respond to a certain input event - pressing +the "jump" button, for example. For other situations, you might want something +to happen as long as a key is pressed, such as movement. In the first case, +you can use the ``_input()`` function, which will be called whenever an input +event occurs. In the second case, Godot provides the :ref:`Input ` +singleton, which you can use to query the state of an input. + +Examples: + +.. tabs:: + .. code-tab:: gdscript GDScript + + # input event - runs when the input happens + func _input(event): + if event.is_action_pressed("jump"): + jump() + + # polling - runs every frame + func _physics_process(delta): + if Input.is_action_pressed("move_right"): + # move as long as the key/button is pressed + position.x += speed * delta + +This gives you the flexibility to mix-and-match the type of input processing +you do. + +For the remainder of this tutorial, we'll focus on capturing individual +events in ``_input()``. + +Input events +------------ + +Input events are objects that inherit from :ref:`InputEvent `. +Depending on the event type, the object will contain specific properties +related to that event. To see what events actually look like, add a Node and +attach the following script: + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends Node + + func _input(event): + print(event.as_text()) + +As you press keys, move the mouse, and perform other inputs, you'll see each +event scroll by in the output window. Here's an example of the output: + +:: + + A + InputEventMouseMotion : button_mask=0, position=(551, 338), relative=(-85, 47), speed=(0, 0) + InputEventMouseButton : button_index=BUTTON_LEFT, pressed=true, position=(551, 338), button_mask=1, doubleclick=false + InputEventMouseButton : button_index=BUTTON_LEFT, pressed=false, position=(551, 338), button_mask=0, doubleclick=false + S + F + InputEventMouseMotion : button_mask=0, position=(547, 338), relative=(-1, 0), speed=(0, 0) + InputEventMouseMotion : button_mask=0, position=(542, 338), relative=(-4, 0), speed=(0, 0) + +As you can see, the results are very different for the different types of +input. Key events are even printed as their key symbols. For example, let's consider :ref:`InputEventMouseButton `. +It inherits from the following classes: + +- :ref:`InputEvent ` - the base class for all input events +- :ref:`InputEventWithModifiers ` - adds the ability to check if modifiers are pressed, such as ``Shift`` or ``Alt``. +- :ref:`InputEventMouse ` - adds mouse event properties, such as ``position`` +- :ref:`InputEventMouseButton ` - contains the index of the button that was pressed, whether it was a double-click, etc. + +.. tip:: It's a good idea to keep the class reference open while you're working + with events so you can check the event type's available properties and + methods. + +You can encounter errors if you try and access a property on an input type that +doesn't contain it - calling ``position`` on ``InputEventKey`` for example. To +avoid this, make sure to test the event type first: + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _input(event): + if event is InputEventMouseButton: + print("mouse button event at ", event.position) + + +InputMap +-------- + +The :ref:`InputMap ` is the most flexible way to handle a +variety of inputs. You use this by creating named input *actions*, to which +you can assign any number of input events, such as keypresses or mouse clicks. +A new Godot project includes a number of default actions already defined. To +see them, and to add your own, open Project -> Project Settings and select +the InputMap tab: + +.. image:: img/inputs_inputmap.png + +Capturing actions +~~~~~~~~~~~~~~~~~ + +Once you've defined your actions, you can process them in your scripts using +``is_action_pressed()`` and ``is_action_released()`` by passing the name of +the action you're looking for: + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _input(event): + if event.is_action_pressed("my_action"): + print("my_action occurred!") + +Keyboard events +--------------- + +Keyboard events are captured in :ref:`InputEventKey `. +While it's recommended to use input actions instead, there may be cases where +you want to specifically look at key events. For this example, let's check for +the "T" key: + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _input(event): + if event is InputEventKey and event.pressed: + if event.scancode == KEY_T: + print("T was pressed") + +.. tip:: See :ref:`@GlobalScope_KeyList ` for a list of scancode + constants. + +Keyboard modifiers +~~~~~~~~~~~~~~~~~~ + +Modifier properties are inherited from +:ref:`InputEventWithModifiers `. This allows +you to check for modifier combinations using boolean properties. Let's imagine +you want one thing to happen when the "T" key is pressed, but something +different when it's "Shift+T": + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _input(event): + if event is InputEventKey and event.pressed: + if event.scancode == KEY_T: + if event.shift: + print("Shift+T was pressed") + else: + print("T was pressed") + +.. tip:: See :ref:`@GlobalScope_KeyList ` for a list of scancode + constants. + +Mouse events +------------ + +Mouse events stem from the :ref:`InputEventMouse ` class, and +are separated into two types: :ref:`InputEventMouseButton ` +and :ref:`InputEventMouseMotion `. Note that this +means that all mouse events will contain a ``position`` property. + +Mouse buttons +~~~~~~~~~~~~~ + +Capturing mouse buttons is very similar to handling key events. :ref:`@GlobalScope_ButtonList ` +contains a list of ``BUTTON_*`` constants for each possible button, which will +be reported in the event's ``button_index`` property. Note that the scrollwheel +also counts as a button - two buttons, to be precise, with both +``BUTTON_WHEEL_UP`` and ``BUTTON_WHEEL_DOWN`` being separate events. + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _input(event): + if event is InputEventMouseButton: + if event.button_index == BUTTON_LEFT and event.pressed: + print("Left button was clicked at ", event.position) + if event.button_index == BUTTON_WHEEL_UP and event.pressed: + print("Wheel up") + +Mouse motion +~~~~~~~~~~~~ + +:ref:`InputEventMouseMotion ` events occur whenever +the mouse moves. You can find the move's distance with the ``relative`` +property. + +Here's an example using mouse events to drag-and-drop a :ref:`Sprite ` +node: + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends Node + + var dragging = false + var click_radius = 32 # Size of the sprite + + func _input(event): + if event is InputEventMouseButton and event.button_index == BUTTON_LEFT: + if (event.position - $Sprite.position).length() < click_radius: + # Start dragging if the click is on the sprite. + if !dragging and event.pressed: + dragging = true + # Stop dragging if the button is released. + if dragging and !event.pressed: + dragging = false + + if event is InputEventMouseMotion and dragging: + # While dragging, move the sprite with the mouse. + $Sprite.position = event.position + +Touch events +------------ + +If you are using a touchscreen device, you can generate touch events. +:ref:`InputEventScreenTouch ` is equivalent to +a mouse click event, and :ref:`InputEventScreenDrag ` +works much the same as mouse motion. + +.. tip:: To test your touch events on a non-touchscreen device, open Project + Settings and go to the "Input Devices/Pointing" section. Enable "Emulate + Touch From Mouse" and your project will interpret mouse clicks and + motion as touch events. diff --git a/tutorials/inputs/inputevent.rst b/tutorials/inputs/inputevent.rst index b64d00205..41e8b7289 100644 --- a/tutorials/inputs/inputevent.rst +++ b/tutorials/inputs/inputevent.rst @@ -185,7 +185,8 @@ 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.action = "move_left" + ev.pressed = true # feedback Input.parse_input_event(ev) diff --git a/tutorials/io/background_loading.rst b/tutorials/io/background_loading.rst index 85ba79981..62913c784 100644 --- a/tutorials/io/background_loading.rst +++ b/tutorials/io/background_loading.rst @@ -7,8 +7,8 @@ When switching the main scene of your game (e.g. going to a new level), you might want to show a loading screen with some indication that progress is being made. The main load method (``ResourceLoader::load`` or just ``load`` from GDScript) blocks your -thread while the resource is being loaded, so it's not good. This -document discusses the ``ResourceInteractiveLoader`` class for smoother +thread, making your game appear frozen and unresponsive while the resource is being loaded. This +document discusses the alternative of using the ``ResourceInteractiveLoader`` class for smoother load screens. ResourceInteractiveLoader @@ -89,7 +89,7 @@ Example This example demonstrates how to load a new scene. Consider it in the context of the :ref:`doc_singletons_autoload` example. -Firstly, we set up some variables and initialize the ``current_scene`` +First, we set up some variables and initialize the ``current_scene`` with the main scene of the game: :: @@ -106,8 +106,8 @@ with the main scene of the game: The function ``goto_scene`` is called from the game when the scene needs to be switched. It requests an interactive loader, and calls ``set_process(true)`` to start polling the loader in the ``_process`` -callback. It also starts a "loading" animation, which can show a -progress bar or loading screen, etc. +callback. It also starts a "loading" animation, which could show a +progress bar or loading screen. :: @@ -127,7 +127,7 @@ progress bar or loading screen, etc. ``_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, -``ERR_FILE_EOF`` means load is done, anything else means there was an +``ERR_FILE_EOF`` means loading is done, anything else means there was an error. Also note we skip one frame (via ``wait_frames``, set on the ``goto_scene`` function) to allow the loading screen to show up. @@ -150,7 +150,7 @@ precise control over the timings. return var t = OS.get_ticks_msec() - while OS.get_ticks_msec() < t + time_max: # use "time_max" to control how much time we block this thread + while OS.get_ticks_msec() < t + time_max: # use "time_max" to control for how long we block this thread # poll your loader var err = loader.poll() @@ -207,7 +207,7 @@ Not blocking main thread during the polling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you have a mutex to allow calls from the main thread to your loader -class, don't lock the former while you call ``poll`` on the latter. When a +class, don't lock the main thread while you call ``poll`` on your loader class. When a resource is done loading, it might require some resources from the low-level APIs (VisualServer, etc), which might need to lock the main thread to acquire them. This might cause a deadlock if the main thread @@ -243,14 +243,14 @@ Remove a resource from the queue, discarding any loading done. func is_ready(path) -Returns true if a resource is done loading and ready to be retrieved. +Returns true if a resource is fully loaded and ready to be retrieved. :: func get_progress(path) -Get the progress of a resource. Returns -1 on error (for example if the -resource is not on the queue), or a number between 0.0 and 1.0 with the +Get the progress of a resource. Returns -1 if there was an error (for example if the +resource is not in the queue), or a number between 0.0 and 1.0 with the progress of the load. Use mostly for cosmetic purposes (updating progress bars, etc), use ``is_ready`` to find out if a resource is actually ready. @@ -260,7 +260,7 @@ actually ready. func get_resource(path) Returns the fully loaded resource, or null on error. If the resource is -not done loading (``is_ready`` returns false), it will block your thread +not fully loaded (``is_ready`` returns false), it will block your thread and finish the load. If the resource is not on the queue, it will call ``ResourceLoader::load`` to load it normally and return it. diff --git a/tutorials/legal/complying_with_licenses.rst b/tutorials/legal/complying_with_licenses.rst new file mode 100644 index 000000000..1a0820b34 --- /dev/null +++ b/tutorials/legal/complying_with_licenses.rst @@ -0,0 +1,106 @@ +.. _doc_complying_with_licenses: + +Complying with Licenses +======================= + +What are licenses? +------------------ + +Godot is created and distributed under the `MIT License `_. It doesn't have a sole owner either, as every contributor that submits code to the project does it under this same license and keeps ownership of the contribution. + +The license is the legal requirement for you (or your company) to use and distribute the software (and derivative projects, including games made with it). Your game or project can have a different license, but it still needs to comply with the original one. + + +Requirements +------------ + +In the case of the MIT license, the only requirement is to include the license text somewhere in your game or derivative project. + +This text reads as follows: + + + Godot Engine + + Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. + Copyright (c) 2014-2019 Godot Engine contributors. + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Inclusion +--------- + +The license does not specify how it has to be included, so anything is valid as long as it can be displayed under some condition. These are the most common approaches (only need to implement one of them, not all). + +Credits screen +^^^^^^^^^^^^^^ + +Include the above license text somewhere in the credits screen. It can be at the bottom after showing the rest of the credits. Most large studios use this approach with open source licenses. + +Licenses screen +^^^^^^^^^^^^^^^ + +Some games have a special menu (often in the settings) to display licenses. + +Output log +^^^^^^^^^^ + +Just printing the licensing text using a print() function may be enough on platforms where a global output log is readable (as an example, mobile devices). + +Accompanying file +^^^^^^^^^^^^^^^^^ + +If the game is distributed on desktop operating systems, a file containing the license can be added to the software that is installed to the user PC. + +Printed manual +^^^^^^^^^^^^^^ + +If the game includes printed manuals, license text can be included there. + +Third Party licenses +-------------------- + +Godot itself contains software written by `third parties `_. Most of it does not requiere license inclusion, but some do. Make sure to do it if you are using them. Here is a list of which ones require it: + +FreeType +^^^^^^^^ + +Godot uses `FreeType `_ to render fonts. It's license requires attribution, so the following text must be included together with the Godot license: + + + Portions of this software are copyright © The FreeType Project (www.freetype.org). All rights reserved. + + +ENet +^^^^ + +If the project being creater is a multiplayer game using the `ENet `_ driver, ENet has similar licensing terms as Godot + + + Copyright (c) 2002-2016 Lee Salzman + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +MBedTLS +^^^^^^^ + +If the is done with Godot 3.1 or above and it utilizes SSL (usually through HTTP requests), the `MBedTLS `_ Apache license needs to be complied by including the following text: + + + MBedTLS is Copyright (C) 2013-2019 ARM + + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +Keep in mind that Godot 2.x and 3.0 use `OpenSSL `_ instead. diff --git a/tutorials/legal/index.rst b/tutorials/legal/index.rst new file mode 100644 index 000000000..d142fc14b --- /dev/null +++ b/tutorials/legal/index.rst @@ -0,0 +1,8 @@ +Legal +===== + +.. toctree:: + :maxdepth: 1 + :name: toc-tutorials-legal + + complying_with_licenses diff --git a/tutorials/math/beziers_and_curves.rst b/tutorials/math/beziers_and_curves.rst new file mode 100644 index 000000000..879a41868 --- /dev/null +++ b/tutorials/math/beziers_and_curves.rst @@ -0,0 +1,209 @@ +.. _doc_beziers_and_curves: + +Beziers, curves and paths +========================= + +Bezier curves are a mathematical approximation of natural geometric shapes. We +use them to represent a curve with as little information as possible and with a +high level of flexibility. + +Unlike more abstract mathematical concepts, Bezier curves were created for +industrial design. They are a popular tool in the graphics software industry. + +They rely on :ref:`interpolation`, which we saw in the +previous article, combining multiple steps to create smooth curves. To better +understand how Bezier curves work, let's start from its simplest form: Quadratic +Bezier. + +Quadratic Bezier +---------------- + +Take three points, the minimum required for Quadratic Bezier to work: + +.. image:: img/bezier_quadratic_points.png + +To draw a curve between them, we first interpolate gradually over the two +vertices of each of the two segments formed by the three points, using values +ranging from 0 to 1. This gives us two points that move along the segments as we +change the value of ``t`` from 0 to 1. + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _quadratic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, t: float): + var q0 = p0.linear_interpolate(p1, t) + var q1 = p1.linear_interpolate(p2, t) + +We then interpolate ``q0`` and ``q1`` to obtain a single point ``r`` that moves +along a curve. + +.. tabs:: + .. code-tab:: gdscript GDScript + + var r = q0.linear_interpolate(q1, t) + return r + +This type of is called a *Quadratic Bezier* curve. + +.. image:: img/bezier_quadratic_points2.gif + +*(Image credit: Wikipedia)* + +Cubic Bezier +------------ + +Building upon the previous example, we can get more control by interpolating +between four points. + +.. image:: img/bezier_cubic_points.png + +We first use a function with four parameters to take four points as an input, +``p0``, ``p1``, ``p2`` and ``p3``: + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float): + +We apply a linear interpolation to each couple of points to reduce them to +three: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var q0 = p0.linear_interpolate(p1, t) + var q1 = p1.linear_interpolate(p2, t) + var q2 = p2.linear_interpolate(p3, t) + +We then take our three points and reduce them to two: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var r0 = q0.linear_interpolate(q1, t) + var r1 = q1.linear_interpolate(q2, t) + +And to one: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var s = r0.linear_interpolate(r1, t) + return s + +Here is the full function: + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float): + var q0 = p0.linear_interpolate(p1, t) + var q1 = p1.linear_interpolate(p2, t) + var q2 = p2.linear_interpolate(p3, t) + + var r0 = q0.linear_interpolate(q1, t) + var r1 = q1.linear_interpolate(q2, t) + + var s = r0.linear_interpolate(r1, t) + return s + +The result will be a smooth curve interpolating between all four points: + +.. image:: img/bezier_cubic_points.gif + +*(Image credit: Wikipedia)* + +.. note:: Cubic Bezier interpolation works the same in 3D, just use ``Vector3`` + instead of ``Vector2``. + +Adding control points +--------------------- + +Building upon Cubic Bezier, we can change the way two of the points work to +control the shape of our curve freely. Instead of having ``p0``, ``p1``, ``p2`` +and ``p3``, we will store them as: + +* ``point0 = p0``: Is the first point, the source +* ``control0 = p1 - p0``: Is a vector relative to the first control point +* ``control1 = p3 - p2``: Is a vector relative to the second control point +* ``point1 = p3``: Is the second point, the destination + +This way, we have two points and two control points which are relative vectors +to the respective points. If you've used graphics or animation software before, +this might look familiar: + +.. image:: img/bezier_cubic_handles.png + +This is how graphics software presents Bezier curves to the users, and how they +work and look in Godot. + +Curve2D, Curve3D, Path and Path2D +--------------------------------- + +There are two objects that contain curves: :ref:`Curve3D ` and :ref:`Curve2D ` (for 3D and 2D respectively). + +They can contain several points, allowing for longer paths. It is also possible to set them to nodes: :ref:`Path ` and :ref:`Path2D ` (also for 3D and 2D respectively): + +.. image:: img/bezier_path_2d.png + +Using them, however, may not be completely obvious, so following is a description of the most common use cases for Bezier curves. + +Evaluating +---------- + +Just evaluating them may be an option, but in most cases it's not very useful. The big drawback with Bezier curves is that if you traverse them at constant speed, from ``t = 0`` to ``t = 1``, the actual interpolation will *not* move at constant speed. The speed is also an interpolation between the distances between points ``p0``, ``p1``, ``p2`` and ``p3`` and there is not a mathematically simple way to traverse the curve at constant speed. + +Let's do a simple example with the following pseudocode: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var t = 0.0 + + func _process(delta): + t += delta + position = _cubic_bezier(p0, p1, p2, p3, t) + + +.. image:: img/bezier_interpolation_speed.gif + +As you can see, the speed (in pixels per second) of the circle varies, even though ``t`` is increased at constant speed. This makes beziers difficult to use for anything practical out of the box. + +Drawing +------- + +Drawing beziers (or objects based on the curve) is a very common use case, but it's also not easy. For pretty much any case, Bezier curves need to be converted to some sort of segments. This is normally difficult, however, without creating a very high amount of them. + +The reason is that some sections of a curve (specifically, corners) may require considerable amounts of points, while other sections may not: + +.. image:: img/bezier_point_amount.png + +Additionally, if both control points were ``0, 0`` (remember they are relative vectors), the Bezier curve would just be a straight line (so drawing a high amount of points would be wasteful). + +Before drawing Bezier curves, *tesselation* is required. This is often done with a recursive or divide and conquer function that splits the curve until the curvature amount becomes less than a certain threshold. + +The *Curve* classes provide this via the +:ref:`Curve2D.tessellate() ` function (which receives optional ``stages`` of recursion and angle ``tolerance`` arguments). This way, drawing something based on a curve is easier. + +Traversal +--------- + +The last common use case for the curves is to traverse them. Because of what was mentioned before regarding constant speed, this is also difficult. + +To make this easier, the curves need to be *baked* into equidistant points. This way, they can be approximated with regular interpolation (which can be improved further with a cubic option). To do this, just use the :ref:`Curve.interpolate_baked()` method together with +:ref:`Curve2D.get_baked_length()`. The first call to either of them will bake the curve internally. + +Traversal at constant speed, then, can be done with the following pseudo-code: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var t = 0.0 + + func _process(delta): + t += delta + position = curve.interpolate_baked(t * curve.get_baked_length(), true) + +And the output will, then, move at constant speed: + +.. image:: img/bezier_interpolation_baked.gif diff --git a/tutorials/math/img/bezier_cubic_handles.png b/tutorials/math/img/bezier_cubic_handles.png new file mode 100644 index 000000000..c587edac5 Binary files /dev/null and b/tutorials/math/img/bezier_cubic_handles.png differ diff --git a/tutorials/math/img/bezier_cubic_points.gif b/tutorials/math/img/bezier_cubic_points.gif new file mode 100644 index 000000000..62d982c38 Binary files /dev/null and b/tutorials/math/img/bezier_cubic_points.gif differ diff --git a/tutorials/math/img/bezier_cubic_points.png b/tutorials/math/img/bezier_cubic_points.png new file mode 100644 index 000000000..53139cdfe Binary files /dev/null and b/tutorials/math/img/bezier_cubic_points.png differ diff --git a/tutorials/math/img/bezier_interpolation_baked.gif b/tutorials/math/img/bezier_interpolation_baked.gif new file mode 100644 index 000000000..af294208b Binary files /dev/null and b/tutorials/math/img/bezier_interpolation_baked.gif differ diff --git a/tutorials/math/img/bezier_interpolation_speed.gif b/tutorials/math/img/bezier_interpolation_speed.gif new file mode 100644 index 000000000..df188d479 Binary files /dev/null and b/tutorials/math/img/bezier_interpolation_speed.gif differ diff --git a/tutorials/math/img/bezier_path_2d.png b/tutorials/math/img/bezier_path_2d.png new file mode 100644 index 000000000..00a4d3b35 Binary files /dev/null and b/tutorials/math/img/bezier_path_2d.png differ diff --git a/tutorials/math/img/bezier_point_amount.png b/tutorials/math/img/bezier_point_amount.png new file mode 100644 index 000000000..414920416 Binary files /dev/null and b/tutorials/math/img/bezier_point_amount.png differ diff --git a/tutorials/math/img/bezier_quadratic_points.png b/tutorials/math/img/bezier_quadratic_points.png new file mode 100644 index 000000000..7cee3dc49 Binary files /dev/null and b/tutorials/math/img/bezier_quadratic_points.png differ diff --git a/tutorials/math/img/bezier_quadratic_points2.gif b/tutorials/math/img/bezier_quadratic_points2.gif new file mode 100644 index 000000000..a97c77fbe Binary files /dev/null and b/tutorials/math/img/bezier_quadratic_points2.gif differ diff --git a/tutorials/math/img/interpolation_follow.gif b/tutorials/math/img/interpolation_follow.gif new file mode 100644 index 000000000..83b452403 Binary files /dev/null and b/tutorials/math/img/interpolation_follow.gif differ diff --git a/tutorials/math/img/interpolation_monkey.gif b/tutorials/math/img/interpolation_monkey.gif new file mode 100644 index 000000000..3da04c1fb Binary files /dev/null and b/tutorials/math/img/interpolation_monkey.gif differ diff --git a/tutorials/math/img/interpolation_positions.png b/tutorials/math/img/interpolation_positions.png new file mode 100644 index 000000000..56e61ae9f Binary files /dev/null and b/tutorials/math/img/interpolation_positions.png differ diff --git a/tutorials/math/img/interpolation_vector.gif b/tutorials/math/img/interpolation_vector.gif new file mode 100644 index 000000000..a61ad01b2 Binary files /dev/null and b/tutorials/math/img/interpolation_vector.gif differ diff --git a/tutorials/math/index.rst b/tutorials/math/index.rst index 147c74f5a..f8124f8dc 100644 --- a/tutorials/math/index.rst +++ b/tutorials/math/index.rst @@ -8,3 +8,5 @@ Math vector_math vectors_advanced matrices_and_transforms + interpolation + beziers_and_curves diff --git a/tutorials/math/interpolation.rst b/tutorials/math/interpolation.rst new file mode 100644 index 000000000..21c4db308 --- /dev/null +++ b/tutorials/math/interpolation.rst @@ -0,0 +1,100 @@ +.. _doc_interpolation: + +Interpolation +============= + +Interpolation is a very basic operation in graphics programming. It's good to become familiar with it in order to expand your horizons as a graphics developer. + +The basic idea is that you want to transition from A to B. A value ``t``, represents the states in-between. + +As an example if ``t`` is 0, then the state is A. If ``t`` is 1, then the state is B. Anything in-between is an *interpolation*. + +Between two real (floating point) numbers, a simple interpolation is usually described as: + +.. tabs:: + .. code-tab:: gdscript GDScript + + interpolation = A * (1 - t) + B * t + +And often simplified to: + +.. tabs:: + .. code-tab:: gdscript GDScript + + interpolation = A + (B - A) * t + +which is exactly the same. + +The name of this type of interpolation, which transforms a value into another at *constant speed* is *"linear"*. So, when you hear about *Linear Interpolation*, you know they are referring to this simple formula. + +There are other types of interpolations, which will not be covered here. A recommended read afterwards is the :ref:`Bezier ` page. + +Vector interpolation +-------------------- + +Vector types (:ref:`Vector2 ` and :ref:`Vector3 `) can also be interpolated, they come with handy functions to do it +:ref:`Vector2.linear_interpolate() ` and :ref:`Vector3.linear_interpolate() `. + +For cubic interpolation, there are also :ref:`Vector2.cubic_interpolate() ` and :ref:`Vector3.cubic_interpolate() `, which do a :ref:`Bezier ` style interpolation. + +Here is simple pseudo-code for going from point A to B using interpolation: + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _physics_process(delta): + t += delta * 0.4 + + $Sprite.position = $A.position.linear_interpolate($B.position, t) + +It will produce the following motion: + +.. image:: img/interpolation_vector.gif + +Transform interpolation +----------------------- + +It is also possible to interpolate whole transforms (make sure they have either uniform scale or, at least, the same non-uniform scale). +For this, the function :ref:`Transform.interpolate_with() ` can be used. + +Here is an example of transforming a monkey from Position1 to Position2: + +.. image:: img/interpolation_positions.png + +Using the following pseudocode: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var t = 0.0 + + func _physics_process(delta): + t += delta + + $Monkey.transform = $Position1.transform.interpolate_with($Position2.transform, t) + +And again, it will produce the following motion: + +.. image:: img/interpolation_monkey.gif + + +Smoothing motion +---------------- + +Interpolation can be used to smooth movement, rotation, etc. Here is an example of a circle following the mouse using smoothed motion: + +.. tabs:: + .. code-tab:: gdscript GDScript + + const FOLLOW_SPEED = 4.0 + + func _physics_process(delta): + var mouse_pos = get_local_mouse_position() + + $Sprite.position = $Sprite.position.linear_interpolate(mouse_pos, delta * FOLLOW_SPEED) + +Here is how it looks: + +.. image:: img/interpolation_follow.gif + +This useful for smoothing camera movement, allies following you (ensuring they stay within a certain range), and many other common game patterns. diff --git a/tutorials/misc/gles2_gles3_differences.rst b/tutorials/misc/gles2_gles3_differences.rst index 52b046e04..f933ac008 100644 --- a/tutorials/misc/gles2_gles3_differences.rst +++ b/tutorials/misc/gles2_gles3_differences.rst @@ -195,3 +195,23 @@ For a complete list of built-in GLSL functions see the :ref:`Shading Language do +------------------------------------------------------------------------+--------------------------------------------------+ | vec_type **fwidth** ( vec_type ) | | +------------------------------------------------------------------------+--------------------------------------------------+ + +Godot also provides many built-in variables and render modes. Some cannot be supported in GLES2. Below is a list of +built-in variables and render modes that, when written to, will have no effect or could even cause issues when using +the GLES2 backend. + ++----------------------------+ +| Variable / Render Mode | ++============================+ +| ``ensure_correct_normals`` | ++----------------------------+ +| ``INSTANCE_ID`` | ++----------------------------+ +| ``DEPTH`` | ++----------------------------+ +| ``ANISOTROPY`` | ++----------------------------+ +| ``ANISOTROPY_FLOW`` | ++----------------------------+ +| ``SSS_STRENGTH`` | ++----------------------------+ diff --git a/getting_started/step_by_step/img/signals_shoot1.gif b/tutorials/misc/img/signals_shoot1.gif similarity index 100% rename from getting_started/step_by_step/img/signals_shoot1.gif rename to tutorials/misc/img/signals_shoot1.gif diff --git a/getting_started/step_by_step/img/signals_shoot2.gif b/tutorials/misc/img/signals_shoot2.gif similarity index 100% rename from getting_started/step_by_step/img/signals_shoot2.gif rename to tutorials/misc/img/signals_shoot2.gif diff --git a/tutorials/misc/index.rst b/tutorials/misc/index.rst index ffec6bb7f..6cd4a04c1 100644 --- a/tutorials/misc/index.rst +++ b/tutorials/misc/index.rst @@ -12,3 +12,4 @@ Miscellaneous running_code_in_the_editor change_scenes_manually gles2_gles3_differences + instancing_with_signals diff --git a/tutorials/misc/instancing_with_signals.rst b/tutorials/misc/instancing_with_signals.rst new file mode 100644 index 000000000..4840077f4 --- /dev/null +++ b/tutorials/misc/instancing_with_signals.rst @@ -0,0 +1,155 @@ +.. _doc_instancing_with_signals: + +Instancing with signals +======================= + +Signals provide a way to decouple game objects, allowing you to avoid forcing a +fixed arrangement of nodes. One sign that a signal might be called for is when +you find yourself using ``get_parent()``. Referring directly to a node's parent +means that you can't easily move that node to another location in the scene tree. +This can be especially problematic when you are instancing objects at runtime +and may want to place them in an arbitrary location in the running scene tree. + +Below we'll consider an example of such a situation: firing bullets. + +Shooting example +---------------- + +Consider a player character that can rotate and shoot towards the mouse. Every +time the mouse button is clicked, we create an instance of the bullet at the +player's location. See :ref:`doc_instancing` for details. + +We'll use an ``Area2D`` for the bullet, which moves in a straight line at a +given velocity: + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends Area2D + + var velocity = Vector2.ZERO + + func _physics_process(delta): + position += velocity * delta + + .. code-tab:: csharp + + public class Bullet : Area2D + { + Vector2 Velocity = new Vector2(); + + public override void _PhysicsProcess(float delta) + { + Position += Velocity * delta; + } + } + +However, if the bullets are added as children of the player, then they will +remain "attached" to the player as it rotates: + +.. image:: img/signals_shoot1.gif + +Instead, we need the bullets to be independent of the player's movement - once +fired, they should continue traveling in a straight line and the player can no +longer affect them. Instead of being added to the scene tree as a child of the +player, it makes more sense to add the bullet as a child of the "main" game +scene, which may be the player's parent or even further up the tree. + +You could do this by adding the bullet to the main scene directly: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var bullet_instance = Bullet.instance() + get_parent().add_child(bullet_instance) + + .. code-tab:: csharp + + Node bulletInstance = Bullet.Instance(); + GetParent().AddChild(bulletInstance); + +However, this will lead to a different problem. Now if you try and test your +"Player" scene independently, it will crash on shooting, because there is no +parent node to access. This makes it a lot harder to test your player code +independently and also means that if you decide to change your main scene's +node structure, the player's parent may no longer be the appropriate node to +receive the bullets. + +The solution to this is to use a signal to "emit" the bullets from the player. +The player then has no need to "know" what happens to the bullets after that - +whatever node is connected to the signal can "receive" the bullets and take the +appropriate action to spawn them. + +Here is the code for the player using signals to emit the bullet: + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends Sprite + + signal shoot(bullet, direction, location) + + var Bullet = preload("res://Bullet.tscn") + + func _input(event): + if event is InputEventMouseButton: + if event.button_index == BUTTON_LEFT and event.pressed: + emit_signal("shoot", Bullet, rotation, position) + + func _process(delta): + look_at(get_global_mouse_position()) + + .. code-tab:: csharp + + public class Player : Sprite + { + [Signal] + delegate void Shoot(PackedScene bullet, Vector2 direction, Vector2 location); + + private PackedScene _bullet = GD.Load("res://Bullet.tscn"); + + public override void _Input(InputEvent event) + { + if (input is InputEventMouseButton mouseButton) + { + if (mouseButton.ButtonIndex == (int)ButtonList.Left && mouseButton.Pressed) + { + EmitSignal(nameof(Shoot), _bullet, Rotation, Position); + } + } + } + + public override _Process(float delta) + { + LookAt(GetGlobalMousePosition()); + } + } + +In the main scene, we then connect the player's signal (it will appear in the +"Node" tab). + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _on_Player_shoot(Bullet, direction, location): + var b = Bullet.instance() + add_child(b) + b.rotation = direction + b.position = location + b.velocity = b.velocity.rotated(direction) + + .. code-tab:: csharp + + public void _on_Player_Shoot(PackedScene bullet, Vector2 direction, Vector2 location) + { + var bulletInstance = (Bullet)bullet.Instance(); + AddChild(bulletInstance); + bulletInstance.Rotation = direction; + bulletInstance.Position = location; + bulletInstance.Velocity = bulletInstance.Velocity.Rotated(direction); + } + +Now the bullets will maintain their own movement independent of the player's +rotation: + +.. image:: img/signals_shoot2.gif diff --git a/tutorials/networking/high_level_multiplayer.rst b/tutorials/networking/high_level_multiplayer.rst index 5d7e5f9f3..aaa4dc27d 100644 --- a/tutorials/networking/high_level_multiplayer.rst +++ b/tutorials/networking/high_level_multiplayer.rst @@ -94,12 +94,11 @@ Initializing as a client, connecting to a given IP and port: peer.create_client(SERVER_IP, SERVER_PORT) get_tree().set_network_peer(peer) -Note that it may make sense to store the local network peer instance on the SceneTree to be able to access it later, -as there currently is no `get_tree().get_network_peer()`. This can be done via SceneTree's metadata feature: +Get the previously set network peer: :: - get_tree().set_meta("network_peer", peer) + get_tree().get_network_peer() Checking whether the tree is initialized as a server or client: diff --git a/tutorials/optimization/index.rst b/tutorials/optimization/index.rst new file mode 100644 index 000000000..0f5d12b88 --- /dev/null +++ b/tutorials/optimization/index.rst @@ -0,0 +1,9 @@ +Optimization +============= + +.. toctree:: + :maxdepth: 1 + :name: toc-learn-features-optimization + + using_servers + using_multimesh diff --git a/tutorials/optimization/using_multimesh.rst b/tutorials/optimization/using_multimesh.rst new file mode 100644 index 000000000..9fb1e20a7 --- /dev/null +++ b/tutorials/optimization/using_multimesh.rst @@ -0,0 +1,71 @@ +.. _doc_using_multimesh: + +Optimization using MultiMeshes +============================== + +For large amount of instances (in the thousands), that need to be constantly processed +(and certain amount of control needs to be retained), +:ref:`using servers directly ` is the recommended optimization. + +When the amount of objects reach the hundreds of thousands or millions, +none of these approaches are efficient anymore. Still, depending on the requirements, there +is one more optimization possible. + +MultiMeshes +----------- + +A :ref:`MultiMesh` is a single draw primitive that can draw up to millions +of objects in one go. It's extremely efficient because it uses the GPU hardware to do this +(in OpenGL ES 2.0, it's less efficient because there is no hardware support for it, though). + +The only drawback is that there is no *screen* or *frustum* culling possible for individual instances. +This means, that millions of objects will be *always* or *never* drawn, depending on the visibility +of the whole MultiMesh. It is possible to provide a custom visibility rect for them, but it will always +be *all-or-none* visibility. + +If the objects are simple enough (just a couple of vertices), this is generally not much of a problem +as most modern GPUs are optimized for this use case. A workaround is to create several MultiMeshes +for different areas of the world. + +It is also possible to execute some logic inside the vertex shader (using the ``INSTANCE_ID`` or +``INSTANCE_CUSTOM`` built-in constants). For an example of animating thousands of objects in a MultiMesh, +see the :ref:`Animating thousands of fish ` tutorial. Information +to the shader can be provided via textures (there are floating point :ref:`Image` formats +which are ideal for this). + +Another alternative is to use GDNative and C++, which should be extremely efficient (it's possible +to set the entire state for all objects using linear memory via the +:ref:`VisualServer.multimesh_set_as_bulk_array() ` +function). This way, the array can be created with multiple threads, then set in one call, providing +high cache efficiency. + +Finally, it's not required to have all MultiMesh instances visible. The amount of visible ones can be +controlled with the :ref:`MultiMesh.visible_instance_count ` +property. The typical workflow is to allocate the maximum amount of instances that will be used, +then change the amount visible depending on how many are currently needed. + +Multimesh example +----------------- + +Here is an example of using a MultiMesh from code (using GDScript). Other languages may be more +efficient for millions of objects, but for a few thousands, GDScript should be fine. + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends MultiMeshInstance + + func _ready(): + # Create the multimesh. + multimesh = MultiMesh.new() + # Set the format first. + multimesh.transform_format = MultiMesh.TRANSFORM_3D + multimesh.color_format = MultiMesh.COLOR_NONE + multimesh.custom_data_format = MultiMesh.CUSTOM_DATA_NONE + # Then resize (otherwise, changing the format is not allowed). + multimesh.instance_count = 10000 + # Maybe not all of them should be visible at first. + multimesh.visible_instance_count = 1000 + # Set the transform of the instances. + for i in multimesh.visible_instance_count: + multimesh.set_instance_transform(i, Transform(Basis(), Vector3(i * 20, 0, 0))) diff --git a/tutorials/optimization/using_servers.rst b/tutorials/optimization/using_servers.rst new file mode 100644 index 000000000..7c445a230 --- /dev/null +++ b/tutorials/optimization/using_servers.rst @@ -0,0 +1,189 @@ +.. _doc_using_servers: + +Optimization using Servers +========================== + +Engines like Godot provide increased ease of use thanks to their high level constructs and features. +Most of them are accessed and used via the :ref:`Scene System`. Using nodes and +resources simplifies project organization and asset management in complex games. + +There are, of course, always drawbacks: + +* There is an extra layer of complexity +* Performance is lower than using simple APIs directly +* It is not possible to use multiple threads to control them +* More memory is needed. + +In many cases, this is not really a problem (Godot is very optimized, and most operations are handled +with signals, so no polling is required). Still, sometimes it can be. For example, dealing with +tens of thousands of instances for something that needs to be processed every frame can be a bottleneck. + +This type of situation makes programmers regret they are using a game engine and wish they could go +back to a more handcrafted, low level implementation of game code. + +Still, Godot is designed to work around this problem. + +Servers +------- + +One of the most interesting design decisions for Godot, is the fact that the whole scene system is +*optional*. While it is not currently possible to compile it out, it can be completely bypassed. + +At the core, Godot uses the concept of Servers. They are very low level APIs to control +rendering, physics, sound, etc. The scene system is built on top of them and uses them directly. +The most common servers are: + +* :ref:`VisualServer `: handles everything related to graphics. +* :ref:`PhysicsServer `: handles everything related to 3D physics. +* :ref:`Physics2DServer `: handles everything related to 2D physics. +* :ref:`AudioServer `: handles everything related to audio. + +Just explore their APIs and you will realize that the all functions provided are low-level +implementations of everything Godot allows you to do. + +RIDs +---- + +The key to using servers is understanding Resource ID (:ref:`RID `) objects. These are opaque +handles to the sever implementation. They are allocated and freed manually. Almost every +function in the servers requires RIDs to access the actual resource. + +Most Godot nodes and resources contain these RIDs from the servers internally, and they can +be obtained with different functions. In fact, anything that inherits :ref:`Resource ` +can be directly casted to an RID (not all resources contain an RID, though, in such cases +the RID will be empty). In fact, resources can be passed to server APIs as RIDs. Just make +sure to keep references to the resources ouside the server, because if the resource is erased, +the internal RID is erased too. + +For nodes, there are many functions available: + +* For CanvasItem, the :ref:`CanvasItem.get_canvas_item() ` + method will return the canvas item RID in the server. +* For CanvasLayer, the :ref:`CanvasLayer.get_canvas() ` + method will return the canvas RID in the server. +* For Viewport, the :ref:`Viewport.get_viewport_rid() ` + method will return the viewport RID in the server. +* For 3D, the :ref:`World ` resource (obtainable in the :ref:`Viewport ` + and :ref:`Spatial ` nodes) + contains functions to get the *VisualServer Scenario*, and the *PhysicsServer Space*. This + allows creating 3D objects directly with the server API and using them. +* For 2D, the :ref:`World2D ` resource (obtainable in the :ref:`Viewport ` + and :ref:`CanvasItem ` nodes) + contains functions to get the *VisualServer Canvas*, and the *Physics2DServer Space*. This + allows creating 2D objects directly with the server API and using them. +* The :ref:`VisualInstance` class, allows getting the scenario *instance* and + *instance base* via the :ref:`VisualInstance.get_instance() ` + and :ref:`VisualInstance.get_base() ` respectively. + +Just explore the nodes and resources you are familiar with and find the functions to obtain the server *RIDs*. + +It is not advised to control RIDs from objects that already have a node associated. Instead, server +functions should always be used for creating and controlling new ones and interacting with the existing ones. + +Creating a sprite +----------------- + +This is a simple example of how to create a sprite from code and move it using the low-level +:ref:`CanvasItem ` API. + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends Node2D + + func _ready(): + # Create a canvas item, child of this node. + var ci_rid = VisualServer.canvas_item_create() + # Make this node the parent. + VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item()) + # Draw a sprite on it. + # Remember, keep this reference. + var sprite = load("res://mysprite.png") + # Add it, centered. + VisualServer.canvas_item_add_texture_rect(ci_rid, Rect2(sprite.get_size() / 2, sprite.get_size()), sprite) + # Add the item, rotated 45 degrees and translated. + var xform = Transform2D().rotated(deg2rad(45)).translated(Vector2(20, 30)) + VisualServer.canvas_item_set_transform(ci_rid, xform) + +The Canvas Item API in the server allows you to add draw primitives to it. Once added, they can't be modified. +The Item needs to be cleared and the primitives re-added (this is not the case for setting the transform, +which can be done as many times as desired). + +Primitives are cleared this way: + +.. tabs:: + .. code-tab:: gdscript GDScript + + VisualServer.canvas_item_clear(ci_rid) + + +Instantiating a Mesh into 3D space +---------------------------------- + +The 3D APIs are different than the 2D ones, so the instantiation API must be used. + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends Spatial + + func _ready(): + # Create a visual instance (for 3D). + var instance = VisualServer.instance_create() + # Set the scenario from the world, this ensures it + # appears with the same objects as the scene. + var scenario = get_world().scenario + VisualServer.instance_set_scenario(instance, scenario) + # Add a mesh to it. + # Remember, keep the reference. + var mesh = load("res://mymesh.obj") + VisualServer.instance_set_base(instance, mesh) + # Move the mesh around. + var xform = Transform(Basis(), Vector3(20, 100, 0)) + VisualServer.instance_set_transform(instance, xform) + +Creating a 2D RigidBody and moving a sprite with it +--------------------------------------------------- + +This creates a :ref:`RigidBody2D ` using the :ref:`Physics2DServer ` API, +and moves a :ref:`CanvasItem ` when the body moves. + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _body_moved(state, index): + # Created your own canvas item, use it here. + VisualServer.canvas_item_set_transform(canvas_item, state.transform) + + func _ready(): + # Create the body. + var body = Physics2DServer.body_create() + Physics2DServer.body_set_mode(body, Physics2DServer.BODY_MODE_RIGID) + # Add a shape. + var shape = RectangleShape2D.new() + shape.extents = Vector2(10, 10) + # Make sure to keep the shape reference! + Physics2DServer.body_add_shape(body, shape) + # Set space, so it collides in the same space as current scene. + Physics2DServer.body_set_space(body, get_world_2d().space) + # Move initial position. + Physics2DServer.body_set_state(body, Physics2DServer.BODY_STATE_TRANSFORM, Transform2D(0, Vector2(10, 20))) + # Add the transform callback, when body moves + # The last parameter is optional, can be used as index + # if you have many bodies and a single callback. + Physics2DServer.body_set_force_integration_callback(body, self, "_body_moved", 0) + +The 3D version should be very similar, as 2D and 3D physics servers are identical (using +:ref:`RigidBody ` and :ref:`PhysicsServer ` respectively). + +Getting data from the servers +----------------------------- + +Try to **never** request any information from ``VisualServer``, ``PhysicsServer`` or ``Physics2DServer`` +by calling functions unless you know what you are doing. These servers will often run asynchronously +for performance and calling any function that returns a value will stall them and force them to process +anything pending until the function is actually called. This will severely decrease performance if you +call them every frame (and it won't be obvious why). + +Because of this, most APIs in such servers are designed so it's not even possible to request information +back, until it's actual data that can be saved. diff --git a/tutorials/physics/files/using_kinematic2d.zip b/tutorials/physics/files/using_kinematic2d.zip index 541b862cf..55a50a0c7 100644 Binary files a/tutorials/physics/files/using_kinematic2d.zip and b/tutorials/physics/files/using_kinematic2d.zip differ diff --git a/tutorials/platform/services_for_ios.rst b/tutorials/platform/services_for_ios.rst index ae0975645..bf1f594c3 100644 --- a/tutorials/platform/services_for_ios.rst +++ b/tutorials/platform/services_for_ios.rst @@ -61,7 +61,7 @@ The pending event interface consists of two methods: Store Kit --------- -Implemented in platform/iphone/in_app_store.mm +Implemented in ``platform/iphone/in_app_store.mm``. The Store Kit API is accessible through the "InAppStore" singleton (will always be available from gdscript). It is initialized automatically. It @@ -173,17 +173,19 @@ The response events will be dictionaries with the following fields: Game Center ----------- -Implemented in platform/iphone/game_center.mm +Implemented in ``platform/iphone/game_center.mm``. The Game Center API is available through the "GameCenter" singleton. It -has 6 methods: +has 8 methods: +- ``bool is_authenticated();`` - ``Error post_score(Variant p_score);`` -- ``Erroraward_achievement(Variant p_params);`` -- ``Error reset_achievements();`` -- ``Error request_achievements();`` -- ``Error request_achievement_descriptions();`` +- ``Error award_achievement(Variant p_params);`` +- ``void reset_achievements();`` +- ``void request_achievements();`` +- ``void request_achievement_descriptions();`` - ``Error show_game_center(Variant p_params);`` +- ``Error request_identity_verification_signature();`` plus the standard pending event interface. diff --git a/tutorials/plugins/editor/img/making_plugins-custom_dock.png b/tutorials/plugins/editor/img/making_plugins-custom_dock.png index 0ab892c0e..8ef388e55 100644 Binary files a/tutorials/plugins/editor/img/making_plugins-custom_dock.png and b/tutorials/plugins/editor/img/making_plugins-custom_dock.png differ diff --git a/tutorials/plugins/editor/img/making_plugins-custom_node_console.png b/tutorials/plugins/editor/img/making_plugins-custom_node_console.png index f09753550..7e7c00759 100644 Binary files a/tutorials/plugins/editor/img/making_plugins-custom_node_console.png and b/tutorials/plugins/editor/img/making_plugins-custom_node_console.png differ diff --git a/tutorials/plugins/editor/img/making_plugins-custom_node_create.png b/tutorials/plugins/editor/img/making_plugins-custom_node_create.png index 989562cc7..4bf0cbe7c 100644 Binary files a/tutorials/plugins/editor/img/making_plugins-custom_node_create.png and b/tutorials/plugins/editor/img/making_plugins-custom_node_create.png differ diff --git a/tutorials/plugins/editor/img/making_plugins-custom_node_icon.png b/tutorials/plugins/editor/img/making_plugins-custom_node_icon.png index a08ff3a53..91f63860c 100644 Binary files a/tutorials/plugins/editor/img/making_plugins-custom_node_icon.png and b/tutorials/plugins/editor/img/making_plugins-custom_node_icon.png differ diff --git a/tutorials/plugins/editor/img/making_plugins-my_custom_dock_scene.png b/tutorials/plugins/editor/img/making_plugins-my_custom_dock_scene.png index 5c332a536..b76faa31c 100644 Binary files a/tutorials/plugins/editor/img/making_plugins-my_custom_dock_scene.png and b/tutorials/plugins/editor/img/making_plugins-my_custom_dock_scene.png differ diff --git a/tutorials/plugins/editor/img/making_plugins-my_custom_mode_folder.png b/tutorials/plugins/editor/img/making_plugins-my_custom_mode_folder.png index afe199709..106207a9f 100644 Binary files a/tutorials/plugins/editor/img/making_plugins-my_custom_mode_folder.png and b/tutorials/plugins/editor/img/making_plugins-my_custom_mode_folder.png differ diff --git a/tutorials/plugins/editor/img/making_plugins-project_settings.png b/tutorials/plugins/editor/img/making_plugins-project_settings.png index 05d782ac1..bc7353752 100644 Binary files a/tutorials/plugins/editor/img/making_plugins-project_settings.png and b/tutorials/plugins/editor/img/making_plugins-project_settings.png differ diff --git a/tutorials/plugins/editor/index.rst b/tutorials/plugins/editor/index.rst index 1d6c2fd04..670a40466 100644 --- a/tutorials/plugins/editor/index.rst +++ b/tutorials/plugins/editor/index.rst @@ -9,3 +9,4 @@ Editor plugins making_main_screen_plugins import_plugins spatial_gizmos + inspector_plugins diff --git a/tutorials/plugins/editor/inspector_plugins.rst b/tutorials/plugins/editor/inspector_plugins.rst new file mode 100644 index 000000000..c1a33b32c --- /dev/null +++ b/tutorials/plugins/editor/inspector_plugins.rst @@ -0,0 +1,83 @@ +.. _doc_inspector_plugins: + +Inspector Plugins +===================== + +Introduction +------------ + +Godot 3.1 comes with a new inspector. Adding plugins to it is now possible. + +This tutorial will explain the process. + + +Inspector Plugin +---------------- + +This short tutorial will explain how to make a simple value editor. +Create an EditorInspectorPlugin first. This is needed to initialize your plugin. + + +.. tabs:: + .. code-tab:: gdscript GDScript + + # MyEditorPlugin.gd + + extends EditorInspectorPlugin + + func can_handle(object): + # if only editing a specific type + # return object is TheTypeIWant + # if everything is supported + return true + + func parse_property(object,type,path,hint,hint_text,usage): + if (type==TYPE_INT): + add_custom_property_editor(path,MyEditor.new()) + return true # I want this one + else: + return false + + +Editor +------ + + +Here is an editor for editing integers + +.. tabs:: + .. code-tab:: gdscript GDScript + + # MyEditor.gd + class_name MyEditor + + var updating = false + + func _spin_changed(value): + if (updating): + return + + emit_changed( get_property(), value ) + + func update_property(): + var new_value = get_edited_object()[ get_edited_property() ] + + updating=true + spin.set_value( new_value ) + updating=false + + var spin = EditorSpinSlider.new() # use the new spin slider + func _init(): + # if you want to put the editor below the label + # set_bottom_editor( spin ) + # else use: + add_child( spin ) + # to remember focus when selected back + add_focusable( spin ) + spin.set_min(0) + spin.set_max(1000) + spin.connect("value_changed",self,"_spin_changed") + + + + diff --git a/tutorials/plugins/gdnative/gdnative-c-example.rst b/tutorials/plugins/gdnative/gdnative-c-example.rst index 5591b3447..8841ff4fa 100644 --- a/tutorials/plugins/gdnative/gdnative-c-example.rst +++ b/tutorials/plugins/gdnative/gdnative-c-example.rst @@ -78,9 +78,9 @@ Let's start by writing our main code. Ideally, we want to end up with a file str main.tscn project.godot -Open up Godot and create a new project called simple. This will create the simple folder and project.godot file. Then manually create a bin and src subfolder in this folder. +Open up Godot and create a new project called simple. This will create the ``simple`` folder and ``project.godot`` file. Then manually create a ``bin`` and ``src`` subfolder in this folder. -We're going to start by having a look at what our simple.c file contains. Now, for our example here we're making a single C source file without a header to keep things simple. Once you start writing bigger projects it is advisable you break your project up into multiple files. That however falls outside of the scope of this tutorial. +We're going to start by having a look at what our ``simple.c`` file contains. Now, for our example here we're making a single C source file without a header to keep things simple. Once you start writing bigger projects it is advisable you break your project up into multiple files. That however falls outside of the scope of this tutorial. We'll be looking at the source code bit by bit so all the parts below should all be put together into one big file. I'll explain each section as we add it. @@ -225,7 +225,7 @@ The variant we return is destroyed automatically by Godot. And that is the whole source code of our module. -If you add a blank .gdignore file to the src folder, Godot will not try to import the compiler-generated temporary files. +If you add a blank ``.gdignore`` file to the src folder, Godot will not try to import the compiler-generated temporary files. Compiling --------- diff --git a/tutorials/plugins/gdnative/gdnative-cpp-example.rst b/tutorials/plugins/gdnative/gdnative-cpp-example.rst index 7434e8e7b..8a158699a 100644 --- a/tutorials/plugins/gdnative/gdnative-cpp-example.rst +++ b/tutorials/plugins/gdnative/gdnative-cpp-example.rst @@ -60,7 +60,7 @@ it is a good idea to add them as Git submodules: git init git submodule add https://github.com/GodotNativeTools/godot-cpp cd godot-cpp - git submodule update -- init + git submodule update --init .. code-tab:: none Godot 3.0 @@ -69,7 +69,7 @@ it is a good idea to add them as Git submodules: git init git submodule add -b 3.0 https://github.com/GodotNativeTools/godot-cpp cd godot-cpp - git submodule update -- init + git submodule update --init If you decide to just download the repositories or clone them into your project folder, make sure to keep the folder layout identical @@ -137,7 +137,7 @@ libraries that can be compiled into your project stored in ``godot-cpp/bin/``. At some point in the future, compiled binaries will be available, making this step optional. -.. note:: You may need to add ``bits=64`` to the command on Windows. We're still working on better auto detection. +.. note:: You may need to add ``bits=64`` to the command on Windows or Linux. We're still working on better auto detection. Creating a simple plugin ------------------------ diff --git a/tutorials/shading/img/vertex_displacement_cos.png b/tutorials/shading/img/vertex_displacement_cos.png deleted file mode 100644 index 850125f2d..000000000 Binary files a/tutorials/shading/img/vertex_displacement_cos.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_cos_amplitude.png b/tutorials/shading/img/vertex_displacement_cos_amplitude.png deleted file mode 100644 index 51e123dd0..000000000 Binary files a/tutorials/shading/img/vertex_displacement_cos_amplitude.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_cos_scaled.png b/tutorials/shading/img/vertex_displacement_cos_scaled.png deleted file mode 100644 index fcf663652..000000000 Binary files a/tutorials/shading/img/vertex_displacement_cos_scaled.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_light1.png b/tutorials/shading/img/vertex_displacement_light1.png deleted file mode 100644 index 8c4c03c38..000000000 Binary files a/tutorials/shading/img/vertex_displacement_light1.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_light2.png b/tutorials/shading/img/vertex_displacement_light2.png deleted file mode 100644 index fd1081033..000000000 Binary files a/tutorials/shading/img/vertex_displacement_light2.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_new_mesh.png b/tutorials/shading/img/vertex_displacement_new_mesh.png deleted file mode 100644 index 362602e73..000000000 Binary files a/tutorials/shading/img/vertex_displacement_new_mesh.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_new_shader.png b/tutorials/shading/img/vertex_displacement_new_shader.png deleted file mode 100644 index b8403df0a..000000000 Binary files a/tutorials/shading/img/vertex_displacement_new_shader.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_new_shader_material.png b/tutorials/shading/img/vertex_displacement_new_shader_material.png deleted file mode 100644 index 5dd967a82..000000000 Binary files a/tutorials/shading/img/vertex_displacement_new_shader_material.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_noise1.png b/tutorials/shading/img/vertex_displacement_noise1.png deleted file mode 100644 index ba81b6775..000000000 Binary files a/tutorials/shading/img/vertex_displacement_noise1.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_noise2.png b/tutorials/shading/img/vertex_displacement_noise2.png deleted file mode 100644 index 55af2d66b..000000000 Binary files a/tutorials/shading/img/vertex_displacement_noise2.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_noise3.png b/tutorials/shading/img/vertex_displacement_noise3.png deleted file mode 100644 index 60c1ddb90..000000000 Binary files a/tutorials/shading/img/vertex_displacement_noise3.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_normal.png b/tutorials/shading/img/vertex_displacement_normal.png deleted file mode 100644 index e6ce765ba..000000000 Binary files a/tutorials/shading/img/vertex_displacement_normal.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_normal_detailed1.png b/tutorials/shading/img/vertex_displacement_normal_detailed1.png deleted file mode 100644 index 96ffc159a..000000000 Binary files a/tutorials/shading/img/vertex_displacement_normal_detailed1.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_normal_detailed2.png b/tutorials/shading/img/vertex_displacement_normal_detailed2.png deleted file mode 100644 index 4aaaa2e1f..000000000 Binary files a/tutorials/shading/img/vertex_displacement_normal_detailed2.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_normal_detailed3.png b/tutorials/shading/img/vertex_displacement_normal_detailed3.png deleted file mode 100644 index a8a6ee01b..000000000 Binary files a/tutorials/shading/img/vertex_displacement_normal_detailed3.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_planemesh.png b/tutorials/shading/img/vertex_displacement_planemesh.png deleted file mode 100644 index d00a10997..000000000 Binary files a/tutorials/shading/img/vertex_displacement_planemesh.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_shader_editor.png b/tutorials/shading/img/vertex_displacement_shader_editor.png deleted file mode 100644 index d4e6c2ce9..000000000 Binary files a/tutorials/shading/img/vertex_displacement_shader_editor.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_subdivided_mesh.png b/tutorials/shading/img/vertex_displacement_subdivided_mesh.png deleted file mode 100644 index 5a1f74344..000000000 Binary files a/tutorials/shading/img/vertex_displacement_subdivided_mesh.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_terrain.png b/tutorials/shading/img/vertex_displacement_terrain.png deleted file mode 100644 index 502828098..000000000 Binary files a/tutorials/shading/img/vertex_displacement_terrain.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_uniform1.png b/tutorials/shading/img/vertex_displacement_uniform1.png deleted file mode 100644 index 8f19d2582..000000000 Binary files a/tutorials/shading/img/vertex_displacement_uniform1.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_uniform2.png b/tutorials/shading/img/vertex_displacement_uniform2.png deleted file mode 100644 index e98648b84..000000000 Binary files a/tutorials/shading/img/vertex_displacement_uniform2.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_viewport_settings.png b/tutorials/shading/img/vertex_displacement_viewport_settings.png deleted file mode 100644 index 28240c47b..000000000 Binary files a/tutorials/shading/img/vertex_displacement_viewport_settings.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_wireframe1.png b/tutorials/shading/img/vertex_displacement_wireframe1.png deleted file mode 100644 index aa6f1814f..000000000 Binary files a/tutorials/shading/img/vertex_displacement_wireframe1.png and /dev/null differ diff --git a/tutorials/shading/img/vertex_displacement_wireframe2.png b/tutorials/shading/img/vertex_displacement_wireframe2.png deleted file mode 100644 index b537b55e4..000000000 Binary files a/tutorials/shading/img/vertex_displacement_wireframe2.png and /dev/null differ diff --git a/tutorials/shading/index.rst b/tutorials/shading/index.rst index aecfde389..e80986415 100644 --- a/tutorials/shading/index.rst +++ b/tutorials/shading/index.rst @@ -6,9 +6,9 @@ Shading :name: toc-learn-features-shading shading_reference/index + your_first_shader/index shader_materials intro_to_shaders_water_workshop screen-reading_shaders migrating_to_godot_shader_language - vertex_displacement_with_shaders advanced_postprocessing diff --git a/tutorials/shading/vertex_displacement_with_shaders.rst b/tutorials/shading/vertex_displacement_with_shaders.rst deleted file mode 100644 index e41050109..000000000 --- a/tutorials/shading/vertex_displacement_with_shaders.rst +++ /dev/null @@ -1,368 +0,0 @@ -.. _doc_vertex_displacement_with_shaders: - -Vertex displacement with shaders -================================ - -Introduction ------------- - -This tutorial will teach you how to displace the vertices of -a :ref:`Plane Mesh` 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 -miniature terrain complete with dynamic lighting. - -By reading this tutorial, you should gain a basic understanding of: - -* How to create and subdivide a :ref:`Plane Mesh` -* How to create and assign a material to a :ref:`Mesh` -* How to write a :ref:`Shader` that displaces the vertices of a :ref:`Mesh` -* How to pass values (Uniforms) into a :ref:`Shader` to update the :ref:`Mesh` in realtime -* How to approximate normals from a height function -* How to use a light with a custom material - -The plane mesh --------------- - -First, add a :ref:`Spatial` node to the scene to act as the root. Next, add a :ref:`MeshInstance` -as a child. - -.. image:: img/vertex_displacement_new_mesh.png - -Select the newly created :ref:`MeshInstance`. Then click on the button that says "null" -next to the :ref:`Mesh` in the Inspector. This will bring up a list of :ref:`PrimitiveMeshes`. -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`. - -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. -Select 'Display Wireframe'. - -.. image:: img/vertex_displacement_viewport_settings.png - -This will allow you to see the triangles making up the plane. - -.. image:: img/vertex_displacement_wireframe1.png - -Now set the ``Subdivide Width`` and ``Subdivide Height`` to ``32``. - -.. image:: img/vertex_displacement_subdivided_mesh.png - -You can see that there are now many more triangles in the :ref:`Mesh`. This will give -us more vertices to work with and thus allow us to add more detail. - -.. image:: img/vertex_displacement_wireframe2.png - - -Shader magic ------------- - -Now that we have a :ref:`Plane Mesh` to draw, let's set up the material that will deform the :ref:`Mesh`. - -Click beside material in the :ref:`Plane Mesh` Menu and create a new :ref:`ShaderMaterial`. - -.. image:: img/vertex_displacement_new_shader_material.png - -Then click on the created :ref:`ShaderMaterial`. - -Then click beside 'shader' and create a new :ref:`Shader`. - -.. image:: img/vertex_displacement_new_shader.png - -Click into the newly created :ref:`Shader`. You should now see Godot's Shader editor. - -.. image:: img/vertex_displacement_shader_editor.png - -Notice how it is throwing an error? This is because the shader editor reloads shaders on -the fly automatically. The first thing Godot shaders need is a declaration of what type of -shader they are. Accordingly, we set the variable ``shader_type`` to ``spatial``. One more -thing we will add is the ``render_mode``, we will set it to ``unshaded``. This means that -Godot won't run the light shader on this object. - -:: - - shader_type spatial; - render_mode unshaded; - -This should remove the errors and your :ref:`Mesh` should turn white. If you were to comment out -the ``render_mode``, the plane would appear blue because it would pick up the sky colors. - -Next we will define a vertex shader. The vertex shader determines where the vertices of your -:ref:`Mesh` 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. - -We define the vertex shader like so: - -:: - - void vertex() { - - } - -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: - -:: - - 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, let's 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``; that gives us -a wave-like appearance across the ``x`` and ``z`` axes. - -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. - -:: - - void vertex() { - VERTEX.y += cos(VERTEX.x * 4.0) * sin(VERTEX.z * 4.0); - } - -.. 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``, -so the range of the output is much too high. We correct this by multiplying the result by ``0.5`` to reduce the size. - -:: - - void vertex() { - VERTEX.y += cos(VERTEX.x * 4.0) * sin(VERTEX.z * 4.0) * 0.5; - } - -.. image:: img/vertex_displacement_cos_amplitude.png - -Looks much more hilly now. But ``cos`` and ``sin`` are boring. Let's move onto something more interesting. - -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 -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. - -:: - - float hash(vec2 p) { - return fract(sin(dot(p * 17.17, vec2(14.91, 67.31))) * 4791.9511); - } - -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. - -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. - -:: - - float noise(vec2 x) { - vec2 p = floor(x); - 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), - mix(hash(p + a.yx), hash(p + a.xx), f.x), f.y); - } - -Lastly, to add detail, we combine successive layers of noise using something called fractal -brownian motion or FBM. Scary name aside, FBM noise just adds together layers of noise with -increasing frequency and decreasing amplitude. To implement it, we run over a for loop where -we increase the frequency each level, decrease the amplitude, and calculate a new layer of noise. - -:: - - float fbm(vec2 x) { - float height = 0.0; - float amplitude = 0.5; - float frequency = 3.0; - for (int i = 0; i < 6; i++){ - height += noise(x * frequency) * amplitude; - amplitude *= 0.5; - frequency *= 2.0; - } - return height; - } - -We can now use this noise function in place of ``cos`` and ``sin`` in the previous section. - -:: - - float height = fbm(VERTEX.xz * 4.0); - VERTEX.y += height * 0.5; - -.. 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. - -Fragment shader ---------------- - -The difference between a vertex shader and a fragment shader is that the vertex shader -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`. - -Now let's look at the :ref:`Mesh` with a regular shader instead of the wireframe. Set the -viewport back to 'Display Normal'. - -.. image:: img/vertex_displacement_noise2.png - -The :ref:`Mesh` 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`. So let's color each pixel based -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. - -:: - - void vertex() { - ... - COLOR.xyz = vec3(height); - } - - void fragment(){ - ALBEDO = COLOR.xyz; - } - -With this change, we can see the detail of the :ref:`Mesh`, even without displaying the :ref:`Mesh`'s wireframe. - -.. image:: img/vertex_displacement_noise3.png - -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 -datatype that can be used in the shader. To use a uniform, you declare it in -your :ref:`Shader` using the keyword ``uniform``. - -Let's make a uniform that changes the height of the terrain. - -:: - - uniform float height_scale = 0.5; - - -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`` -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`. You can use the uniform variable anywhere -inside your :ref:`Shader`. Here, we will use it to set the height value instead -of arbitrarily multiplying by ``0.5``. - -:: - - VERTEX.y += height * height_scale; - -The terrain should look exactly the same, but now we have control over the height easily. -Here is the same terrain with ``height_scale`` set to ``1``: - -.. image:: img/vertex_displacement_uniform1.png - -And here it is with ``height_scale`` set to ``0.2``: - -.. image:: img/vertex_displacement_uniform2.png - -Using uniforms, we can even change the value every frame to animate the height of the terrain. -Combined with :ref:`Tweens`, this can be especially useful for simple animations. - -Interacting with light ----------------------- - -As a final part of this tutorial, let's try to set up the terrain to interact with light. -First, we will add an :ref:`OmniLight` to the scene. - - -.. image:: img/vertex_displacement_light1.png - -You should notice that nothing changes. That is because we set the ``render_mode`` to ``unshaded`` -at the beginning of this tutorial; let's remove that. - -:: - - shader_type spatial; - //render_mode unshaded; - -.. image:: img/vertex_displacement_light2.png - -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` to calculate light. The normals are stored in the :ref:`Mesh`, but we are changing -the shape of the :ref:`Mesh` 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 -the slope at the vertex from that. After all, a normal is just an indicator of the slope of the -noise. - -We calculate the normal with one line in the vertex shader. - -:: - - vec2 e = vec2(0.01, 0.0); - 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. - -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 -``VERTEX``, it does not align with what we see in the final :ref:`Mesh`. In order to fix -this, we add more vertices. The below image is made with a :ref:`Mesh` with ``subdivision`` set -to ``100``. - -.. image:: img/vertex_displacement_normal_detailed1.png - -Now, we can drag the light around and the lighting will update automatically. - -.. image:: img/vertex_displacement_normal_detailed2.png - -.. image:: img/vertex_displacement_normal_detailed3.png - -If you zoom the camera out, you can see that the :ref:`Mesh` now looks like a small terrain. - -.. image:: img/vertex_displacement_terrain.png - -That is everything for this tutorial. Hopefully, you now understand the basics of vertex -shaders in Godot. As a further exercise, try changing the ``height_scale`` from gdscript, -try using different :ref:`Primitive Meshes`, and try making your -own functions to calculate ``height``. - -For further information on how to use shaders in Godot, -you should check out the :ref:`doc_shading_language` page. - diff --git a/tutorials/shading/your_first_shader/img/PBR.png b/tutorials/shading/your_first_shader/img/PBR.png new file mode 100644 index 000000000..9335dfe36 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/PBR.png differ diff --git a/tutorials/shading/your_first_shader/img/UV.png b/tutorials/shading/your_first_shader/img/UV.png new file mode 100644 index 000000000..c7563f38e Binary files /dev/null and b/tutorials/shading/your_first_shader/img/UV.png differ diff --git a/tutorials/shading/your_first_shader/img/albedo.png b/tutorials/shading/your_first_shader/img/albedo.png new file mode 100644 index 000000000..7187c98e1 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/albedo.png differ diff --git a/tutorials/shading/your_first_shader/img/blue-box.png b/tutorials/shading/your_first_shader/img/blue-box.png new file mode 100644 index 000000000..44cdce9d8 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/blue-box.png differ diff --git a/tutorials/shading/your_first_shader/img/blue-tex.png b/tutorials/shading/your_first_shader/img/blue-tex.png new file mode 100644 index 000000000..4da7c3109 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/blue-tex.png differ diff --git a/tutorials/shading/your_first_shader/img/cos.png b/tutorials/shading/your_first_shader/img/cos.png new file mode 100644 index 000000000..f24ce7a04 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/cos.png differ diff --git a/tutorials/shading/your_first_shader/img/cos4.png b/tutorials/shading/your_first_shader/img/cos4.png new file mode 100644 index 000000000..99581a86d Binary files /dev/null and b/tutorials/shading/your_first_shader/img/cos4.png differ diff --git a/tutorials/shading/your_first_shader/img/dark-water.png b/tutorials/shading/your_first_shader/img/dark-water.png new file mode 100644 index 000000000..eebfa6682 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/dark-water.png differ diff --git a/tutorials/shading/your_first_shader/img/fresnel.png b/tutorials/shading/your_first_shader/img/fresnel.png new file mode 100644 index 000000000..7eedd11b0 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/fresnel.png differ diff --git a/tutorials/shading/your_first_shader/img/iconuv.png b/tutorials/shading/your_first_shader/img/iconuv.png new file mode 100644 index 000000000..d6fdd6f72 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/iconuv.png differ diff --git a/tutorials/shading/your_first_shader/img/light.png b/tutorials/shading/your_first_shader/img/light.png new file mode 100644 index 000000000..1dfb16674 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/light.png differ diff --git a/tutorials/shading/your_first_shader/img/noise-flat.png b/tutorials/shading/your_first_shader/img/noise-flat.png new file mode 100644 index 000000000..7234c16d1 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/noise-flat.png differ diff --git a/tutorials/shading/your_first_shader/img/noise-low.png b/tutorials/shading/your_first_shader/img/noise-low.png new file mode 100644 index 000000000..ace60a039 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/noise-low.png differ diff --git a/tutorials/shading/your_first_shader/img/noise-mesh.png b/tutorials/shading/your_first_shader/img/noise-mesh.png new file mode 100644 index 000000000..0b058333c Binary files /dev/null and b/tutorials/shading/your_first_shader/img/noise-mesh.png differ diff --git a/tutorials/shading/your_first_shader/img/noise-normal-light.png b/tutorials/shading/your_first_shader/img/noise-normal-light.png new file mode 100644 index 000000000..19a72efcb Binary files /dev/null and b/tutorials/shading/your_first_shader/img/noise-normal-light.png differ diff --git a/tutorials/shading/your_first_shader/img/noise-normal-sub.png b/tutorials/shading/your_first_shader/img/noise-normal-sub.png new file mode 100644 index 000000000..f60b190eb Binary files /dev/null and b/tutorials/shading/your_first_shader/img/noise-normal-sub.png differ diff --git a/tutorials/shading/your_first_shader/img/noise-normal.png b/tutorials/shading/your_first_shader/img/noise-normal.png new file mode 100644 index 000000000..f0d151e09 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/noise-normal.png differ diff --git a/tutorials/shading/your_first_shader/img/noise-set.png b/tutorials/shading/your_first_shader/img/noise-set.png new file mode 100644 index 000000000..10b5cac53 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/noise-set.png differ diff --git a/tutorials/shading/your_first_shader/img/noise.png b/tutorials/shading/your_first_shader/img/noise.png new file mode 100644 index 000000000..31e1cb13d Binary files /dev/null and b/tutorials/shading/your_first_shader/img/noise.png differ diff --git a/tutorials/shading/your_first_shader/img/normal-set.png b/tutorials/shading/your_first_shader/img/normal-set.png new file mode 100644 index 000000000..b4533c924 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/normal-set.png differ diff --git a/tutorials/shading/your_first_shader/img/normal.png b/tutorials/shading/your_first_shader/img/normal.png new file mode 100644 index 000000000..7bd9b1249 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/normal.png differ diff --git a/tutorials/shading/your_first_shader/img/normalmap.png b/tutorials/shading/your_first_shader/img/normalmap.png new file mode 100644 index 000000000..192ea6223 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/normalmap.png differ diff --git a/tutorials/shading/your_first_shader/img/normalmap2.png b/tutorials/shading/your_first_shader/img/normalmap2.png new file mode 100644 index 000000000..4e7a28af0 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/normalmap2.png differ diff --git a/tutorials/shading/your_first_shader/img/plane-sub-set.png b/tutorials/shading/your_first_shader/img/plane-sub-set.png new file mode 100644 index 000000000..d4aac7bb7 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/plane-sub-set.png differ diff --git a/tutorials/shading/your_first_shader/img/plane-sub.png b/tutorials/shading/your_first_shader/img/plane-sub.png new file mode 100644 index 000000000..265b79ced Binary files /dev/null and b/tutorials/shading/your_first_shader/img/plane-sub.png differ diff --git a/tutorials/shading/your_first_shader/img/plane.png b/tutorials/shading/your_first_shader/img/plane.png new file mode 100644 index 000000000..52a2a9059 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/plane.png differ diff --git a/tutorials/shading/your_first_shader/img/plastic.png b/tutorials/shading/your_first_shader/img/plastic.png new file mode 100644 index 000000000..1767b17e9 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/plastic.png differ diff --git a/tutorials/shading/your_first_shader/img/rim.png b/tutorials/shading/your_first_shader/img/rim.png new file mode 100644 index 000000000..d0a870834 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/rim.png differ diff --git a/tutorials/shading/your_first_shader/img/shader-error.png b/tutorials/shading/your_first_shader/img/shader-error.png new file mode 100644 index 000000000..522988887 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/shader-error.png differ diff --git a/tutorials/shading/your_first_shader/img/specular-toon.png b/tutorials/shading/your_first_shader/img/specular-toon.png new file mode 100644 index 000000000..3e20ea513 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/specular-toon.png differ diff --git a/tutorials/shading/your_first_shader/img/wave1.png b/tutorials/shading/your_first_shader/img/wave1.png new file mode 100644 index 000000000..97292d743 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/wave1.png differ diff --git a/tutorials/shading/your_first_shader/img/wave2.png b/tutorials/shading/your_first_shader/img/wave2.png new file mode 100644 index 000000000..a25183ba3 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/wave2.png differ diff --git a/tutorials/shading/your_first_shader/img/wave3.png b/tutorials/shading/your_first_shader/img/wave3.png new file mode 100644 index 000000000..3e50bff61 Binary files /dev/null and b/tutorials/shading/your_first_shader/img/wave3.png differ diff --git a/tutorials/shading/your_first_shader/index.rst b/tutorials/shading/your_first_shader/index.rst new file mode 100644 index 000000000..0a2ce7e9d --- /dev/null +++ b/tutorials/shading/your_first_shader/index.rst @@ -0,0 +1,26 @@ +Your first shader +================= + +This tutorial series will walk you through writing your first shader. It is intended +for people who have very little prior experience with shaders and want to get started +with the basics. This tutorial will not cover advanced topics and it is not +comprehensive. For a comprehensive and detailed overview of shaders in Godot see the +:ref:`Shading Reference Page `. + +"What are shaders" gives you a high-level overview of what shaders are and how they fit +into the rendering pipeline. + +The "your first shader" tutorials walk you through the process of writing a shader +step-by-step. + +For a more general introduction into shaders and the OpenGL Shading Language, use +`The Book of Shaders `_. + +.. toctree:: + :maxdepth: 1 + :name: toc-your-first-shader + + what_are_shaders + your_first_canvasitem_shader + your_first_spatial_shader + your_second_spatial_shader diff --git a/tutorials/shading/your_first_shader/what_are_shaders.rst b/tutorials/shading/your_first_shader/what_are_shaders.rst new file mode 100644 index 000000000..54dcdfc43 --- /dev/null +++ b/tutorials/shading/your_first_shader/what_are_shaders.rst @@ -0,0 +1,113 @@ +.. _doc_what_are_shaders: + +What are shaders? +================= + +Introduction +------------ + +So, you have decided to give shaders a try. You have likely heard that they can be used to +create interesting effects that run incredibly fast. You have also likely heard that they +are terrifying. Both are true. + +Shaders can be used to create a wide range of effects (in fact everything drawn in a modern +rendering engine is done with shaders). + +Writing shaders can also be very difficult for people unfamiliar with them. Godot tries to make writing +shaders a little easier by exposing many useful built-in features and handling some of the +lower-level initialization work for you. However, GLSL (the OpenGL Shading Language, which Godot uses) +is still unintuitive and restricting, especially for users who are used to GDScript. + +But what are they? +------------------ + +Shaders are a special kind of program that runs on Graphics Processing Units (GPUs). Most computers +have some sort of GPU, either one integrated into their CPU or discrete (meaning it is a separate +hardware component, for example, the typical graphics card). GPUs are especially useful for +rendering because they are optimized for running thousands of instructions in parallel. + +The output of the shader is typically the colored pixels of the object drawn to the viewport. But some +shaders allow for specialized outputs (this is especially true for APIs like Vulkan). Shaders operate +inside the shader pipeline. The standard process is the vertex -> fragment shader pipeline. The vertex +shader is used to decided where each vertex (point in a 3D model, or corner of a Sprite) goes and the +fragment shader decides what color individual pixels receive. + +Suppose you want to update all the pixels in a texture to a given color, on the CPU you would write: + +:: + + for x in range(width): + for y in range(height): + set_color(x, y, some_color) + +In a shader you are given access only to the inside of the loop so what you write looks like this: + +.. code-block:: glsl + + // function called for each pixel + void fragment() { + COLOR = some_color; + } + +You have no control over how this function is called. So you have to design your shaders +differently from how you would design programs on the CPU. + +A consequence of the shader pipeline is that you cannot access the results from a previous +run of the shader, you cannot access other pixels from the pixel being drawn, and you cannot +write outside of the current pixel being drawn. This enables the GPU to execute the shader +for different pixels in parallel, as they do not depend on each other. This lack of +flexibility is designed to work with the GPU which allows shaders to be incredibly fast. + +What can they do +^^^^^^^^^^^^^^^^ + +- position vertices very fast +- compute color very fast +- compute lighting very fast +- lots and lots of math + +What can't they do +^^^^^^^^^^^^^^^^^^ + +- draw outside mesh +- access other pixels from current pixel (or vertices) +- store previous iterations +- update on the fly (they can, but they need to be compiled) + +Structure of a shader +--------------------- + +In Godot, shaders are made up of 3 main functions: the ``vertex()`` function, the ``fragment()`` +function and the ``light()`` function. + +The ``vertex()`` function runs over all the vertices in the mesh and sets their positions as well +as some other per-vertex variables. + +The ``fragment()`` function runs for every pixel that is covered by the mesh. It uses the variables +from the ``vertex()`` function to run. The variables from the ``vertex()`` function are interpolated +between the vertices to provide the values for the ``fragment()`` function. + +The ``light()`` function runs for every pixel and for every light. It takes variables from the +``fragment()`` function and from previous runs of itself. + +For more information about how shaders operate specifically in Godot see the :ref:`Shaders ` doc. + +Technical overview +------------------ + +GPUs are able to render graphics much faster than CPUs for a few reasons, but most notably, +because they are able to run calculations massively in parallel. A CPU typically has 4 or 8 cores +while a GPU typically has thousands. That means a GPU can do hundreds of tasks at once. GPU architects +have exploited this in a way that allows for doing many calculations very quickly, but only when +many or all cores are doing the same calculation at once, but with different data. + +That is where shaders come in. The GPU will call the shader a bunch of times simultaneously, and then +operate on different bits of data (vertices, or pixels). These bunches of data are often called wavefronts. +A shader will run the same for every thread in the wavefront. For example, if a given GPU can handle 100 +threads per wavefront, a wavefront will run on a 10x10 block of pixels together. And it will continue to +run for all pixels in that wavefront until they are complete. Accordingly, if you have one pixel slower +than the rest (due to excessive branching), the entire block will be slowed down, resulting in massively +slower render times. This is different than CPU based operations, on a CPU if you can speed up even one +pixel the entire rendering time will decrease. On a GPU, you have to speed up the entire wavefront +to speed up rendering. + diff --git a/tutorials/shading/your_first_shader/your_first_canvasitem_shader.rst b/tutorials/shading/your_first_shader/your_first_canvasitem_shader.rst new file mode 100644 index 000000000..d30c0f518 --- /dev/null +++ b/tutorials/shading/your_first_shader/your_first_canvasitem_shader.rst @@ -0,0 +1,220 @@ +.. _doc_your_first_canvasitem_shader: + +Your first CanvasItem shader +============================ + +Introduction +------------ + +Shaders are special programs that execute on the GPU and are used for rendering +graphics. All modern rendering is done with shaders. For a more detailed description +of what shaders are please see :ref:`What are shaders `. + +This tutorial will focus on the practical aspects of writing shader programs by walking +you through the process of writing a shader with both vertex and fragment functions. +This tutorial targets absolute beginners to shaders. + +.. note:: If you have experience writing shaders and are just looking for + an overview of how shaders work in Godot, see the :ref:`Shading Reference `. + +Setup +----- + +:ref:`CanvasItem ` shaders are used to draw all 2D objects in Godot, +while :ref:`Spatial ` shaders are used to draw all 3D objects. + +In order to use a shader it must be attached inside a :ref:`Material ` +which must be attached to an object. Materials are a type of :ref:`Resource `. +To draw multiple objects with the same material, the material must be attached to each object. + +All objects derived from a :ref:`CanvasItem ` have a material property. +This includes all :ref:`GUI elements `, :ref:`Sprites `, :ref:`TileMaps `, +:ref:`MeshInstance2Ds ` etc. +They also have an option to inherit their parent's material. This can be useful if you have +a large number of nodes that you want to use the same material. + +To begin, create a Sprite node. You can use any CanvasItem, but for this tutorial we will +use a Sprite. + +In the Inspector, click beside "Texture" where it says "[empty]" and select "Load", then select +"Icon.png". For new projects, this is the Godot icon. You should now see the icon in the viewport. + +Next, look down in the Inspector, under the CanvasItem section, click beside "Material" and select +"New ShaderMaterial". This creates a new Material resource. Click on the sphere that appears. Godot currently +doesn't know whether you are writing a CanvasItem Shader or a Spatial Shader and it previews the output +of spatial shaders. So what you are seeing is the output of the default Spatial Shader. + +Click beside "Shader" and select "New Shader". Finally, click on the new shader resource and the shader +editor will open. You are now ready to begin writing your first shader. + +Your first CanvasItem shader +---------------------------- + +In Godot, all shaders start with a line specifying what type of shader they are. It uses +the following format: + +.. code-block:: glsl + + shader_type canvas_item; + +Becuase we are writing a CanvasItem shader, we specify ``canvas_item`` in the first line. All our code will +go beneath this declaration. + +This line tells the engine which built-in variables and functionality to supply you with. + +In Godot you can override three functions to control how the shader operates; ``vertex``, ``fragment``, and ``light``. +This tutorial will walk you through writing a shader with both vertex and fragment functions. Light +functions are significantly more complex than vertex and fragment functions and so will not be covered here. + +Your first fragment function +---------------------------- + +The fragment function runs for every pixel in a Sprite and determines what color that pixel should be. + +They are restricted to the pixels covered by the Sprite, that means you cannot use one to, for example, +create an outline around a Sprite. + +The most basic fragment function does nothing except assign a single color to every pixel. + +We do so by writing a ``vec4`` to the built-in variable ``COLOR``. ``vec4`` is shorthand for constructing +a vector with 4 numbers. For more information about vectors see the :ref:`Vector math tutorial ` +``COLOR`` is both an input variable to the fragment function and the final output from it. + +.. code-block:: glsl + + void fragment(){ + COLOR = vec4(0.4, 0.6, 0.9, 1.0); + } + +.. image:: img/blue-box.png + +Congratulations! You're done. You have successfully written your first shader in Godot. + +Now let's make things more complex. + +There are many inputs to the fragment function that you can use for calculating ``COLOR``. +``UV`` is one of them. UV coordinates are specified in your Sprite (without you knowing it!) +and they tell the shader where to read from textures for each part of the mesh. + +In the fragment function you can only read from ``UV``, but you can use it in other functions +or to assign values to ``COLOR`` directly. + +``UV`` varies between 0-1 from left-right and from top-bottom. + +.. image:: img/iconuv.png + +.. code-block:: glsl + + void fragment() { + COLOR = vec4(UV, 0.5, 1.0); + } + +.. image:: img/UV.png + +Using ``TEXTURE`` built-in +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When you want to adjust a color in a Sprite you cannot just adjust the color from the texture +manually like in the code below. + +.. code-block:: glsl + + void fragment(){ + //this shader will result in an all white rectangle + COLOR.b = 1.0; + } + +The default fragment function reads from a texture and displays it. When you overwrite the default fragment function, +you lose that functionality, so you have to implement it yourself. You read from textures using the +``texture`` function. Certain nodes, like Sprites, have a dedicated texture variable that can be accessed in the shader +using ``TEXTURE``. Use it together with ``UV`` and ``texture`` to draw the Sprite. + +.. code-block:: glsl + + void fragment(){ + COLOR = texture(TEXTURE, UV); //read from texture + COLOR.b = 1.0; //set blue channel to 1.0 + } + +.. image:: img/blue-tex.png + +Uniform input +^^^^^^^^^^^^^ + +Uniform input is used to pass data into a shader that will be the same across the entire shader. + +You can use uniforms by defining them at the top of your shader like so: + +.. code-block:: glsl + + uniform float size; + +For more information about usage see the :ref:`Shading Language doc `. + +Add a uniform to change the amount of blue in our Sprite. + +.. code-block:: glsl + + uniform float blue = 1.0; // you can assign a default value to uniforms + + void fragment(){ + COLOR = texture(TEXTURE, UV); //read from texture + COLOR.b = blue; + } + +Now you can change the amount of blue in the Sprite from the editor. Look back at the Inspector +under where you created your shader. You should see a section called "Shader Param". Unfold that +section and you will see the uniform you just declared. If you change the value in the editor, it +will overwrite the default value you provided in the shader. + +Interacting with shaders from code +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can change uniforms from code using the function ``set_shader_param()`` which is called on the node's +material resource. With a Sprite node, the following code can be used to set the ``blue`` uniform. + +:: + + var blue_value = 1.0 + material.set_shader_param("blue", blue_value) + +Note that the name of the uniform is a string. The string must match exactly with how it is +written in the shader, including spelling and case. + +Your first vertex function +-------------------------- + +Now that we have a fragment function, let's write a vertex function. + +Use the vertex function to calculate where on the screen each vertex should end up. + +The most important variable in the vertex function is ``VERTEX``. Initially, it specifies +the vertex coordinates in your model, but you also write to it to determine where to actually +draw those vertices. ``VERTEX`` is a ``vec2`` that is initially presented in local-space +(i.e. not relative to the camera, viewport, or parent nodes). + +You can offset the vertices by directly adding to ``VERTEX``. + +.. code-block:: glsl + + void vertex() { + VERTEX += vec2(10.0, 0.0); + } + +Combined with the ``TIME`` built-in variable, this can be used for simple animation. + +.. code-block:: glsl + + void vertex() { + // Animate Sprite moving in big circle around its location + VERTEX += vec2(cos(TIME)*100.0, sin(TIME)*100.0); + } + +Conclusion +---------- + +At their core, shaders do what you have seen so far, they compute ``VERTEX`` and ``COLOR``. It is +up to you to dream up more complex mathematical strategies for assigning values to those variables. + +For inspiration, take a look at some of the more advanced shader tutorials, and look at other sites +like `Shadertoy `_ and `The Book of Shaders `_. diff --git a/tutorials/shading/your_first_shader/your_first_spatial_shader.rst b/tutorials/shading/your_first_shader/your_first_spatial_shader.rst new file mode 100644 index 000000000..9170e62f6 --- /dev/null +++ b/tutorials/shading/your_first_shader/your_first_spatial_shader.rst @@ -0,0 +1,340 @@ +.. _doc_your_first_spatial_shader: + +Your first Spatial shader +============================ + +You have decided to start writing your own custom Spatial shader. Maybe you saw a cool trick +online that was done with shaders, or you have found that the +:ref:`SpatialMaterial ` isn't quite meeting your needs. Either way, +you have decided to write your own and now you need figure out where to start. + +This tutorial will explain how to write a Spatial shader and will cover more topics than the +:ref:`CanvasItem ` tutorial. + +Spatial shaders have more built-in functionality than CanvasItem shaders. The expectation with +spatial shaders is that Godot has already provided the functionality for common use cases and all +the user needs to do in the shader is set the proper parameters. This is especially true for a +PBR (physically based rendering) workflow. + +This is a two-part tutorial. In this first part we are going to go through how to make a simple terrain +using vertex displacement from a heightmap in the vertex function. In the :ref:`second part ` +we are going to take the concepts from this tutorial and walk through how to set up custom materials +in a fragment shader by writing an ocean water shader. + +.. note:: This tutorial assumes some basic shader knowledge such as types (``vec2``, ``float``, + ``sampler2D``), and functions. If you are uncomfortable with these concepts it is + best to get a gentle introduction from `The Book of Shaders + `_ before completing this tutorial. + +Where to assign my material +--------------------------- + +In 3D, objects are drawn using :ref:`Meshes `. Meshes are a resource type that store geometry +(the shape of your object) and materials (the color and how the object reacts to light) in units called +"surfaces". A Mesh can have multiple surfaces, or just one. Typically, you would +import a mesh from another program (e.g. Blender). But Godot also has a few :ref:`PrimitiveMeshes ` +that allow you to add basic geometry to a scene without importing Meshes. + +There are multiple node types that you can use to draw a mesh. The main one is :ref:`MeshInstance `, +but you can also use :ref:`Particles `, :ref:`MultiMeshes ` (with a +:ref:`MultiMeshInstance `), or others. + +Typically, a material is associated with a given surface in a mesh, but some nodes, like MeshInstance, allow +you to override the material for a specific surface, or for all surfaces. + +If you set a material on the surface or mesh itself, then all MeshInstances that share that mesh will share that material. +However, if you want to reuse the same mesh across multiple mesh instances, but have different materials for each +instance then you should set the material on the Meshinstance. + +For this tutorial we will set our material on the mesh itself rather than taking advantage of the MeshInstance's +ability to override materials. + +Setting up +---------- + +Add a new :ref:`MeshInstance ` node to your scene. + +In the inspector tab beside "Mesh" click "[empty]" and select "New PlaneMesh". +Then click on the image of a plane that appears. + +This adds a :ref:`PlaneMesh ` to our scene. + +Then, in the viewport, click in the upper left corner on the button that says "Perspective". +A menu will appear. In the middle of the menu are options for how to display the scene. +Select 'Display Wireframe'. + +This will allow you to see the triangles making up the plane. + +.. image:: img/plane.png + +Now set ``Subdivide Width`` and ``Subdivide Depth`` to ``32``. + +.. image:: img/plane-sub-set.png + +You can see that there are now many more triangles in the :ref:`Mesh`. This will give +us more vertices to work with and thus allow us to add more detail. + +.. image:: img/plane-sub.png + +:ref:`PrimitiveMeshes `, like PlaneMesh, only have one surface, so instead of +an array of materials there is only one. Click beside "Material" where it says "[empty]" and +select "New ShaderMaterial". Then click the sphere that appears. + +Now click beside "Shader" where it says "[empty]" and select "New Shader". + +The shader editor should now pop up and you are ready to begin writing your first Spatial shader! + +Shader magic +------------ + +.. image:: img/shader-error.png + +Notice how there is already error? This is because the shader editor reloads shaders on +the fly. The first thing Godot shaders need is a declaration of what type of shader they are. +We set the variable ``shader_type`` to ``spatial`` becuase this is a spatial shader. + +.. code-block:: glsl + + shader_type spatial; + +Next we will define the ``vertex()`` function. The ``vertex()`` function determines where +the vertices of your :ref:`Mesh` 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. + +We define the vertex shader like so: + +.. code-block:: glsl + + void vertex() { + + } + +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: + +.. code-block:: glsl + + void vertex() { + VERTEX.y += cos(VERTEX.x) * sin(VERTEX.z); + } + +Adding this line, you should get an image like the one below. + +.. image:: img/cos.png + +Okay, let's 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``; that gives us +a wave-like appearance across the ``x`` and ``z`` axes. + +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. + +.. code-block:: glsl + + void vertex() { + VERTEX.y += cos(VERTEX.x * 4.0) * sin(VERTEX.z * 4.0); + } + +.. image:: img/cos4.png + +This looks better, but it is still too spiky and repetitive, let's make it a little more interesting. + +Noise heightmap +--------------- + +Noise is a very popular tool for faking the look of terrain. Think of it as similar to the cosine function +where you have repeating hills except, with noise, each hill has a different height. + +Godot provides the :ref:`NoiseTexture ` resource for generating a noise texture +that can be accessed from a shader. + +To access a texture in a shader add the following code near the top of your shader, outside the +``vertex()`` function. + +.. code-block:: glsl + + uniform sampler2D noise; + +This will allow you to send a noise texture to the shader. Now look in the inspecter under your material. +You should see a section called "Shader Params". If you open it up you will see a section called "noise". + +Click beside it where it says "[empty]" and select "New NoiseTexture". Then in your NoiseTexture click beside +where it says "Noise" and select "New OpenSimplexNoise". + +:ref:`OpenSimplexNoise ` is used by the NoiseTexture to generate a heightmap. + +Once you set it up and should look like this. + +.. image:: img/noise-set.png + +Now, access the noise texture using the ``texture()`` function. ``texture()`` takes the texture as a first +argument and the position on the texture as the second. We use the ``x`` and ``z`` channels of ``VERTEX`` to +determine where on the texture to look up. + +.. code-block:: glsl + + float height = texture(noise, VERTEX.xz / 2.0 ); //divide by the size of the PlaneMesh + VERTEX.y += height; + +Using this code you can see the texture creates random looking hills. + +.. image:: img/noise.png + +Right now it is too spiky, we want to soften the hills a bit. To do that we will use a uniform. +You already used a uniform above to pass in the noise texture, now let's learn how they work. + +Uniforms +-------- + +Uniform variables allow you to pass data from the game into the shader. They are +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` using the keyword ``uniform``. + +Let's make a uniform that changes the height of the terrain. + +.. code-block:: glsl + + uniform float height_scale = 0.5; + + +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()`` +on the material corresponding to the shader. The value passed from GDScript takes +precedence over the value used to initialize it in the shader. + +:: + + # called from the MeshInstance + mesh.material.set_shader_param("height_scale", 0.5) + +.. note:: Changing uniforms from Spatial nodes is different than in CanvasItem nodes. Here, + we set the material inside the PlaneMesh resource. In other mesh resources you may + need to first access the material by calling ``surface_get_material()``. While in + the MeshInstance you would access the material using ``get_surface_material()`` or + ``material_override``. + +Remember that the string passed into ``set_shader_param()`` must match the name +of the uniform variable in the :ref:`Shader`. You can use the uniform variable anywhere +inside your :ref:`Shader`. Here, we will use it to set the height value instead +of arbitrarily multiplying by ``0.5``. + +.. code-block:: glsl + + VERTEX.y += height * height_scale; + +Now it looks much better. + +.. image:: img/noise-low.png + +Using uniforms, we can even change the value every frame to animate the height of the terrain. +Combined with :ref:`Tweens `, this can be especially useful for simple animations. + +Interacting with light +---------------------- + +First, turn wireframe off. To do so, click in the upper-left of the Viewport again, where it says +"Perspective", and select "Display Normal". + +.. image:: img/normal.png + +Note how the mesh color goes flat. This is because the lighting on it is flat. Let's add a light! + +First, we will add an :ref:`OmniLight` to the scene. + +.. image:: img/light.png + +You can see the light affecting the terrain, but it looks odd. 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 from the :ref:`Mesh ` to calculate light. + +The normals are stored in the Mesh, but we are changing the shape of the Mesh in the +shader, so the normals are no longer correct. To fix this, we can recalculate the normals +in the shader or use a normal texture that corresponds to our noise. Godot makes both easy for us. + +You can calculate the new normal manually in the vertex function and then just set ``NORMAL``. +With ``NORMAL`` set, Godot will do all the difficult lighting calculations for us. We will cover +this method in the next part of this tutorial, for now we will read normals from a texture. + +Instead we will rely on the NoiseTexture again to calculate normals for us. We do that by passing in +a second noise texture. + +.. code-block:: glsl + + uniform sampler2D normalmap; + +Set this second uniform texture to another NoiseTexture with another OpenSimplexNoise. But this time, check +off "As Normalmap". + +.. image:: img/normal-set.png + +Now, becuase this is a normalmap and not a per-vertex normal. We are going to assign it in the ``fragment()`` +function. The ``fragment()`` function will be explained in more detail in the next part of this tutorial. + +.. code-block:: glsl + + void fragment() { + } + +When we have normals that correspond to a specific vertex we set ``NORMAL``, but if you have a normalmap +that comes from a texture, set the normal using ``NORMALMAP``. This way Godot will handle the wrapping the +texture around the mesh automatically. + +Lastly, in order to ensure that we are reading from the same places on the noise texture and the normalmap +texture, we are going to pass the ``VERTEX.xz`` position from the ``vertex()`` function to the ``fragment()`` +function. We do that with varyings. + +Above the ``vertex()`` define a ``vec2`` called ``vertex_position``. And inside the ``vertex()`` function +assign ``VERTEX.xz`` to ``vertex_position``. + +.. code-block:: glsl + + varying vec2 vertex_position; + + void vertex() { + ... + vertex_position = VERTEX.xz / 2.0; + } + +And now we can access ``vertex_position`` from the ``fragment()`` function. + +.. code-block:: glsl + + void fragment() { + NORMALMAP = texture(normalmap, vertex_position).xyz; + } + +With the normals in place the light now reacts to the height of the mesh dynamically. + +.. image:: img/normalmap.png + +We can even drag the light around and the lighting will update automatically. + +.. image:: img/normalmap2.png + +Here is the full code for this tutorial. You can see it is not very long as Godot handles +most of the difficult stuff for you. + +.. code-block:: glsl + + shader_type spatial; + + uniform float height_scale = 0.5; + uniform sampler2D noise; + uniform sampler2D normalmap; + + varying vec2 vertex_position; + + void vertex() { + vertex_position = VERTEX.xz / 2.0; + float height = texture(noise, vertex_position).x * height_scale; + } + + void fragment() { + NORMALMAP = texture(normalmap, vertex_position).xyz; + } + +That is everything for this part. Hopefully, you now understand the basics of vertex +shaders in Godot. In the next part of this tutorial we will write a fragment function +to accompany this vertex function and we will cover a more advanced technique to turn +this terrain into an ocean of moving waves. diff --git a/tutorials/shading/your_first_shader/your_second_spatial_shader.rst b/tutorials/shading/your_first_shader/your_second_spatial_shader.rst new file mode 100644 index 000000000..28df342ae --- /dev/null +++ b/tutorials/shading/your_first_shader/your_second_spatial_shader.rst @@ -0,0 +1,320 @@ +.. _doc_your_second_spatial_shader: + +Your first Spatial shader: part 2 +================================= + +From a high-level, what Godot does is give the user a bunch of parameters that can be optionally set +(``AO``, ``SSS_Strength``, ``RIM``, etc.). These parameters correspond to different complex effects +(Ambient Occlusion, SubSurface Scattering, Rim Lighting, etc.). When not written to, the code is thrown +out before it is compiled and so the shader does not incur the cost of the extra feature. This makes it +easy for users to have complex PBR-correct shading, without writing complex shaders. Of course, Godot +also allows you to ignore all these parameters and write a fully customized shader. + +For a full list of these parameters see the :ref:`spatial shader ` reference doc. + +A difference between the vertex function and a fragment function is that the vertex function +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`. + +Your first spatial fragment function +------------------------------------ + +As mentioned in the previous part of this tutorial. The standard use of the fragment function +in Godot is to set up different material properties and let Godot handle the rest. In order +to provide even more flexibility, Godot also provides things called render modes. Render +modes are set at the top of the shader, directly below ``shader_type``, and they specify +what sort of functionality you want the built-in aspects of the shader to have. + +For example, if you do not want to have lights affect an object, set the render mode to +``unshaded``: + +.. code-block:: glsl + + render_mode unshaded; + +You can also stack multiple render modes together. For example, if you want to use toon +shading instead of more-realistic PBR shading, set the diffuse mode and specular mode to toon: + +.. code-block:: glsl + + render_mode diffuse_toon, specular_toon; + +This model of built-in functionality allows you to write complex custom shaders by changing +only a few parameters. + +For a full list of render modes see the :ref:`Spatial shader reference `. + +In this part of the tutorial, we will walk through how to take the bumpy terrain from the +previous part and turn it into an ocean. + +First let's set the color of the water. We do that by setting ``ALBEDO``. + +``ALBEDO`` is a ``vec3`` that contains the color of the object. + +Let's set it to a nice shade of blue. + +.. code-block:: glsl + + void fragment() { + ALBEDO = vec3(0.1, 0.3, 0.5); + } + +.. image:: img/albedo.png + +We set it to a very dark shade of blue because most of the blueness of the water will +come from reflections from the sky. + +The PBR model that Godot uses relies on two main parameters: ``METALLIC`` and ``ROUGHNESS``. + +``ROUGHNESS`` specifies how smooth/rough the surface of a material is. A low ``ROUGHNESS`` will +make a material appear like a shiny plastic, while a high roughness makes the material appear +more solid in color. + +``METALLIC`` specifies how much like a metal the object is. It is better set close to ``0`` or ``1``. +Think of ``METALLIC`` as changing the balance between the reflection and the ``ALBEDO`` color. A +high ``METALLIC`` almost ignores ``ALBEDO`` altogether, and looks like a mirror of the sky. While +a low ``METALLIC`` has a more equal representation of sky color and ``ALBEDO`` color. + +``ROUGHNESS`` increases from ``0`` to ``1`` from left to right while ``METALLIC`` increase from +``0`` to ``1`` from top to bottom. + +.. image:: img/PBR.png + +.. note:: ``METALLIC`` should be close to ``0`` or ``1`` for proper PBR shading. Only set it between + them for blending between materials. + +Water is not a metal, so we will set it's ``METALLIC`` property to ``0.0``. But, water is also highly +reflective, so we will set it's ``ROUGHNESS`` property to by quite low as well. + +.. code-block:: glsl + + void fragment() { + METALLIC = 0.0; + ROUGHNESS = 0.01; + ALBEDO = vec3(0.1, 0.3, 0.5); + } + +.. image:: img/plastic.png + +Now we have a smooth plastic looking surface. It is time to think about some particular properties of +water that we want to emulate. There are two main ones that will take this from a weird plastic surface +to nice stylized water. The first is specular reflections. Specular reflections are those bright spots +you see from where the sun reflects directly into your eye. The second is fresnel reflectance. +Fresnel reflectance is the property of objects to become more reflective at shallow angles. It is the +reason why you can see into water below you, but farther away it reflects the sky. + +In order to increase the specular reflections, we will do two things. First, we will change the render +mode for specular to toon because the toon render mode has larger specular highlights. + +.. code-block:: glsl + + render_mode specular_toon; + +.. image:: img/specular-toon.png + +Second we will +add rim lighting. Rim lighting increases the effect of light at glancing angles. Usually it is used +to emulate the way light passes through fabric on the edges of an object, but we will use it here to +help achieve a nice watery effect. + +.. code-block:: glsl + + void fragment() { + RIM = 0.2; + METALLIC = 0.0; + ROUGHNESS = 0.01; + ALBEDO = vec3(0.1, 0.3, 0.5); + } + +.. image:: img/rim.png + +In order to add fresnal reflectance we will compute a fresnel term in our fragment shader. +We are not going to use a real fresnel term, instead we will approximate it using the dot +product of the ``NORMAL`` and ``VIEW`` vectors. The ``NORMAL`` vector points away from a +surface of the, while the ``VIEW`` vector is the direction between your eye and that point +on the surface. The dot product between them is a handy way to tell when you are looking +at the surface head-on your at a glancing angle. + +.. code-block:: glsl + + float fresnel = sqrt(1.0 - dot(NORMAL, VIEW)); + +And mix it into both ``ROUGHNESS`` and ``ALBEDO``. This is the benefit of ShaderMaterials over +SpatialMaterials. With SpatialMaterials we could set these properties with a texture, or to a flat +number. But with shaders we can set them based on any mathematical function that we can dream up. + + +.. code-block:: glsl + + void fragment() { + float fresnel = sqrt(1.0 - dot(NORMAL, VIEW)); + RIM = 0.2; + METALLIC = 0.0; + ROUGHNESS = 0.01 * (1.0 - fresnel); + ALBEDO = vec3(0.1, 0.3, 0.5) + (0.1 * fresnel); + } + +.. image:: img/fresnel.png + +And now, with only 5 lines of code, you can have complex looking water. Now that we have +lighting, this water is looking too bright. Let's darken it. This is done easily by +decreasing the values of the ``vec3`` we pass into ``ALBEDO``. Let's set them to +``vec3(0.01, 0.03, 0.05)``. + +.. image:: img/dark-water.png + +Animating with ``TIME`` +----------------------- + +Going back to the vertex function, we can animated the waves using the built-in variable ``TIME``. + +``TIME`` is a built-in variable that is accessible from the vertex and fragment functions. + + +In the last tutorial we calculated height by reading from a heightmap. For this tutorial, +we will do the same. Put the heightmap code in a function called ``height()``. + +.. code-block:: glsl + + float height(vec2 position) { + return texture(noise, position / 10.0).x; //scaling factor is based on mesh size (This PlanMesh is 10x10) + } + +In order to use ``TIME`` in the ``height()`` function we need to pass it in. + +.. code-block:: glsl + + float height(vec2 position, float time) { + } + +And make sure to correctly pass it in inside the vertex function. + +.. code-block:: glsl + + void vertex() { + vec2 pos = VERTEX.xz; + float k = height(pos, TIME); + VERTEX.y = k; + } + +Instead of using a normalmap to calculate normals. We are going to compute them manually in the +``vertex()`` function. To do so use the following line of code. + +.. code-block:: glsl + + NORMAL = normalize(vec3(k - height(pos + vec2(0.1, 0.0), TIME), 0.1, k - height(pos + vec2(0.0, 0.1), TIME))); + +We need to compute ``NORMAL`` manually because in the next section we will be using math to create +complex-looking waves. + +Now, we are going to make the ``height()`` function a little more complicated by offsetting ``position`` +by the cosine of ``TIME``. + +.. code-block:: glsl + + float height(vec2 position, float time) { + vec2 offset = 0.01 * cos(position + time); + return texture(noise, (position / 10.0) - offset).x; + } + +This results in waves that move slowly, but not in a very natural way. The next section will dig deeper +into using shaders to create more complex effects, in this case realistic waves, by adding a few +more mathematical functions. + +Advanced effects: waves +----------------------- + +What makes shaders so powerful is that you can achieve complex effects by using math. To illustrate +this, we are going to take our waves to the next level by modifying the ``height()`` function and +by introducing a new function called ``wave()``. + +``wave()`` has one parameter, ``position``, which is the same as it is in ``height()``. + +We are going to call ``wave()`` multiple times in ``height()`` in order to fake the way waves look. + +.. code-block:: glsl + + float wave(vec2 position){ + position += texture(noise, position / 10.0).x * 2.0 - 1.0; + vec2 wv = 1.0 - abs(sin(position)); + return pow(1.0 - pow(wv.x * wv.y, 0.65), 4.0); + } + +At first this looks complicated. So let's go through it line-by-line. + +.. code-block:: glsl + + position += texture(noise, position / 10.0).x * 2.0 - 1.0; + +Offset the position by the ``noise`` texture. This will make the waves curve so they are not straight lines +completely aligned with the grid. + +.. code-block:: glsl + + vec2 wv = 1.0 - abs(sin(position)); + +Define a wave-like function using ``sin()`` and ``position``. Normally ``sin()`` waves are very round. +We use ``abs()`` to absolute to give them a sharp ridge and constrain them to the 0-1 range. And then we +subtract it from ``1.0`` to put the peak on top. + +.. code-block:: glsl + + return pow(1.0 - pow(wv.x * wv.y, 0.65), 4.0); + +Multiply the x-directional wave by the y-directional wave and raise it to a power to sharpen the peaks. +Then subtract that from ``1.0`` so that the ridges become peaks and raise that to a power to sharpen the +ridges. + +We can now replace the contents of our ``height()`` function with ``wave()``. + +.. code-block:: glsl + + float height(vec2 position, float time) { + float h = wave(position); + } + +Using this you get: + +.. image:: img/wave1.png + +The shape of the sin wave is too obvious. So let's spread the waves out a bit. We do this by scaling ``position``. + +.. code-block:: glsl + + float height(vec2 position, float time) { + float h = wave(position*0.4); + } + +Now it looks much better. + +.. image:: img/wave2.png + +We can do even better if we layer multiple waves on top of each other at varying +frequencies and amplitudes. What this means is that we are going to scale position for each one to make +the waves thinner or wider (frequency). And we are going to multiply the output of the wave to make them shorter +or taller (amplitude). + +Here is an example for how you could layer the four waves to achieve nicer looking waves. + +.. code-block:: glsl + + float height(vec2 position, float time) { + float d = wave((position + time) * 0.4, 8.0) * 0.3; + d += wave((position - time) * 0.3, 8.0) * 0.3; + d += wave((position + time) * 0.5, 4.0) * 0.2; + d += wave((position - time) * 0.6, 4.0) * 0.2; + return d; + } + +Note that we add time to two and subtract it from the other two. This makes the waves move in different directions +creating a complex effect. Also note that the amplitudes (the number the result is multiplied by) all +add up to ``1.0``. This keeps the wave in the 0-1 range. + +With this code you should end up with more complex looking waves and all you had to do was add a little bit +of math! + +.. image:: img/wave3.png + +For more information about Spatial shaders read the :ref:`Shading Language ` +doc and the :ref:`Spatial Shaders ` doc. Also look at more advanced tutorials +in the :ref:`Shading section ` and the :ref:`3D ` sections. diff --git a/tutorials/threads/index.rst b/tutorials/threads/index.rst index e685f79fd..aafd4f6bd 100644 --- a/tutorials/threads/index.rst +++ b/tutorials/threads/index.rst @@ -5,4 +5,5 @@ Multi-threading :maxdepth: 1 :name: toc-learn-features-threads + using_multiple_threads thread_safe_apis diff --git a/tutorials/threads/thread_safe_apis.rst b/tutorials/threads/thread_safe_apis.rst index d9e9bd6cd..99572e007 100644 --- a/tutorials/threads/thread_safe_apis.rst +++ b/tutorials/threads/thread_safe_apis.rst @@ -49,8 +49,6 @@ you are doing and you are sure that a single resource is not being used or set in multiple ones. Otherwise, you are safer just using the servers API (which is fully thread-safe) directly and not touching scene or resources. - - GDScript arrays, dictionaries ----------------------------- @@ -60,5 +58,3 @@ Resources --------- Modifying a unique resource from multiple threads is not supported, but loading them on threads or handling a reference is perfectly supported. Scenes, textures, meshes, etc. Can be loaded and manipulated on threads, then added to the active scene in the main thread. - - diff --git a/tutorials/threads/using_multiple_threads.rst b/tutorials/threads/using_multiple_threads.rst new file mode 100644 index 000000000..cb87de19d --- /dev/null +++ b/tutorials/threads/using_multiple_threads.rst @@ -0,0 +1,160 @@ +.. _doc_using_multiple_threads: + +Using multiple threads +====================== + +Threads +------- + +Threads allow simultaneous execution of code. It allows off-loading work from the main thread. + +Godot supports threads and provides many handy functions to use them. + +.. note:: If using other languages (C#, C++), it may be easier to use the threading classes they support. + +Creating a Thread +----------------- + +Creating a thread is very simple, just use the following code: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var thread + + # The thread will start here. + func _ready(): + thread = Thread.new() + # Third argument is optional userdata, it can be any variable. + thread.start(self, "_thread_function", "Wafflecopter") + + # Run here and exit. + # The argument is the userdata passed from start(). + # If no argument was passed, this one still needs to + # be here and it will be null. + func _thread_function(userdata): + # Print the userdata ("Wafflecopter") + print("I'm a thread! Userdata is: ", userdata) + + # Thread must be disposed (or "joined"), for portability. + func _exit_tree(): + thread.wait_to_finish() + +Your function will, then, run in a separate thread until it returns. +Even if the function has returned already, the thread must collect it, so call +:ref:`Thread.wait_to_finish()`, which will wait until the +thread is done (if not done yet), then properly dispose of it. + +Mutexes +------- + +Accessing objects or data from multiple threads is not always supported (if you do it, it will +cause unexpected behaviors or crashes). Read the :ref:`Thread safe APIs` +to understand which engine APIs support multiple thread access. + +When processing your own data or calling your own functions, as a rule, try to avoid accessing +the same data directly from different threads. You may run into synchronization problems, as the +data is not always updated between CPU cores when modified. +Always use a :ref:`Mutex` when accessing a piece of data from different threads. + +When calling :ref:`Mutex.lock()`, a thread ensures that all other threads will be blocked (put on suspended state) if they try to *lock* the same mutex. When the mutex us unlocked by calling :ref:`Mutex.unlock()`, the other threads will be allowed to proceed with the lock (but only one at a time). + +Here is an example of using a Mutex: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var counter = 0 + var mutex + var thread + + # The thread will start here. + func _ready(): + mutex = Mutex.new() + thread = Thread.new() + thread.start(self, "_thread_function") + + # Increase value, protect it with Mutex. + mutex.lock() + counter += 1 + mutex.unlock() + + # Increment the value from the thread, too. + func _thread_function(userdata): + mutex.lock() + counter += 1 + mutex.unlock() + + # Thread must be disposed (or "joined"), for portability. + func _exit_tree(): + thread.wait_to_finish() + print("Counter is: ", counter) # Should be 2. + +Semaphores +---------- + +Sometimes you want your thread to work *"on demand"*. In other words, tell it when to work +and let it suspend when it isn't doing anything. +For this :ref:`Semaphores` are used. The function :ref:`Semaphore.wait()` +is used in the thread to suspend it until some data arrives. + +The main thread, instead, uses :ref:`Semaphore.post()` to signal that data is ready to be processed: + +.. tabs:: + .. code-tab:: gdscript GDScript + + var counter = 0 + var mutex + var semaphore + var thread + var exit_thread = false + + # The thread will start here. + func _ready(): + mutex = Mutex.new() + semaphore = Semaphore.new() + exit_thread = false + + thread = Thread.new() + thread.start(self, "_thread_function") + + func _thread_function(userdata): + while true: + semaphore.wait() # Wait until posted. + + mutex.lock() + var should_exit = exit_thread # Protect with Mutex. + mutex.unlock() + + if should_exit: + break + + mutex.lock() + counter += 1 # Increment counter, protect with Mutex. + mutex.unlock() + + func increment_counter(): + semaphore.post() # Make the thread process. + + func get_counter(): + mutex.lock() + # Copy counter, protect with Mutex. + var counter_value = counter + mutex.unlock() + return counter_value + + # Thread must be disposed (or "joined"), for portability. + func _exit_tree(): + # Set exit condition to true. + mutex.lock() + exit_thread = true # Protect with Mutex. + mutex.unlock() + + # Unblock by posting. + semaphore.post() + + # Wait until it exits. + thread.wait_to_finish() + + # Print the counter. + print("Counter is: ", counter) diff --git a/tutorials/viewports/multiple_resolutions.rst b/tutorials/viewports/multiple_resolutions.rst index e26364afe..c540e5df4 100644 --- a/tutorials/viewports/multiple_resolutions.rst +++ b/tutorials/viewports/multiple_resolutions.rst @@ -3,6 +3,45 @@ Multiple resolutions ==================== + +The problem of multiple resolutions +----------------------------------- + +Developers often have trouble understanding how to best support multiple resolutions +in their games. For Desktop and Console games this is more or less straightforward, +as most screen aspect ratios are 16:9 and resolutions are standard (720,1080,2k,4k,etc). + +For mobile games at first it was easy. For many years, the iPhone (and iPad) used the same +resolution. When *Retina* was implemented, they just doubled the amount of pixel density +(so most developers had assets in default and double resolutions). + +Nowadays this is no longer the case, as there are plenty of different screen sizes, densities +and aspect ratios for mobile, and non conventional sizes are becoming trendy for Desktop, +such as ultra-wide. + +For 3D games there is not much of a need to support multiple resolutions (from the aesthetic +point of view). The 3D geometry will just fill the screen based on the field-of-view, disregarding +the aspect ratio. The main reason one may want to support this, in this case, is for *performance* reasons (running +in lower resolution to increase frames per second). + +For 2D and game UIs, this is a different matter, as art needs to be created using specific pixel sizes +in software such as Photoshop, Gimp, Krita, etc. + +Given layouts, aspect ratios, resolutions and pixel densities can change so much, it is no longer possible +to design UIs for every specific screen. Another method must be used. + +One size fits all +----------------- + +The most common approach nowadays is to just use a single *base* resolution and then fit it to everything else. +This resolution is how most players are expected to play the game (given their hardware). For mobile, Google +has useful `stats `_ online, and for desktop, +Steam `also does `_. + +As an example, Steam shows that the most common *primary display resolution* is 1920x1080, so a sensible approach is to develop a game for this resolution, then handle scaling for different sizes and aspect ratios. + +Godot provides a several useful tools to do this easily. + Base size --------- @@ -189,3 +228,25 @@ From scripts To configure stretching at runtime from a script, use the ``get_tree().set_screen_stretch()`` function (see :ref:`SceneTree.set_screen_stretch() `). + +Reducing aliasing on downsampling +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If the game has a very high base resolution (say 4k), aliasing might appear +when downsampling to something considerably lower like 720. This can be +detected and reduced by forcing to shrink all images by 2 upon load (this is +very fast). This can be achieved by calling + +:: + + VisualServer.texture_set_shrink_all_x2_on_set_data(true) + + +Before most game data is loaded. + +Handling aspect ratios +^^^^^^^^^^^^^^^^^^^^^^ + +Once scaling for different resolutions is accounted for, just make sure that your *user interface* +also scales for different aspect ratios. This can be easily done using :ref:`anchors ` +and/or :ref:`containers `.