mirror of
https://github.com/godotengine/godot-docs.git
synced 2026-01-04 14:11:02 +03:00
Add C++ snippets to tutorials/best_practices
This commit is contained in:
@@ -102,6 +102,29 @@ implementing a Timer-timeout loop is another option.
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: cpp C++
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class MyNode : public Node {
|
||||
GDCLASS(MyNode, Node)
|
||||
|
||||
public:
|
||||
// Allows for recurring operations that don't trigger script logic
|
||||
// every frame (or even every fixed frame).
|
||||
virtual void _ready() override {
|
||||
Timer *timer = memnew(Timer);
|
||||
timer->set_autostart(true);
|
||||
timer->set_wait_time(0.5);
|
||||
add_child(timer);
|
||||
timer->connect("timeout", callable_mp(this, &MyNode::run));
|
||||
}
|
||||
|
||||
void run() {
|
||||
UtilityFunctions::print("This block runs every 0.5 seconds.");
|
||||
}
|
||||
};
|
||||
|
||||
Use ``_physics_process()`` when one needs a framerate-independent delta time
|
||||
between frames. If code needs consistent updates over time, regardless
|
||||
of how fast or slow time advances, this is the right place.
|
||||
@@ -160,6 +183,30 @@ delta time methods as needed.
|
||||
|
||||
}
|
||||
|
||||
.. code-tab:: cpp C++
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class MyNode : public Node {
|
||||
GDCLASS(MyNode, Node)
|
||||
|
||||
public:
|
||||
// Called every frame, even when the engine detects no input.
|
||||
virtual void _process(double p_delta) override {
|
||||
if (Input::get_singleton->is_action_just_pressed("ui_select")) {
|
||||
UtilityFunctions::print(p_delta);
|
||||
}
|
||||
}
|
||||
|
||||
// Called during every input event. Equally true for _input().
|
||||
virtual void _unhandled_input(const Ref<InputEvent> &p_event) override {
|
||||
Ref<InputEventKey> key_event = event;
|
||||
if (key_event.is_valid() && Input::get_singleton->is_action_just_pressed("ui_accept")) {
|
||||
UtilityFunctions::print(get_process_delta_time());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_init vs. initialization vs. export
|
||||
-----------------------------------
|
||||
|
||||
@@ -223,6 +270,35 @@ values will set up according to the following sequence:
|
||||
// the setter, changing _test's value from "two!" to "three!".
|
||||
}
|
||||
|
||||
.. code-tab:: cpp C++
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class MyNode : public Node {
|
||||
GDCLASS(MyNode, Node)
|
||||
|
||||
String test = "one";
|
||||
|
||||
protected:
|
||||
static void _bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_test"), &MyNode::get_test);
|
||||
ClassDB::bind_method(D_METHOD("set_test", "test"), &MyNode::set_test);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "test"), "set_test", "get_test");
|
||||
}
|
||||
|
||||
public:
|
||||
String get_test() { return test; }
|
||||
void set_test(String p_test) { return test = p_test; }
|
||||
|
||||
MyNode() {
|
||||
// Triggers the setter, changing _test's value from "one" to "two!".
|
||||
set_test("two");
|
||||
}
|
||||
|
||||
// If someone sets test to "three" in the Inspector, it would trigger
|
||||
// the setter, changing test's value from "two!" to "three!".
|
||||
};
|
||||
|
||||
As a result, instantiating a script versus a scene may affect both the
|
||||
initialization *and* the number of times the engine calls the setter.
|
||||
|
||||
@@ -309,3 +385,38 @@ nodes that one might create at runtime.
|
||||
GD.Print("I'm reacting to my parent's interaction!");
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: cpp C++
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class MyNode : public Node {
|
||||
GDCLASS(MyNode, Node)
|
||||
|
||||
Node *parent_cache = nullptr;
|
||||
|
||||
void on_parent_interacted_with() {
|
||||
UtilityFunctions::print("I'm reacting to my parent's interaction!");
|
||||
}
|
||||
|
||||
public:
|
||||
void connection_check() {
|
||||
return parent_cache->has_user_signal("interacted_with");
|
||||
}
|
||||
|
||||
void _notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_PARENTED:
|
||||
parent_cache = get_parent();
|
||||
if (connection_check()) {
|
||||
parent_cache->connect("interacted_with", callable_mp(this, &MyNode::on_parent_interacted_with));
|
||||
}
|
||||
break;
|
||||
case NOTIFICATION_UNPARENTED:
|
||||
if (connection_check()) {
|
||||
parent_cache->disconnect("interacted_with", callable_mp(this, &MyNode::on_parent_interacted_with));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -100,6 +100,23 @@ either? Let's see an example:
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: cpp C++
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class MyBuildings : public Node {
|
||||
GDCLASS(MyBuildings, Node)
|
||||
|
||||
public:
|
||||
const Ref<PackedScene> building = ResourceLoader::get_singleton()->load("res://building.tscn");
|
||||
Ref<PackedScene> a_building;
|
||||
|
||||
virtual void _ready() override {
|
||||
// Can assign the value during initialization.
|
||||
a_building = ResourceLoader::get_singleton()->load("res://office.tscn");
|
||||
}
|
||||
};
|
||||
|
||||
Preloading allows the script to handle all the loading the moment one loads the
|
||||
script. Preloading is useful, but there are also times when one doesn't wish
|
||||
for it. To distinguish these situations, there are a few things one can
|
||||
|
||||
@@ -69,6 +69,19 @@ initialize it:
|
||||
// Child
|
||||
EmitSignal("SignalName"); // Triggers parent-defined behavior.
|
||||
|
||||
.. code-tab:: cpp C++
|
||||
|
||||
// Parent
|
||||
Node *node = get_node<Node>("Child");
|
||||
if (node != nullptr) {
|
||||
// Note that get_node may return a nullptr, which would make calling the connect method crash the engine if "Child" does not exist!
|
||||
// So unless you are 1000% sure get_node will never return a nullptr, it's a good idea to always do a nullptr check.
|
||||
node->connect("signal_name", callable_mp(this, &ObjectWithMethod::method_on_the_object));
|
||||
}
|
||||
|
||||
// Child
|
||||
emit_signal("signal_name"); // Triggers parent-defined behavior.
|
||||
|
||||
2. Call a method. Used to start behavior.
|
||||
|
||||
.. tabs::
|
||||
@@ -88,6 +101,17 @@ initialize it:
|
||||
// Child
|
||||
Call(MethodName); // Call parent-defined method (which child must own).
|
||||
|
||||
.. code-tab:: cpp C++
|
||||
|
||||
// Parent
|
||||
Node *node = get_node<Node>("Child");
|
||||
if (node != nullptr) {
|
||||
node->set("method_name", "do");
|
||||
}
|
||||
|
||||
// Child
|
||||
call(method_name); // Call parent-defined method (which child must own).
|
||||
|
||||
3. Initialize a :ref:`Callable <class_Callable>` property. Safer than a method
|
||||
as ownership of the method is unnecessary. Used to start behavior.
|
||||
|
||||
@@ -108,6 +132,17 @@ initialize it:
|
||||
// Child
|
||||
FuncProperty.Call(); // Call parent-defined method (can come from anywhere).
|
||||
|
||||
.. code-tab:: cpp C++
|
||||
|
||||
// Parent
|
||||
Node *node = get_node<Node>("Child");
|
||||
if (node != nullptr) {
|
||||
node->set("func_property", Callable(&ObjectWithMethod::method_on_the_object));
|
||||
}
|
||||
|
||||
// Child
|
||||
func_property.call(); // Call parent-defined method (can come from anywhere).
|
||||
|
||||
4. Initialize a Node or other Object reference.
|
||||
|
||||
.. tabs::
|
||||
@@ -127,6 +162,17 @@ initialize it:
|
||||
// Child
|
||||
GD.Print(Target); // Use parent-defined node.
|
||||
|
||||
.. code-tab:: cpp C++
|
||||
|
||||
// Parent
|
||||
Node *node = get_node<Node>("Child");
|
||||
if (node != nullptr) {
|
||||
node->set("target", this);
|
||||
}
|
||||
|
||||
// Child
|
||||
UtilityFunctions::print(target);
|
||||
|
||||
5. Initialize a NodePath.
|
||||
|
||||
.. tabs::
|
||||
@@ -146,6 +192,17 @@ initialize it:
|
||||
// Child
|
||||
GetNode(TargetPath); // Use parent-defined NodePath.
|
||||
|
||||
.. code-tab:: cpp C++
|
||||
|
||||
// Parent
|
||||
Node *node = get_node<Node>("Child");
|
||||
if (node != nullptr) {
|
||||
node->set("target_path", NodePath(".."));
|
||||
}
|
||||
|
||||
// Child
|
||||
get_node<Node>(target_path); // Use parent-defined NodePath.
|
||||
|
||||
These options hide the points of access from the child node. This in turn
|
||||
keeps the child **loosely coupled** to its environment. You can reuse it
|
||||
in another context without any extra changes to its API.
|
||||
@@ -199,6 +256,42 @@ in another context without any extra changes to its API.
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: cpp C++
|
||||
|
||||
// Parent
|
||||
get_node<Left>("Left")->target = get_node<Node>("Right/Receiver");
|
||||
|
||||
class Left : public Node {
|
||||
GDCLASS(Left, Node)
|
||||
|
||||
protected:
|
||||
static void _bind_methods() {}
|
||||
|
||||
public:
|
||||
Node *target = nullptr;
|
||||
|
||||
Left() {}
|
||||
|
||||
void execute() {
|
||||
// Do something with 'target'.
|
||||
}
|
||||
};
|
||||
|
||||
class Right : public Node {
|
||||
GDCLASS(Right, Node)
|
||||
|
||||
protected:
|
||||
static void _bind_methods() {}
|
||||
|
||||
public:
|
||||
Node *receiver = nullptr;
|
||||
|
||||
Right() {
|
||||
receiver = memnew(Node);
|
||||
add_child(receiver);
|
||||
}
|
||||
};
|
||||
|
||||
The same principles also apply to non-Node objects that maintain dependencies
|
||||
on other objects. Whichever object owns the other objects should manage
|
||||
the relationships between them.
|
||||
|
||||
Reference in New Issue
Block a user