This post is mostly about the design of the Drone AI, but there’s a fairly big contextual section first: that ends at the horizontal line break.
Neon Arena isn’t just a parkour game: I also wanted the player to be attacked, or have some light combat element. My thoughts on this went as such:
- Mirror’s Edge just locks you in a room with it’s enemies until you’ve beaten all of them. This sucks.
- Mirror’s Edge: Catalyst has enemies constantly jumping out just in front of you, and if you run away it spawns a helicopter to chase you. This doesn’t suck, but the helicopter chases always felt a bit abstract, just asking you to run out of an area on the minimap.
- What would be best would be AI enemies that could chase you. However, programming AI to use all the clever walljumps etc that the players can do sounds hard (Even Mirror’s Edge’s jumpy-chasey enemies do only minimal actions).
- So, the solution: flying drones.
I rather naively assumed that “flying AI” would be really easy to do (“it’s just like non-flying AI but in 3D, right?”) and immediately found that it was Not. UE4’s AI is navmesh-based, and so would have real trouble dealing with big vertical changes smoothly, and would massively struggle with placing bots at heights other than “a bit above the navmesh”. I could manually stitch together navmeshes on different heights, of course, but that seemed like it could be quite a bit of work. Ditto for a waypoint-based system: not only would these be a lot of work on my part, the bots would be limited and stupid, with the player being challenged not to outrun them, but to outhink whatever limited intelligence I could provide. (As a level designer originally, anything that reduces the work of the level designer is important to me, and as a stupid person anything that lets a computer do my thinking is good.)
I was beginning to worry that there wasn’t an easy solution to this: a way to achieve everything I wanted immediately and with no work on my part whatsoever.
Luckily, this turned out not to be the case, and I once again narrowly avoided an opportunity for personal growth.
Drunk On Nectar is a game about and heavily featuring bees, and the creator released his 3D pathfinding code for free. It’s a thing of beauty, with AI bots able to fly from place to place, to dynamically repath based on changing environment, and even with the ability to chase a player built in. I can’t recommend it enough: by far the bulk of the hard work for the drone AI was based on it.
There are two kinds of drone in the game: the player can summon one to fly from their location to the current beacon as a guide. This is wholly based on the DoN code, so I won’t go into detail on it: what I do want to talk about is the AI for the other drone kind, that hunts the player down.
I wanted these drones to be something you could fight tactically: since the game relies on being seen but not shot by them for as long as possible, it was important that the player should be able to control and understand when they were seen and when they weren’t. The drones thus have a 45 degree viewing cone, play a noise when “seeing” or “unseeing” the player, and cast a “targeting laser” in front of them: this aims through the player, so that players being chased can identify how many drones are following them by how many targeting beams are aimed over their shoulders.
I also wanted the drones to be stupid enough to take advantage of. No combat exists in the game at the moment, but I wanted to add it later, and clever players should be able to duck around the drones and attack from behind, to turn sharp corners and wait in ambush, or even jump down from above. The first thing I did in aid of this was slow the drones dramatically when not actively hunting a player, and make sure they face in the direction of their movement at all times: an easy rule that should allow players to ambush or catch up with drones. I also made sure that drones kept going for a little while after losing the player, thus allowing the player to dodge around corners and wait for the drone to follow them.
Below is the full behavior tree for the drones.
- This node picks a random possible-beacon-spawn location, and sets it as the flight goal.
- This node repeats itself every half a second or so when the 3-4-5 section below is running: if the drone is too far from the player, toggle a switch to trick it into recalculating its path to the player. This is a trick stolen from the DoN “chase the player” behavior tree.
- Check if the drone can see the player. If it can, run the code to fire the targeting laser, and if this is the first time set it into “can see the player” mode*.
- Check the drone isn’t too close to the player. If it is, we don’t want it to keep chasing or it’ll just constantly headbutt the player.
- Set the player’s location as the goal, and then fly to the goal. The “fly to” node is from the DoN code, and handles the pathfinding itself.
- If the drone is close to the player, stay in the right “mode” but don’t try and get closer. (So the drones don’t gleefully run into melee range.)
- If the drone can’t see the player…
- Firstly, head to the last known player position (or the original randomly selected beacon, if we’ve not seen the player yet), and if it’s not there in five seconds then give up. (So if a player dodges around a corner the drone will edge towards that corner, ready to be pounced on.)
- This is just to ensure that no matter whether the drone fails or succeeds in reaching that place, we go on to the next step…
- Randomly pick a possible-beacon-spawn location, and fly there, then repeat.
So from the drone’s perspective, the drone chases the player if it can see them, staying at arms length, until it’s properly lost track of the player, at which point it starts flying back and forth between beacon locations hoping to run into them.
*When the drone can see the player, it’s faster, it constantly turns to face them, and it constantly fires the targeting laser. The targeting laser has a slight delay, but when it hits the player, the drone starts firing it’s “taser” at the player. When the drone can’t see the player, it’s slower and faces the direction of travel (to make it easy to ambush/avoid). In addition, it plays a happy beep when it sees the player, and a sad beep when it loses them.
From the player’s perspective, when the drone is chasing them, it’s targeting beam is constantly following them, and whenever it hits the taser fires. The taser is more inaccurate the faster the player is moving perpendicular to the drone – so just running away at full speed is less likely to help than dodging sideways, wallclimbing, or breaking line of sight. The taser slows you down briefly, so several shots in a row can reduce you to a crawl and get you stunned and out of the game.
I’m happy with the system: it’s pretty easy to run away from one drone, but when two or three are chasing you you need to think and move more quickly.
Plus it gave me the opportunity to write this Macro:
Hire me, so your game code looks like that.