I had been writing softwares for a long time in Java and switched to Scala a few years ago. I always wanted to make my own game but never had the chance until now. Finally, I made up my mind to give it a shot so I just started to learn C# and Unity, and it’s been a quite fun experience so far.
I started making some sort of a generic framework to build my game upon, but I’m not too sure if I’m doing it in a correct way, so I wish to get some feedback from more experienced developers from here before I proceed further.
What I’m especially want to know is, if I’m using the language correctly, like adhering to the recommended coding conventions, choosing the right design approaches, or in general, whether or not my code is following best practices that a seasoned C#/.NET developer would recommend.
For now, I’ve written a few classes for camera and movement control:
I’m particularly struggling with the lack of mixin(composition) feature in C#, which I’ve relied on so much when I’m writing in Scala.
So, if you have any suggestions about the code, like using a different API, or design approach (i.e. Replacing event delegates with Rx), please share your thoughts.
Thanks in advance!
P.S.: Please don’t mind the project description. It’s obviously written in a tongue-in-cheek manner.
I’m going to first point out that a lot of C#/.net conventions and Unity actually don’t play well.
many conventions aren’t highly concerned with GC, where as unity (due to its outdated runtime) GC is a huge concern. For example the standard ‘event’ structure of EventHandler(object sender, EventArgs e) isn’t very common place in unity (though I personally usually stick to it out of tradition).
a lot of unity developer outright ignore many conventions just because. C# isn’t their concern, developing for Unity is their concern.
I can definitely see your heavy Java background. Especially with your Pacing factory.
So some things I’d point out.
much of your design ignores the cost of GC. This can cause much annoyance down the line. For example, your Pacing class returns ‘new’ Pacing objects with every creation. Thing is the fields of the Pacing class are immutable. You could just have static singleton instances for each Pacing (walking, crawling, crouching, etc) and return the same object each time. It’s ok for multiple Walkers to reference the same Pacing object… like we said, it’s immutable anyways.
your design ignores any concern for serialization. A big part of making games in Unity is going to be creating scripts inheriting from MonoBehaviour, that are than attached to GameObjects, and should be configurable through the inspector.
Your composition is going to come forward through the Component pattern used by GameObjects. Your Walker and Locomotion might be 2 distinct components on the GameObject, and you can have one get the other during Awake. If Walker requires a Locomotion, it can even decorate the class with the ‘RequireComponentAttribute’ forcing the GameObject to have both.
Note, unity monobehaviours do not support constructor. I know… I know… this is freaking annoying. But again, set up is often done through the inspector, so it’s no big deal.
I too came from a design background like you did… but slowly over the years had to move away from it to a more unity friendly design. This API will show many ‘odd’ signs in it as I wrote stuff, and then later found out I had to backtrack and redesign it to fit a more unity-centric design. A lot of it being designer focused, rather than programmer focused.
For example I created my own custom attribute called ‘DefaultFromSelfAttribute’ for easy composition in the inspector.
As well as ‘TypeRestrictionAttribute’ to support type restriciton of interfaces, since unity doesn’t actually like you having serialized fields typed as an interface.
Note… my Timers namespace - useless. My Camera namespace, in a weird limbo state right now, I only really use the CameraManager and the UnityCamera at this time.
Stuff like Collections, Scenario, Scenes, StateMachine, Tween, Utils (inside SpacepuppyBase)… I use constantly. I also use my Movement, Anim, AI, Input, Movement, Spawn, and Waypoints libraries a LOT (though I only recently added these to this repository, so the builds don’t actually contain them).
@lordofduct That’s the exactly the kind of advices I wanted to hear In particular, I didn’t think much of serialization & GC, but your comment made me realize at least couple of places in my code which could be problematic in that regard.
As to the issues of configurability and instantiating MonoBehaviors, I suppose I should have mentioned that I’m using Zenject first, which enables me to stick to the traditional non-Unity way of things as much as possible.
I guess I could probably take some ideas from studying your works, and see if I can improve my code with them.
Thanks much for your feedback, it was really helpful for me!