Making 'Pokemon Go'-Style Creature Spawns With OpenStreetMap Data (And Without Niantic's Activity Database)
One of the most important parts of a location-based game, for the typical game you envision with that description, is making the locations involved matter. If all you do is walk around, your game is just an exercise tracker. If every space feels identical, there's no reason to explore or change your habits.
Variety is the simplest way to add that uniqueness to places on the map. In some games, that means interacting with what you see on-screen. Orna and Pokemon Go both alter what elements appear to interact with in-game based on your location, and allow you to interact with those sufficiently close to your position. In our example, we're going to have you walk into the same Cell10 as a game element to interact with it. For reference, Pokemon Go spawn points are approximately equal to a Cell11, and you could fit roughly 20 pokemon into one space we'll look at for our example. Comparing the default scale assumptions between PraxisMapper and other big-name location based games is a topic for it's own post.
Orna uses map data, your character's level,in-game randomized weather, and possibly other factors to affect which enemies spawn randomly around you. Pokemon Go uses a little more info, though. It relies heavily on a set of cell phone activity data it acquired from Google's Maps or Android group long ago. It used this data set to determine XM spawns in Ingress, and appears to have recycled that data for generating most of Pokemon Go's spawn points.
These spawn points create a variety of Pokemon, and which Pokemon are available to a spawn point depends on multiple factors. The base factor is the 'biome', which is more or less the various terrain types around the spawn point. This is created based on OpenStreetMap data, which contains tags on what a place on the map is or is used for. Pokemon Go uses approximately 60 different rules based on tags, though exactly how they're used, or if there are significant differences between several similar rules, isn't clear.
In PraxisMapper, we can do something similar, and it can be customized to your particular game. We have an endpoint, Data/Terrain/{plusCode}, that will search each Cell10 in the passed-in PlusCode (a Cell8), and tell us what the smallest map element in each Cell10 is and which map style rule applies to it. So, at 86HVF8P8PQ, it would tell us that's Cedar Point Beach, and it's 'beach' terrain, instead of the larger Lake Erie and 'water' terrain that also appears inside the requested Area. [NOTE: if you want to sell EVERY map element in a Cell10, and not just the smallest one, call Data/Terrain/All instead]
Niantic has a big set of data points on where people usually clump together, and we don't. That's not a problem, we will just have to think slightly harder about where we want our creatures to appear. We can get our own OpenStreetMap data set, and we are going to spawn creatures all across a Cell8 first, and let each terrain type in it's Cell10s influence which creatures appear. We'll apply some extra rules on the 2nd pass on this logic.
An example game would have a list of creatures, and those creatures would have entries for how often they appear in various terrain types. Let's say we have 4 total creatures for this math: a Bird that spawns everywhere, a Fish that spawns in water areas, a Crab that spawns in beach areas, and a Bear that spawns in nature reserves. In our Cell8, we will populate the spawn table off of it's terrain info, and pick random points to place these random spawns. This lets us catch creatures by being nearby a place rather than exclusively in it, which may be important if you want creatures to appear in places that are off-limits or difficult to walk directly to, like wetlands or protected natural areas.
Bird gets 20 entries in the spawn table for every Cell8 automatically so there's always something to catch. For each Cell10 with the appropriate type, we add 5 entries to the spawn table for Fish, Crab, or Bear. So, in a rural field, with nothing special, we would have a spawn table of 20 Bird entries, and would be guaranteed to only catch Bird. In the middle of Lake Erie, where all 400 Cell10s in a Cell8 would be 'water' terrain, we'd have 2,000 entries for Fish (99%) and 20 for Bird (1%). In a Cell8 with a parking lot and empty space buffering a beach along the lake, we might see 200 entries for Crab(48%), 200(48%) entries for Fish, and 20(4%) for Bird.
This logic is pretty solid, and any time we want a creature to appear, we pick one random entry in that spawn table and create the creature named in it. If we have multiple creatures in a terrain, we add them both to the spawn table. If some creatures in a terrain are rarer than others, we add fewer of those to the spawn table. If we want to add new terrain types, those can be added to the style set in the database, and they'll appear in the output without changing any code. The proportions scale automatically with the map data, and if we don't like how quickly or slowly the output changes we can tweak a few numbers to handle that.
Now, let's circle back and look at the spawn locations. For the most part, Niantic puts more creatures where their data says more people have been, and calls that good enough. We don't know that information. What we do have is the map data, and the terrain info for all the places in it.
A Cell8 contains 400 Cell10s, and we could spawn a creature in any 400 of them. Maybe that's good enough, since the big named competitor doesn't limit Pokemon from spawning in the middle of a lake if their data set shows someone's cell phone reporting in from the middle of a lake. But we can make an attempt at doing better, without tracking people either.
First, we may say that some terrain types are off-limits. We could check our list of 400 valid Cell10 spawn points, and say that 'trunk', 'primary', and 'secondary' roads are off-limits for a game about walking around, removing any spaces with those vehicle-only roads from our list of places. We might also pick 'industrial' or 'military' areas as off-limits to discourage people from walking in them for creatures. This might not be fair to people that work inside those areas and not let them play where it's most convenient for them on a break, so it's a balance to consider.
'tertiary' roads usually have sidewalks, so we might allow creatures to spawn there, or ensure that some minimum number of creatures DO spawn on them when they're present in a Cell8. Likewise, if we're deep in a nature reserve, odds are good that we can't actually walk though every Cell10 there, but there will be hiking trails, and we could ensure that when 'trail' terrain is present, that a minimum number of spawns happen on those Cell10s. Since we pick from the full spawn table, we'll still get the creatures from the nature reserve to show up on the walking trail Cell10s we choose to spawn at. After the minimums on the preferred terrain types are spawned, we can let the rest fall randomly around the rest of the permitted Cell10s.
And there we go. With a few simple rules in place, we negate the need for an additional large data set to determine where creatures would spawn in our example monster collecting game. We might even do better, giving consideration to the actual terrain for safety and user experience reasons, not simply checking if cell phones had ever reported standing in a spot a dozen years ago. You may find that you want to make your own rules, or tweak them further for a game's specific needs, and that's great!