-
-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add OnReady annotation to C# #2425
Comments
As far as I know this is not possible: field assignment is evaluated in the constructor, and For a more compact version of the workaround, I wrote a library (C# source generator, https://github.com/31/GodotOnReady) that lets you write this instead, and it generates the [OnReadyGet("Other")] private MyOtherNodeClass _other; |
I'm not sure this is the right place to ask about this--it sounds like you have a fairly broad C# question.
The way I fixed this in GodotOnReady (without changing the Godot Engine source code) is the generator writes the |
I usually do a lazy-getter using c# 8's private MyNode? someNode;
protected MyNode SomeNode => someNode ??= GetNode<MyNode>("SomeNode"); Course, you still have to make sure not to call the property too early. |
I just noticed this was added to the 4.0 roadmap/project/milestone 19 days ago, and I'm curious--what is the plan for the API? What will it look like to use this feature in 4.0? |
Out of interest, since this is on the 4.0 roadmap but #5216 is not, which is the preferred syntax for the Godot implementation? A variant of #5216:
From the original post:
I personally really like the first variant but since I can't use GodotOnReady because they have hit a roadblock for their Godot 4 implementation (31/GodotOnReady#49 (comment)), I'm looking for an alternative. I assume implementing this directly in Godot would still be feasible? |
I came here to actually create this proposal until I came across this one. I'm not sure about the GodotOnReady roadblock, but this is how I implemented it in my source generator. Please note that this is code thrown together and can/will fail in scenarios (things like nested classes, etc). First I decided to hook in to
This code allows the following variations to be defined.
or no name passed where it assumes the name is the type (just like when you add a type to the scene its name defaults to the type).
While this is hacked together code, it has saved me quite a lot of time just filling out boilerplate _ready functions even though its not a whole lot of keystrokes saved, it just feels like an entirely better flow. Edit: Source generator code was designed from this article https://www.infoq.com/articles/CSharp-Source-Generator/ and it seems the GodotOnReady issue points out that its scanning for implemented methods as I suspected. |
Would not godotengine/godot#62789 probably be preferred. Only case I could think of for an |
I've been looking into this as well and I've come across this https://github.com/firebelley/GodotUtilities#other-notes |
I personally do prefer that, but a significant number of people want to define the node path in their C# code, not in the scene editor, so it doesn't apply. (Or they have even more advanced things in mind.) GodotOnReady supported both of these scenarios in 3.x. In 4.x, I'd still really like clarification from the devs what this issue has actually been taken to mean. |
@31 I think we wanted to support exporting nodes, but since that's already supported with Other features from your library, such as an OnReady attribute for methods, sound interesting but I think it'd be preferable to leave that to third-parties. Currently, implementing some of these features is too cumbersome (or maybe even impossible), but we'd like to give room for third-party source generators to extend current functionality. I don't think there are any concrete plans yet, but I'd like to look into this after the 4.0 release. |
I dont have anything rather technical to add to the conversation. But I've found an I think i spend at least 30% of my time while coding creating a variable to store a node in, calling Unity has Wish i was making it up, most of my classes look like this: Very long example code [Click to expand]public RigidBody2D FrontWheel;
public RigidBody2D BackWheel;
private GpuParticles2D backTireSmoke;
private AudioStreamPlayer tireScreech;
private AudioStreamPlayer musicPlayer;
private AudioStreamPlayer fallDamage;
private AudioStreamPlayer impactDamage;
private Timer impactDamageCooldown;
private RayCast2D groundCheck;
private Control userInterface;
public Gauge FuelGauge;
public Gauge BoostGauge;
public override void _Ready() {
FrontWheel = GetNode<RigidBody2D>("FrontWheelHolder/FrontWheel");
BackWheel = GetNode<RigidBody2D>("BackWheelHolder/BackWheel");
backTireSmoke = GetNode<GpuParticles2D>("BackWheelHolder/TireSmoke");
tireScreech = GetNode<AudioStreamPlayer>("Audio/TireScreech");
musicPlayer = GetNode<AudioStreamPlayer>("Audio/MusicPlayer");
groundCheck = GetNode<RayCast2D>("GroundCheck");
userInterface = GetNode<Control>("UI/UI/UI");
FuelGauge = userInterface.GetNode<Gauge>("Gauges/FuelGauge");
BoostGauge = userInterface.GetNode<Gauge>("Gauges/BoostGauge");
fallDamage = GetNode<AudioStreamPlayer>("Audio/FallDamage");
impactDamage = GetNode<AudioStreamPlayer>("Audio/ImpactDamage");;
impactDamageCooldown = GetNode<Timer>("Audio/ImpactDamage/Timer");
// [..] other stuff
} When they could just look like: [OnReady("FrontWheelHolder/FrontWheel")] public RigidBody2D FrontWheel;
[OnReady("BackWheelHolder/BackWheel")] public RigidBody2D BackWheel;
[OnReady("BackWheelHolder/TireSmoke")] private GpuParticles2D backTireSmoke;
[OnReady("Audio/TireScreech")] private AudioStreamPlayer tireScreech;
[OnReady("Audio/MusicPlayer")] private AudioStreamPlayer musicPlayer;
[OnReady("Audio/FallDamage")] private AudioStreamPlayer fallDamage;
[OnReady("Audio/ImpactDamage")] private AudioStreamPlayer impactDamage;
[OnReady("Audio/ImpactDamage/Timer")] private Timer impactDamageCooldown;
[OnReady("GroundCheck")] private RayCast2D groundCheck;
[OnReady("UI/UI/UI")] private Control userInterface;
[OnReady("Gauges/FuelGauge")] public Gauge FuelGauge;
[OnReady("Gauges/BoostGauge")] public Gauge BoostGauge; Mind you.. that first mess is present in classes where i have a lot of other variables and methods.. I basically have to depent on More examples [Click to expand]This is how it actually looks in a code editor, I have to go all the way down to 10 pt font size to even fit that monstrosity on my screen. I and a lot of other people also prefer to keep our node declarations all the way at the top of classes, which makes this problem EVEN WORSE This proposal might also be able to check if a node is present or not at compile-time in simple scenarios as well, depending on the implementation. It's been YEARS since this proposal was made. Please devs, 👏 add this to 4.2 👏 |
Adding my support for this. To make matters for C# even worse, if we use nullability checks, then we have to declare all the types as nullable: In general, though, this would remove a lot of boilerplate code required in C#. |
I think that the problem with just using You first add the new child node in the editor, perhaps creating and assigning a new script to it, then you have to go to your C# IDE, add a new member variable with the export attribute for the node you just added, then go back to the editor, build the .NET assemblies, then select the root node again to make the editor properties refresh, then finally assign the default value of your newly exported variable to the child node you created. That is a whole lot of steps, especially when you have multiple child nodes you are trying to assign. And it is easy to forget to assign the default value back in the editor. Or when the reference gets lost due to a rename or whatever. For non-node types like numbers, strings, etc. exporting is much easier because you can assign a default value in script so you often don't need to even go back to the editor until you want to customize it. The way I see it, is that most of the people using If there was a magical way to give |
@BlankSourceCode I personally like doing everything from my scripts since it's more verbose; allows me to know exactly what node a variable connects to or what a signal binds to without having to leave my IDE. And I'd also trust my code more since I fear Godot might bug out and remove references to signals or nodes (used to Unreal and Unity doing stuff like that anyway). It's a tad faster to just do everything from my IDE in general. I've always seen An |
@FlooferLand Isn't the problem with node moving/deletion also an issue if you use the If changes in the editor could force a rebuild/source generator to run, then we could maybe replace the runtime error with a compile time error (because you could do some source generation validation). I don't know if that's possible though and seems like an addition that would be nice to have but not necessary to get usefulness out of |
@BlankSourceCode Very fair point XD |
Instead of making the type nullable you can use the null forgiving operator ! and use the following code: |
Another issue with I've had countless bugs from exported nodes resetting due to compilation errors (godotengine/godot#78513) or broken references when copying nodes from one scene to another. So unless we have something like private or scene-only export (#7453) to ensure that these values won't get changed, I hesitate to use |
Hi all, Inspired by work done by @cphillips83 and @cgbeutler, I have written my own codegen solution for this problem. I have decided to go with the getter solution, as it plays much nicer with nullability. I have used it successfully in my toy project, here's a snippet showing usage: [GetNode("./EnemySprite")]
private AnimatedSprite2D? _sprite;
[GetNode]
private RayCast2D? _rayCastLeft;
[GetNode]
private RayCast2D? _rayCastRight;
public override void _Process(double delta)
{
if (RayCastLeft.IsColliding())
{
Sprite.FlipH = false;
}
// ...
} Please go to https://github.com/dkaszews/GetNodeAutoProp to download it and play around. It is very much a proof of concept, so there are likely some bugs to resolve and features to implement, feel free to add those as issues. |
Describe the project you are working on
Any game project written in C#
Describe the problem or limitation you are having in your project
In many cases, a scene has known relatives in the tree which need to be referenced, but cannot be referenced until after the scene is ready. To do this, references would have to be set during the
_Ready()
function. However, GDScript has a shortcut for this utilizing theonready
keyword. This helps a lot with improving the class functionality organization. It prevents the_Ready()
function from being clogged with a bunch of property sets that search for nodes.Describe the feature / enhancement and how it helps to overcome the problem or limitation
Add a
[OnReady]
annotation (similar to[Export]
) that marks a class field as being assigned during theOnReady
step of the scene setup.Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
Adds a new annotation that adjusts the initialization. An example would look like:
I'm not an expert enough on C# to know how to implement this, but I will update this Issue when/if I figure out more details or someone else can help provide them.
If this enhancement will not be used often, can it be worked around with a few lines of script?
There is currently a workaround, but it makes the code messier and does not match existing GDScript syntax.
Is there a reason why this should be core and not an add-on in the asset library?
It is an addition to the built in Godot C# functionality and mimics existing GDScript behavior.
The text was updated successfully, but these errors were encountered: