Part of the “realism” of a sector is having other activity around the player in the form of NPCs who are going about their business. The “AI” — I put it in quotes because I really hesitate to call it that — for a merchant NPC is pretty simple: spawn in at a jumpgate, head to a randomly selected station and “dock” (vanish), wait for a while, reappear, and head for a jumpgate to leave the system.
I currently have about 20 NPC records created for testing, and the spawn mechanism works great in creating instances just outside the trigger area of the jumpgate, but there were two problems: clones, and spawn rates.
Although the game is set in The Future, that doesn’t mean we have to accept that the NPCs that we see are clones. Ideally, when a player tags an NPC merchant, that merchant should ID as unique by name.
When an NPC spawns in, it does so after the system picks a random NPC record. It assigns this record to the prefab and uses SetActive() to make that object visible in the world. When it wakes up, it goes about it’s business. But I don’t want another NPC with the same captain to follow up right behind the first one. So I had to create a query that takes into account all of the active NPCs who are moving, as well as all of the non-active NPCs who are “docked”. The system then selects an NPC from the NPC object pool that is not in this list, and spawns them.
An NPC is considered to be “docked” when she enters the trigger of a station. This adds her to a global sector list of ships who are docked, regardless of which station they’re at. I use this for two purposes. The first is to ID the NPCs who are out of the inactive pool, so they don’t get re-assigned to another ship that’s just entered the sector, and the second is to randomly select a “docked” NPC from this list to reactivate to continue on their journey.
Once I got the limit set so that I didn’t have 200 copies of the same ship zooming around the sector, I found I had a different problem which I’m calling the “dispatcher’s nightmare”.
Without a limit on when the NPCs spawn, they’ll all spawn at once, and if there’s a limited number of spawning locations, the NPCs will all bunch up. This not only looks stupid and unrealistic, but if there’s a capsule collider on the NPCs (not sure if I’ll bother with worrying about collisions), spawning them in the exact same spot makes the prefabs bounce off one another, sending the placeholder primitives spinning off into space.
What I needed was a way to make it look like NPCs were jumping into the system at random intervals, but not insanely infrequent intervals. Once an NPC enters the system, they’re on the move and visible until they dock, and are on the move and visible again until they reach the exit jumpgate. If the points of interest are far away, a low spawn rate means the player might not actually see any NPCs. That’s not exciting. On the flip-side, I don’t want the space-lanes to be clogged with NPCs because that would be overkill, and I’d need to seed the NPC name database with an astronomical amount of records (or create a random naming dictionary, which I might do in the end).
So in the Update() method, the system checks the current Time.time() (which is the number of seconds since the game has started) against the last spawn check time, plus a variable amount of seconds. If the check passes, there’s a secondary check — a “coin flip” that picks a random number between 0 and 20. If that number is less than 2, an NPC is spawned. The time check ensures that we’re not flipping the coin every frame, because statistically that is the same as pumping out NPCs at an insane rate, and the coin flip ensures that NPCs aren’t spawning in at a predictable rate.