16 Feb 2020
16 Feb 2020
This sprint I spent a lot of time getting familiar with the repo and learning how the existing scripts interacted with each other. This was my first time working on such a large game project with so many people. I was tasked with implementing the following features:
When the player gets close to an enemy, functions must be called that change the enemy’s behavior. Since there aren’t any finalized enemy concepts at this point, I worked on a generic and flexible script that could be applied to all future enemies. In my past experience with game jams and EECS 494 projects, the priority was in making it work quickly, which resulted in “hacky” code. For this project, I had to make sure that my scripts could be used by other people without much modification.
To achieve this, I learned how to use UnityEvents. This provides an easily accesible interface to use in the editor and now all I can think of is how useful these would have been in past projects rather than using delegates at runtime. Also, I found out about editor attributes like Tooltip and Header. I’ve never had to use these in my projects but they seemed useful when making components other people would use.
I tried out two approaches for deciding when the player enters and exits the range of an enemy. My first thought was to calculate an enemy’s distance to the player on each frame. A private boolean
inRange could be set appropriately to track state. This worked, but I quickly realized there would be problems scaling up this solution. First, each enemy would need a reference to the player. Performing a name search in
Start is a hacky solution that would break if the name changed, and manually dragging the player GameObject to every enemy in the editor is messy and inefficient. The in-editor references would have to be set in every new scene as well. Second, every enemy would be independently accessing the player’s transform and calculating the player’s distance each frame which may not be performant.
I decided to use the built-in functions OnTriggerEnter2D and OnTriggerExit2D. The aggro range could be set as the radius of the trigger. This turned out to be a more elegant solution. My first attempt added the CircleCollider at runtime. However, during code review, Max suggested I use RequireComponent instead. I think the biggest problem with the initial solution is that it adds some obfuscation to how the code works when you look at the editor. Since the collider is added at runtime, it doesn’t show up in the editor. If someone added a CircleCollider2D component for another purpose, it would conflict with mine and it would be very hard for them to debug and figure out where it’s being modified.
I tested the component by interfacing with the public UnityEvents I made available in the script. I decided on just changing the enemy’s color since this wouldn’t need any additional components and would make it clearly obvious if it was working. Also, the enemy’s visuals are defined in a child GameObject of the parent Enemy, so this would show that functions on other GameObjects can be invoked by the events. When the player enters the enemy’s aggro range, they turn blue. When they exit, they turn red.
I created several variants of a shielding enemy. It’s not clear whether these will be used in the final game, but the prototypes will help us determine which mechanics are interesting or engaging enough to take further. The first shield variant reflects the player’s teleportation projectile at the same angle it hits the shield. The shield is just a BoxCollider and when it collides with the projectile, it computes the angle from where the player sent it, and just negates this direction vector.
I thought this one could be used for a boss battle or environmental hazard where a large vertical shield is used. However, the reflection wasn’t very realistic and since it just sent you backwards, it didn’t seem like it would be useful for platforming. The second variant was similar, but instead of just negating the directional vector, it reflected it along the shield’s normal vector. It took some experimentation to make sure I had the correct normals for all possible shield angles (it ended up being the shield’s
I still had time to log so I decided to attempt a third variant that lets the player’s projectile pass through the shield but actually reflects the player when they try to teleport through. This ended up causing a lot of problems. Currently, the player instantly teleports to the projectile when the right trigger is lifted up. In order to change this behavior, I would have to edit the actual
CharacterController2D script used in the production build of the game. Up until this point, I was only making and modifying scripts in the
_Development folder. I foolishly decided to make these changes in the same branch I had made my other shielding enemy variants. I ended up having to dig into some git documentation to figure out how to undo my changes (which I even more foolishly decided to commit and push). For future reference, it’s
git revert. Also branch before you revert so you don’t have to checkout an old commit and branch from there.
I ended up making minimal changes to the
CharacterController2D script by adding a UnityEvent to the
HandleTeleportationProjectile function. This allowed me to add the bouncing functionality from the shield component without breaking existing functionality. As it stands, it’s more of a proof of concept. The shield stores the location where the projectile hit it, and then adds a listener to the UnityEvent on the player controller. This listener teleports the player to this saved position and adds some velocity to their rigidbody. It uses the same reflection along the shield’s normal as the previous variant.
These past two weeks definitely had some growing pains, but I learned a lot of things that will help me become more efficient at contributing to the project later on. I read up on UnityEvents, editor attributes, and our Unity style guide to improve the readability and maintainability of the components I created. I ended up reading a lot of other people’s code too. In 494, my partners’ code was more like a black box and only they knew how it worked. I had some git troubles as well, but now I’ll be smarter about handling them in the future.