02 Mar 2020

Io Progress Report - Alpha

02 Mar 2020

At a Glance

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.

  • Shield Enemy Prototype was merged into the game
    • Moved out of _Development and renamed
  • Created PrototypeRegularProjectile script
    • Fires projectiles with specified direction, speed, and periodicity
    • Will be used for the Gungnir enemy
  • Fix EnemyAggro range bug
    • Refactored EnemyAggro into a prefab
    • Wrote confluence documentation on its new usage
  • Submitted detailed playtest feedback
    • I had a lot of feedback based on my experience playing the latest build regarding bugs and potential UX improvements

Projectile Component

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.

Three test enemies fire projectiles at regular intervals.

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.

A Gungnir shoots projectiles and jumps to match the player's height.

Fixed EnemyAggro Bug

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.

The player hits an enemy with their sword despite not making visual contact.

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

An enemy's hierarchical structure. The EnemyAggro component was put on the Visuals GameObject, but the bug persisted.

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

The new enemy hierarchy includes an EnemyAggroRange object. Since it's not a child of the LAB_Enemy, it does not merely contribute to its hitbox collider.

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.

The enemy no longer takes damage when the player uses the sword within the aggro radius.

Confluence Documentation

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.

An editor screenshot showing how to use the new EnemyAggro component/prefab.

An editor screenshot showing how to use the new EnemyAggro component/prefab.

Playtest Feedback

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.

The player jumps. Notice how the jumping animation takes a while to fully come out.

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 player slides against the wall, but the collision appears off. The animation doesn't always update.

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.

The background has a nice parallax effect, but it glitches out sometimes.

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.