Heist – Devlog 1

While I thought a lot about structuring the whole “tactical AI” thing, I figured it’s better to start working on something and figure things out in the meantime. I decided to implement the approach shown by Anton in this devlog (which I couldn’t find for the longest time). It unfortunately means that I need to manually place down waypoints, but I’ll keep it that way until I can think of something better.

The NpcPoint class, with 8 directional traces for two positions each. Pretty standard business.

The NpcPoint class is quite simple, it’s just a Vector3 and a list of floats for each polar angle. I attempted using three traces (in 22.5 degree intervals) instead of one when generating a node and picking the minimum, but the result underestimated all distances and didn’t seem to help at all, so I kept the documented implementation.

Next step is to actually make the AI use these points. I implemented some simple idealness factors just to play around.

First iteration of the desirability evaluation. It’s pretty bad.

The AI did start using these points. However, they used them constantly (since any point with a score can be chosen), turning it into a ghetto version of the nodegraph. In addition, when using weights in the original system, nodes without cover is preferred over nodes with cover due to line-of-sight checking. The cover usage is also kind of bad due to the limited precision provided by polar angles.

The first change I made was to ignore any node that is providing zero cover. This makes the AI revert to the strafe-and-shoot behavior more suitable when fighting in open terrain.

With only 3 nodes that all provide no cover, the AI resorts to strafing and doesn’t use the nodes at all.

Next, I attempted to increase the polar angles from 8 to 16, which seemed to improve cover finding by a bit. I’m unsure whether the increased cost is worthwhile, but I left the value at a nice 12 (30 degrees for each angle), which should be a happy compromise.

The limited precision of the polar visibility system really shows when in close quarters. The AI often choose points that are terrible for cover, but there isn’t really a good solution. (Besides, CQC combat is chaotic anyways.)

To make the AI actually use cover (especially full-length ones) in combat, I made them strafe around a tiny bit within the point. It’s not perfect, but the end result makes it seem as if the AI is popping in and out of cover to shoot.

The AI pops in and out of cover to shoot. It tends to shoot the wall a lot – not sure about how to solve that yet.

To encourage cover use, I also made “distance to chest-height obstruction” a desirability factor. This makes the AI choose to hug cover more often, which should improve their general survivability.

Instead of choosing the closest point that is obscured at chest height, the AI now chooses a point that is actually good cover – i.e. close to the actual thing blocking bullets. Note the highlighted node on the right.

All of the above behavior is only done for engage mode. In push, our desired outcome is a little different – we don’t care about cover and just want to get up and personal! Because of this, I elected not to use the nodes at all, and just keep on using the old system of “get close and strafe a little”.

The AI switches into murder mode when your health is lower than 40. Probably needs to adjust the push state change more, but it will suffice for now.

So far, I think I’ve made some progress. There remains a lot of unsolved issues though. For one, I would need a system of saving these nodes to a file, and/or a system to generate them dynamically. Next up, an actual overall goal for the AI to achieve, such as a patrol route, or an attack vector. And at last, I have to actually make a game objective of sorts. Doing that will undoubtedly reveal a lot more issues, but breaking things is part of the job description, I suppose.