[GeneralWarning]
Unity is a game engine, a development platform, not a framework you can simply choose to use or not to use. Similar to writing extensions for Adobe products or any other existing software, you’re limited in your access and methodology to those supported by the software. Trying to go for a pure OOP approach will only hurt you- not just because OOP is deeply flawed itself (so swapping one implementation for another is just extra work with little payoff), but because swimming against a current is hard, if not impossible.
Unity has no Main() entry point, nor is there some core services lookup where you can just swap out their systems with your own. What you’re trying to do probably isn’t possible even in theory, but even if it were, in trying to rid yourself of what you see as the mistakes in fundamental methodology, you also rid yourself of every benefit of actually using Unity to begin with. If that’s what you want, you should go check out Godot instead, as that’s so raw as to not even really possess a “methodology” you can go against.
I won’t really argue with you on this (I’ve had enough of that lately), just consider it a warning you can heed or choose to ignore. shrugs
[/GeneralWarning]
Getting to the specific issues though, operations having to do with in-scene interactions are not really negotiable. If you go check out the Unity source code, you’ll only see about half of it (the half that’s in C#). The source for the unmanaged side isn’t available, and that side is where the physics interactions lie, among other things. Colliders, Triggers, gravity, and many other elements simply aren’t able to be directly manipulated in a way that would support moving outside of the MonoBehaviour paradigm.
So you should change your mindset IMO- if you want to remove yourself as much as possible from the encouraged workflow of GameObjects and MonoBehaviours, use those only for things that have explicit in-scene interactions. Create your own external services or MVC pattern or whatever you like, but when you need something to be done in the scene, use proxy objects to manage it. In other words, generate the scene object either manually using “new GameObject” and “AddComponent” and the like, or instantiate from prefabs, then whenever the owner service needs scene information / interactions, it can run them through its proxy. This is entirely possible.
One of the first things we did in our latest project is to create a service that handles update event calls- Update is called once per frame on GameObjects/Components in the scene and can’t really be wired into externally. A service class that isn’t a part of the scene has no way to receive these update calls, so we create a proxy object in the scene, set it to DontDestroyOnLoad, then add a MonoBehaviour that we’ve made to invoke delegates every time Update, LateUpdate, and FixedUpdate are called on it. The service sets these delegates, and receives the invokes, then fires events that all other services can subscribe to.
At the cost of a single in-scene GameObject and MonoBehaviour, all services (managed through a service locator) can now be notified whenever one of the various Update calls are made. We have similar services/proxies that serve as in-game Object pools, UI management roots, etc… There are a lot of things that simply must exist in some form in the scene, but the proxy approach helps abstract that out so that knowledge of the scene and its interactions isn’t necessary by consumers of various services.
That’s just one example, but it should give a good idea of how you can possible manage some of what you want.