Merge pull request #429 from d9n/docpolish+scripting

Polish text in the "Scripting" section
This commit is contained in:
Rémi Verschelde 2017-08-18 21:59:34 +02:00 committed by GitHub
commit 1032ab3a5d
1 changed files with 108 additions and 102 deletions

View File

@ -7,75 +7,84 @@ Introduction
------------
Much has been said about tools that allow users to create video games
without programming. It's been a dream for many independent developers
to create games without learning how to code. This need has been around
for a long time, even inside companies, where game designers wish to
have more control of the game flow.
without programming. This appeals to many people who would love to be able to
make a game without having to learn how to code. This need has been around for
a long time, even inside game companies, where game designers would like to
have more control of their game's flow.
Many products have been shipped promising a no-programming environment,
but the result is often incomplete, too complex or inefficient compared
to traditional code. As a result, programming is here to stay for a long
time. In fact, the general direction in game engines has been to add
tools that try to reduce the amount of code that needs to be written for
specific tasks, to speed up development.
Many products have been shipped promising such a no-programming environment,
but the results often fall short of expectations. The projects they produce end
up being too complex or require solutions that are too inefficient compared to
what could have been accomplished with traditional code.
In that sense, Godot has taken some useful design decisions towards that
goal. The first, and most important, is the scene system. The aim of it is
not obvious at first, but is understandable later on. That is, it relieves
programmers from the responsibility of designing and structuring code.
As a result, we think that programming is here to stay. In fact, game engines
have been moving in this direction, adding tools that try to reduce the amount
of code that needs to be written rather than eliminating it. The engine can
provide many general solutions, while the developer can use code to accomplish
specific tasks.
When designing games using the scene system, the whole project is
fragmented into *complementary* scenes (not individual ones). Scenes
complement (i.e. help) each other, instead of being separate. There will be
plenty of examples of this later on, but it's very important to remember it.
Godot has embraced this goal from the beginning, and it has influenced many of
its design decisions. First and foremost is the scene system. This system has
many benefits, but fundamentally its goal is to relieve programmers from the
responsibility of having to implement an overall architecture.
For those with a good amount of programming expertise, this means a
different design pattern to MVC. Godot promises efficiency at the
expense of dropping the MVC habits, which are replaced by the *scenes as
a complement* pattern.
Godot also uses the `extend <http://c2.com/cgi/wiki?EmbedVsExtend>`__
pattern for scripting, meaning that scripts extend from all the
available engine classes.
When designing games using the scene system, the whole project is fragmented
into *complementary* scenes (not individual ones). Scenes complement
(i.e. help) each other, instead of being separate. There will be plenty of
examples of this later on, but it's very important to remember it.
GDScript
--------
:ref:`doc_gdscript` is a dynamically-typed scripting language designed to
fit inside Godot. It was designed with the following goals:
:ref:`doc_gdscript` is a dynamically typed scripting language which was
designed with the following goals:
- First and most importantly, making it simple, familiar and as easy to
learn as possible.
- Making the code readable and error safe. The syntax is mostly
borrowed from Python.
- Most importantly, it should feel simple, familiar, and as easy to learn as
possible.
- It should have a syntax that's very readable. The syntax is mostly borrowed
from Python.
- It should integrate tightly with Godot itself, for example sharing its memory
model, taking advantage of the scene/node system, and exposing useful
game-related classes already part of the Godot engine.
Programmers generally take a few days to learn it, and within two weeks
feel comfortable with it.
Programmers generally take a few days to learn GDScript and feel comfortable
with it within two weeks.
As with most dynamically typed languages, the higher productivity
(code is easier to learn, faster to write, no compilation, etc) is
balanced with a performance penalty. But most critical code is written
in C++ already in the engine (vector ops, physics, math, indexing, etc),
resulting in more than sufficient performance for most types of
games.
As with most dynamically typed languages, the higher productivity (code is
easier to learn, faster to write, no compilation, etc.) is balanced with a
performance penalty. However, keep in mind that most critical code is already
written in C++ in the engine (vector ops, physics, math, indexing, etc.), which
results in more than sufficient performance for most types of games.
In any case, if more performance is required, critical sections can be
rewritten in C++ and exposed transparently to the script. This allows
the replacement of a GDScript class with a C++ class without altering
the rest of the game.
rewritten in C++ and registered with Godot, which in turn exposes them to all
scripts. In this way, you can write a class in GDScript first but convert it to
a C++ class later, and the rest of the game will work the same as before.
Finally, note that GDScript provides the powerful
`extend <http://c2.com/cgi/wiki?EmbedVsExtend>`__ keyword. Many classes in the
Godot engine are available as base classes to be extended from.
Scripting a scene
-----------------
In the rest of this tutorial, we'll set up a simple GUI scene consisting of a
button and a label, where pressing the button will update the label. This will
demonstrate:
- how to write a basic script and attach it to a node
- how to hook up UI elements via *signals*
- how to write a script that can access other nodes in the scene
Before continuing, please make sure to read the :ref:`doc_gdscript` reference.
It's a simple language and the reference is short, it will not take
more than a few minutes to get an overview of the concepts.
It's a simple language and the reference is short, so it will not take more
than a few minutes to get an overview of the concepts.
Scene setup
~~~~~~~~~~~
This tutorial will begin by scripting a simple GUI scene. Use the add
node dialog to create the following hierarchy, with the following nodes:
Use the add node dialog to create the following hierarchy, with the following
nodes:
- Panel
@ -91,129 +100,126 @@ look like the image below. You can set the text in the Inspector pane.
.. image:: /img/label_button_example.png
Finally, save the scene, a fitting name could be "sayhello.tscn"
Finally, save the scene, with a name such as "sayhello.tscn"
.. _doc_scripting-adding_a_script:
Adding a script
~~~~~~~~~~~~~~~
Right click on the panel node, then select "Add Script" in the context
Right click on the panel node, and then select "Add Script" in the context
menu:
.. image:: /img/add_script.png
The script creation dialog will pop up. This dialog allows to select
the language, class name, etc. GDScript does not use class names in
script files, so that field is not editable. The script should inherit
from "Panel" (as it is meant to extend the node, which is of Panel type,
this is automatically filled).
The script creation dialog will pop up. This dialog allows you to set the
language, class name, and other relevant options.
Enter a path name for the script and then select "Create":
Actually, in GDScript, the file itself represents the class, so in this case,
the class name field is not editable.
The node we're attaching the script to is a panel, so the "Inherits" field
should automatically be filled in with "Panel". This is what we want as our
script's goal is to extend this panel node's functionality.
Finally, enter a path name for the script and select "Create":
.. image:: /img/script_create.png
Once this is done, the script will be created and added to the node. You
can see this both as an extra icon in the node, as well as in the script
property:
Once this is done, the script will be created and added to the node. You can
see this both as an extra icon in the node as well as in the script property:
.. image:: /img/script_added.png
To edit the script, select either of the highlighted buttons.
This will bring you to the script editor where an existing template will
be included by default:
To edit the script, select either of the highlighted buttons. This will bring
you to the script editor where an existing template will be included by default:
.. image:: /img/script_template.png
There is not much in there. The "_ready()" function is called when the
node (and all its children) entered the active scene. (Remember, it's
not a constructor, the constructor is "_init()").
node (and all its children) enter the active scene. (Note: "_ready()" is not
the a constructor; the constructor is "_init()").
The role of the script
~~~~~~~~~~~~~~~~~~~~~~
A script adds behavior to a node. It is used to control the
node functions as well as other nodes (children, parent, siblings, etc).
The local scope of the script is the node (just like in regular
inheritance) and the virtual functions of the node are captured by the
script.
A script adds behavior to a node. It is used to control how the node functions
as well as how it interacts with other nodes (children, parent, siblings,
etc.). The local scope of the script is the node. In other words, the script
inherits the functions provided by that node.
.. image:: /img/brainslug.jpg
Handling a signal
~~~~~~~~~~~~~~~~~
Signals are used mostly in GUI nodes (although other nodes have them
too). Signals are "emitted" when some specific kind of action happens,
and can be connected to any function of any script instance. In this
step, the "pressed" signal from the button will be connected to a custom
function.
Signals are "emitted" when some specific kind of action happens, and they can be
connected to any function of any script instance. Signals are used mostly in
GUI nodes (although other nodes have them too, and you can even define custom
signals in your own scripts).
An interface for connecting signals to your scripts exists in the editor.
You can access this by selecting the node in the scene tree and then
selecting the "Node" tab. Make sure that you have "Signals" selected.
In this step, we'll connect the "pressed" signal to a custom function.
The editor provides an interface for connecting signals to your scripts. You
can access this by selecting the node in the scene tree and then selecting the
"Node" tab. Next, make sure that you have "Signals" selected.
.. image:: /img/signals.png
In any case, at this point it is clear that we are interested in
the "pressed" signal. Instead of using the visual interface, we will opt
to code the connection.
At this point, you could use the visual interface to hook up the "pressed"
signal by double clicking on it and selecting a target node that already has a
script attached to it. But for the sake of learning, we're going to code up the
connection manually.
For this, a function exists that is probably the one most used by Godot
programmers, namely :ref:`Node.get_node() <class_Node_get_node>`.
This function uses paths to fetch nodes in the current tree or anywhere
in the scene, relative to the node holding the script.
To accomplish this, we will introduce a function that is probably the most used
by Godot programmers, namely :ref:`Node.get_node() <class_Node_get_node>`.
This function uses paths to fetch nodes anywhere in the scene, relative to the
node that owns the script.
To fetch the button, the following must be used:
In our case, because the button and the label are siblings under the panel
where the script is attached, you can fetch the button as follows:
::
get_node("Button")
Next, a callback will be added that will change the label's text when
the button is pressed:
Next, write a function which will be called when the button is pressed:
::
func _on_button_pressed():
get_node("Label").set_text("HELLO!")
Finally, the button "pressed" signal will be connected to that callback
in _ready(), by using :ref:`Object.connect() <class_Object_connect>`.
Finally, connect the button's "pressed" signal to that callback in _ready(), by
using :ref:`Object.connect() <class_Object_connect>`.
::
func _ready():
get_node("Button").connect("pressed",self,"_on_button_pressed")
The final script should look like this:
The final script should look basically like this:
::
extends Panel
# member variables here, example:
# var a=2
# var b="textvar"
func _on_button_pressed():
get_node("Label").set_text("HELLO!")
func _ready():
get_node("Button").connect("pressed",self,"_on_button_pressed")
Running the scene should have the expected result when pressing the
button:
Run the scene and press the button. You should get the following result:
.. image:: /img/scripting_hello.png
**Note:** As it is a common misunderstanding in this tutorial, let's clarify
again that get_node(path) works by returning the *immediate* children of
the node controlled by the script (in this case, *Panel*), so *Button*
must be a child of *Panel* for the above code to work. To give this
clarification more context, if *Button* were a child of *Label*, the code
to obtain it would be:
Why hello there! Congratulations on scripting your first scene.
**Note:** A common misunderstanding in this tutorial is how get_node(path)
works. For some given node, get_node(path) searches its immediate children.
In the above code, this means that *Button* must be a child of *Panel*. If
*Button* were instead a child of *Label*, the code to obtain it would be:
::