02 Mar 2020
02 Mar 2020
The main tasks I tackled this sprint were the creation of a component that fires projectiles for a specific new enemy, as well as a more generic component that will handle enemy deaths. Aside from these tasks, I also got to work on writing some documentation, fixing somg bugs, and submitting some playtesting feedback based on my experience playing the latest build.
My goal was to create a component for instantiating projectiles and firing them off at regular intervals. I was specifically assigned this task to be used for the Gungnir enemy, but I figured I should make the script as generic as possible so it could be reused on other enemies/obstacles that use projectiles. I read the Confluence documentation for the accepted enemy designs so I know that they can probably reuse my code. After reading the Gungnir design spec, I spoke with Nikhil about which mechanics from the enemy I would be implementing. He said that it was really only the projectile component. It seemed like a lot of the details from the spec (e.g. the Gungnir stops firing when the player approaches) weren’t going to be implemented—at least not in this sprint.
Based on what I had learned last sprint, I used Editor Attributes and serialized properties to keep the code easy to understand and use. There are fields for projectilePrefab
, projectileDirection
, projectileSpeed
, and firingInterval
. I used a coroutine to handle the cooldown. Everything went smoothly and the code was merged without issue.
I wrote the EnemyAggro
component last sprint, but I realized that a pretty serious bug had made its way through the code review. If the player used their sword anywhere within the enemy’s EnemyAggro
collider/trigger, the enemy would take damage. You could kill every enemy without even approaching them. As soon as I discovered the bug, I had a good idea of what was causing it. The EnemyAggro
component adds a CircleCollider2D
trigger to the enemy. However, the enemy also already has a BoxCollider2D
trigger which acts as its hitbox. The code that determines when an enemy takes damage is located in PrototypeEnemy.cs
and uses OnTriggerStay2D
and CompareTag("PlayerAttack")
. The EnemyAggro collider essentially just expands the enemy’s hitbox.
While I was confident I had found the source of the bug, fixing it proved more challenging. My first thought was to move the EnemyAggro
component onto a child of the enemy’s GameObject. Each enemy already has a child which stores its visual components, so I decided to move the EnemyAggro
component there. However, the bug still persisted. I understood that the enemy was checking if a PlayerWeapon-tagged object entered a trigger on the enemy, but now the larger aggro trigger wasn’t on the enemy—it was on a child of the enemy. Why was it still counting as part of the enemy’s hitbox?
LAB_EnemyPrototype
├── LAB_Enemy (Patrol)
│ └── Visuals
├── Left Bound
└── Right Bound
I found a helpful post on the Unity forums from someone having the exact same issue I was having: Trigger in child object calls OnTriggerEnter in parent object. It pointed me to the RigidBody documentation, specifically the portion describing “Compound Colliders.” Colliders on child GameObjects actually contribute to the parent’s collider. This allows for approximating complicated and expensive mesh colliders. It’s an interesting use case, but it wasn’t what I wanted for the enemy; the aggro range has to be completely separate from its hitbox.
LAB_EnemyPrototype
├── LAB_Enemy (Patrol)
│ └── Visuals
├── EnemyAggroRange
├── Left Bound
└── Right Bound
One answer on the forum post suggested putting the collider on a sibling object rather than a child object. Then, a follow script could be used to update the AggroRange’s position to the enemy’s each frame. This worked, but it wasn’t the elegant solution I was hoping for. Reorganzing the EnemyAggroRange
into its own prefab and adding an EnemyToFollow
property would break existing code. I knew that other people were already using my aggro script in their work, so this was something I was trying to avoid. Nevertheless, I couldn’t find a way to fix the bug without breaking the existing interface I provided so I had to commit the changes.
Since this bugfix will break previously written code, I wrote a Confluence page describing the new, correct usage. Most of it has already been written here: I talk about the reasons why I had to change it and describe how I did it. I also feature an image showing how the new implmentation should look in the editor. I made this page because Nico pinged me on discord because he was getting an error due to my changes. I responded to him but wanted to have a resource ready in case anybody else had problems.
At the February 22nd studio meeting, a survey was released for any playtesting feedback we had. While this isn’t directly related to the programming I did this sprint, I spent a lot of time writing my thoughts on the current build of the game and recording gameplay footage to support my thoughts. It was submitted in a Google Form so I don’t have direct access to what I said, but I can paraphrase some of the more important feedback I gave:
For the first build, there was a change to the controls which I believe makes the teleportation mechanic less intuitive and more difficult to use effectively. Your teleportation direction is controlled with the left stick which also controls your player's movement. Sometimes you want to move to the right but quickly change your teleportation direction. By having both of these bound to the same analog stick, it becomes difficult to change your momentum quickly. Since, from my understanding, a lot of the gameplay will revolve around avoiding projectiles and precise platforming, the player is required to use the teleport mechanic to move in the opposite direction they are moving.
I suggested that the right stick should be used to control the teleport direction independently of the player's movement. For me, this is way more intuitive since it mimics the controls of a twin-stick shooter. There's a visual indication of your teleport direction as well which invokes the image of a reticle you might see in one of these shooters. Also, this control scheme was used earlier in development so I already played the game this way. This might make me biased since I'm used to it already, but it also showed me how jarring it was when it was taken away.
Mechanically, the jump is very responsive. It happens as soon as the player presses the button. However, the jump animation is fairly long and makes the jump *feel* less responsive than it actually is. I suggested shortening the jump animation substantially—maybe cutting it to two or three frames. The animation itself looks great, but I think the timing just makes it sluggish.
The wall jump works well, but there were some problems with the animations I found. It's inconsistent, but sometimes the player does not enter the wall slide animation when they should. Instead, they just hold a single frame from their idle or jump pose. Also, the collision on the walls looks off since the player can walk so far into them before they stop.
One of the questions in the survey asked specifically about the parallax effect. I think the painted background looks great and the parallax effect is juicy. I did find some glitchy behavior as shown in the video above. Also, I think the background is a little blurry from having been zoomed in so much to make the parallax work.