Skip to content

Commit 7e1389d

Browse files
committed
Add documentation for traits to relevant pages
Add trait-related keywords to syntax highlighting Update style guide and static typing pages Currently, the style guide uses what seem like good reasonable policies to me (notably, PascalCase adjectives for trait names, and `uses` after `extends`). We'll need to make sure this is agreed-upon as the good way to do it before merging, though Document behavior of static variables in traits [no ci] Document behavior of casting to traits Apply suggestions from code review Co-authored-by: tetrapod <145553014+tetrapod00@users.noreply.github.com> Updated description of static variables for traits Added `uses` to order of things in GDScript files
1 parent d316eac commit 7e1389d

File tree

4 files changed

+244
-36
lines changed

4 files changed

+244
-36
lines changed

_extensions/gdscript.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,9 @@ def innerstring_rules(ttype):
151151
"namespace", # Reserved for potential future use.
152152
"signal",
153153
"static",
154-
"trait", # Reserved for potential future use.
154+
"trait",
155+
"trait_name",
156+
"uses",
155157
"var",
156158
# Other keywords.
157159
"await",

tutorials/scripting/gdscript/gdscript_basics.rst

Lines changed: 190 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ here's an example of how GDScript looks.
4242
# Inheritance:
4343
extends BaseClass
4444

45+
# Trait usage:
46+
uses Talkable, Flammable
47+
4548

4649
# Member variables.
4750
var a = 5
@@ -103,6 +106,19 @@ here's an example of how GDScript looks.
103106
super.something(p1, p2)
104107

105108

109+
# When traits are used, they may have to implement abstract
110+
# methods defined by the trait:
111+
func talk_to():
112+
print("Hi, you just talked to me!")
113+
114+
115+
# Traits can also implement methods that the concrete class
116+
# can use without defining itself:
117+
func yet_another_something():
118+
print("I'm going to light on fire!")
119+
light_on_fire()
120+
121+
106122
# Inner class
107123
class Something:
108124
var a = 10
@@ -174,7 +190,13 @@ in case you want to take a look under the hood.
174190
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
175191
| extends | Defines what class to extend with the current class. |
176192
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
177-
| is | Tests whether a variable extends a given class, or is of a given built-in type. |
193+
| trait | Defines an inner trait. See `Inner traits`_. |
194+
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
195+
| trait_name | Defines the script as a globally accessible trait with the specified name. See `Registering named traits`_. |
196+
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
197+
| uses | Defines what trait(s) the current class should use. |
198+
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
199+
| is | Tests whether a variable extends a given class, uses a given trait, or is of a given built-in type. |
178200
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
179201
| in | Tests whether a value is within a string, array, range, dictionary, or node. When used with ``for``, it iterates through them instead of testing. |
180202
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
@@ -865,15 +887,16 @@ The GDScript static analyzer takes typed arrays into account, however array meth
865887
``front()`` and ``back()`` still have the ``Variant`` return type.
866888

867889
Typed arrays have the syntax ``Array[Type]``, where ``Type`` can be any ``Variant`` type,
868-
native or user class, or enum. Nested array types (like ``Array[Array[int]]``) are not supported.
890+
native or user class, trait, or enum. Nested array types (like ``Array[Array[int]]``) are not supported.
869891

870892
::
871893

872894
var a: Array[int]
873895
var b: Array[Node]
874896
var c: Array[MyClass]
875897
var d: Array[MyEnum]
876-
var e: Array[Variant]
898+
var e: Array[MyTrait]
899+
var f: Array[Variant]
877900

878901
``Array`` and ``Array[Variant]`` are the same thing.
879902

@@ -1081,9 +1104,10 @@ Valid types are:
10811104

10821105
- Built-in types (Array, Vector2, int, String, etc.).
10831106
- Engine classes (Node, Resource, RefCounted, etc.).
1084-
- Constant names if they contain a script resource (``MyScript`` if you declared ``const MyScript = preload("res://my_script.gd")``).
1085-
- Other classes in the same script, respecting scope (``InnerClass.NestedClass`` if you declared ``class NestedClass`` inside the ``class InnerClass`` in the same scope).
1107+
- Constant names if they contain a script or trait resource (``MyScript`` if you declared ``const MyScript = preload("res://my_script.gd")``).
1108+
- Other classes or traits in the same file, respecting scope (``InnerClass.NestedClass`` if you declared ``class NestedClass`` inside the ``class InnerClass`` in the same scope).
10861109
- Script classes declared with the ``class_name`` keyword.
1110+
- Traits declared with the ``trait_name`` keyword.
10871111
- Autoloads registered as singletons.
10881112

