Pathfinding to a randomly ordered set of object locations

I have a few different person objects, which I want to create lots of, and have them pathfind their way around the map. There is a group Persons with all the person objects in it. There are a few different sets of static objects: let’s assume that there are two groups, Statues and Plants, where Statues contains two objects, Statue_king and Statue_queen and Plants contains two objects, Plant_tree and Plant_bush. The idea is that I create lots of Person_plant objects, which are people, and each Person_plant instance goes to visit each of the Plants objects on the map in turn, and so on.

So in pseudocode, this would look something like:

Repeat for each instance of Persons
  The speed of Persons = 0 (i.e., they've reached their destination)
Work out which group this Person is interested in (let's say it's Statues)
Choose a random unvisited object from the group Statues
Move Person to the location of the randomly chosen object

…but… I don’t know how to do that. I don’t know how to give a Person a reference to a Group they’re interested in, and even if I did, I don’t know how to choose a random object from that group in an Action. (I can use Pick a random object as a condition, but I don’t know how to do it as an action.)

What’s the best way to model this? I don’t really want to duplicate Group settings in an array; that is, I can imagine giving each Person an instance variable with an Array listing all the objects it wants to visit, but this is duplicating the setup I’ve already done by putting objects in groups, and that will also be a list of objects, not of instances; there might be more than one Statue_king on the map, and so I don’t know how to make a Person want to visit each of them in turn.

I guess this extension could help:

The POI can be put in the stack of each person and shuffled. Then, a person would pick one POI from their stack until the stack is empty.

Just out of curiosity, are there Persons that are interested in more than one object? Like a guy that visits both statues and plants?

(And also, are there object that can be visited by different kind of Persons? Like a plant can be visited by a Person from group 1 or by a person from group 2)

I don’t plan to have that, but I can imagine that that plan might change (and I’d expect that, if I have the system working, that making that change ought to be relatively easy to add).

OK, I’ve modelled this with some custom JavaScript, and careful naming of objects. At the beginning of the scene, I do this:

let pois = {Statue: [], Plant: []}
let allObjectNames = [];
allObjectNames.forEach(function(on) {
    Object.keys(pois).forEach(function(poigroup) {
        if (on.substr(0, poigroup.length + 1) == poigroup + "_") {
            let objs = runtimeScene.getObjects(on);
            if (objs && objs.length > 0) {
                objs.forEach(function(obj) {
                    pois[poigroup].push([obj.getX(), obj.getY()]);

which builds a JS object which contains “object types” and their positions that looks like {Statues: [[50,150], [200,75]]}, and then stores (the JSON serialisation of) it in a scene variable. Note that this required careful naming of my objects: all the statue-type objects are called Statue_Something. (I originally planned to put all the statue-type objects in a group called Statues, but as far as I can tell, groups aren’t available to JS code at all; they don’t exist at runtime.)

Then, I have given every Person (which are called Character) four variables: Mode, TargetX, TargetY, and Exhibit. Exhibit is a string which contains the type of object that this Character is interested in: it looks like “Statue” or “Plant”. Mode is a string variable describing what this Character is doing, and is by default set to “Targetless”.

Then, a condition
The text of variable Mode of Characters = “Targetless”

let pois = JSON.parse(runtimeScene.getVariables().get("POIs_JSON").getAsString());

objects.forEach(function(o) {
    let vars = o.getVariables();
    let exhibit = vars.get("Exhibit").getAsString();
    let target;
    let ox = o.getX();
    let oy = o.getY();
    while (true) {
        target = pois[exhibit][Math.floor(Math.random() * pois[exhibit].length)];
        if ((Math.abs(target[0] - ox) < 50) && (Math.abs(target[1] - oy) < 50)) {
            // picked the same one, so let's pick a different one
        } else {
    let deltaX = -16; let deltaY = -16;
    if (Math.random() < 0.5) deltaX = 32;
    if (Math.random() < 0.5) deltaY = 32;
    vars.get("TargetX").setNumber(target[0] + deltaX);
    vars.get("TargetY").setNumber(target[1] + deltaY);

This finds all targetless Characters, looks up their preferred Exhibit type, then gets the list of POIs from the scene variable and chooses a random one from it that’s in their preferred type. (It tries to pick one that’s not near where the Character is, and it chooses a slightly random location around the object rather than always its top left.) It then stores the X and Y coordinates of the object from the POI list into the Character’s TargetX and TargetY number variables, so they can be got at shortly by normal events rather than JS code, and sets their Mode to “Targeted”.

Another condition finds Characters who are targeted, and actually sets them in motion:

The text of variable Mode of Characters = “Targeted”
Trigger once
Repeat for each instance of Characters:
Move Characters to Characters.Variable(TargetX);Characters.Variable(TargetY)
Change the text of variable Mode of Characters: set to “Moving”

(I’m not sure I need to manually repeat over each instance here! but it works.)

and finally, when a character arrives at their destination point:

Characters reached its destination
Trigger once
Change the text of variable Mode of Characters: set to “Targetless”

and then everything works, as far as I can tell; I can dynamically create new Characters and they do the right thing.

1 Like