Oh buddy, it happens every project. All the crap is hitting the fan and I’m refactoring the whole damn thing. Ok. Not really, but I’ve added a component called Target to the Player and to the Enemy. Target does nothing except provide:
- A way to identify an object as a target.
- An event type that is serializable and passes a reference to the target object to other methods.
Why? It has to do with taking damage, compartmentalizing AI, and other fun things. It started when I added a Health component to the Enemy and realized that the Player was setting the Aggression of the Enemy. That’s not right. The Enemy should be dialing its own Aggression in response to getting hurt by the Player. This implies that the Enemy needs the Health and Aggression scripts to communicate (1) that it was hurt and (2) who did the hurting.
So you see here that there are two methods for taking damage. Take Damage is used from the outside for things like traps. If you want to generate Aggression, you would use TakeDamageFrom.
The Health component invokes three events: Damaged By, Damage Taken, and Zero Health. In reverse order, Zero Health is used to trigger death events. Damage Taken is for particle effects like blood spatter or hit animations. Damaged By is to identify the attacker to other AI components, such as the Aggression module.
The Follow Player script was refactored to Follow Target. The key change is the removal of the else from update. We do not need to constantly Set Destination to self and, in fact, this would produce a bug that would prevent a waypoint system from working, for instance. Instead, we only need to Set Destination to self when if the Target is set as null.
I thought about adding a boolean flag but setting a null Target acts as its own flag for now.
The Search For Targets component needed the following update. We don’t care about any collider that isn’t a Target. Keep in mind that the Layer Mask prevents the Searcher from detecting itself, but that there is a potential bug here.
I decoupled the sound FX creation from the Weapon component into a One Off Sound FX Handler component. It’s possible this should be called a Sound FX Ring Buffer component. Whatever, here’s the first part of it:
For the implementation, the Play Sound FX method decides if we need to create new Sound FX or reuse exiting ones.
The Weapon script received a major overhaul as I stripped the sound and raycast functionality out of it completely. You can see a complete breakdown of the components that go into the Assault Rifle game object here.
Weapon’s job now is to fire off the Attack Started event and restrict how often that can be done.
Here is the Attack Started event which sends messages to the Sound FX Handler (to play the gunshot sound) and Raycast To Target objects (to see if we hit anything).
The Raycast To Target component uses the FPS camera to fire off a ray. Range varies from weapon to weapon, of course, but if it hits something we first check to see if the struck thing is a Target. If it is, we invoke the Raycast Hit event.
The Raycast Hit event sends a message to the Damage Target component:
The Damage Target component checks for a Health component on the target object and applies damage. In this case, we also need to identify the shooter so we send up the hierarchy looking for a Target component which is on the Player.
Here’s a rundown of the major changes: