godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.58k stars 21.27k forks source link

_Init() is not being called for C# scripts. #22633

Open KellyThomas opened 6 years ago

KellyThomas commented 6 years ago

Godot version: dacc3f3 and 3.1 alpha

OS/device including version: Window 7 x64

Issue description: _Init() is not being called on my c# scripts

Steps to reproduce: Create a node with this script:

using Godot;
using System;

public class InitTest : Node
{
    public override void _Init()
    {
        GD.Print("Hello World!");
    }
}

But when it runs no message is printed.

Minimal reproduction project: init-test.zip

star-tek-mb commented 6 years ago

i think you should use _init()

neikeq commented 6 years ago

Is _init actually something other than a GDScript feature? Where is it called from?

reduz commented 6 years ago

_init is the GDScript constructor, its not godot related.

On Tue, Oct 2, 2018 at 2:22 PM Ignacio Etcheverry notifications@github.com wrote:

Is _init actually something other than a GDScript feature? Where is it called from?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/godotengine/godot/issues/22633#issuecomment-426358810, or mute the thread https://github.com/notifications/unsubscribe-auth/AF-Z2wOdvCQwsvCf7qFaHwrRGZVGnrdSks5ug6DkgaJpZM4XEZFu .

KellyThomas commented 6 years ago

It is exposed as part of Godot.Object

http://docs.godotengine.org/en/latest/classes/class_object.html#class-object-init

willnationsdev commented 6 years ago

I guess the workflow in C# is just to use a constructor then? That would make sense.

Edit: Oh, if it's exposed, then idk what. Weird.

reduz commented 6 years ago

Indeed it is, It was hacked on purpose to be there. Wonder if it should be removed or the documentation just changed to ensure it is GDScript only.

On Tue, Oct 2, 2018 at 2:32 PM Kelly Thomas notifications@github.com wrote:

It is exposed as part of Godot.Object

http://docs.godotengine.org/en/latest/classes/class_object.html#class-object-init

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/godotengine/godot/issues/22633#issuecomment-426362084, or mute the thread https://github.com/notifications/unsubscribe-auth/AF-Z21EK530Oef_VmS6vCW204vCZ8wAIks5ug6MXgaJpZM4XEZFu .

neikeq commented 6 years ago

I will hard-code the bindings generator to ignore _init then.

