Restructuring

This commit is contained in:
Unknown
2018-01-26 14:07:48 +01:00
committed by Dio
parent 17d334e047
commit 882b31d361
60 changed files with 100 additions and 30 deletions

View File

@@ -1,32 +0,0 @@
.. _doc_consoles:
Console Support in Godot
========================
Official Support
----------------
Godot does not officially support consoles (save for XBox One via UWP), and
this situation will most likely never change.
The reasons for this are:
* To develop for consoles, one must be licensed as a company. Godot, as an open source project, does not have such legal figure.
* Console SDKs are secret, and protected by non-disclosure agreements. Even if we could get access to them, we could not publish the code as open-source.
* Consoles require specialized hardware to develop for, so regular individuals can't create games for them anyway.
This, however, does not mean you can't port your games to console. It's
quite the contrary.
Third-Party Support
--------------------
Console ports of Godot are offered via third party companies (which have
ported Godot on their own) and offer porting and publishing services of
your games to consoles.
Following is the list of providers:
* .. _a Lone Wolf Technology: http://www.lonewolftechnology.com/ offers
Switch, PS4 and XBox One porting and publishing of Godot games.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,420 +0,0 @@
.. _doc_import_plugins:
Import plugins
==============
Introduction
------------
An import plugin is a special type of editor tool that allows custom resources
to be imported by Godot and be treated as first-class resources. The editor
itself comes bundled with a lot of import plugins to handle the common resources
like PNG images, Collada and glTF models, OGG Vorbis sounds, and many more.
This tutorial will show you how to create a simple import plugin to load a
custom text file as a material resource. This text file will contain three
numeric values separated by comma, which represents the three channels of a
color, and the resulting color will be used as the albedo (main color) of the
imported material.
.. note:: This tutorial assumes you already know how to make generic plugins. If
in doubt, refer to the :ref:`doc_making_plugins` page. This also
assumes you are acquainted with Godot's import system.
The sample file to import contains only a line representing the pure blue color
(zero red, zero green, and full blue):
.. code-block:: none
0,0,255
Configuration
-------------
First we need a generic plugin that will handle the initialization and
destruction of our import plugin. Let's add the ``plugin.cfg`` file first:
.. code-block:: ini
[plugin]
name="Silly Material Importer"
description="Imports a 3D Material from an external text file."
author="Yours Truly"
version="1.0"
script="material_import.gd"
Then we need the ``material_import.gd`` file to add and remove the import plugin
when needed:
::
# material_import.gd
tool
extends EditorPlugin
var import_plugin
func _enter_tree():
import_plugin = preload("import_plugin.gd").new()
add_import_plugin(import_plugin)
func _exit_tree():
remove_import_plugin(import_plugin)
import_plugin = null
When this plugin is activated, it will create a new instance of the import
plugin (which we'll soon make) and add it to the editor using the
:ref:`add_import_plugin<class_EditorPlugin_add_import_plugin>` method. We store
a reference to it in a class member ``import_plugin`` so we can refer to it
later when removing it. The
:ref:`remove_import_plugin<class_EditorPlugin_remove_import_plugin>` method is
called when the plugin is deactivated to clean up the memory and let the editor
know the import plugin isn't available anymore.
Note that the import plugin is a reference type so it doesn't need to be
explicitly released from the memory with the ``free()`` function. It will be
released automatically by the engine when it goes out of scope.
The EditorImportPlugin class
----------------------------
The main character of the show is the
:ref:`EditorImportPlugin class<class_EditorImportPlugin>`. It is responsible to
implement the methods that are called by Godot when it needs to know how to deal
with files.
Let's begin to code our plugin, one method at time:
::
# import_plugin.gd
tool
extends EditorImportPlugin
func get_importer_name():
return "demos.sillymaterial"
The first method is the
:ref:`get_importer_name<class_EditorImportPlugin_get_importer_name>`. This is a
unique name to your plugin that is used by Godot to know which import was used
in a certain file. When the files needs to be reimported, the editor will know
which plugin to call.
::
func get_visible_name():
return "Silly Material"
The :ref:`get_visible_name<class_EditorImportPlugin_get_visible_name>` method is
responsible to inform the name of the type it imports and will be shown to the
user in the Import dock.
You should choose this name as a continuation to "Import as". Eg. *"Import as
Silly Material"*. Yes, this one is a bit silly, but you certainly can come up
with a descriptive name for your plugin.
::
func get_recognized_extensions():
return ["mtxt"]
Godot's import system detects file types by their extension. In the
:ref:`get_recognized_extensions<class_EditorImportPlugin_get_recognized_extensions>`
method you return an array of strings to represent each extension that this
plugin can understand. If an extension is recognized by more than one plugin,
the user can select which one to use when importing the files.
.. tip:: Common extensions like ``.json`` and ``.txt`` might be used by many
plugins. Also, there could be files in the project that are just data
for the game and should not be imported. You have to be careful when
importing to validate the data. Never expect the file to be well-formed.
::
func get_save_extension():
return "material"
The imported files are saved in the ``.import`` folder at the project's root.
Their extension should match the type of resource you are importing, but since
Godot can't tell what you'll use (because there might be multiple valid
extensions for the same resource), you need to inform what will be the used in
the import.
Since we're importing a Material, we'll use the special extension for such
resource types. If you are importing a scene, you can use ``scn``. Generic
resources can use the ``res`` extension. However, this is not enforced in any
way by the engine.
::
func get_resource_type():
return "SpatialMaterial"
The imported resource has a specific type, so the editor can know which property
slot it belongs to. This allows drag and drop from the FileSystem dock to a
property in the Inspector.
In our case it's a :ref:`class_SpatialMaterial`, which can be applied to 3D
objects.
.. note:: If you need to import different types from the same extension, you
have to create multiple import plugins. You can abstract the import
code on another file to avoid duplication in this regard.
Options and presets
-------------------
Your plugin can provide different options to allow the user to control how the
resource will be imported. If a set of selected options is common, you can also
create different presets to make it easier for the user. The following image
shows how the options will appear in the editor:
.. image:: img/import_plugin_options.png
Since there might be many presets and they are identified with a number, it's a
good practice to use an enum so you can refer to them using names.
::
tool
extends EditorImportPlugin
enum Presets { PRESET_DEFAULT }
...
Now that the enum is defined, let's keep looking at the methods of an import
plugin:
::
func get_preset_count():
return Presets.size()
The :ref:`get_preset_count<class_EditorImportPlugin_get_preset_count>` method
returns the amount of presets that this plugins defines. We only have one preset
now, but we can make this method future-proof by returning the size of our
``Presets`` enumeration.
::
func get_preset_name(preset):
match preset:
PRESET_DEFAULT: return "Default"
_ : return "Unknown"
Here we have the
:ref:`get_preset_name<class_EditorImportPlugin_get_preset_name>` method, which
gives names to the presets as they will be presented to the user, so be sure to
use short and clear names.
We can use the ``match`` statement here to make the code more structured. This
way it's easy to add new presets in the future. We use the catch all pattern to
return something too. Although Godot won't ask for presets beyond the preset
count you defined, it's always better to be on the safe side.
If you have only one preset you could simply return its name directly, but if
you do this you have to be careful when you add more presets.
::
func get_import_options(preset):
match preset:
PRESET_DEFAULT:
return [{
"name": "use_red_anyway",
"default_value": false
}]
_: return []
This is the method which defines the available options.
:ref:`get_import_options<class_EditorImportPlugin_get_import_options>` returns
an array of dictionaries, and each dictionary contains a few keys that are
checked to customize the option as its shown to the user. The following table
shows the possible keys:
+-------------------+------------+---------------------------------------------------------------------------------------------------------+
| Key | Type | Description |
+===================+============+=========================================================================================================+
| ``name`` | String | The name of the option. When showed, underscores become spaces and first letters are capitalized. |
+-------------------+------------+---------------------------------------------------------------------------------------------------------+
| ``default_value`` | Any | The default value of the option for this preset. |
+-------------------+------------+---------------------------------------------------------------------------------------------------------+
| ``property_hint`` | Enum value | One of the :ref:`PropertyHint<enum_@GlobalScope_PropertyHint>` values to use as hint. |
+-------------------+------------+---------------------------------------------------------------------------------------------------------+
| ``hint_string`` | String | The hint text of the property. The same as you'd add in the ``export`` statement in GDScript. |
+-------------------+------------+---------------------------------------------------------------------------------------------------------+
| ``usage`` | Enum value | One of the :ref:`PropertyUsageFlags<enum_@GlobalScope_PropertyUsageFlags>` values to define the usage. |
+-------------------+------------+---------------------------------------------------------------------------------------------------------+
The ``name`` and ``default_value`` keys are **mandatory**, the rest are optional.
Note that the ``get_import_options`` method receives the preset number, so you
can configure the options for each different preset (especially the default
value). In this example we use the ``match`` statement, but if you have lots of
options and the presets only change the value you may want to create the array
of options first and then just change it based on the preset.
.. warning:: The ``get_import_options`` method is called even if you don't
define presets (by making ``get_preset_count`` return zero). You
have to return an array even it's empty, otherwise you can get
errors.
::
func get_option_visibility(option, options):
return true
For the
:ref:`get_option_visibility<class_EditorImportPlugin_get_option_visibility>`
method, we simply return ``true`` because all of our options (i.e. the single
one we defined) are visible all the time.
If you need to make certain option visible only if another is set with a certain
value, you can add the logic in this method.
The ``import`` method
---------------------
The heavy part of the process, responsible for the converting the files into
resources, is covered by the :ref:`import<class_EditorImportPlugin_import>`
method. Our sample code is a bit long, so let's split in a few parts:
::
func import(source_file, save_path, options, r_platform_variants, r_gen_files):
var file = File.new()
var err = file.open(source_file, File.READ)
if (err != OK):
return err
var line = file.get_line()
file.close()
The first part of our import method opens and reads the source file. We use the
:ref:`File<class_File>` class to do that, passing the ``source_file``
parameter which is provided by the editor.
If there's an error when opening the file, we return it to let the editor know
that the import wasn't successful.
::
var channels = line.split(",")
if channels.size() != 3:
return ERR_PARSE_ERROR
var color
if options.use_red_anyway:
color = Color8(255, 0, 0)
else:
color = Color8(int(channels[0]), int(channels[1]), int(channels[2]))
This code takes the line of the file it read before and splits it in pieces
that are separated by a comma. If there are more or less than the three values,
it considers the file invalid and reports an error.
Then it creates a new :ref:`Color<class_Color>` variable and sets its values
according to the input file. If the ``use_red_anyway`` option is enabled, then
it sets the color as a pure red instead.
::
var material = SpatialMaterial.new()
material.albedo_color = color
This part makes a new :ref:`SpatialMaterial<class_SpatialMaterial>` that is the
imported resource. We create a new instance of it and then set its albedo color
as the value we got before.
::
return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], material)
This is the last part and quite an important one, because here we save the made
resource to the disk. The path of the saved file is generated and informed by
the editor via the ``save_path`` parameter. Note that this comes **without** the
extension, so we add it using :ref:`string formatting<doc_gdscript_printf>`. For
this we call the ``get_save_extension`` method that we defined earlier, so we
can be sure that they won't get out of sync.
We also return the result from the
:ref:`ResourceSaver.save<class_ResourceSaver_save>` method, so if there's an
error in this step, the editor will know about it.
Platform variants and generated files
-------------------------------------
You may have noticed that our plugin ignored two arguments of the ``import``
method. Those are *return arguments* (hence the ``r`` at the beginning of their
name), which means that the editor will read from them after calling your import
method. Both of them are arrays that you can fill with information.
The ``r_platform_variants`` argument is used if you need to import the resource
differently depending on the target platform. While it's called *platform*
variants, it is based on the presence of :ref:`feature tags<doc_feature_tags>`,
so even the same platform can have multiple variants depending on the setup.
To import a platform variant, you need to save it with the feature tag before
the extension, and then push the tag to the ``r_platform_variants`` array so the
editor can know that you did.
For an example, let's say we save a different material for mobile platform. We
would need to do something like the following:
::
r_platform_variants.push_back("mobile")
return ResourceSaver.save("%s.%s.%s" % [save_path, "mobile", get_save_extension()], mobile_material)
The ``r_gen_files`` argument is meant for extra files that are generated during
your import process and need to be kept. The editor will look at it to
understand the dependencies and make sure the extra file is not inadvertently
deleted.
This is also an array and should be filled with full paths of the files you
save. As an example, let's create another material for the next pass and save it
in a different file:
::
var next_pass = SpatialMaterial.new()
next_pass.albedo_color = color.inverted()
var next_pass_path = "%s.next_pass.%s" % [save_path, get_save_extension()]
err = ResourceSaver.save(next_pass_path, next_pass)
if err != OK:
return err
r_gen_files.push_back(next_pass_path)
Trying the plugin
-----------------
This has been very theoretical, but now that the import plugin is done, let's
test it. Make sure you created the sample file (with the contents described in
the introduction section) and save it as ``test.mtxt``. Then activate the plugin
in the Project Settings.
If everything goes well, the import plugin is added to the editor and the file
system is scanned, making the custom resource appear on the FileSystem dock. If
you select it and focus the Import dock, you can see the only option to select
there.
Create a MeshInstance node in the scene and for it's Mesh property set up a new
SphereMesh. Unfold the Material section in the Inspector and then drag the file
from the FileSystem dock to the material property. The object will update in the
viewport with the blue color of the imported material.
.. image:: img/import_plugin_trying.png
Go to Import dock, enable the "Use Red Anyway" option, and click on "Reimport".
This will update the imported material and should automatically update the view
showing the red color instead.
And that's it! Your first import plugin is done! Now get creative and make
plugins for your own beloved formats. This can be quite useful to write your
data in a custom format and then use it in Godot as if they were native
resources. This shows how the import system is powerful and extendable.

