My urge is to lift logic up to the parent of any group of interactions, and let nodes just be renderers. But I understand that is absolutely not the design philosiphy of Godot, and probably won't scale.
My urge is to lift logic up to the parent of any group of interactions, and let nodes just be renderers. But I understand that is absolutely not the design philosiphy of Godot, and probably won't scale.
The default approach in game engines is to bind data including state to an actor, then have define per actor, in a given state, how it modifies its own and other actors' data over a frame (usu. processing user input as necessary), and how it renders to the screen. This is what game engines are designed to make easy, by providing something like an "Update" and "OnDraw" virtual functions you fill in for every element in your world.
Inevitably every application needs to have a few "directors" who need to take care of business that can't be isolated to an individual "actor" or per-frame state manipulation, and these are usually structured around async-awaits, callbacks, is a thread handler/dispatcher. But if you go overboard with directors, actors that operate like directors, or actor-director interactions, you're inviting trouble in game-engine-land.
There are some alternate paradigms like "entity component system" and entirely callback-driven event-driven game architectures, but they tend to never exist in purity without the actor-update model.
I almost think if someone were to build applications with Godot (or another engine) that you would need to mentally translate from something like React components and their data to the actor/sprite+state in an event loop model.
For example a todo list in Godot might have an actor that is the checkbox with some text next to it and the state of checked/unchecked, then allow the user to click something which generates more of these actors/sprites dynamically. I think where it really becomes cumbersome with a game engine is no real organization of a scene exists without you explicitly laying out x,y pixels. There are concepts of "relative" but common application concepts of pagination, etc. would be hard to pull off in a simple way in the code I would imagine.
I’m trying to build a circuit simulator. It’s got two main problems to solve: placing and connecting drag and drop electrical components, and the simulation itself.
How would you go about this? The existence of the sim means there’s already guaranteed to be some global manager that understands the current layout and can compute from it. What I don’t know is if that means most things should be handled that way.
Should components be heavy, and make their own changes to the tree, and then let manager read the tree in and build up its schema that way? Or should components be just emit everything to the manager, who then can both build the tree and a state representation.
The second feels cleaner for this use case, but it feels not standard for Godot. Maybe it’s a bad project to start with.
I did build a basic platformer too, and found the distributed logic much easier to deal with there.
This is where game engines are annoying. You have to do "collision checks" between your cursor and any points of interest, like a slot or a grid or a connection. Usually in a 2D use-case like yours, you can just do a geometry condition (if component.x > slot1.x && component.x < slot1.x + threshold...), especially while your UI elements are regular shapes. Engines are fast at doing lots of native collision checks in 3D and with irregular shapes too with an engine-specific "collider" for each element, as it's expected to do collision checks for almost every visible actor in every frame in a game.
> and the simulation itself
Depending on the eventual complexity of your circuit simulator, there are pros and cons to different approaches. My undergraduate electronics degree is fading from memory so I might not have the best advice here.
A purely node/actor-based computational simulator is one approach, which reduces dependency on the global manager with knowledge of the layout or handling of the computation. You have to define how each component responds to charge at its input nodes, and manipulates charge at its output nodes, ie, define its response curve in terms of charge. You just have to work with the differential time-equations for computational analysis of electric circuits, not the closed-form analytical ones. ie, substitute I with dQ/dt (charge over time) everywhere, and in every "update" iteration (or "tick" or "frame" or whatever your engine calls them) multiply the differential equation by timeDelta (every engine provides timeDelta as the time elapsed since the last frame). I would give this method a try scoping the project for very simple components (maybe just DC power source, LED, resistors, connector junctions) that can be sized to a grid of discrete sizes.
This can however feel like rediscovering electricity from first principles because traditional circuit equations usually don't work with charge or with time, since they're defining the steady state behaviour.
If you would rather work with traditional steady state electronics formula for each component type, I think your second solution is a decent approach, "passing the result on" from one node into the next (which is the same as making changes to the tree iinm?). Technically using the steady state equations renders ticks irrelevant, other than updating to reflect any changes in the circuit, or if you have time-dependent component behaviour (like an AC signal or an inductor), but that's still a nice feature to have.
I think you should try avoiding reliance on a global manager for anything that goes on in the simulation as much as possible, because I sense you will tend towards "centralized application logic" if you allow yourself that luxury. The whole point of game engines is to break down your logic into different actors that interact with each other, and a simulation is exactly that.
"Parent of any group"
"Business logic"
Are just concepts you made up in your head.
Those are just nouns your using. They don't really mean anything.
You design your abstractions, you design your scene trees, such that you create an abstraction level that represents your "business logic".
Ie, in our games, we have a FriendsPanel which has a script that will render your friends list. It exposes it's dependencies: a FriendRow packed scene, and a reference of which nodepath to add the FriendRows to.
The game itself, well, the "business logic" will be different for each style of game and style of developer. I'm 10 years deep in indie game dev. My advice is to just fucking send it. My code now is pretty good, but gameplay code tends to be complicated AF and usually is huge procedural blocks. Resist your urge to modularize gameplay code. Your years away from being able to do that. Copy + Paste is a better modularization technique than whatever gong show abstraction your thinking of anyways.
(The best gameplay abstraction code I've ever seen is basically object type based collision response handling, a system like this is great for many games)
Your urge to refactor shit is your brain trying to do something that will take less calories than solving the problem of "why is this game not fun". It's procrastination.