Your Godot version: Applies to all versions since this article was added.
Issue description:
This article is incorrect, and at a minimum, extremely confusing. A scene is not a class, a scene is a collection of nodes. In object-oriented programming, a class is a type with linear inheritance. You can't have a script extend MyScene.tscn, you can't have MyScene.tscn extend a type (the closest equivalent is having the root node extend a type). Scenes can be composed of many nodes, but a class always extends a single base type (which can extend other single types, linearly). Scenes are completely different from classes and this documentation article makes no sense.
I'm not sure what the original intention of this article was, CC @willnationsdev @NathanLovato
There are also other confusing statements in this article, such as:
User-created types are not technically classes
...but what about in C#, where all user scripts are literally C# classes?
The scene is always an extension of the script attached to its root node. You can see all the nodes it contains as part of a single class.
No, this does not make any sense. Child nodes are not part of their parent's class, they are part of the scene tree. If you take a script attached to a root node of a scene and instance another node with that node's script, the children of that scene do not come with it... because the child nodes aren't part of the parent's class.
In a way, scenes do not even exist at runtime at all. Scenes are just reusable, instanceable, and inheritable groups of nodes. If you run a game and look at the remote scene tree in the editor, you do not see any scenes, you only see the editor.
I think this article should just be deleted. At a minimum it's extremely confusing and misleads users.
URL to the documentation page: https://docs.godotengine.org/en/latest/getting_started/workflow/best_practices/what_are_godot_classes.html
The intention of the page is at the top.
In Godot, scripts and scenes can both be the equivalent of classes in an Object-Oriented programming language. The main difference is that scenes are declarative code, while scripts can contain imperative code.
As a result, many best practices in Godot boil down to applying Object-Oriented design principles to the scenes, nodes, or scripts that make up your game.
Here's the definition of a class in computer programming from wikipedia:
In object-oriented programming, a class is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods).
Scenes are templates you can instance to get objects with their initial values and attached behavior, aren't they? If scenes and their children are not a class, what is this GDScript file then?
class_name Character
extends KinematicBody2D
var camera = Camera2D.new()
var body_sprite = Sprite.new()
func _ready():
add_child(camera)
add_child(body_sprite)
Couldn't you produce the same results in your game by instancing a scene containing these nodes or instancing this class defined in GDScript? I think that's, in part, what the page is trying to point out.
In general, OO principles apply to scenes in Godot. That's because scenes with their attached scripts, if created outside of an engine like Godot, for instance in pure C++, would have to be classes. In that sense, you can think of them the way you'd think of classes in a raw OO language and try to apply principles such as single responsibility, etc.
I just re-read the page in full and it's clear to me.
An important nuance regarding your issue description is that the article doesn't say a scene is a class, but rather, scripts and scenes can both be the equivalent of classes in an Object-Oriented programming language. If that can help clear out the confusion, we could write something like can be seen as the equivalent of..., or can be thought of the same way as....
If there are parts that don't apply e.g. to C# or it needs improvements in the phrasing, to clear confusion for others, go ahead. But the general idea, that you want to think of your scenes like you'd think of a class in an OO language, that is, as one encapsulated unit with its properties and behaviors, is useful for users.
As Nathan said, the intent is to demonstrate how Godot's scripting layer internally interprets scripts to define content, and draws comparisons with how scenes reproduce a subset of the same behavior in a batch engine operation.
A scene is not a class, a scene is a collection of nodes. In object-oriented programming, a class is a type with linear inheritance.
Scenes can be composed of many nodes, but a class always extends a single base type (which can extend other single types, linearly).
Unlike other game engines, Godot is unique in that its scene structure forces a singular tree arrangement. A scene isn't a list of nodes or a list of node trees. As such, you can consider a scene to "be" whatever type its root node has. I can even perform type checks on an instance of a "scene".
const MyType = load("my_type.gd")
var instance = load("my_type.tscn").instance()
if instance is MyType:
print("root node of my_type.tscn is a node with my_type.gd attached")
Now, technically speaking, a PackedScene more-so fulfills the role of a constructor rather than a fully-fledged type of its own, but it is still useful to think of them like types because, in OOP, part of what defines a class are the other class instances it owns as dependencies. Nodes are an easy, dynamic way of adding and removing such dependencies, but having a scene that formally declares "I have a Sprite, a Camera2D", etc. is very much akin to the way, in C++, you might see something similar to...
class MyScene : public Node {
Sprite _sprite;
Camera2D _camera2d;
public:
MyScene() {_sprite = memnew(Sprite); _camera2d = memnew(Camera2D);}
}
You can't have a script extend MyScene.tscn, you can't have MyScene.tscn extend a type (the closest equivalent is having the root node extend a type).
I feel the behavior that Godot supports is more akin to this:
I actually did a bunch of work to completely synchronize all of this type checking and inheritance tracking logic in the ClassType script I wrote for Godot Next. It's pretty outdated and should be cleaned up a lot, but the core functionality in there can be really useful for having a unified API to query engine, script, and scene "classes".
User-created types are not technically classes
...but what about in C#, where all user scripts are literally C# classes?
That's what I mean above by how a given scripting language may perceive a script's class as a legitimate class, but Godot itself does not keep track of such things. It more or less just says, "Hey, you have a Script. Tell me what properties, methods, and signals are associated with your ScriptInstance." It doesn't know that there is a C# class hiding behind "MyClass.cs".
Theoretically, I could create a scripting language that takes in a .txt file where I write out some simple property information Dictionaries and boom, I can load a Script that defines properties. But if you looked at the .txt file, you wouldn't say that it's a "class" necessarily. It'd be more like a simple data file.
In a way, scenes do not even exist at runtime at all. Scenes are just reusable, instanceable, and inheritable groups of nodes.
You are right; scenes, as types, don't technically exist at runtime. Or...do they? Well, hmm...
Type checking: I can get the filename of a Node to learn what "type" of scene it is associated with, just the same as I could get the path to the script it uses with get_script().resource_path. And before 3.1, file paths were all we had to go on when finding classes (this is still the case for C# when it comes to Script interop).
Inheritance checking: If I query the PackedScene's internal data, then I can find the root node's Script easily enough to perform a type check. Or, if it has no Script, I can check the type of the Node.
If scenes and their children are not a class, what is this GDScript file then?
That GDScript file is a class which instances child nodes. The child nodes are not part of the class IMO, they are separate, even if that GDScript file directly depends on them. They may together form one "object", but they are multiple classes.
Aside from Godot, in C++/C# or any other OO language, if I have class A and class B, and A has a member of type B, I don't consider that B is a part of A. (the following 2 paragraphs you wrote depend on the assumption that children are part of the class, so I can't really reply to them independently).
An important nuance regarding your issue description is that the article doesn't say
a scene is a class
...except in the title of the article, "Godot scenes and scripts are classes".
The way I read the entire rest of the content of the article was with the assumption that it backed up the title, as one would expect people to read any article.
I can even perform type checks on an instance of a "scene".
TIL, that's really cool actually.
Once you attach a script, you effectively mount a new, completely isolated inheritance branch from that point forward that survives in parallel to the traditional C++ inheritance tree.
If you consider scenes to be classes, then there are actually three inheritance branches: Godot's node inheritance, the script inheritance, and the scene inheritance (since you can make one scene inherit another). This is confusing. In my opinion, we should make it less confusing by not referring to scenes as classes in the documentation.
That's what I mean above by how a given scripting language may perceive a script's class as a legitimate class, but Godot itself does not keep track of such things.
This is extremely confusing to users who know classes from GDScript's class_name and C#'s class keyword, then they are told that neither of these are "real" classes, but scenes are...?
This is extremely confusing to users
If there are users you talked to that were confused by the article, we should tell them to go and create issues, and work with us to edit, rewrite, or remove the corresponding content. Getting users used to voicing out their confusion or reporting missing information is key in improving the docs.
I'll try to raise awareness about that when I get started working on the docs again.
...except in the title of the article, "Godot scenes and scripts are classes".
@aaronfranke Would one of the alternate titles below work?
Thinking of scenes like types
Thinking of scenes as if they were types
Apply Object-Oriented principles to scenes
Reasoning with scenes and classes
If not, suggestions are welcome. Other steps and suggestions to improve the article are welcome too, besides the one mentioned in your first comment.
if I have class A and class B, and A has a member of type B, I don't consider that B is a part of A
It's part of the influence I get from coding in duck-typed languages. As you lack interfaces, you tend to use methods and properties to test for can-do or has-a relationships.
If I have a Character that instances a Weapon and a Shield, my gdscript file or equivalent scene is a template for a character that has a weapon and a shield. And I use that to apply and reason in terms of OO principles: if my class instances too many other classes or types, it might be breaking single responsibility, it might be a code smell. Etc.
If you consider scenes to be classes, then there are actually three inheritance branches: Godot's node inheritance, the script inheritance, and the scene inheritance (since you can make one scene inherit another). This is confusing. In my opinion, we should make it less confusing by not referring to scenes as classes in the documentation.
This is what I meant by the three different bullet points. Only, with scenes in particular, it's especially weird because they can have multiple positions within the would-be inheritance graph.
base.gd extends Nodederived.gd extends base.gdbase.tscn has root node with base.gdderived.tscn has root node with derived.gdreverse_derived.tscn extends derived.tscn, but has root node script changed back to base.gdThe above sequence is entirely valid and effectively allows reverse_derived.tscn to be both a specific implementation of a scene, and a basic implementation of a script simultaneously. So, yes, indeed, it is very confusing.
However, if you were to take the "scenes are NOT classes" idea to the extreme, then technically neither are scripts, as far as Godot is concerned. And yet, GDScript, Godot's main language for interpreting Godot ideas, does its best to seamlessly interpret a Script as a class, allowing you to do both if obj is Node and if obj is load("base.gd"). It directly accesses scripted properties as if they were properties of the engine class. Same with methods and signals.
In my opinion, Godot intentionally removes the boundary between engine classes and scripts, but draws the line at scenes even though it really shouldn't, since scenes exhibit many of the same qualities that classes do. I mean, a scene like this:
- Node2D "MyNode" (my_node.gd)
- Sprite2D "Sprite2D"
is equivalent to a C++ class that just adds a private, inaccessible property, as I mentioned in an earlier reply:
class MyNode : public Node2D {
Sprite2D _sprite;
public:
MyNode() { _sprite = memnew(Sprite2D); add_child(_sprite); }
}
Since these are pretty much comparable, I would argue that, if anything, we should improve the class emulation of PackedScene resources since the engine already does its best to produce this behavior for scripts, but fails to properly do so for scenes.
filename set to that scene's path and also for testing if the root node of the scene has a type that is comparable.However, all of that admittedly does not describe the current state of Godot, and so scenes are these halfway types that are not truly understood as classes in the engine or documentation. So, I agree that the docs shouldn't explicitly say that scenes are classes, but that they should be treated as if they represented classes. It is more clear that way. But I standby my points. It feels like Godot started implementing scenes as a declarative class mechanism and then stopped before they were done. I suppose I'll just have to put together a proposal at some point.
If not, suggestions are welcome.
I find that the original article makes a lot more sense: https://github.com/godotengine/godot-docs/blame/1d5f0aa60e32c3e4876afced0ae630765d3e5826/getting_started/workflow/best_practices/what_are_godot_classes.rst (I clicked "View blame prior to this change" on @NathanLovato's changes), though it has other problems like writing style and organization. In the original article, the mention of scenes as classes is not stated in the title or opening paragraph, but instead later on.
I think refactoring the article to something like this would make sense:
Godot offers two main ways to create reusable objects: scripts and scenes. Both of these can be thought of as representing a "class" in object-oriented programming, but neither are technically classes under the hood. Why this is the case may not be clear to beginner or intermediate users. Many best practices in Godot boil down to applying object-oriented design principles to the scripts, scenes, and nodes that make up your game, so it's useful to understand how these can be thought of as classes.
Godot Engine provides classes out-of-the-box (like :ref:Node <class_Node>). However, user-created types, such as those created with class_name, are not technically classes under the hood. Scripts are resources that tell the engine a sequence of initializations to perform on an engine class. insert more content here
The behavior of scenes has many similarities to classes, so it can make sense to think of a scene as a class. Scenes are reusable, instanceable, and inheritable groups of nodes. This is similar to having a class that automatically creates nodes and adds them via add_child. insert more content here
While neither scripts nor scenes are technically classes, both behave similarly to classes and both can be used to create reusable objects. Which tool you use depends on your situation. insert more content here
And yet, GDScript, Godot's main language for interpreting Godot ideas, does its best to seamlessly interpret a Script as a class
Yes, this is nice, though there are still a few things missing (like get_class() doesn't return the class_name name).
is equivalent to a C++ class that just adds a private, inaccessible property
Private, inaccessible? my_node.get_node("Sprite2D") would disagree.
Since these are pretty much comparable, I would argue that, if anything, we should improve the class emulation of PackedScene resources since the engine already does its best to produce this behavior for scripts, but fails to properly do so for scenes.
I agree with all your points here.
I like the general style of how you rearranged the content and clarified the word use, but I do think the sample you've given is a little...intense? Redundant? It just reiterates the "they are like classes, but they're not classes!" a little more often than I'd like. Still, useful approach.
Private, inaccessible? my_node.get_node("Sprite2D") would disagree.
Well, yes. I just meant, these are "internal" things that outside classes aren't necessarily supposed to access. It's bad practice to anyway. The best practice is to declare a variable for referencing it in the root node script and have it exposed as a public property in that fashion; thereby discouraging external APIs from mucking with the internals and potentially running into a runtime error if the internal structure changes. We similarly call underscore-prefixed GDScript properties "private", but we all know they are just as accessible as any other property.
Your suggestion looks solid to me. Agreed with Will about redundancy, the title + intro you wrote makes it clear scenes are not to be mistaken with classes.
If you want to take care of the edits go ahead, if not let me know and I can put up a PR.
Some of the redundancy can possibly be removed, but I think it might seem less redundant if more content is added and therefore the density of the words I wrote becomes far lower.
I would prefer to let you handle the PR, I think you know better what ideas you are trying to communicate with this article and now that we have this thread with technical details provided by @willnationsdev, and some of my concerns/suggestions, I think you can write a much better article than we currently have. I'll gladly review the PR.
Sure. I was trying to help Will communicate his ideas. I'll ping the two of you for review so you can ensure the changes are both clear and accurate.
Note I'm in the middle of a crowdfunding campaign. I'll try to get to it in the coming days but can't promise to deliver until after the campaign.
Most helpful comment
The intention of the page is at the top.
Here's the definition of a class in computer programming from wikipedia:
Scenes are templates you can instance to get objects with their initial values and attached behavior, aren't they? If scenes and their children are not a class, what is this GDScript file then?
Couldn't you produce the same results in your game by instancing a scene containing these nodes or instancing this class defined in GDScript? I think that's, in part, what the page is trying to point out.
In general, OO principles apply to scenes in Godot. That's because scenes with their attached scripts, if created outside of an engine like Godot, for instance in pure C++, would have to be classes. In that sense, you can think of them the way you'd think of classes in a raw OO language and try to apply principles such as single responsibility, etc.
I just re-read the page in full and it's clear to me.
An important nuance regarding your issue description is that the article doesn't say
a scene is a class, but rather,scripts and scenes can both be the equivalent of classes in an Object-Oriented programming language. If that can help clear out the confusion, we could write something likecan be seen as the equivalent of..., orcan be thought of the same way as....