View File

@@ -1,9 +0,0 @@
Editor plugins
==============
.. toctree::
:maxdepth: 1
:name: toc-devel-plugins
making_plugins
import_plugins

View File

@@ -1,249 +0,0 @@
.. _doc_making_plugins:
Making Plugins
==============
About Plugins
~~~~~~~~~~~~~
A plugin is a great way to extend the editor with useful tools. It can be made
entirely with GDScript and standard scenes, without even reloading the editor.
Unlike modules, you don't need to create C++ code nor recompile the engine.
While this makes plugins not as powerful, there's still a lot of things you can
do with them. Note that a plugin is not different from any scene you already
can make, except that it is made via script to add functionality.
This tutorial will guide you through the creation of two simple plugins so
you can understand how they work and be able to develop your own. The first
will be a custom node that you can add to any scene in the project and the
other will be a custom dock added to the editor.
Creating a plugin
~~~~~~~~~~~~~~~~~
Before starting, create a new empty project wherever you want. This will serve
as base to develop and test the plugins.
The first thing you need to do is to create a new plugin that the editor can
understand as such. For that you need two files: ``plugin.cfg`` for the
configuration and a custom GDScript with the functionality.
Plugins have a standard path like ``addons/plugin_name`` inside the project
folder. So create the folder ``my_custom_node`` inside ``addons``. So you'll
have a directory structure like this:
.. image:: img/making_plugins-my_custom_mode_folder.png
To make the ``plugin.cfg`` file, open your favorite text editor with a blank
file. Godot is not able (yet) to open text files besides scripts, so this must
be done in an external editor. Add the following structure to your
``plugin.cfg``::
[plugin]
name="My Custom Node"
description="A custom node made to extend the Godot Engine."
author="Your Name Here"
version="1.0"
script="custom_node.gd"
This is a simple ``ini`` file with metadata about your plugin. You need to set
up the name and description so users can understand what it does. Add your
own name so you can be properly credited. A version number is useful so users can see if
they have an outdated version (if you are unsure on how to come up with
the version number, check `SemVer <http://semver.org/>`_). And finally a main
script file to load when your plugin is active.
The script file
^^^^^^^^^^^^^^^
Open the script editor (F3) and create a new GDScript file called
``custom_node.gd`` inside the ``my_custom_node`` folder. This script is special
and it has two requirements: it must be a ``tool`` script and it has to
inherit from :ref:`class_EditorPlugin`.
It's important to deal with initialization and clean-up of resources. So a good
practice is to use the virtual function
:ref:`_enter_tree() <class_Node__enter_tree>` to initialize your plugin and
:ref:`_exit_tree() <class_Node__exit_tree>` to clean it up. You can delete the
default GDScript template from your file and replace it with the following
structure:
.. _doc_making_plugins_template_code:
.. code-block:: python
tool
extends EditorPlugin
func _enter_tree():
# Initialization of the plugin goes here
pass
func _exit_tree():
# Clean-up of the plugin goes here
pass
This is a good template to use when devising new plugins.
A custom node
~~~~~~~~~~~~~~~~~~~~
Sometimes you want a certain behavior in many nodes. Maybe a custom scene
or control that can be reused. Instancing is helpful in a lot of cases but
sometimes it can be cumbersome, especially if you're using it between many
projects. A good solution to this is to make a plugin that adds a node with a
custom behavior.
To create a new node type, you can avail of the function
:ref:`add_custom_type() <class_EditorPlugin_add_custom_type>` from the
:ref:`class_EditorPlugin` class. This function can add new types to the editor,
be it nodes or resources. But before you can create the type you need a script
that will act as the logic for the type. While such script doesn't need to have
the ``tool`` keyword, it is interesting to use it so the user can see it acting
on the editor.
For this tutorial, we'll create a simple button that prints a message when
clicked. And for that we'll need a simple script that extends from
:ref:`class_Button`. It could also extend
:ref:`class_BaseButton` if you prefer::
tool
extends Button
func _enter_tree():
connect("pressed", self, "clicked")
func clicked():
print("You clicked me!")
That's it for our basic button. You can save this as ``button.gd`` inside the
plugin folder. You'll also need a 16x16 icon to show in the scene tree. If you
don't have one, you can grab the default one from the engine:
.. image:: img/making_plugins-custom_node_icon.png
Now we need to add it as a custom type so it shows on the Create New Node
dialog. For that, change the ``custom_node.gd`` script to the following::
tool
extends EditorPlugin
func _enter_tree():
# Initialization of the plugin goes here
# Add the new type with a name, a parent type, a script and an icon
add_custom_type("MyButton", "Button", preload("button.gd"), preload("icon.png"))
func _exit_tree():
# Clean-up of the plugin goes here
# Always remember to remove it from the engine when deactivated
remove_custom_type("MyButton")
With that done, the plugin should already be available in the plugin list at
Project Settings. So activate it and try to add a new node to see the result:
.. image:: img/making_plugins-custom_node_create.png
When you add the node, you can see that it already have the script you created
attached to it. Set a text to the button, save and run the scene. When you
click the button, you can see a text in the console:
.. image:: img/making_plugins-custom_node_console.png
A custom dock
^^^^^^^^^^^^^
Maybe you need to extend the editor and add tools that are always available.
An easy way to do it is to add a new dock with a plugin. Docks are just scenes
based on control, so how to create them is not far beyond your knowledge.
The way to start this plugin is similar to the custom node. So create a new
``plugin.cfg`` file in the ``addons/my_custom_dock`` folder. And then with
your favorite text editor add the following content to it::
[plugin]
name="My Custom Dock"
description="A custom dock made so I can learn how to make plugins."
author="Your Name Here"
version="1.0"
script="custom_dock.gd"
Then create the script ``custom_dock.gd`` in the same folder. Fill with the
:ref:`template we've seen before <doc_making_plugins_template_code>` to get a
good start.
Since we're trying to add a new custom dock, we need to create the contents of
such dock. This is nothing more than a standard Godot scene. So you can create
a new scene in the editor and start creating it.
For an editor dock, it is mandatory that the root of the scene is a
:ref:`Control <class_Control>` or one of its child classes. For this tutorial,
you can make a single button. The name of the root node will also be the name
that appears on the dock tab, so be sure to put a descriptive but short one.
Don't forget to add a text to your button.
.. image:: img/making_plugins-my_custom_dock_scene.png
Save this scene as ``my_dock.tscn``.
Now you need to grab that scene you just created and add it as a dock in the
editor. For this you can rely on the function
:ref:`add_control_to_dock() <class_EditorPlugin_add_control_to_dock>` from the
:ref:`EditorPlugin <class_EditorPlugin>` class.
The code is very straightforward, you just need to select a dock position to
add it and have a control to add (which is the scene you just created). It is
also very important that you remember to **remove the dock** when the plugin is
deactivated. The code can be like this::
tool
extends EditorPlugin
var dock # A class member to hold the dock during the plugin lifecycle
func _enter_tree():
# Initialization of the plugin goes here
# First load the dock scene and instance it:
dock = preload("res://addons/my_custom_dock/my_dock.tscn").instance()
# Add the loaded scene to the docks:
add_control_to_dock( DOCK_SLOT_LEFT_UL, dock)
# Note that LEFT_UL means the left of the editor, upper-left dock
func _exit_tree():
# Clean-up of the plugin goes here
# Remove the scene from the docks:
remove_control_from_docks( dock ) # Remove the dock
dock.free() # Erase the control from the memory
While the dock position is chosen when adding it, the user is free to move it
and save the layout with the dock in any position.
Checking the results
^^^^^^^^^^^^^^^^^^^^
Now it is the moment to check the results of your work. Open the *Project
Settings* and click on the *Plugins* tab. Your plugin should be the only on
the list. If it is not showing, click on the *Update* button at the top right
corner.
.. image:: img/making_plugins-project_settings.png
At the *Status* column, you can see that the plugin is inactive. So you just
need to click on the status to select *Active*. The dock should be immediately
visible, even before you close the settings window. And now, lo and behold, you
have a custom dock! In just a bit of coding and a simple scene.
.. image:: img/making_plugins-custom_dock.png
Going beyond
~~~~~~~~~~~~
Now that you learned how to make basic plugins, you can extend the editor in
many nice ways. Many functions can be added to editor on the fly with GDScript,
it is a powerful way to create special editors without having to delve into C++
modules.
You can make your own plugins to help you and also share them in Godot's Asset
Library so many people can benefit of your work.