Skip to content

Commit

Permalink
Update GDScript reference page to discuss traits [no ci]
Browse files Browse the repository at this point in the history
Add trait-related keywords to syntax highlighting
  • Loading branch information
Meorge committed Dec 9, 2024
1 parent 469176a commit c4b5a31
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 8 deletions.
4 changes: 3 additions & 1 deletion _extensions/gdscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ def innerstring_rules(ttype):
"namespace", # Reserved for potential future use.
"signal",
"static",
"trait", # Reserved for potential future use.
"trait",
"trait_name",
"uses",
"var",
# Other keywords.
"await",
Expand Down
182 changes: 175 additions & 7 deletions tutorials/scripting/gdscript/gdscript_basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ here's an example of how GDScript looks.
# Inheritance:
extends BaseClass

# Trait usage:
uses Talkable, Flammable


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


# When traits are used, they may have to implement abstract
# methods defined by the trait:
func talk_to():
print("Hi, you just talked to me!")


# Traits can also implement methods that the concrete class
# can use without defining itself:
func yet_another_something():
print("I'm going to light on fire!")
light_on_fire()


# Inner class
class Something:
var a = 10
Expand Down Expand Up @@ -174,7 +190,13 @@ in case you want to take a look under the hood.
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| extends | Defines what class to extend with the current class. |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| is | Tests whether a variable extends a given class, or is of a given built-in type. |
| trait | Defines an inner trait. See `Inner traits`_. |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| trait_name | Defines the script as a globally accessible trait with the specified name. See `Registering named traits`_. |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| uses | Defines what trait(s) the current class should use. |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| is | Tests whether a variable extends a given class, uses a given trait, or is of a given built-in type. |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| 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. |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
Expand Down Expand Up @@ -853,15 +875,16 @@ The GDScript static analyzer takes typed arrays into account, however array meth
``front()`` and ``back()`` still have the ``Variant`` return type.

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

::

var a: Array[int]
var b: Array[Node]
var c: Array[MyClass]
var d: Array[MyEnum]
var e: Array[Variant]
var e: Array[MyTrait]
var f: Array[Variant]

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

Expand Down Expand Up @@ -1036,9 +1059,10 @@ Valid types are:

- Built-in types (Array, Vector2, int, String, etc.).
- Engine classes (Node, Resource, RefCounted, etc.).
- Constant names if they contain a script resource (``MyScript`` if you declared ``const MyScript = preload("res://my_script.gd")``).
- Other classes in the same script, respecting scope (``InnerClass.NestedClass`` if you declared ``class NestedClass`` inside the ``class InnerClass`` in the same scope).
- Constant names if they contain a script or trait resource (``MyScript`` if you declared ``const MyScript = preload("res://my_script.gd")``).
- 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).
- Script classes declared with the ``class_name`` keyword.
- Traits declared with the ``trait_name`` keyword.
- Autoloads registered as singletons.

.. note::
Expand Down Expand Up @@ -1324,7 +1348,7 @@ or ``0`` if it is the first entry in the enum. Multiple keys with the same value
Functions
---------

Functions always belong to a `class <Classes_>`_. The scope priority for
Functions always belong to a `class <Classes_>`_ or a `trait <Traits_>`_. The scope priority for
variable look-up is: local → class member → global. The ``self`` variable is
always available and is provided as an option for accessing class members, but
is not always required (and should *not* be sent as the function's first
Expand Down Expand Up @@ -1941,6 +1965,11 @@ A class (stored as a file) can inherit from:

Multiple inheritance is not allowed.

.. note::
Godot 4.x introduces `traits <Traits_>`_ to GDScript, which may cover many of
the use cases for multiple inheritance. See their section for more information
about how they work.

Inheritance uses the ``extends`` keyword::

# Inherit/extend a globally available class.
Expand All @@ -1957,7 +1986,7 @@ Inheritance uses the ``extends`` keyword::
If inheritance is not explicitly defined, the class will default to inheriting
:ref:`class_RefCounted`.

To check if a given instance inherits from a given class,
To check if a given instance inherits from a given class or uses a given trait,
the ``is`` keyword can be used::

# Cache the enemy class.
Expand All @@ -1969,6 +1998,15 @@ the ``is`` keyword can be used::
if entity is Enemy:
entity.apply_damage()

# Cache the Flammable trait.
const Flammable = preload("flammable.gdt")

# [...]

# Use 'is' to check usage of the trait.
if entity is Flammable:
entity.light_on_fire()

To call a function in a *super class* (i.e. one ``extend``-ed in your current
class), use the ``super`` keyword::

Expand Down Expand Up @@ -2121,6 +2159,136 @@ class resource is done by calling the ``new`` function on the class object::
var a = MyClass.new()
a.some_function()

Traits
------

Traits are collections of behaviors and attributes that classes can use to guarantee
functionality to themselves and other objects that may be attempting to use them.

Like classes, by default all ``.gdt`` files are unnamed traits, and you must reference
them using a relative or absolute path.
::
# Use the trait 'interactable.gdt'.
uses "res://path/to/interactable.gdt"

Note that traits on their own *cannot* be instantiated the same way that classes can.

.. _doc_gdscript_basics_trait_name:

Registering named traits
~~~~~~~~~~~~~~~~~~~~~~~~

Traits can be given a global name by using the ``trait_name`` keyword.
::
trait_name MyTrait
Using traits in a class
~~~~~~~~~~~~~~~~~~~~~~~

For a class to use a trait, use the ``uses`` keyword:
::
class_name MyScript
uses MyTrait


Traits may also extend classes. If a trait extends a class, then any class
that uses that trait must also have that class as an ancestor.
::
# movable.gdt
trait_name Movable
extends PhysicsBody2D

# character.gd
class_name Character
extends CharacterBody2D
uses Movable # Allowed, since CharacterBody2D inherits from PhysicsBody2D.

The ``is`` keyword can be used to determine if a given instance uses a particular trait.
::
if entity is Movable:
entity.move()

If a trait provides a method signature, but no body, then the using class must implement
a body for the method.
::
# explosive.gdt
trait_name Explosive
func explode() # Body is not defined here, so it must be defined in each class that uses it.


# exploding_barrel.gd
class_name ExplodingBarrel
extends Sprite2D
uses Explosive

func explode(): # If this definition of Explosive.explode isn't provided, we will get an error.
print("Kaboom!")
queue_free()

If a trait provides a method signature *and* a body, then the using class inherits it by default
and doesn't need to provide its own implementation. It still can override the trait's
implementation if desired, but the parameter count must stay the same, and the parameter and return
types must be compatible.
::
# damageable.gdt
trait_name Damageable

func take_damage():
print("Ouch!")


# invincible_npc.gd
class_name InvincibleNPC
extends Sprite2D
uses Damageable

# Allowed, and will run instead of Damageable's original take_damage method.
func take_damage():
print("You can't hurt me!")

..
TODO: Confirm these behaviors
Other class members have similar rules:

- Variables and constants can be overridden, as long as the type is compatible and the value is changed.
- Signals can be overriden, as long as the parameter count is maintained and the parameter types are compatible.
- Named enums can be overriden and have new enum values.

.. _doc_gdscript_basics_inner_traits:

Inner traits
~~~~~~~~~~~~

Like inner classes, a class or trait file may contain inner traits, defined with the ``trait``
keyword. Unlike inner classes, they cannot be instantiated directly, but their name can be
referenced for using or checking use of themselves.
::
# An inner trait in this class file.
trait SomeInnerTrait:
func do_something():
print("I did something!")


# An inner class in this class file, which uses the inner trait.
class SomeInnerClass:
uses SomeInnerTrait


func _init():
var c = SomeInnerClass.new()
if c is SomeInnerTrait:
c.do_something()

.. _doc_gdscript_basics_traits_as_resources:

Traits as resources
~~~~~~~~~~~~~~~~~~~

Traits stored as files are treated as :ref:`GDTraits <class_GDTrait>`, and must
be loaded similarly to classes (see `Classes as resources`_).

Exports
-------

Expand Down

0 comments on commit c4b5a31

Please sign in to comment.