So I’ve been having an internal battle with myself the last couple days over whether to follow the course’s sidelining of the tank class, where the controllers talk directly to the tank components, or to maintain the intuitive structure we started with, where the controllers talk through the tank to the tank components. While I appreciate that we can simplify the architecture by taking the course’s approach, I ended up scrapping all the changes I made and reverting to the point right before the overhaul.
Now, in the lecture about dependency mapping, we found a bad bunch of spaghetti code revolving around the player controller depending on the tank and the aiming component, and then both the tank and aiming component depending on the barrel (maybe I am saying this backwards, but there is a diamond of dependencies nonetheless). So I set out to remove these dependencies while keeping our tank class safe.
We can remove the tank->barrel dependence, since the barrel is only needed for the socket position/rotation to give to the SpawnActor function in the Fire method. I just added a couple of getters for these parameters to the aiming component, and since they’re 2 lines (including an ensure condition) I don’t really mind.
The hard part was realizing why we have the player controller->aiming component dependence, which come about from us trying to remove some race conditions - this is discussed in the lecture on decoupling our architecture. I realized here that THIS was (probably?) the primary reason why we decided to sideline the tank class in the course. But the reason that we had to deal with all this stuff was that we added UI dependence (the color changes depending on the firing status of the tank) on the tank’s aiming component, rather than the tank itself. If we simply added the EFiringStatus to the tank instead, then we have the player controller just depending on the tank.
Overall, I’m glad I did the giant refactor into the new architecture because I learned a lot from it (“ironically,” I think I learned more from undoing it!)
My repository:
Now, I have a small question. In my TankPlayerController code, I essentially just replaced the Blueprint implementable event function that used to find the AimingComponent with a function that finds the Tank. But can I let the method GetControlledTank on the TankPlayerController be that Blueprint implementable event?
Or simpler, can I just call the GetControlledTank method in blueprint (since it’s already blueprint callable) to get the tank reference? It doesn’t seem possible, since the blueprint callable method doesn’t have an execution port. Would this disregard all our concern about race conditions?
I just feel like this should be able to be simplified, but I might be wrong.