hey Twiik,
Last things first, I did the music and sound myself (I’ve produced electronic music for many years). I’m glad to hear you liked it
I’ll try to go step by step through your questions, but holistically I understand your complaint. Everyone has to find a workflow that balances readability, efficiency and makes sense for them and their projects. In this project I actually wrote the game first and then collaborated with Mike Geig and James Bouckley (gameplay programmer on the content team) to write the final scripts.
One of the goals of an intermediate as opposed to a beginner tutorial is to help our users to stretch and learn new concepts. In this case some of the concepts that I think are a little bit of a stretch for some but are definitely quite important and valuable are some you mentioned: singletons and inheritance, and generics which you didn’t mention. So your point that it’s “intermediate for the sake of being intermediate” is partially true. This felt like a good opportunity to show those concepts and so we decided to include them. We could have cut those parts or worked around them to make it simple but we felt it was a good teachable moment that would help people.
Now to your specific questions:
1. Why have the loader class which instantiates the two managers instead of just adding the two managers to the scene manually?
This prevents cases where you might inadvertently cause two managers to be added to a scene, especially with both set to DontDestroyOnLoad. If the GameMeaner is set to not destroy, and then you reload the scene with it in there, you’ll end up with two.
2. What are the benefits to singletons? I usually just have my managers as gameobjects and then I use GameObject.Find in the Awake() function of my scripts to find them. That way I don’t have to access them with manager.instance.whatever each time. I can just do manager.whatever. This benefit in itself I find is big enough to never want to use singletons. Otherwise I just use a static class if I can.
The risk of GameObject.find is that you’ll have a case where the object isn’t there, throw a null reference exception and crash. It’s also expensive performance wise, especially if you’re instantiating lots of enemies for example in a complex scene, each which has to find the manager at runtime during it’s Awake().
3. Why use DontDestroyOnLoad() on the managers and then reload the scene each time you complete a level instead of just have a function that resets the board? Everything happens in the same scene anyway. Doing it this way just seems so unnecessary?
In this case reloading the scene is just easier and cleaner in my mind. I don’t see exactly what about it is un-necessary or particularly undesirable? I actually wrote one version with resetting the board and re-initializing everything and it was just way more work and less reliable.
4. I’m still unsure as to what virtual, abstract, override and protected does. What are the benefits of all this over just using Unity’s component system? Why not have MovingObject as a normal component that you add to both the player and the enemies?
In this case part of the motivation here was to get people to start thinking about inheritance. I actually did try writing a motor component and doing it the way that you said and in the end ending up duplicating my movement code into the enemy and player scripts, which James then suggested I do using inheritance. I can’t remember what the issue I had was originally unfortunately but this would be a good case for you to try both and see what you like.
That said I think you should be able to imagine situations where using inheritance in a larger project and having lets say a base class Weapon which MachineGun inherits from would be sensible and useful so this was partly an opportunity to start people thinking about the potential there.
5. Is that optimized move code really necessary? I find it very hard to read. Is it something that will make a difference in a game like this or will it only be noticeable if we were to have thousands of enemies moving at once or something? I’m thinking about the square magnitude and multiplying instead of dividing parts. Basically part 6 of the tutorial. I find it very easy to relate to the magnitude of a vector as a distance on my screen, but square magnitude becomes just an abstract concept so I stay away from it. Also why do you have to compare it to float.Epsilon instead of just 0? I’ve never seen float.Epsilon before. I didn’t even know it was a thing.
Yes. Generally speaking we try to show best practices that people can learn from in terms of architecture and performance. Because this game is small and simple we certainly could have cut corners and gotten away with things performance wise, but then we would have taught people those behaviors were OK and they might have taken them into larger projects.
Basically, are there performance benefits to doing it this way? With all this inheritance stuff I mean. Will it scale better than components? It’s the Enemy, Player and MovingObject scripts I’m having the most issues with. The rest of the tutorial/project seem very straight forwards, but these scripts just feel much more complex than they could have been.
Short answer, there are not (to my knowledge) performance benefits in terms of speed, but this will allow you to keep your project organized and avoid spaghetti code. For example lets say I had the move code copied in both player and enemy, then realized I needed to change it, I’d have to change two scripts (and potentially forget to change one) etc. Inheritance is pretty standard practice in object oriented programming, check out Mike’s lesson inheritance (as well as some other intermediate scripting concepts) here:
http://unity3d.com/learn/tutorials/modules/intermediate/scripting/inheritance
Hope that helps! And as always, find an approach that works for your skill level and your projects. There are many, many ways to skin a cat.