utopia-rise / fmod-gdextension

FMOD Studio GDExtension bindings for the Godot game engine
MIT License
385 stars 42 forks source link

Plans for Godot 4.0 and forward. #138

Closed CedNaru closed 4 months ago

CedNaru commented 1 year ago

Godot 4.0 and its new extension system is an opportunity to improve how this plugin is structurally made. In the current Godot 3.x era, the way to go is relatively straightforward: There is one FMOD singleton and every operation is done through it. We all interact with the entirety of FMOD with that singleton, either referencing entities only by their string identifier or some integer returned by a few of the functions (the case for event instances) which in reality is literally the memory address of the instance. On top of that, we have plans to have Nodes for ease of use (with one already implemented by @bitbrain)

The goal is to split the plugin into 2 layers with a more object-oriented mindset :

Most people should be able to do what they wish using Nodes only but the low-level layer would still be available for the ones wanting a more custom behavior.

Discussions are open of course, and the future implementation of it should start after #https://github.com/utopia-rise/fmod-gdnative/pull/123 is merged so we keep its size relatively small. That first step should be to make the plugin run in Godot 4.0 using the "old" model.

I can also foresee ambiguity between what should be put in the low-level or user layers. One example just to boot the discussion but not the only one of course: EventInstances can be used as OneShot, which currently are handled a bit differently in the code (automatically released). In those cases, there are 3 "main" implementations. -Memory management in the low-level layer with EventInstance simply having an isOneShot property -Memory management in the low-level layer with 2 different classes inheriting the same abstract class for the regular events and one-shot events. -Memory management only in the user Nodes, everything is manually handled inside the low-level layer.

Might seem trivial but the idea of "low level" is not really the same between a C++ library and an editor plugin with scripts. So it's better to clearly define it.

bitbrain commented 1 year ago

As discussed an FmodAudioServer that can be accessed via getSingleton() might be a great foundation. This server then could be used to implement all other nodes accordingly. The main difference to the 3.x approach would be that we do not require an autoload singleton at all. Instead, we'd have some sort of thread that runs the server and we could use signals to sync with the Godot main threads.

CedNaru commented 1 year ago

I don't think it's necessary to have a FmodAudioServer running on its own thread. Fmod is already running on its own set of threads so in a sense it's already its own server. The FMOD API exposed by its libs is just a way to send commands to those threads. There is no real benefit in adding one extra layer of thread, it would just add more latency and a need for synchronization.

bitbrain commented 1 year ago

@CedNaru this means however that we will have to rely on an autoload singleton still - unless there is another way to somehow connect to the Godot process thread to update the callbacks.

CedNaru commented 1 year ago

I think it will be something like FmodManager becoming a regular node, not an autoload. You would add a single instance of that node in your scene tree and with the new extension system users will even be able to give it a custom behavior by adding a script on top of it. It's useful, not only for callbacks like process but for other kinds of events like handling unfocusing the windows, taping home in android, and pausing the game.

You can also just make the FmodAudioServer connect to the SceneTree.process_frame signal so you can still have a default update loop.

You can see that like something similar to nodes like Viewport, Camera, and WorldEnvironnement. You always have one basic default one in Godot even if you add nothing to the scene tree yourself. Their behavior is then immediately replaced by the first node of those types added to the tree.