Describe the project you are working on:
Financial modeling software which requires a lot of custom classes.
Describe the problem or limitation you are having in your project:
Using class_name is inelegant and looks ugly in the source code.
Describe how this feature / enhancement will help you overcome this problem or limitation:
This improvement would stop making me cringe when I'm reading my code.
Show a mock up screenshots/video or a flow diagram explaining how your proposal will work:
Replace -> "class_name" -> with -> "as"
Describe implementation detail for your proposal (in code), if possible:
Replace keyword "class_name" with the keyword "as", with the following implementation:
extends Node as MyClass, "res://myicon.ico"
Replace keyword "class_name" with "MyClass extends Node", where the class icon is implicitly
inferred from the class script file location, having the same name as the class script file.
If this enhancement will not be used often, can it be worked around with a few lines of script?:
No
Is there a reason why this should be core and not an add-on in the asset library?:
Because it's elegant and pythonic.
The keyword as is already used for type casting when using static typing, so I'm not sure about this. Surely it can be made to work, but it might not be trivial.
why not the other way around?
having a .gd script file like:
MyClass extends Node
func do_stuff():
pass
why not the other way around?
having a .gd script file like:
MyClass extends Node func do_stuff(): pass
Absolutely brilliant. But how would one define the class icon?
why not the other way around?
having a .gd script file like:
MyClass extends Node func do_stuff(): pass
That's so clean, I love it. I hope it gets implemented in 4.0
Absolutely brilliant. But how would one define the class icon?
class_icon res://icon.png maybe?
@Jummit To me, the as syntax proposed by OP feels more natural for declaring an icon. You also wouldn't need to write a second line when you want to declare an icon.
If using the as keyword is too cumbersome, we could just use class_name (which basically means we'd put the current class_name declaration on the same line as extends).
I just had a realization. Simply turning class_name into class would also make things cleaner. Just a thought.
MyClass extends Node with "res://myclass.ico" ???
Glad you like the suggestion :-)
Got another idea for defining the icon, how about using braces for further class configuration?
MyClass("icon-res.png") extends Node
func do_stuff():
pass
Probably breaks the clean look, but since the icon more refers to MyClass instead of Node it might be better to put it next to MyClass
Hmm, well, using MyClass("res://myclass.ico") looks like a constructor, which makes sense.
However, when the path of an icon is very long, it will make the code look very dirty.
What if the path of the icon is not up to the programmer to define, but the system implicitly
looks for an .ico file with the same file name as the class file name in the same directory and
if one is found, automatically assigns that icon to that class?
Example:
I have "myclass.gd" in "res://classes/", and in it I have declared "MyClass extends Node".
If I want this class to have an icon in the editor, I could put a file "myclass.ico" into "res://classes/" next to the "myclass.gd" file and the engine would automatically assign that icon file to the class node.
We have to think about the reason for class icons.
They are useful for developers to make their classes better identifiable during the game development process.
There is no need to change the class icons during the game execution, because these icons are irrelevant for the player and probably they are not included into the export exe anyways, which is why there's no need to explicitly declare their path in the source files, as most developers put the class icon files next to the class script files anyways I assume.
It would be like how Windows Media Player discovers and uses subtitle files - they need to have the exact same name as the movie file and be in the same folder next to the movie file.
Therefore, with this knowledge, the script file is spared of explicitly declaring class icons,
which keeps the source cleaner and pretty.
What if the path of the icon is not up to the programmer to define, but the system implicitly
looks for an .ico file with the same file name as the class file name in the same directory and
if one is found, automatically assigns that icon to that class?
This sounds good to me, but Godot expects PNG or SVG icons for classes, not ICO.
What if the path of the icon is not up to the programmer to define, but the system implicitly
looks for an .ico file with the same file name as the class file name in the same directory and
if one is found, automatically assigns that icon to that class?This sounds good to me, but Godot expects PNG or SVG icons for classes, not ICO.
I didn't know that, but this would work with PNG and SVG also.
Pseudocode running in editor tool:
if new_file in folder:
if file.extension == "SVG" or file.extension == "PNG":
var script_file = new_file.name + ".gd"
if folder has script_file and script_file.class_exists:
script_file.class_icon = new_file.path
if script_file.save:
var script_file_png = script_file.name + ".png"
var script_file_svg = script_file.name + ".svg"
if folder has script_file_png:
script_file.class_icon = script_file_png.path
elif folder has script_file_svg:
script_file.class_icon = script_file_svg.path
Therefore, with this knowledge, the script file is spared of explicitly declaring class icons,
which keeps the source cleaner and pretty.
Your solution sounds perfect to me :-)
There's also no reason why you wouldn't name the icon like you name the class
For some reference, here's the current way things work.
GDScript file without global class:
extends Node
GDScript file with global class:
extends Node
class_name MyNode
GDScript file with global class and icon:
extends Node
class_name MyNode, "whatever.png"
C# file without global class:
public class MyNode : Node
Proposed in https://github.com/godotengine/godot/issues/26875, C# file with global class and icon:
[GlobalClass("res://icon_path")]
public class MyNode : Node
Another important existing proposal: #22 cc @willnationsdev
Now, that being said... to be honest, I don't see why we even need to provide a class name. It's best practice to have a class named MyNode stored in MyNode.gd. In C# providing the name of the class is required by the language, but also C# in Godot currently doesn't support mismatching class and file name for attached scripts.
So, for GDScript, why not eliminate it altogether? Something like:
class extends Node, "res://MyNode.png"
Or, even further, we could even eliminate the icon path, and just have it search for MyNode.svg followed by MyNode.png in the same folder as the script.
So, in a nutshell, my idea is to have the class and icon name inferred from the file name, like this:
Folder structure:
MyCustomNodeFolder/
MyNode.gd
MyNode.svg
OtherNode.gd
NormalScript.gd
MyNode.gd:
class extends Node # Class name is inferred to be "MyNode", gets icon from "MyNode.svg"
OtherNode.gd:
class extends Node # Class name is inferred to be "OtherNode".
# Searches for "OtherNode.svg" and "OtherNode.png", icon not found so it has no icon.
NormalScript.gd:
extends Node # No "class" keyword, so not registered as a global class
How about a class keyword that works like tool? It would register it as a global class with the filename as a name.
While the idea of a class keyword isn't too bad I am not sure if it'd be a 100% good solution since I guess there are a couple of programmers out there who want to name their files in snake_case while having their classes in CamelCase.
This would force some people adapting their style to either filesystem or coding.
@aaronfranke
It's best practice to have a class named
MyNodestored inMyNode.gd.
Is it though? I've generally followed the C++ source code's conventions where possible. So I use snake_case filenames with PascalCase class names, as @TheWhiteLlama mentioned above.
In C# providing the name of the class is required by the language, but also C# in Godot currently doesn't support mismatching class and file name for attached scripts.
I would actually be okay with all scripting languages having to follow this convention, since it makes the codebase more consistent and would simplify writing the code. However, I also think that a lot of people would be frustrated by it since they often prefer the freedom Godot gives them to do things the way they want.
However, even if you did implement the auto-collection of the name/icon in the means you've specified, it wouldn't affect people who didn't use it, so I think it would be a good QOL improvement. The only concern I have would be the effect it has on online code readability. But in most cases, it would probably be alright since people often add a # my_file.gd tidbit to code segments anyway.
It's also possible to have our cake and eat it too. We can have the class name "generator" start with a case conversion, where the first letter, and each letter following _, is capitalized, and all _ are removed. So my_node.gd and MyNode.gd would both set the class name to MyNode. This would also work for myNode.gd and My_Node.gd if someone wanted to do that.
To allow flexibility, perhaps the format class MyNode extends Node, "res://my_node.png" is best, just with class, MyNode, and the icon being optional.
@aaronfranke That sounds really good to me! And that would include being able to have the icon seek out a same-class-named icon in the same directory, optionally. Those would all be really convenient.
reusing the class keyword sounds like it could get confusing. GDScript files already allow you to define internal classes, something I use quite a lot in my own projects. It also wouldn't make sense to use an implied class name without a few adjustments to the limitations of class_name. Right now you can have scripts embedded in tscn files that have class_name on them but using implied name would not be compatible with that.
Instead of adding to the classdb using code, perhaps a cleaner approach would be allow you to specify class name and icon as part of the resource options in the editor, and that could provide a unified approach for both GDScript and C#
I was looking at the reserved keywords and class_name stuck out like a sore thumb, and found this thread with good suggestions.
My suggestion is:
extends <someclass> [as <class name>[, <icon path>]]
@wyattbiker I like that syntax, personally. Avoids the class term confusion, but still communicates the intent decently without resorting to a custom keyword.
@nhydock I don't like the resource-property-based approach. It is convenient in the sense that all languages would be able to be configured with a single API, but there would be no place in the script source code that actually stores the information. You either can't pass the class name along if you share the script online (not conducive to open source) or Godot would have to edit the source code to match the property value (which puts you back where you started AND is just a bad idea overall).
I just had a realization. Simply turning
class_nameintoclasswould also make things cleaner. Just a thought.
I just had a flashback now: https://github.com/godotengine/godot/issues/31056#issuecomment-518162389.
I just had a realization. Simply turning
class_nameintoclasswould also make things cleaner. Just a thought.I just had a flashback now: godotengine/godot#31056 (comment).
class is a keyword used for inner classes. So would be confusing and possibly break existing .gd files.
https://docs.godotengine.org/en/3.1/getting_started/scripting/gdscript/gdscript_basics.html#example-of-gdscript
# Inner Class
class Something:
var a = 10
Except that the definition of inner classes end with a colon:

So instead of the error the parser could treat this as class_name definition instead (the first occurrence of such usage).
If we fear the possible collision between existing class keywords, how about renaming class_name to global_class, since this is what it actually does, making your class available globally, isn't it?
I think at this point it'd be better to just leave things as is. It won't be changing for 3.2, and there's a lot of documentation already using class_name, as well as a bunch of publicly available scripts. We'd just be changing a key syntactical feature so it "feels good" without really adding much value. People can just look at the plethora of examples and tutorials that the community has come up with now to understand how it works and when to use it.
If we fear the possible collision between existing
classkeywords, how about renamingclass_nametoglobal_class, since this is what it actually does, making your class available globally, isn't it?
I thought about calling it global instead. Just a keyword rename, function is the same. Though I'm not sure if it's worth changing just for the sake of it.
I think at this point it'd be better to just leave things as is. It won't be changing for 3.2, and there's a lot of documentation already using class_name, as well as a bunch of publicly available scripts. We'd just be changing a key syntactical feature so it "feels good" without really adding much value. People can just look at the plethora of examples and tutorials that the community has come up with now to understand how it works and when to use it.
3.2 won't be the last Godot version, we can always change it later. About existing stuff, yes, it can be a bit problematic to breaking compatibility. But if we can make it objectively better then it might be worth it. "Feels good" is also an argument: if it's pleasant to write/read code we can be more productive.
The problem is not "understanding". The problem is that it doesn't feel natural. Which is true since it was pretty much botched in the engine without a lot of thought.
I still believe we can get to something better (I'm not fond of the class_name keyword either).
I really like that MyClass extends Node syntax
Edit: or the as option. They feel like a true extension and like they fit in woth the language, while class_name feels like it was hacked in (for lack of a better explanation)
I'm currently rewriting the GDScript parser and this proposal was brought to my attention since it's a good time to settle it.
I see no real consensus in this thread. People seems to want to change it but there's no clear winner in the alternatives.
To gauge opinions from the community, I started a Twitter poll: https://twitter.com/vnen/status/1257705167077679104
I do have my favorites, but I'll avoid pushing what I want just because I'm writing the code. Though some of the options makes parsing harder for no much gain, so I'll refrain from implementing them (unless there's no better alternative, which doesn't seem to be the case), and didn't put those in the poll. Twitter doesn't make it easy by allowing only four options, so I hope I got the better alternatives.
@vnen I can't vote on that on twitter because I don't have a twitter.
I'm in favor of just doing class Name extends parent and if it doesn't extend from some derivative of Node or Resource - it just doesn't get added to the global menu.
EDIT: I completely disregarded the possibility of inner classes extending from nodes/resources. My mistake.
I'm in favor of just doing
class Name extends parentand if it doesn't extend from some derivative ofNodeorResource- it just doesn't get added to the global menu.
Problem with class Name extends parent is that it looks exactly like an inner class, apart from the colon at the end. It makes it not only harder to parse, but also confusing for people reading the code.
Also, while only Node and Resource types are added to the menu, they are always added to the global namespace if use the class_name (or whatever the new alternative is). The menu presence is a bonus.
@vnen Yeah - you must've been typing the reply before I actually edited my statement. I mentioned that I completely didn't even think about that part with inner classes.
In that case, mark my vote for global as a keyword. (Again, because I don't have a twitter account)
Why do inner class definition should be different?
It may be even better change inner classes too to make these more clear, like:
class Something extends Node('res://icon.svg'):
inner class SomethingElse extends Reference:
@eon-s That adds a level of indentation to the whole script, which isn't great for small displays or split editors :slightly_frowning_face:
Yeah, I like the idea behind the inner class if this improves parsing stuff, but I wouldn't really want to indent script like that.
@Calinou ok, make it without indentation, since it is has the inner prefix it is explicit.
I want to point out that extends is optional. So while I put Name extends Node int the poll I'll not use it because without extends it's the class name hanging there alone, which is weird.
I really like the idea of using inner and global. I think ditching the class keyword altogether would be a good idea, since it's causing so much confusion.
My take on the syntax
global MyClass [icon_path]
extends Object
var _something
func _init() -> void:
_something = InnerClass.new()
inner InnerClass:
global
extends Object
# Maybe global can be used here as an "access modifier", for deciding whether
# the class can be accessed outside MyClass (e.g MyClass.InnerClass.new())
var _something
Maybe instead of inner, something like local? It would more closely match the way global is used in other areas (transforms come to mind)
This is clean:
extends Object
class InnerClass extends Object:
…
This is not:
extends Object
class_name MyClass, "path/to/icon"
class InnerClass extends Object:
…
Solution:
MyClass extends Object, "path/to/icon"
class InnerClass extends Object:
…
This does not change how it functions and doesn't add new keywords.
From the proposed options, in my opinion, it is extends Node as Name.
This option has two disadvantages:
class MyClass extends Nodeextends Node as MyClassHowever, on the other hand, this is the opposite, an advantage. A script is not required to have a global name, but is required to have a base class. It is not necessary* for inner class to specify the base class, but the inner class must have a name.
* Proof
tool
extends EditorScript
class T:
var a
func _run():
var t = T.new()
print(t.get_class()) # Reference
It is logical that in both cases the obligatory part is written first, and the optional part at the end.
object as SomeClass syntax. (But I don't think this is critical.)It also seems to me that annotations are good here:
@class_name MyClass
@class_icon ./my_class.png
extends Node
# ...
or:
@class MyClass
@icon ./my_class.png
extends Node
# ...
or even:
@class MyClass
@base_class Node
@class_icon ./my_class.png
# ...
Regarding annotations, I think it may be good to have tool keyword to be replaced by @tool annotation, especially when it comes to fine-tuning editor vs in-game behavior (@tool - runs everywhere, @plugin - runs only in editor) as described in godotengine/godot#17592.
@dalexeev
The reverse order of subclass and base class
Yeah that's what makes me cringe too, I always tend to write class_name Name extends Node, but I'm biased as a C++ dev. extends Node as Name seems to be familiar to those with Python programming experience (import os.path as path ?), and I'd personally like that we remove the association with Python for the good sake as people tend to request GDScript to look like Python nowadays, at least that's my impression.
@Xrayez The problem of notations like Name extends Node, global Name extends Node etc is that it seems that name is a required attribute of each script. What is wrong with GDScript, most often a script is an unnamed class.
There are two ways to avoid this:
In other cases, I say "GDScript is not Python", but in this case it seems to me that this is the best way available, although not ideal. :smiley:
it seems that name is a required attribute of each script.
That's not true.
Without name:
extends Node
With name:
ClassName extends Node
ClassName extends Node
Not good, because all other entities use the preceding keywords: var, const, enum, signal, func.
My personal opinion, but usually something optional is added at the end, and not at the beginning of the line.
My 2cts:
In another language, the concept of class is not tied to a file, a file represent somewhat of a namespace/module which may contain classes, functions and constants, and the only things that are exposed are those which are explicitly exported via a dictionary…
And we can also create "meta-modules", which import some part of others, and export them under its own "_namespace_".
So, a big plus for those who like to:
Plus it's just getting ride of the current class_name keyword, keeping the class and extends ones and tweak them a bit, and wrapping each file/module load or preload in a function that return only the module dictionary, which enable any script that load them to get only the dictionary, which would act as a namespace , or only the required exposed parts we want via destructuring, but we don't have destructuring in GDScript (yet ?)… :cry:
Side effect, this is very nice for when the lambdas will be available :wink:
Example:
myModule.gd
enum UnitStatusEnum {UNIT_NEUTRAL, UNIT_ENEMY, UNIT_ALLY}
class MyClass extends Node:
func _init() -> void:
var b = SomeInnerClass.new()
print(b.a)
class SomeInnerClass extends Node:
var a: String = "Hello" setget a_set, a_get
func a_set(new_value: String) -> void:
a = new_value
func a_get() -> String:
return a + " World !"
module = {
"UnitStatusEnum" : UnitStatusEnum,
"MyClass" : MyClass,
}
myOtherScript.gd:
const myModule = preload("myModule.gd")
onready var unknownUnit: Node = $unknownUnit
func _ready() -> void:
var something := myModule.MyClass.new()
if unknownUnit.status == myModule.UnitStatusEnum.UNIT_NEUTRAL:
print(something.a)
_P.S. The language highlighter for gdscript on Github is not yet aware about static typing, so I trolled it by specifying python as language, hence the "strange" coloration…_
_P.P.S. I just don't care about icons, but as annotations that would be cool I guess…_
I personally like the treating files as a module approach, that's how python and javascript both handle things. Honestly, I also like the explicitness of using preload instead of ever relying on the classdb because doing so means you have side effect free code and you don't have to worry about namespace collisions in your classdb when designing reusable libraries. The only thing stopping me from only using preloads is class_name is the only way to expose custom types to the editor, and preloads can't be used for type hinting. If the type hinting thing could be fixed, the I could honestly live without ever using class_name.
But, to throw out an alternative approach, have we considered possibly avoiding having this defined in code at all. Instead you could have an explicit file written by hand, akin to Java Spring Beans, where you register types that your project uses. I know this is currently already built for you automatically in your project.godot, but since it's only for gdscript it feels limited.
I guess it depends on how "easy" it is for users to understand that's what they'd have to do to expose types to the editor. Would also make it simpler I feel to allow defining types in other languages and native libs so we have a consistent way to do it. It would also be helpful since it acts as a form of documentation for addons of what they intend to expose to your project.
project.types
---
MyResource:
source: res://resources/my_resource.gd # module is the class
icon: res://resources/my_resource.png # custom icon in editor
OtherResource:
# support exposing inner classes
source: res://resources/extra.gd/OtherResource
icon: res://resources/other_resource.png
MyCSType:
# support paths to other languages and their exposed classes
source: res://resources/example.cs/ExampleClass
# extend this types by including types defined in an addon
:include res://addons/cool-library/project.types
:include res://addons/music-mixer/project.types
@dalexeev
A script is not required to have a global name, but is required to have a base class.
As I have mentioned previously: extends is optional. So both are optional (except for inner classes which do require a name).
@kane-thornwyrd
Plus it's just getting ride of the current
class_namekeyword, keeping theclassandextendsones and tweak them a bit, and wrapping each file/moduleloadorpreloadin a function that return only themoduledictionary, which enable any script that load them to get only the dictionary, which would act as a namespace
You can already do this without the dictionary. The difference is that the file is also a class. And here you didn't mention how to make the class global, which is the main point of this feature (and also has the effect of adding them to the add node/resource menus).
So, a big plus for those who like to:
- be explicit rather than implicit
- enforce encapsulation
The thing is that GDScript is a language meant for fast prototyping. When we make things too much "explicit rather than implicit" we now add extra boilerplate that gets in the way of writing code, defeating the purpose.
@nhydock
[...] preloads can't be used for type hinting
They can: just do const MyClass = preload("script.gd") then MyClass can be used as a type.
But, to throw out an alternative approach, have we considered possibly avoiding having this defined in code at all. Instead you could have an explicit file written by hand, akin to Java Spring Beans, where you register types that your project uses. I know this is currently already built for you automatically in your project.godot, but since it's only for gdscript it feels limited.
The plan is to have this in C# and GDNative eventually.
The problem I see in this is that it adds unnecessary friction. Even if we make an UI for this, having to add everything you want in the list takes time, and you might forget. For people using class_name all the time, it would be bothersome.
You can see the class_name approach more like C# does: you make one class in each file but every class is available without explicitly importing them. Sure it depends on namespace, but GDScript will have namespaces eventually.
@Xrayez Python people being familiar with the as approach is because of the module importing, which Godot doesn't have really. Classes in Python is just class MyClass(ParentClass):.
@vnen Having it as global class for ones to be in the global space would make it quite easy to just make a global attribute in C# to have it register. Perhaps @neikeq would be able to chime in on how the different approaches could be handled in C#.
@Duroxxigar
I previously worked on building a C# attribute that can register "script classes", however, I ran into issues getting the CSharpScript code to properly parse and detect the attribute. I'd asked for help on it before, but never heard back from anyone. In the meantime, the branch has lagged behind in commits and needs to be rebased due to the 3.2 release and subsequent core revisions.
[ScriptClass] // assumes "MyClass" with no icon
[ScriptClass("MyCustomNameClass")]
[ScriptClass("MyClass", "res://MyClass.svg")]
public class MyClass : Node
{
...
}
I also have another branch that adds Script Class support to VisualScript that likewise needs to be rebased, but was working at the time. I was trying to get VisualScript, C#, and NativeScript all set up and working with the same script class features before submitting a PR for #22, but never quite got that far.
2cts
@icon "res://My/icon.png"
class MyScriptClass extends Node
func my_function():
pass
# Class definition before a colon - Inner class
class SomeOtherClass:
func my_other_function():
pass
@tool can also precede the class definition.
@icon isn't required, and has a default value as proposed by others above.
I still feel that handling this inside the code becomes problematic. While C# and GDScript are the official languages of Godot, there's plenty of other language bindings out there that would have to figure out themselves how to integrate with this kind of functionality in their own way to expose classes to the Editor.
Since this is largely Godot specific metadata, couldn't there just be exposed fields in the editor on any recognized "script" resource that would allow you to register it globally and provide details? Kind of like an optional sibling file such as import settings on other resource types. That way it wouldn't dirty the code, it could work for any language, and it'd be easy to crawl any project to discover which scripts should be registered in the editor and the classdb in an automated fashion invisible to end users of scripts.
@nhydock I think this has both pros and cons. The pros is that everything seems to become manageable, and the cons is the lack of the scripts being self-contained (easy to copy a single script file to share entire functionality), and that's where Godot development philosophy shines: simplicity (but I personally think we should also strive for completeness given this simplicity, which is a bit harder to achieve as it requires taking a few steps forward to anticipate what users will need in the future, and I'm not necessarily talking about future-proofing which can be considered bad practice by some).
I think both systems could coexist if either can solve these limitations. For instance, in order to fetch an icon from a script, currently it requires the GDScriptParser to actually fetch the path to icon by parsing scripts (entire script, not just icon AFAIK), but that also leads to numerous performance issues like godotengine/godot#27333. Because of that, we have a huge list of icon paths pre-parsed and saved to project.godot file to avoid this (still it's easy to make mistakes which re-introduce performance issues if you're not aware about this particular implementation godotengine/godot#39793), which could as well be done on per-script basis (script import/metadata as you say).
All of this doesn't matter because I hope that vnen likely works on fixing this already, and who knows better.
@nathanfranke
class MyScriptClass extends Node # Class definition before a colon - Inner class class SomeOtherClass:
I still think this would be a little too confusing. This would imply that the colon is all that separates the main class from an inner class, which is much too fundamental of a difference than it is for other things that support optional colons (e.g. static typing). It also just makes the code overall less readable since it can be easy to miss a colon if you are reading code quickly.
# some_class.gd
class SomeClass extends Node:
pass
func do_something():
pass
Any beginner who reads could easily become confused and think that SomeClass is the global name of the script when in fact it is a nested class: load("some_class.gd").SomeClass where some_class.gd is a Reference type.
+1 for @icon and @tool becoming annotations though.
@nhydock
While C# and GDScript are the official languages of Godot, there's plenty of other language bindings out there that would have to figure out themselves how to integrate with this kind of functionality in their own way to expose classes to the Editor.
Currently, every language has its own implementation of the ScriptLanguage::get_global_class_name(script_path, icon_path, base) method. Creating a core interface with per-module implementations of that interface is extremely common, and allows each language to dictate for themselves how they wish to register the information. This kind of flexibility gives a lot more power to the feature.
Since this is largely Godot specific metadata, couldn't there just be exposed fields in the editor on any recognized "script" resource that would allow you to register it globally and provide details?
This is actually the approach I took for NativeScript when I added support for it. If you make a .gdns file, you can see that it has script_class_name and script_class_icon_path properties. It does this within the NativeScript-specific implementation. However, this solution only works for non-text-based languages because it stores this information within the resource directly. If the content of the resource is already text (like GDScript/C#), then you can't even "save" the resource without invalidating the save by triggering a modification to its data.
However, VisualScript which uses a visual editor and NativeScript which is compiled into a separate dynamic library first and is just ultimately metadata of class info anyway - these two languages can take this approach. However, I have already added support for VisualScripts to define script classes within their visual editor inside a branch of mine (P22/script-classes), and I would like, in the future, to modify the NativeScript approach so that users must define the class name from code somehow (e.g. in the register_* methods in C++ and whatnot). That way, the full context of features can be delivered purely by sharing source code rather than arbitrary resource files.
That way it wouldn't dirty the code, it could work for any language, and it'd be easy to crawl any project to discover which scripts should be registered in the editor and the classdb in an automated fashion invisible to end users of scripts.
I think "dirtying the code" is actually the intent of it. The team wants source code alone to be able to deliver all of the information necessary.
It already has the potential to work with any language. There's just a little work that needs to be done to spread it around.
The engine already crawls the project and detects changes without issues.
It wouldn't be invisible to end-users either way. Whether it's a centralized script configuration GUI or the source code, end-users would still have the ability to view the information. And I'm 100% sure that reduz would be adamant that we should not be hiding information from users even if we could find a way to better hide it.
inner_class maybe. (Maybe, I still think class is better everywhere)
Although I don't think inner classes should be in the style-guide since I've always used one class per file.
I like inner_class, but I had this discussion on Discord before and someone was very against it, I forget who.
By the way, I still stand by my original comments above. It would make sense to allow inferring the class and icon name from the file name, since you usually wouldn't want to mismatch these aside from a casing difference (which can be converted automatically).
Could someone explain to me why this is not the cleanest?
extends <someclass> as <class name> [icon <icon path>]
E.g.
extends Node as MyGame icon "./assets/icon.png"
or
extends Node as MyGame
@silocoder I don't really like it personally, but regardless, the biggest downside is that this looks different from other object-oriented programming languages. Java has class MyNameHere extends BaseClassName, C# has class MyNameHere : BaseClassName. We want the language to be beginner friendly, in cases where is reasonable to do so.
Java and C# not exactly beginner friendly. Why invent yet one more keyword when you can extend extends with couple of optional keywords. Saves parsing, documentation, learning new syntax.
2cts
@icon "res://My/icon.png" class MyScriptClass extends Node func my_function(): pass # Class definition before a colon - Inner class class SomeOtherClass: func my_other_function(): pass
@toolcan also precede the class definition.
@iconisn't required, and has a default value as proposed by others above.
The difference between this and what's implemented in my refactor are minimal. Annotations require parentheses, @icon isn't inferred from the path, and you use class_name instead of class (again, to not be ambiguous with inner classes).
Could someone explain to me why this is not the cleanest?
extends <someclass> as <class name> [icon <icon path>]E.g.
extends Node as MyGame icon "./assets/icon.png"or
extends Node as MyGame
You have to consider that both the type and class name are optional. Without the type this would be just as MyGame which looks out of place on its own.
what about just doing so?..
class MyGame extends Node:
export(float) var my_float
class MyGameSub:
export(float) var my_float2
its more consistent with how classes work
what about just doing so?..
class MyGame extends Node: export(float) var my_float class MyGameSub: export(float) var my_float2its more consistent with how classes work
It sucks to write code like this because the whole file has an extra level of indentation. Since you cannot have code outside a class and only one class per file it's not convenient to adopt such notation.
And again: class name is optional and extends too (it defaults to Reference). So this line is not always present in a script.
what if it was more like so?..
class MyGame extends Node
export(float) var my_float
#sub classes must have a : and indentation
class MyGameSub:
export(float) var my_float2
what if it was more like so?..
class MyGame extends Node export(float) var my_float #sub classes must have a : and indentation class MyGameSub: export(float) var my_float2
Isn't it already like this in 3.2? (with the exception of the inner-export)
EDIT I'm a moron. class_name -> class
@FlashDaggerX nah your not an moron.
also I just realized we would need a way to tell weather or not to export.
may I suggest this..
#exports resource
export class MyResource extends Resource
export(float) var my_float
and
#doesn't export resource
class MyResource extends Resource
export(float) var my_float
That doesn't really make sense. How would you access the variable?
Try this instead
export(MyResouce) var my_res
Unless I am misinterpreting what you mean by export
@nathanfranke I mean adding the resource to the resource list like what class_name does currently
what if it was more like so?..
class MyGame extends Node export(float) var my_float #sub classes must have a : and indentation class MyGameSub: export(float) var my_float2
I feel like we are walking in circles. This was already proposed and I already commented on: https://github.com/godotengine/godot-proposals/issues/209#issuecomment-656714044 (TLDR this creates unneeded ambiguity with inner class declaration).
Side note: it doesn't make sense to use export on inner classes since they can't be attached to nodes.
well then the next best thing would probably be..
MyClass extends Node
export(float) var value
After reviewing most of what has been said, it seems like the least contentious alternative to class_name is simply the straight-up removal of it, as per @Shadowblitz16's latest comment. The only opposing view I found on it was https://github.com/godotengine/godot-proposals/issues/209#issuecomment-624580269 which was concerned that not having any prefix made it less consistent with the rest of the syntax and that it put an optional section at the start of the statement which breaks conventions.
So, I think it really just comes down to whether people think that's fine or not. Most people upvoting seem to support that syntax over requiring the use of class_name. And personally, I like the idea of the implicit icon path, although it may be convenient to allow ProjectSettings to have additional icon path inclusions so that they don't necessarily have to be kept in the same directory as the script (e.g. I often have a /icons folder where I put such things).
hence going back to my suggestion :) which does not break convention.
extends Node <class classname> <icon somepath>
Examples of variations:
extends Node
extends Node class AmazingGame
extends Node class AmazingGame icon 'game/icons/icon.ico'
extends Node icon 'game/icons/icon.ico'
After reviewing most of what has been said, it seems like the least contentious alternative to
class_nameis simply the straight-up removal of it, as per @Shadowblitz16's latest comment. The only opposing view I found on it was #209 (comment) which was concerned that not having any prefix made it less consistent with the rest of the syntax and that it put an optional section at the start of the statement which breaks conventions.So, I think it really just comes down to whether people think that's fine or not. Most people upvoting seem to support that syntax over requiring the use of
class_name.
Well, except also that I always mention but everyone seems to forget that both the class name and the superclass are optional. so MyClass extends Node doesn't look so bad but without the extends it's just MyClass alone in the file, which is kind of weird to say the least.
I also heard from some people that the main problem is not in class_name but in the fact that it must be in a different line than extends. This is solved already since in master you can type both on the same line.
And personally, I like the idea of the implicit icon path, although it may be convenient to allow ProjectSettings to have additional icon path inclusions so that they don't necessarily have to be kept in the same directory as the script (e.g. I often have a
/iconsfolder where I put such things).
The new way is to use an annotation for the icon. We could potentially use an implicit name, but I'd rather have that in another proposal at this point.
hence going back to my suggestion :) which does not break convention.
extends Node <class classname> <icon somepath>Examples of variations:
extends Node extends Node class AmazingGame extends Node class AmazingGame icon 'game/icons/icon.ico' extends Node icon 'game/icons/icon.ico'
Still missing the variation when extends is omitted. If it's supposed to be just class AmazingGame again it conflicts with inner class declaration. Also: icon without name is not really useful because then it won't show on the "create" dialog so the icon won't be seen anywhere (and .ico isn't a type recognized by Godot).
extends should be required if you need classname. Here is current extends definition example
extends Label
class_name AmazingGame, "res://accessories-text-editor.svg"
func pr(text):
print(text)
extends AmazingGame
func _ready():
pr('start')
pass # Replace with function body.
Based on your observation it would look like this. The icon path is optional. Keeps in the spirit of convention.
extends Label classname AmazingGame, "res://accessories-text-editor.svg"
func pr(text):
print(text)
extends AmazingGame
func _ready():
pr('start')
pass # Replace with function body.
extendsshould be required if you need classname
I personally don't like it. Many times I don't need a base class and being forced to write extends Reference in those cases is somewhat bothersome.
Based on your observation it would look like this. The icon path is optional. Keeps in the spirit of convention.
script 1
extends Label classname AmazingGame, "res://accessories-text-editor.svg" func pr(text): print(text)
At this point I don't know what's so different from the current way:
class_name AmazingGame extends Label
@vnen I don't think you can actually do that.
I think class_name has to be on another line
@Shadowblitz16 It can be on the same line. Just tested it locally. But you have to make sure the class_name bit comes first, then do extends:
class_name MyClass extends Reference
Just tested it in 3.2.2.
@vnen
I agree, the possibility of a lone identifier sitting at the top of a file, and that identifier being user-defined, and it having meaning, is extremely confusing. Like, tool makes sense, because it's a keyword. But just having a plain customizable name is very strange. I was just trying to catalogue / summarize the feedback I've seen thus far.
I like the use of annotations for the icon. Makes it a lot more clear what the string is for in that case. :-)
Personally, I'm all for leaving things as they are in regards to the class_name keyword.
...but why "class_name" though? Isn't it obvious, that we're dealing with class names? The underscore really throws off the "pythonic" vibe of keywords for me. Wouldn't just "class" suffice? IMO, it should be illegal to use underscores in language keywords, 'cause they make my visual receptors burn.
@aspenforest If it's specifically the term itself that is the source of annoyance, then changing it to something more explicit about what it is doing might make more sense, e.g. making the keyword global or something. Because that's actually what it does: register the given name as a globally recognized typename to be associated with the loaded Script.
C# uses indented decelerations I still don't see why gd script can't do something like so..
class MyNode extends Node:
var my_var = 0
@Shadowblitz16 I'd argue that adding one level of indentation to the whole script makes the script harder to read on narrow displays for questionable benefit.
this is only due to python syntax though.
also you would be able to define multiple top level scripts in one file this way
If it's specifically the term itself that is the source of annoyance, then changing it to something more explicit about _what_ it is doing might make more sense, e.g. making the keyword
globalor something.
This was one of the options I added to the poll (using global instead of class_name). I'm just not sure that we should change it just for the sake of changing. I know GDScript is already breaking compatibility in 4.0 but should break it even more just because some people don't like the keyword? People are already used to class_name, redundant or not.
BTW GDScript has no obligation of being "pythonic" because GDScript isn't Python.
Of course, GDScript is not Python, but nevertheless heavily influenced by it.
My rationale is that if you're already breaking stuff, then break a lot of stuff, 'cause people
are going to have to refactor anyways and they would rather refactor once, than
keep refactoring every once in a while when the language spec keeps changing.
Edit: I wholeheartedly support the keyword global instead of class_name, if it
accurately describes what it is doing with the script.
P.S. Thank you @vnen for your dedication and work improving the GDScript.
@Calinou so wait class_name is not being replaced by class?
I think forcing people to name their classes is a good thing while still having the option to export it
@Shadowblitz16 It seems class_name is here to stay for the foreseeable future.
I think forcing people to name their classes is a good thing while still having the option to export it
I don't think so. If you don't plan to reuse a class anywhere, there's no real point in giving it a name. Also, until https://github.com/godotengine/godot/issues/27333 is fixed, this would be counter-productive in any sizeable project.
forcing people to name their classes is a good thing
No. For example, because it is possible to compile scripts at runtime.
func _ready():
var script = GDScript.new()
script.source_code = "func run(): print('Hello!')"
script.reload()
var instance = script.new()
instance.run() # Prints "Hello!".
@dalexeev But godot engine classes are already named so if your implying it's an issue with mod script sandboxing, not naming your classes doesn't fix the issue
I you want something like sandboxing then godot should have a editor to allow you to select what classes and functions end users can use
Most helpful comment
why not the other way around?
having a .gd script file like: