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 = [];
runtimeScene._instances.keys(allObjectNames);
allObjectNames.sort();
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()]);
})
}
}
});
});
runtimeScene.getVariables().get("POIs_JSON").setValue(JSON.stringify(pois));
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 {
break;
}
}
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);
vars.get("Mode").setString("Targeted");
});
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.