@willnationsdev Yes. However, you will have to add a default constructor as well, since the implicit one is removed if you add a constructor overload. (There was an issue about this but I can't find it).

neikeq commented 6 years ago

@reduz Not sure about removing it from the docs. I would rather make it clear in the method description that it's a GDScript-only thing.

willnationsdev commented 6 years ago

Is _init also used in VisualScript? I had thought it was (in which case, we should clarify that it's for "Godot languages" like GDScript and VisualScript).

fire commented 5 years ago

Is this related to visual script?

neikeq commented 5 years ago

Searching in the source code doesn't give any result that would indicate it's used in VisualScript, so I think it's GDScript-only.

Regarding C#, I'm not so sure about removing it anymore. I'm actually thinking of making it work. The Godot editor has now we create placeholder instances of script classes in the editor for getting the default values of exported properties. The constructor has to be called for those instances. Having a _Init function that would only be called for real non-placeholder instances would be great.

neikeq commented 5 years ago

I'm removing _Init for now in #29140, but I plan to add a working implementation in the future as explained in my previous comment.

Pilvinen commented 1 year ago

Any news on this?

I don't feel C# constructors are the C# version of _init due the way Godot works. C# constructor with Godot are largely useless due to the initialization order. We often end up implementing and manually calling our own Initialize() methods. But. Having _Init support in Godot's .NET version would, for instance, allow for a separate two-step construction and initialization for objects.

@neikeq

lenscas commented 1 year ago

How do C#'s constructors behave diffrently than GDScript's _init in this @Pilvinen ? I never used GDScript so I may be wrong but from what I know about them it sounds like they behave exactly the same?

EnlitHamster commented 1 year ago

@Pilvinen could you expand on your point? I also haven't used GDScript so I would be curious to know

lenscas commented 1 year ago

@EnlitHamster I managed to talk to @Pilvinen about it on discord. However this was a while ago so I might be misremembering so take the following with a grain of salt.

Basically, Pilvinen wants a method that gets called when the node is put into the tree together with its children, similar to OnEnterTree (Or whatever the method was called). However, unlike OnEnterTree it will only be called once.

Ready doesn't work for this (At least, not for Pilvinen) because this is called after every child has had it's Ready method called. Or said another way, the order is the opposite way around for Pilvinen.

Pilvinen was thinking/hoping that _init could be this method in C# but as far as I'm aware this isn't what _init does in GDScript.

Pilvinen commented 1 year ago

That pretty much sums it up.

The problem is that if you want to use Godot in a way where your work flow is code-centric you will get into all sorts of trouble with initialization order when you try to pass dependencies around. Anything that is passed to you from the editor via the export attribute is pretty much guaranteed to work. No problem there. But this is not always the case if you want to primarily be responsible for instantiating your own objects from code, passing them to child nodes that require them and adding nodes to the tree manually.

The work flow becomes reversed from what is customary in Godot.

_Ready of course doesn't accept parameters so you can't use it to pass dependencies around. C# has constructors and while constructor injection is normally a very powerful pattern in C#, this doesn't always work that well with Godot because by the time constructor runs - nothing is ready in Godot, so you can't depend on anything. You can only rely on your own code. If you need some piece of information from Godot, it is not available at this time.

It was my hope that _Init might have been an additional step in initialization which would have allowed clean separation between:

While _Ready would have quaranteed that all child objects are, well, ready, the order would have been reverse with init - allowing us to do away with constructing and using our own Initialize() methods.

I spent a full year studying all the various approaches which could be used to pass data around and get dependencies in Godot in code-first approaches, essentially treating Godot as a library. For me reflection probably worked the best, but also felt like an overkill for something which should be simple.

Godot 4.x largely fixed all the problems which drove me into this in the first place by allowing export of nodes.

But in the end after a year of experimenting I simply finally gave up. The problem at the core wasn't really ever resolved. Godot is difficult to work with if you want to go code-first and ignore the editor. There is room for improvement. And my hope was (a long time ago) that _Init might have been a simple work flow improvement. There was some talk, a long time ago, of adding _Init support to Godot's C# version and bringing feature parity between GDScript and C# versions.

To sum it up.

My thinking was that you would do what ever injection you want with parameter/CTOR injection. Then you would go down the tree hierarchy with _Init and initialize the objects with data from a parent class. Then on _Ready you would come up the hierarchy child-to-parent and do final steps with your fully initialized objects and reliably present objects.

Now, maybe this is not how _Init works in GDScript. I don't know.

But what I do know is _EnterTree would have worked excellently for this exact purpose if it weren't for the annoying fact that it runs each time the node enters the tree vs. once on creation. I also tried using _EnterTree in my experiments and it required adding bool IsInitialized as class member to every object + logic for checking for this. Which became really old really soon. But other than that it did work exactly as described.

This was a long time ago and I've probably forgotten a lot of what the real and practical problems were, but I do believe the out of the ordinary way Godot required networking to be initialized in 3.x was a big part of the issue. And similar practices were also present in other places. But I've forgotten the details of the issues.

And I'm not sure what the situation in Godot 4.x is as I've mostly transitioned to using node exports after giving up on code-first approach.

I'm sure you can figure out the bottle necks for yourself with some experimentation.

EnlitHamster commented 1 year ago

Thank you very much for the detailed answer, it was illuminating.

I am doing some simulation work in Godot, and need to do some particular library loading. I hope I can figure out how to load the various modules just in their specific node to code-split the dependencies, but I'll just have to see what brick walls I find on the way.