10891113
.. note::
@@ -1155,11 +1179,11 @@ A class member variable can be declared static:
11551179

11561180
static var a
11571181

1158-
Static variables belong to the class, not instances. This means that static variables
1182+
Static variables belong to the class or trait, not instances. This means that static variables
11591183
share values between multiple instances, unlike regular member variables.
11601184

1161-
From inside a class, you can access static variables from any function, both static and non-static.
1162-
From outside the class, you can access static variables using the class or an instance
1185+
From inside a class or trait, you can access static variables from any function, both static and non-static.
1186+
From outside the class or trait, you can access static variables using the class or an instance
11631187
(the second is not recommended as it is less readable).
11641188

11651189
.. note::
@@ -1234,6 +1258,18 @@ A base class static variable can also be accessed via a child class:
12341258
B.x = 3
12351259
prints(A.x, B.x) # 3 3
12361260

1261+
Likewise, if a class uses a trait with static variables, it will inherit those
1262+
variables::
1263+
1264+
trait HasStatic:
1265+
static var static_var = 3
1266+
1267+
class UsingClass:
1268+
uses HasStatic
1269+
1270+
func _ready():
1271+
print(UsingClass.static_var)
1272+
12371273
``@static_unload`` annotation
12381274
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12391275

@@ -1263,15 +1299,15 @@ Values assigned to typed variables must have a compatible type. If it's needed t
12631299
coerce a value to be of a certain type, in particular for object types, you can
12641300
use the casting operator ``as``.
12651301

1266-
Casting between object types results in the same object if the value is of the
1267-
same type or a subtype of the cast type.
1302+
Casting between object types or traits results in the same object if the value is of the
1303+
same type or a subtype of the cast type, or if the value uses the trait.
12681304

12691305
::
12701306

12711307
var my_node2D: Node2D
12721308
my_node2D = $Sprite2D as Node2D # Works since Sprite2D is a subtype of Node2D.
12731309

1274-
If the value is not a subtype, the casting operation will result in a ``null`` value.
1310+
If the value is not a subtype or does not use the trait, the casting operation will result in a ``null`` value.
12751311

12761312
::
12771313

@@ -1383,7 +1419,7 @@ or ``0`` if it is the first entry in the enum. Multiple keys with the same value
13831419
Functions
13841420
---------
13851421

1386-
Functions always belong to a `class <Classes_>`_. The scope priority for
1422+
Functions always belong to a `class <Classes_>`_ or a `trait <Traits_>`_. The scope priority for
13871423
variable look-up is: local → class member → global. The ``self`` variable is
13881424
always available and is provided as an option for accessing class members
13891425
(see `self`_), but is not always required (and should *not* be sent as the
@@ -2143,6 +2179,7 @@ A class (stored as a file) can inherit from:
21432179

21442180
Multiple inheritance is not allowed.
21452181

2182+
Multiple inheritance is not allowed, but Godot does support `traits <Traits_>`_, which cover many of the same use cases.
21462183
Inheritance uses the ``extends`` keyword::
21472184

21482185
# Inherit/extend a globally available class.
@@ -2159,7 +2196,7 @@ Inheritance uses the ``extends`` keyword::
21592196
If inheritance is not explicitly defined, the class will default to inheriting
21602197
:ref:`class_RefCounted`.
21612198

2162-
To check if a given instance inherits from a given class,
2199+
To check if a given instance inherits from a given class or uses a given trait,
21632200
the ``is`` keyword can be used:
21642201

21652202
::
@@ -2173,6 +2210,15 @@ the ``is`` keyword can be used:
21732210
if entity is Enemy:
21742211
entity.apply_damage()
21752212

2213+
# Cache the Flammable trait.
2214+
const Flammable = preload("flammable.gdt")
2215+
2216+
# [...]
2217+
2218+
# Use 'is' to check usage of the trait.
2219+
if entity is Flammable:
2220+
entity.light_on_fire()
2221+
21762222
To call a function in a *super class* (i.e. one ``extend``-ed in your current
21772223
class), use the ``super`` keyword::
21782224

@@ -2335,6 +2381,137 @@ class resource is done by calling the ``new`` function on the class object:
23352381
var a = MyClass.new()
23362382
a.some_function()
23372383

2384+
Traits
2385+
------
2386+
2387+
Since Godot 4.x, GDScript supports traits, which are collections of behaviors and attributes
2388+
that classes can use to guarantee
2389+
functionality to themselves and other objects that may be attempting to use them.
2390+
2391+
Like classes, by default all ``.gdt`` files are unnamed traits, and you must reference
2392+
them using a relative or absolute path.
2393+
::
2394+
# Use the trait 'interactable.gdt'.
2395+
uses "res://path/to/interactable.gdt"
2396+
2397+
Note that traits on their own *cannot* be instantiated the same way that classes can.
2398+
2399+
.. _doc_gdscript_basics_trait_name:
2400+
2401+
Registering named traits
2402+
~~~~~~~~~~~~~~~~~~~~~~~~
2403+
2404+
Traits can be given a global name by using the ``trait_name`` keyword.
2405+
::
2406+
trait_name MyTrait
2407+
2408+
Using traits in a class
2409+
~~~~~~~~~~~~~~~~~~~~~~~
2410+
2411+
For a class to use a trait, use the ``uses`` keyword:
2412+
::
2413+
class_name MyScript
2414+
uses MyTrait
2415+
2416+
2417+
Traits may also extend classes. If a trait extends a class, then any class
2418+
that uses that trait must also have that class as an ancestor.
2419+
::
2420+
# movable.gdt
2421+
trait_name Movable
2422+
extends PhysicsBody2D
2423+
2424+
# character.gd
2425+
class_name Character
2426+
extends CharacterBody2D
2427+
uses Movable # Allowed, since CharacterBody2D inherits from PhysicsBody2D.
2428+
2429+
The ``is`` keyword can be used to determine if a given instance uses a particular trait.
2430+
::
2431+
if entity is Movable:
2432+
entity.move()
2433+
2434+
If a trait provides a method signature, but no body, then the using class must implement
2435+
a body for the method.
2436+
::
2437+
# explosive.gdt
2438+
trait_name Explosive
2439+
2440+
func explode() # Body is not defined here, so it must be defined in each class that uses it.
2441+
2442+
2443+
# exploding_barrel.gd
2444+
class_name ExplodingBarrel
2445+
extends Sprite2D
2446+
uses Explosive
2447+
2448+
func explode(): # If this definition of Explosive.explode isn't provided, we will get an error.
2449+
print("Kaboom!")
2450+
queue_free()
2451+
2452+
If a trait provides a method signature *and* a body, then the using class inherits it by default
2453+
and doesn't need to provide its own implementation. It still can override the trait's
2454+
implementation if desired, but the parameter count must stay the same, and the parameter and return
2455+
types must be compatible.
2456+
::
2457+
# damageable.gdt
2458+
trait_name Damageable
2459+
2460+
func take_damage():
2461+
print("Ouch!")
2462+
2463+
2464+
# invincible_npc.gd
2465+
class_name InvincibleNPC
2466+
extends Sprite2D
2467+
uses Damageable
2468+
2469+
# Allowed, and will run instead of Damageable's original take_damage method.
2470+
func take_damage():
2471+
print("You can't hurt me!")
2472+
2473+
..
2474+
TODO: Confirm these behaviors
2475+
2476+
Other class members have similar rules:
2477+
2478+
- Variables and constants can be overridden, as long as the type is compatible and the value is changed.
2479+
- Signals can be overriden, as long as the parameter count is maintained and the parameter types are compatible.
2480+
- Named enums can be overriden and have new enum values.
2481+
2482+
.. _doc_gdscript_basics_inner_traits:
2483+
2484+
Inner traits
2485+
~~~~~~~~~~~~
2486+
2487+
Like inner classes, a class or trait file may contain inner traits, defined with the ``trait``
2488+
keyword. Unlike inner classes, they cannot be instantiated directly, but their name can be
2489+
referenced for using or checking use of themselves.
2490+
::
2491+
# An inner trait in this class file.
2492+
trait SomeInnerTrait:
2493+
func do_something():
2494+
print("I did something!")
2495+
2496+
2497+
# An inner class in this class file, which uses the inner trait.
2498+
class SomeInnerClass:
2499+
uses SomeInnerTrait
2500+
2501+
2502+
func _init():
2503+
var c = SomeInnerClass.new()
2504+
if c is SomeInnerTrait:
2505+
c.do_something()
2506+
2507+
.. _doc_gdscript_basics_traits_as_resources:
2508+
2509+
Traits as resources
2510+
~~~~~~~~~~~~~~~~~~~
2511+
2512+
Traits stored as files are treated as :ref:`GDTraits <class_GDTrait>`, and must
2513+
be loaded similarly to classes (see `Classes as resources`_).
2514+
23382515
Exports
23392516
-------
23402517

0 commit comments

Comments
 (0)