About enemy part, i create them as inert GameObjects and they get enabled and come to screen only when time comes. Before that, physical system (collision and update), rendering system (mesh, texture and sprite animation), controller system would not update with these inert objects. World system is keeping track of wakening up these inert objects. Different enemy have different hit points. Player can also gain energy creates by destroy special enemies.
Memory is carefully managed in my game, take an enemy object as example, they only get awaken when at the right timing and then considered by functioning systems, they get destroyed if bullets killed them or they fly out of the screen area. During destroy object, it's shared_ptr reference is released in every components (renderable, collidable, controller) and also world GameObject list. Then in its dtor, component dtor are called and finally the object itself. Another principle I'm keeping is to minimize calling ctor and dtor during the game, especially ctor. 2 options and the main fighter has bullet and missile pools, after flying out of the screen or collided with matched types in collision mask, it get "hidden" to non-observable locations and reused after next fire. This would keep the game as fluent as it should. Thinking of the original game in 1986, hardware is limited. I see that they would like to keep resources in real time update pool as small as possible and try to do the same.
A game is not a good one without sound effects. I also added sound library based on XAudio2 which works depending on callbacks. It's very nice to see these old sprites work with familiar sounds.
Thanks for JP and Joe's class, I have a pretty reliable game engine to create my beloved game. It's very efficient to work with the engine because I know it's mechanics very well. I also added component based design in its structure that it's a little bit like no-graphic-interface unity. I really like the feeling of gathering assets and make them work perfect with the game. I would like to add scaling next so I could reuse meshes in the game.
During making the game, I started to feel why certain design decisions are made so the game turns out to have its shape. When I was playing the game before on TV during 90s. I sometimes see the force effect not rendered for a while and sometimes the fighter itself doesn't get rendered but the force effect comes back. That's all because limitation of NES console only allowing 8 sprites on the same rendering horizontal line. Although less limited by current PC, I still want to respect the old game makers and their wise decisions.
I like the course a lot because I realized that I should delve into structure design more than ever before, especially considering my current job. I'm getting a better sense of component based system, but this is mostly used in game industry. For my work in medical ultrasound system, I need to study more about pipe, filter and parallel programming design techniques. I'm sure I have my passion in game programming all the time. If currently it's not used in a job, it would come out later in another form. If we could secure what we're going to work on in the midterm, probably people with more passion could have more preparation for their project. That's just a little suggestion i have for our course.
About general good code, during the last 4 months in my job, I was working on QA from time to time. Company purchased Coverity service and I was using that to improve code quality. What comes to my mind first is always memory leak. More RAII is mostly a good approach to improve code quality and maintainability. Conditional/partial initialization is another problem that often happens that I would make sure always initialize everything and free everything in ctor dtor. Also paying attention to McCabe complexity is good for code structure. Singletons are evil creatures and should be mostly avoided. Static analysis tool has its limitation. Run time efficiency would also be important. Cache friendly code and better code structure could help. Unit tests and TDD are important to improve code reliability and reduce future trouble.