You, of course, can use a third party solution. You can develop C# games with (in decreasing complexity) Unity, Godot, Monogame, OpenTK, among other solutions.
Making a game without a third party game engine is perfectly viable. You can work with a window toolkit library, a graphics library, and so on. You will have a lot of flexibility... Yet, it is a lot of work. Because of that, it usually only makes sense for small-ish projects.
Making a game and a game engine at the same time is possible. You are not braking any law of physics or anything like that. However, you are likely to lose focus. The requirements of the game engine and the requirements of the game are not the same. In particular if you are making an editor.
I want to note that there are game framework and game IDEs. So a game framework is a game engine in the sense that it is the software component that makes the game run. You know, like an engine. However, often when people think about getting a "game engine", they are thinking about an editor where they can make a game, that is the game IDE.
A game IDE, a.k.a Game Engine, will allow you to build games that use a game framework, a.k.a game engine.
Thus, there are three components:
- The game
- The framework
- The IDE
You do not deploy the IDE. You deploy the game with its framework, the game won't work without the framework (they may or may not be separable on the release build).
Now, if you develop a game without a third party game engine. You would arguably be making the framework and the game at the same time. The problem is separating them.
You have knowledge of the game you are making while writing framework code. You could end up in a situation where the engine is not useful for a different (kind of) game, and tweaking it to be would break your old game. This happens because game specific abstractions made their way into the framework code. The more of them you put in the framework, the easier it is to make the game, and the harder it is to adapt the framework for a different game.
The flip side is that without a game, it is hard to pin down the requirements for the framework.
For a commercial game engine, the framework was built before the game. Thus, it does not know anything about the game. This leads naturally to a situation where the game is a plugin for the framework... and thus, switching from one game engine to another is hard. Oh, and the companies that produce the game engines like that. It also leads naturally to very a versatile framework. This is the state of the art. Although not every commercial game engine is like this.
Good software architecture will dictate to decouple the game. For instance, changes in the framework should not propagate to game logic. Because of this, I’m against the framework requiring to inherit from its classes to use it. You want to be able to make adapters with the right abstractions for the game at hand. Any change to the framework will only propagate to the adapters, and not any further. Of course, if the framework wants inheritance, you can make the adapter inherit, and avoid placing game logic inside them.
Will come back to the idea of adapters.
I would argue against an editor. It does not come naturally to write an IDE, unless you want to release it. What comes naturally is to make a set of tools. Each one for a specific task (such as a terrain editor, for example). And in that, you can often rely on third party solutions.
Do you mean networking? I think you mean networking. Couch multiplayer is not as popular as it used to be.
You need to decide if you are going to do any networking at all, and decide early. Having a big scary thing called network in the middle of your game will wreck your software architecture.
What kind of networking are we talking about? Will there be a central server? Will you do P2P? Should the game engine even decide that?
You want to isolate any external system. That includes input, network, graphics, sound, etc...
Isolating the input means, that you make an interface with whatever verbs the game needs and an adapter that implements the interface and uses some input device. If you want to support more input devices, you add more adapters.
Furthermore, who says that the input comes from an input device? It could come from a recording of inputs. This is useful for those good old game demos that arcade games did. It also useful to record a game session and replay it later. It is also useful for debugging.
In fact, who says the input comes from the same computer at all? The input could come from another player across the network!
There are two approaches that you may take around the network: send inputs, and send states. That is, you could stream every input a player does and replay them on the other side. Or you could send the state of the game objects and rebuild them on the other side. in fact, I would argue for supporting (if not using) both.
However, over which protocol? TCP? UDP? Websockets? WebRTC? How about using the Discord SDK? You know how this goes... you make an interface with the network operations the game needs, and then you implement the adapter with the underlying protocols you want. If you decide to switch protocols, you can implement another adapter.
And, yes, the same goes for physics. Perhaps you want to use Box2D, or Bullet, or PhysX... whatever, you make an interface and an adapter, you know the drill.
Similarly... OpenGl? How about DirectX or Vulkan or Metal? Well, you make... hmm... This one is trickier, because you have to deal with a shader languages. Perhaps we can make a custom langu... wait...
-- xkcd: Standards
I'd say go full GLSL and thus OpenGL. Screw DirectX and Metal. And if you decide to switch to Vulkan, you can convert GLSL to SPIR-V with KhronosGroup/glslang.
With that said, you probably should still make an adapter for OpenGL, and expose an interface that understand loading models and scenes instead of buffers and draw calls. You may also want it to compose shader programs.
I don't want to tell you here what to do about your game loop, or whatever or not you should use ECS, or stuff like that. You can research and figure what you want.
However, you should have a set of interfaces under you control, which the game can use to work. Keeping the game code free from details about the hardware or the libraries that the framework uses.
To pick what adapter to use should depend on configuration. You are going to need a composition root.
Oh, do not forget some good infrastructure: error handling, logging, loading and saving configuration, etc.
Thus, by virtue of software architecture we can distinguish:
- The game
- The interfaces
- The adapters
- The framework
- The composition root
- The IDE or other tools
You can choose to make modules like this, for example:
- The game
- The framework, composition root, interfaces and adapters
- The IDE or other tools
The company that makes the engine is in control.
That is not what software architecture would suggest, but it leaves the less work to develop the game module, while tying the game to the engine. Like the company likes it. You do not have to do it like that, bundle them however you want.
Finally, I want to recommend the video: Write a Game Engine? - WHY and HOW.