mirror of
https://github.com/godotengine/godot-docs.git
synced 2026-01-03 05:48:42 +03:00
Merge pull request #2183 from YeldhamDev/best_practices_fixup
Numerous fixes to the new "Best practices" section
This commit is contained in:
@@ -15,7 +15,7 @@ The rest of this tutorial outlines the various ways of doing all this.
|
||||
Acquiring object references
|
||||
---------------------------
|
||||
|
||||
For all :ref:`Object <class_Object>`s, the most basic way of referencing them
|
||||
For all :ref:`Object <class_Object>`\s, the most basic way of referencing them
|
||||
is to get a reference to an existing object from another acquired instance.
|
||||
|
||||
.. tabs::
|
||||
@@ -47,20 +47,20 @@ access.
|
||||
const MyScene : = preload("my_scene.tscn") as PackedScene # Static load
|
||||
const MyScript : = preload("my_script.gd") as Script
|
||||
|
||||
# This type's value varies, i.e. it is a variable, so it uses snake_case
|
||||
# This type's value varies, i.e. it is a variable, so it uses snake_case.
|
||||
export(Script) var script_type: Script
|
||||
|
||||
# If need an "export const var" (which doesn't exist), use a conditional
|
||||
# If need an "export const var" (which doesn't exist), use a conditional
|
||||
# setter for a tool script that checks if it's executing in the editor.
|
||||
tool # must place at top of file
|
||||
tool # Must place at top of file.
|
||||
|
||||
# Must configure from the editor, defaults to null
|
||||
# Must configure from the editor, defaults to null.
|
||||
export(Script) var const_script setget set_const_script
|
||||
func set_const_script(value):
|
||||
if Engine.is_editor_hint():
|
||||
const_script = value
|
||||
|
||||
# Warn users if the value hasn't been set
|
||||
# Warn users if the value hasn't been set.
|
||||
func _get_configuration_warning():
|
||||
if not const_script:
|
||||
return "Must initialize property 'const_script'."
|
||||
@@ -68,7 +68,7 @@ access.
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Tool script added for the sake of the "const [Export]" example
|
||||
// Tool script added for the sake of the "const [Export]" example.
|
||||
[Tool]
|
||||
public MyType : extends Object
|
||||
{
|
||||
@@ -85,14 +85,14 @@ access.
|
||||
// But, value can be set during constructor, i.e. MyType().
|
||||
public Script Library { get; } = GD.Load("res://addons/plugin/library.gd") as Script;
|
||||
|
||||
// If need a "const [Export]" (which doesn't exist), use a
|
||||
// If need a "const [Export]" (which doesn't exist), use a
|
||||
// conditional setter for a tool script that checks if it's executing
|
||||
// in the editor.
|
||||
[Export]
|
||||
public PackedScene EnemyScn
|
||||
{
|
||||
get;
|
||||
|
||||
|
||||
set
|
||||
{
|
||||
if (Engine.IsEditorHint())
|
||||
@@ -102,7 +102,7 @@ access.
|
||||
}
|
||||
};
|
||||
|
||||
// Warn users if the value hasn't been set
|
||||
// Warn users if the value hasn't been set.
|
||||
public String _GetConfigurationWarning()
|
||||
{
|
||||
if (EnemyScn == null)
|
||||
@@ -120,7 +120,7 @@ Note the following:
|
||||
|
||||
3. Keep in mind that loading a resource fetches the cached resource
|
||||
instance maintained by the engine. To get a new object, one must
|
||||
:ref:`duplicate <class_Resource_method_duplicate>` an existing reference or
|
||||
:ref:`duplicate <class_Resource_method_duplicate>` an existing reference or
|
||||
instantiate one from scratch with ``new()``.
|
||||
|
||||
Nodes likewise have an alternative access point: the SceneTree.
|
||||
@@ -130,11 +130,11 @@ Nodes likewise have an alternative access point: the SceneTree.
|
||||
|
||||
extends Node
|
||||
|
||||
# Slow
|
||||
# Slow.
|
||||
func dynamic_lookup_with_dynamic_nodepath():
|
||||
print(get_node("Child"))
|
||||
|
||||
# Faster. GDScript only
|
||||
# Faster. GDScript only.
|
||||
func dynamic_lookup_with_cached_nodepath():
|
||||
print($Child)
|
||||
|
||||
@@ -147,27 +147,27 @@ Nodes likewise have an alternative access point: the SceneTree.
|
||||
onready var child = $Child
|
||||
func lookup_and_cache_for_future_access():
|
||||
print(child)
|
||||
|
||||
|
||||
# Delegate reference assignment to an external source
|
||||
# Con: need to perform a validation check
|
||||
# Pro: node makes no requirements of its external structure.
|
||||
# 'prop' can come from anywhere.
|
||||
var prop
|
||||
func call_me_after_prop_is_initialized_by_parent():
|
||||
# validate prop in one of three ways
|
||||
# Validate prop in one of three ways.
|
||||
|
||||
# fail with no notification
|
||||
# Fail with no notification.
|
||||
if not prop:
|
||||
return
|
||||
|
||||
# fail with an error message
|
||||
|
||||
# Fail with an error message.
|
||||
if not prop:
|
||||
printerr("'prop' wasn't initialized")
|
||||
return
|
||||
|
||||
# fail and terminate
|
||||
|
||||
# Fail and terminate.
|
||||
# Compiled scripts in final binary do not include assert statements
|
||||
assert prop
|
||||
assert prop.
|
||||
|
||||
# Use an autoload.
|
||||
# Dangerous for typical nodes, but useful for true singleton nodes
|
||||
@@ -212,14 +212,14 @@ Nodes likewise have an alternative access point: the SceneTree.
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Fail with an error message
|
||||
if (prop == null)
|
||||
{
|
||||
GD.PrintErr("'Prop' wasn't initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Fail and terminate
|
||||
Debug.Assert(Prop, "'Prop' wasn't initialized");
|
||||
}
|
||||
@@ -285,7 +285,7 @@ accesses:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
|
||||
# All Objects have duck-typed get, set, and call wrapper methods
|
||||
get_parent().set("visible", false)
|
||||
|
||||
@@ -302,13 +302,13 @@ accesses:
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// All Objects have duck-typed Get, Set, and Call wrapper methods
|
||||
// All Objects have duck-typed Get, Set, and Call wrapper methods.
|
||||
GetParent().Set("visible", false);
|
||||
|
||||
// C# is a static language, so it has no dynamic symbol access, e.g.
|
||||
// `GetParent().Visible = false` won't work
|
||||
// `GetParent().Visible = false` won't work.
|
||||
|
||||
- A method check. In the case of
|
||||
- A method check. In the case of
|
||||
:ref:`CanvasItem.visible <class_CanvasItem_property_visible>`, one can
|
||||
access the methods, ``set_visible`` and ``is_visible`` like any other method.
|
||||
|
||||
@@ -316,18 +316,18 @@ accesses:
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
var child = GetChild(0)
|
||||
|
||||
# Dynamic lookup
|
||||
|
||||
# Dynamic lookup.
|
||||
child.call("set_visible", false)
|
||||
|
||||
# Symbol-based dynamic lookup
|
||||
# Symbol-based dynamic lookup.
|
||||
# GDScript aliases this into a 'call' method behind the scenes.
|
||||
child.set_visible(false)
|
||||
|
||||
# Dynamic lookup, checks for method existence first
|
||||
# Dynamic lookup, checks for method existence first.
|
||||
if child.has("set_visible"):
|
||||
child.set_visible(false)
|
||||
|
||||
|
||||
# Cast check, followed by dynamic lookup
|
||||
# Useful when you make multiple "safe" calls knowing that the class
|
||||
# implements them all. No need for repeated checks.
|
||||
@@ -336,7 +336,7 @@ accesses:
|
||||
if child is CanvasItem:
|
||||
child.set_visible(false)
|
||||
child.show_on_top = true
|
||||
|
||||
|
||||
# If one does not wish to fail these checks without notifying users, one
|
||||
# can use an assert instead. These will trigger runtime errors
|
||||
# immediately if not true.
|
||||
@@ -363,7 +363,7 @@ accesses:
|
||||
print(quest.text)
|
||||
quest.complete() # or quest.fail()
|
||||
print(quest.text) # implied new text content
|
||||
|
||||
|
||||
# Note that these interfaces are project-specific conventions the team
|
||||
# defines (which means documentation! But maybe worth it?).
|
||||
# Any script that conforms to the documented "interface" of the name/group can fill in for it.
|
||||
@@ -373,7 +373,7 @@ accesses:
|
||||
Node child = GetChild(0);
|
||||
|
||||
// Dynamic lookup
|
||||
child.Call("SetVisible", false);
|
||||
child.Call("SetVisible", false);
|
||||
|
||||
// Dynamic lookup, checks for method existence first
|
||||
if (child.HasMethod("SetVisible"))
|
||||
@@ -420,8 +420,8 @@ accesses:
|
||||
// 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
|
||||
quest.Call("Complete"); // or "Fail".
|
||||
GD.Print(quest.Get("Text")); // Implied new text content.
|
||||
|
||||
// 2. Use a group
|
||||
foreach (Node AChild in GetChildren())
|
||||
@@ -429,11 +429,11 @@ accesses:
|
||||
if (AChild.IsInGroup("quest"))
|
||||
{
|
||||
GD.Print(quest.Get("Text"));
|
||||
quest.Call("Complete"); // or "Fail"
|
||||
GD.Print(quest.Get("Text")); // implied new text content
|
||||
quest.Call("Complete"); // or "Fail".
|
||||
GD.Print(quest.Get("Text")); // Implied new text content.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Note that these interfaces are project-specific conventions the team
|
||||
// defines (which means documentation! But maybe worth it?)..
|
||||
// Any script that conforms to the documented "interface" of the
|
||||
@@ -444,9 +444,9 @@ accesses:
|
||||
in cases where one needs the max level of freedom from dependencies. In
|
||||
this case, one relies on an external context to setup the method.
|
||||
|
||||
..tabs::
|
||||
..code-tab:: gdscript GDScript
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# child.gd
|
||||
extends Node
|
||||
var fn = null
|
||||
@@ -454,7 +454,7 @@ accesses:
|
||||
func my_method():
|
||||
if fn:
|
||||
fn.call_func()
|
||||
|
||||
|
||||
# parent.gd
|
||||
extends Node
|
||||
|
||||
@@ -467,8 +467,8 @@ accesses:
|
||||
func print_me():
|
||||
print(name)
|
||||
|
||||
..code-tab:: csharp
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Child.cs
|
||||
public class Child extends Node
|
||||
{
|
||||
@@ -485,7 +485,7 @@ accesses:
|
||||
public class Parent extends Node
|
||||
{
|
||||
public Node Child;
|
||||
|
||||
|
||||
public void _Ready()
|
||||
{
|
||||
Child = GetNode("Child");
|
||||
|
||||
@@ -38,7 +38,7 @@ than Node alone:
|
||||
- :ref:`Object::NOTIFICATION_PREDELETE <class_Object_constant_NOTIFICATION_PREDELETE>`:
|
||||
a callback that triggers before the engine deletes an Object, i.e. a
|
||||
'destructor'.
|
||||
|
||||
|
||||
- :ref:`MainLoop::NOTIFICATION_WM_MOUSE_ENTER <class_MainLoop_constant_NOTIFICATION_WM_MOUSE_ENTER>`:
|
||||
a callback that triggers when the mouse enters the window in the operating
|
||||
system that displays the game content.
|
||||
@@ -59,12 +59,12 @@ methods, but are still quite useful.
|
||||
*before* its appearance.
|
||||
|
||||
One can access all these custom notifications from the universal
|
||||
``_notification`` method.
|
||||
``_notification`` method.
|
||||
|
||||
..note::
|
||||
.. note::
|
||||
Methods in the documentation labeled as "virtual" are also intended to be
|
||||
overridden by scripts.
|
||||
|
||||
|
||||
A classic example is the
|
||||
:ref:`_init <class_Object_method__init>` method in Object. While it has no
|
||||
NOTIFICATION_* equivalent, the engine still calls the method. Most languages
|
||||
@@ -73,8 +73,8 @@ One can access all these custom notifications from the universal
|
||||
So, in which situation should one use each of these notifications or
|
||||
virtual functions?
|
||||
|
||||
_process vs. _physics_process vs. *_input
|
||||
-----------------------------------------
|
||||
_process vs. _physics_process vs. \*_input
|
||||
------------------------------------------
|
||||
|
||||
Use ``_process`` when one needs a framerate-dependent deltatime between
|
||||
frames. If code that updates object data needs to update as often as
|
||||
@@ -84,8 +84,8 @@ the evaluations to update. If they don't need to execute every frame, then
|
||||
implementing a Timer-yield-timeout loop is another option.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab::
|
||||
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Infinitely loop, but only execute whenever the Timer fires.
|
||||
# Allows for recurring operations that don't trigger script logic
|
||||
# every frame (or even every fixed frame).
|
||||
@@ -97,7 +97,7 @@ implementing a Timer-yield-timeout loop is another option.
|
||||
Use ``_physics_process`` when one needs a framerate-independent deltatime
|
||||
between frames. If code needs consistent updates over time, regardless
|
||||
of how fast or slow time advances, this is the right place.
|
||||
Recurring kinematic and object transform operations should execute here.
|
||||
Recurring kinematic and object transform operations should execute here.
|
||||
|
||||
While it is possible, to achieve the best performance, one should avoid
|
||||
making input checks during these callbacks. ``_process`` and
|
||||
@@ -112,12 +112,12 @@ If one wants to use delta time, one can fetch it from the
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Called every frame, even when the engine detects no input
|
||||
# Called every frame, even when the engine detects no input.
|
||||
func _process(delta):
|
||||
if Input.action_just_pressed("ui_select"):
|
||||
print(delta)
|
||||
|
||||
# Called during every input event
|
||||
# Called during every input event.
|
||||
func _unhandled_input(event):
|
||||
match event.get_class():
|
||||
"InputEventKey":
|
||||
@@ -128,7 +128,7 @@ If one wants to use delta time, one can fetch it from the
|
||||
|
||||
public class MyNode : public Node {
|
||||
|
||||
// Called every frame, even when the engine detects no input
|
||||
// Called every frame, even when the engine detects no input.
|
||||
public void _Process(float delta) {
|
||||
if (GD.Input.ActionJustPressed("UiSelect")) {
|
||||
GD.Print(string(delta));
|
||||
@@ -161,7 +161,7 @@ instantiation:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
|
||||
# "one" is an "initialized value". These DO NOT trigger the setter.
|
||||
# If someone set the value as "two" from the Inspector, this would be an
|
||||
# "exported value". These DO trigger the setter.
|
||||
@@ -173,7 +173,7 @@ instantiation:
|
||||
test = "three"
|
||||
# These DO trigger the setter. Note the `self` prefix.
|
||||
self.test = "three"
|
||||
|
||||
|
||||
func set_test(value):
|
||||
test = value
|
||||
print("Setting: ", test)
|
||||
@@ -213,7 +213,7 @@ following sequence:
|
||||
|
||||
As a result, instantiating a script versus a scene will affect both the
|
||||
initialization *and* the number of times the engine calls the setter.
|
||||
|
||||
|
||||
_ready vs. _enter_tree vs. NOTIFICATION_PARENTED
|
||||
------------------------------------------------
|
||||
|
||||
@@ -235,8 +235,8 @@ For example, here is a snippet that connects a node's method to
|
||||
a custom signal on the parent node without failing. Useful on data-centric
|
||||
nodes that one might create at runtime.
|
||||
|
||||
..tabs::
|
||||
..code-tab:: gdscript GDScript
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
extends Node
|
||||
|
||||
@@ -244,7 +244,7 @@ nodes that one might create at runtime.
|
||||
|
||||
func connection_check():
|
||||
return parent.has_user_signal("interacted_with")
|
||||
|
||||
|
||||
func _notification(what):
|
||||
match what:
|
||||
NOTIFICATION_PARENTED:
|
||||
@@ -254,11 +254,11 @@ nodes that one might create at runtime.
|
||||
NOTIFICATION_UNPARENTED:
|
||||
if connection_check():
|
||||
parent_cache.disconnect("interacted_with", self, "_on_parent_interacted_with")
|
||||
|
||||
|
||||
func _on_parent_interacted_with():
|
||||
print("I'm reacting to my parent's interaction!")
|
||||
|
||||
..code-tab:: csharp
|
||||
.. code-tab:: csharp
|
||||
|
||||
public class MyNode extends Node {
|
||||
|
||||
@@ -281,7 +281,7 @@ nodes that one might create at runtime.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void OnParentInteractedWith() {
|
||||
GD.Print("I'm reacting to my parent's interaction!");
|
||||
}
|
||||
|
||||
@@ -18,17 +18,17 @@ your project's features.
|
||||
difficult to create one's own custom data structures, even node structures,
|
||||
that are also lighter than the :ref:`Node <class_Node>` class.
|
||||
|
||||
- Example: see the :ref:`Tree <class_Tree>` node. It supports a high level
|
||||
- **Example:** See the :ref:`Tree <class_Tree>` node. It supports a high level
|
||||
of customization for a table of content with an arbitrary number of
|
||||
rows and columns. The data that it uses to generate its visualization
|
||||
though is actually a tree of :ref:`TreeItem <class_TreeItem>` Objects.
|
||||
|
||||
- Advantages: Simplifying one's API to smaller scoped objects helps improve
|
||||
- **Advantages:** Simplifying one's API to smaller scoped objects helps improve
|
||||
its accessibility improve iteration time. Rather than working with the
|
||||
entire Node library, one creates an abbreviated set of Objects from which
|
||||
a node can generate and manage the appropriate sub-nodes.
|
||||
|
||||
- Note: One should be careful when handling them. One can store an Object
|
||||
.. note:: One should be careful when handling them. One can store an Object
|
||||
into a variable, but these references can become invalid without warning.
|
||||
For example, if the object's creator decides to delete it out of nowhere,
|
||||
this would trigger an error state when one next accesses it.
|
||||
@@ -38,21 +38,21 @@ your project's features.
|
||||
further references to themselves exist. These are useful in the majority of
|
||||
cases where one needs data in a custom class.
|
||||
|
||||
- Example: see the :ref:`File <class_File>` object. It functions
|
||||
- **Example:** See the :ref:`File <class_File>` object. It functions
|
||||
just like a regular Object except that one need not delete it themselves.
|
||||
|
||||
- Advantages: same as the Object.
|
||||
- **Advantages:** same as the Object.
|
||||
|
||||
3. :ref:`Resource <class_Resource>`: Only slightly more complex than Reference.
|
||||
They have the innate ability to serialize/deserialize (i.e. save and load)
|
||||
their object properties to/from Godot resource files.
|
||||
|
||||
- Example: Scripts, PackedScene (for scene files), and other types like
|
||||
- **Example:** Scripts, PackedScene (for scene files), and other types like
|
||||
each of the :ref:`AudioEffect <class_AudioEffect>` classes. Each of these
|
||||
can be save and loaded, therefore they extend from Resource.
|
||||
|
||||
- Advantages: Much has
|
||||
:ref:`already been said <http://docs.godotengine.org/en/latest/getting_started/step_by_step/resources.html#creating-your-own-resources>`
|
||||
- **Advantages:** Much has
|
||||
:ref:`already been said <doc_resources>`
|
||||
on :ref:`Resource <class_Resource>`'s advantages over traditional data
|
||||
storage methods. In the context of using Resources over Nodes though,
|
||||
their main advantage is in Inspector-compatibility. While nearly as
|
||||
|
||||
@@ -41,7 +41,7 @@ That is, one should create scenes that keep everything they need within
|
||||
themselves.
|
||||
|
||||
If a scene must interact with an external context, experienced developers
|
||||
recommend the use of
|
||||
recommend the use of
|
||||
`Dependency Injection <https://en.wikipedia.org/wiki/Dependency_injection>`_.
|
||||
This technique involves having a high-level API provide the dependencies of the
|
||||
low-level API. Why do this? Because classes which rely on their external
|
||||
@@ -54,99 +54,99 @@ initialize it:
|
||||
behavior, not start it. Note that signal names are usually past-tense verbs
|
||||
like "entered", "skill_activated", or "item_collected".
|
||||
|
||||
..tabs::
|
||||
..code-tab:: gdscript GDScript
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Parent
|
||||
$Child.connect("signal_name", object_with_method, "method_on_the_object")
|
||||
|
||||
# Child
|
||||
emit_signal("signal_name") # Triggers parent-defined behavior
|
||||
emit_signal("signal_name") # Triggers parent-defined behavior.
|
||||
|
||||
..code-tab:: csharp
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Parent
|
||||
GetNode("Child").Connect("SignalName", ObjectWithMethod, "MethodOnTheObject");
|
||||
|
||||
// Child
|
||||
EmitSignal("SignalName"); // Triggers parent-defined behavior
|
||||
EmitSignal("SignalName"); // Triggers parent-defined behavior.
|
||||
|
||||
2. Call a method. Used to start behavior.
|
||||
|
||||
..tabs::
|
||||
..code-tab:: gdscript GDScript
|
||||
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Parent
|
||||
$Child.method_name = "do"
|
||||
|
||||
# Child, assuming it has String property 'method_name' and method 'do'
|
||||
call(method_name) # Call parent-defined method (which child must own)
|
||||
# Child, assuming it has String property 'method_name' and method 'do'.
|
||||
call(method_name) # Call parent-defined method (which child must own).
|
||||
|
||||
..code-tab:: csharp
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Parent
|
||||
GetNode("Child").Set("MethodName", "Do");
|
||||
|
||||
// Child
|
||||
Call(MethodName); // Call parent-defined method (which child must own)
|
||||
Call(MethodName); // Call parent-defined method (which child must own).
|
||||
|
||||
3. Initialize a :ref:`FuncRef <class_FuncRef>` property. Safer than a method
|
||||
as ownership of the method is unnecessary. Used to start behavior.
|
||||
|
||||
..tabs::
|
||||
..code-tab:: gdscript GDScript
|
||||
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Parent
|
||||
$Child.func_property = funcref(object_with_method, "method_on_the_object")
|
||||
|
||||
# Child
|
||||
func_property.call_func() # Call parent-defined method (can come from anywhere)
|
||||
func_property.call_func() # Call parent-defined method (can come from anywhere).
|
||||
|
||||
..code-tab:: csharp
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Parent
|
||||
GetNode("Child").Set("FuncProperty", GD.FuncRef(ObjectWithMethod, "MethodOnTheObject"));
|
||||
|
||||
// Child
|
||||
FuncProperty.CallFunc(); // Call parent-defined method (can come from anywhere)
|
||||
FuncProperty.CallFunc(); // Call parent-defined method (can come from anywhere).
|
||||
|
||||
4. Initialize a Node or other Object reference.
|
||||
|
||||
..tabs::
|
||||
..code-tab:: gdscript GDScript
|
||||
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Parent
|
||||
$Child.target = self
|
||||
|
||||
# Child
|
||||
print(target) # Use parent-defined node
|
||||
print(target) # Use parent-defined node.
|
||||
|
||||
..code-tab:: csharp
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Parent
|
||||
GetNode("Child").Set("Target", this);
|
||||
|
||||
// Child
|
||||
GD.Print(Target); // Use parent-defined node
|
||||
GD.Print(Target); // Use parent-defined node.
|
||||
|
||||
5. Initialize a NodePath.
|
||||
|
||||
..tabs::
|
||||
..code-tab:: gdscript GDScript
|
||||
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Parent
|
||||
$Child.target_path = ".."
|
||||
|
||||
# Child
|
||||
get_node(target_path) # Use parent-defined NodePath
|
||||
get_node(target_path) # Use parent-defined NodePath.
|
||||
|
||||
..code-tab:: csharp
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Parent
|
||||
GetNode("Child").Set("TargetPath", NodePath(".."));
|
||||
|
||||
// Child
|
||||
GetNode(TargetPath); // Use parent-defined NodePath
|
||||
GetNode(TargetPath); // Use parent-defined NodePath.
|
||||
|
||||
These options hide the source of accesses from the child node. This in turn
|
||||
keeps the child **loosely coupled** to its environment. One can re-use it
|
||||
@@ -159,23 +159,23 @@ in another context without any extra changes to its API.
|
||||
are siblings should only be aware of their hierarchies while an ancestor
|
||||
mediates their communications and references.
|
||||
|
||||
..tabs::
|
||||
..code-tab:: gdscript GDScript
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Parent
|
||||
$Left.target = $Right.get_node("Receiver")
|
||||
|
||||
# Left
|
||||
var target: Node
|
||||
func execute():
|
||||
# Do something with 'target'
|
||||
# Do something with 'target'.
|
||||
|
||||
# Right
|
||||
func _init():
|
||||
var receiver = Receiver.new()
|
||||
add_child(receiver)
|
||||
|
||||
..code-tab:: csharp
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Parent
|
||||
GetNode("Left").Target = GetNode("Right/Receiver");
|
||||
@@ -185,7 +185,7 @@ in another context without any extra changes to its API.
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
// Do something with 'Target'
|
||||
// Do something with 'Target'.
|
||||
}
|
||||
|
||||
// Right
|
||||
@@ -196,7 +196,7 @@ in another context without any extra changes to its API.
|
||||
Receiver = ResourceLoader.load("Receiver.cs").new();
|
||||
AddChild(Receiver);
|
||||
}
|
||||
|
||||
|
||||
The same principles also apply to non-Node objects that maintain dependencies
|
||||
on other objects. Whichever object actually owns the objects should manage
|
||||
the relationships between them.
|
||||
@@ -211,7 +211,7 @@ in another context without any extra changes to its API.
|
||||
documentation to keep track of object relations on a microscopic scale; this
|
||||
is otherwise known as development hell. Writing code that relies on external
|
||||
documentation for one to use it safely is error-prone by default.
|
||||
|
||||
|
||||
To avoid creating and maintaining such documentation, one converts the
|
||||
dependent node ("child" above) into a tool script that implements
|
||||
:ref:`_get_configuration_warning() <class_Node_method__get_configuration_warning>`.
|
||||
@@ -222,7 +222,7 @@ in another context without any extra changes to its API.
|
||||
:ref:`CollisionShape2D <class_CollisionShape2D>` nodes defined. The editor
|
||||
then self-documents the scene through the script code. No content duplication
|
||||
via documentation is necessary.
|
||||
|
||||
|
||||
A GUI like this can better inform project users of critical information about
|
||||
a Node. Does it have external dependencies? Have those dependencies been
|
||||
satisfied? Other programmers, and especially designers and writers, will need
|
||||
@@ -290,13 +290,13 @@ If one has a system that...
|
||||
:ref:`autoload 'singleton' node <doc_singletons_autoload>`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
For smaller games, a simpler alternative with less control would be to have
|
||||
a "Game" singleton that simply calls the
|
||||
:ref:`SceneTree.change_scene() <class_SceneTree_method_change_scene>` method
|
||||
to swap out the main scene's content. This structure more or less keeps
|
||||
the "World" as the main game node.
|
||||
|
||||
|
||||
Any GUI would need to also be a
|
||||
singleton, be transitory parts of the "World", or be manually added as a
|
||||
direct child of the root. Otherwise, the GUI nodes would also delete
|
||||
@@ -318,7 +318,7 @@ own place in the hierachy as a sibling or some other relation.
|
||||
|
||||
In some cases, one needs these separated nodes to *also* position themselves
|
||||
relative to each other. One can use the
|
||||
:ref:`RemoteTransform <class_RemoteTransform>` /
|
||||
:ref:`RemoteTransform <class_RemoteTransform>` /
|
||||
:ref:`RemoteTransform2D <class_RemoteTransform2D>` nodes for this purpose.
|
||||
They will allow a target node to conditionally inherit selected transform
|
||||
elements from the Remote\* node. To assign the ``target``
|
||||
@@ -335,15 +335,19 @@ own place in the hierachy as a sibling or some other relation.
|
||||
- Add a "player" node to a "room".
|
||||
- Need to change rooms, so one must delete the current room.
|
||||
- Before the room can be deleted, one must preserve and/or move the player.
|
||||
|
||||
Is memory a concern?
|
||||
|
||||
- If not, one can just create the two rooms, move the player
|
||||
and delete the old one. No problem.
|
||||
- If so, one will need to...
|
||||
- Move the player somewhere else in the tree.
|
||||
- Delete the room.
|
||||
- Instantiate and add the new room.
|
||||
- Re-add the player.
|
||||
|
||||
|
||||
If so, one will need to...
|
||||
|
||||
- Move the player somewhere else in the tree.
|
||||
- Delete the room.
|
||||
- Instantiate and add the new room.
|
||||
- Re-add the player.
|
||||
|
||||
The issue is that the player here is a "special case", one where the
|
||||
developers must *know* that they need to handle the player this way for the
|
||||
project. As such, the only way to reliably share this information as a team
|
||||
@@ -353,15 +357,15 @@ own place in the hierachy as a sibling or some other relation.
|
||||
|
||||
In a more complex game with larger assets, it can be a better idea to simply
|
||||
keep the player somewhere else in the SceneTree entirely. This involves...
|
||||
|
||||
|
||||
1. More consistency.
|
||||
2. No "special cases" that must be documented and maintained somewhere.
|
||||
3. No opportunity for errors to occur because these details are not accounted
|
||||
for.
|
||||
|
||||
|
||||
In contrast, if one ever needs to have a child node that does *not* inherit
|
||||
the transform of their parent, one has the following options:
|
||||
|
||||
|
||||
1. The **declarative** solution: place a :ref:`Node <class_Node>` in between
|
||||
them. As nodes with no transform, Nodes will not pass along such
|
||||
information to their children.
|
||||
|
||||
Reference in New Issue
Block a user