Abilities & Enemies
I’ve been traveling a fair bit recently, so I’m still covering work that I completed over the past few months. I’m looking forward to sharing new developments, but I have more to talk about before then!
Gravity Lifting
Another big influence on my design sensibilities has been the immersive sim – mostly modern titles like Arkane’s Dishonored and Prey – and the degrees of freedom and choice they give players. I would love to work on a game with this kind of player sandbox someday, and while I’ve scoped this current project based on what I’m capable of as a single developer, I still want to provide interesting options for players to approach combat and exploration.
As such, I wanted to create a player ability that had applications in both combat and exploration. A ‘gravity lift’ ability seemed like a compelling idea to try, and simple enough to implement with Unity’s physics. I started by creating a prefab object that was simply a collider that applied an upwards force to objects within it. Only objects with rigidbodies work for this – the player already had one, and I added them to my target “enemy” capsules and a handful of cubes that I scattered about the environment. I also wanted to be sure that the lift object destroyed itself shortly after it was instantiated, to provide the appearance of a short burst of upwards force rather than a constantly lifting one.
I also had to tie spawning the object to a player input, and wanted to create a placeholder targeting effect to ensure the player knew what area they were affecting. The simplest input, in my mind, would be using the right mouse button – hold to show targeting, and release to activate the ability. The gravity lift effect should deploy where the player is looking, so I drew a raycast (actually, a linecast to ensure there was a maximum range) to see if the player was looking at a floor. At present only vertical lifting is implemented, but I might come back to some sort of horizontal implementation in the future.
To actually show the player where they’d be placing the gravity effect, I created a placeholder “ghost” targeting object that moved with the linecast hit position while aiming. I though this would work perfectly, but something strange was happening – regardless of movement inputs, the targeting object jumped back and forth between two separate positions, one where the player was looking, the other a few units further away. After an hour or so of hair pulling, I eventually used Unity’s debug tools to draw the linecast – which revealed one of my prior unresolved issues had come back to haunt me. The origin of the linecast was, for some reason, rapidly alternating between its intended origin and a point slightly above the player. I immediately realized this was the same issue I had discussed in my previous update, where bullets were occasionally spawning too high; for the frames during which those objects were instantiated, the origin was offset. I couldn’t find anything in my code that would cause this to happen, so I eventually resorted to turning various components on and off – not the most efficient debugging method, but one that I’ve used plenty before.
The issue was ultimately some conflict between Unity’s built-in character controller and how I was handling movement. Turning off the controller and re-building my movement code solved both problems, ensuring that the proper origin was used consistently for look-based aiming effects. In building my gravity lift aim functionality, I had also successfully implemented raycasting, so I made the decision to switch the player’s weapon fire from projectiles to hitscan, solving my inconsistent collisions issue.
Lastly for the gravity lift, I added a simple particle effect to the actual lifting collider, providing some visual feedback indicating where the lift was. The result was a cool ability that could pop the player, enemies, and various objects into the air, and by playing with the added force and the masses of objects I could ensure the physics all felt believable.
Enemies
My next big step was creating simple enemy behaviors; I could certainly create a polished player controller with movement, weapons, and abilities, but without something to shoot at (and shoot back), I can’t fully test my systems. I wanted simple enemies that could walk, shoot, and react based on the player’s actions in combat.
I started with a list of behaviors I wanted enemies to be able to exhibit. These included a stationary and patrolling idle, advancing to effective weapon range, repositioning in combat, finding cover, and attacking the player. I roughly implemented each of these using Unity’s built-in navigation functionality in various ways, and I set up a navmesh and put navmesh agents on my targets to take them from stationary targets to ‘thinking’ enemies. Some behaviors were easier than others, and the two I feel are most deserving of mention are finding cover and attacking.
To find and utilize cover, I figured that enemies needed to handle three distinct steps – finding suitable cover, finding where to stand to ensure the cover was effective, and actually moving to that point. The final step was as easy as feeding a point into Unity’s pathing functions, though the first two could be accomplished in a number of ways. The simplest way to find cover in my mind was to look for all objects with a specific tag and find the closest one; currently, I generate this list at runtime and don’t foresee this being an issue. That left step number two, which ended up being a bit of a challenge.
To use cover effectively, I wanted enemies to position themselves such that cover broke line of sight, putting the cover object between the enemy and the player. I figured out some Vector3 math that took one position and ‘flipped’ it to the other side of an object, and plugged in a raycast hit from the player to the found cover. I used this position as the enemy’s destination when moving to cover, which ended up working pretty well.
Implementing basic enemy shooting is pretty easy, but making sure enemy attacks feel good can be just as important as the player’s attacks. I had a few initial thoughts:
Perfect aim on enemies isn’t super fun – enemies should have a chance to miss, just like the player.
Enemies shouldn’t just stand still and shoot on a timer – they present tricker targets if they weave in and out of cover, and stationary enemies just aren’t believable. Enemies should fire in bursts, then return to cover.
Shots from enemies should be projectiles, rather than hitscan – players should be able to react to incoming fire
Taking all of this into consideration, I implemented a rough version of what I wanted to achieve. Fortunately I had kept my old player bullet prefab, which I modified with a green glowing particle effect (resulting in a moderately slow projectile like something out of Halo). Every shot comes with an aim scalar, similar to the player’s attacks, that slightly offset the initial rotation and ensures that enemies can miss a stationary target – this scalar can be modified on individual enemies if necessary. Shots don’t actually damage the player yet, but that’s an easy future change.
Enemies have a minimum cooldown between attacks, and every frame past this cooldown have a chance to attack. Attacks are chosen from a list of coroutines, some of which are single shots while others are bursts of two or three. I also created a separate list of attacks that begin with the enemy standing from a crouched state, for when enemies were behind low cover.
At present, all of my enemy behaviors exist in a very simple if/then setup to choose between them; while I don’t need anything more complex yet, creating the behaviors independent from the code that selects which behavior to run will allow me to reconfigure behaviors individually in the future.