Making paths for multiple instances of the same object

D

Darren

Guest
I feel like I know how to do this in theory but not in practice. So I have multiple pathfinders (people) in my room, and they all set up their own start and end points for a path to run along. But then in my create event they all have

ai_path = path_add();​

am I right in thinking they are not all creating their own paths using this and I should run some kind of for loop to make each individual pathfinder create its own path? I can't seem to get this working.
 
D

Darren

Guest
If that line is in your Create event, then each instance of that object will have its own independent ai_path. This is clearly a case of instance scope. If you can't get this to work, chances are that you have the wrong idea about the difference between objects and instances.
I've been following this tutorial
https://www.yoyogames.com/blog/459/dynamic-mp-grids

I'm guessing that the issue is the obj_start and obj_finish is just calling form the first instance of the object in the room, rather an each individual one.

I would guess the line "if mp_grid_define_path(obj_start.x, obj_start.y, obj_finish.x, obj_finish.y)" is causing my issues, as I want it to define the path for the obj_start and the obj_finish created by each separate instance. The code is below.

if mp_grid_define_path(obj_start.x, obj_start.y, obj_finish.x, obj_finish.y)
{
with (obj_pathfinder)
{
x_goto = path_get_point_x(ai_path, pos);
y_goto = path_get_point_y(ai_path, pos);
}
}
Maybe something like the below would work? I'll try it when I get in from work.

with (obj_pathfinder)
{
mp_grid_define_path(obj_start.x, obj_start.y, obj_finish.x, obj_finish.y)
x_goto = path_get_point_x(ai_path, pos);
y_goto = path_get_point_y(ai_path, pos);
}​
 

DukeSoft

Member
Ah, you're referencing objects (obj_start) and _not_ the spawned instance. If you create a start point for an object, you have to remember the instance ID (reference to the instance) so you can use that in your pathfinding;
Code:
//Create:
startInstance = instance_create_depth(20, 50, 0, obj_start);
finishInstance = instance_create_depth(50, 450, 0, obj_finish);

// path finding event
if mp_grid_define_path(startInstance.x, startInstance.y, finishInstance.x, finishInstance.y)
{
with (obj_pathfinder)
{
x_goto = path_get_point_x(ai_path, pos);
y_goto = path_get_point_y(ai_path, pos);
}
}
Using an object name to reference an instance will always return you the first spawned instance;
Code:
a = instance_create_depth(20, 50, 0, obj_object);
// a.x = 20
// obj_object.x = 20
b = instance_create_depth(50, 450, 0, obj_object);
// b.x = 50
// obj_object.x = 20
 
D

Darren

Guest
Ah, you're referencing objects (obj_start) and _not_ the spawned instance. If you create a start point for an object, you have to remember the instance ID (reference to the instance) so you can use that in your pathfinding;
Code:
//Create:
startInstance = instance_create_depth(20, 50, 0, obj_start);
finishInstance = instance_create_depth(50, 450, 0, obj_finish);

// path finding event
if mp_grid_define_path(startInstance.x, startInstance.y, finishInstance.x, finishInstance.y)
{
with (obj_pathfinder)
{
x_goto = path_get_point_x(ai_path, pos);
y_goto = path_get_point_y(ai_path, pos);
}
}
Using an object name to reference an instance will always return you the first spawned instance;
Code:
a = instance_create_depth(20, 50, 0, obj_object);
// a.x = 20
// obj_object.x = 20
b = instance_create_depth(50, 450, 0, obj_object);
// b.x = 50
// obj_object.x = 20
Thank you very much! Really appreciate you taking the time to do this, I'll try it when I'm home from work. Still grasping a lot of concepts and the way you laid this out has helped me understand it so much better.

& good luck with your Steam release, that's an end-goal of mine.
 
D

Darren

Guest
I now have an issue, using my wall object I want to define a new path

ai_path = obj_pathfinder.ai_path;

if path_exists(ai_path)

{
if !mp_grid_path(global.ai_grid, ai_path, _sx, _sy, _fx, _fy, true)
{
show_debug_message("ERROR: mp_grid_define_path() - No path created");
return false;
}
else
{
path_set_kind(ai_path, 1);
path_set_precision(ai_path, 8);
return true;
}
}
That is my code, because I want to refer to all of my objects, but then when one of them cannot set a path, it unfortunately then stops both of them from moving. I understand why, but I can't use other.ai_path because the script is being called from my wall object. Any ideas how I can call this to refer to EVERY instance but then only stopping the trapped one from moving, not stopping every instance?
 
Last edited by a moderator:

FrostyCat

Redemption Seeker
Why isn't this line raising any red flags for you?
Code:
ai_path = obj_pathfinder.ai_path;
If there are multiple instances of obj_pathfinder and you've genuinely read my article, you'd know this is invalid by my standards and why.
NEVER access a single instance by object ID if multiple instances of the object exist. This includes attempts to reference or set object.variable (which is inconsistent across exports) and using with (object) to apply actions to it (this encompasses all instances of the object instead of just the one you want). Verbally, "Dog's colour" makes sense with one dog, but not with multiple dogs.
If your goal is to get every instance of obj_pathfinder to calculate a path of its own (assuming that each have ai_path defined in instance scope), you need to use a with block to loop through all these instances AND not interrupt the loop by returning midway.
Code:
with (obj_pathfinder) {
  if (path_exists(ai_path)) {
    if (mp_grid_path(global.ai_grid, ai_path, _sx, _sy, _fx, _fy, true)) {
      path_set_kind(ai_path, 1);
      path_set_precision(ai_path, 8);
    } else {
      show_debug_message("ERROR: mp_grid_define_path() - No path created");
    }
  }
}
There is a reason why I am much stricter than most other instructors when it comes to using the terms "object" and "instance" in GML. Topics like yours crop up by the dozen every week, and they all come from people who don't know the difference or code like they don't.
 
D

Darren

Guest
Why isn't this line raising any red flags for you?
Code:
ai_path = obj_pathfinder.ai_path;
If there are multiple instances of obj_pathfinder and you've genuinely read my article, you'd know this is invalid by my standards and why.

If your goal is to get every instance of obj_pathfinder to calculate a path of its own (assuming that each have ai_path defined in instance scope), you need to use a with block to loop through all these instances AND not interrupt the loop by returning midway.
Code:
with (obj_pathfinder) {
  if (path_exists(ai_path)) {
    if (mp_grid_path(global.ai_grid, ai_path, _sx, _sy, _fx, _fy, true)) {
      path_set_kind(ai_path, 1);
      path_set_precision(ai_path, 8);
    } else {
      show_debug_message("ERROR: mp_grid_define_path() - No path created");
    }
  }
}
There is a reason why I am much stricter than most other instructors when it comes to using the terms "object" and "instance" in GML. Topics like yours crop up by the dozen every week, and they all come from people who don't know the difference or code like they don't.
I mean, I did understand why that wasn't working, I actually said that I knew what I was doing wrong, I just didn't know how to then make it right and wanted to 'show my working' as I am actually trying to use the manual and work this out for myself not just blindly asking, I'm just struggling a bit. I don't mind you being strict though I understand it must be irritating 100's of people asking similar questions day in, day out, but I'm trying my best to learn every bit of info from all you guys kind enough to help me out.

I now get the error

Variable obj_pathfinder.ai_path(100017, -2147483648) not set before reading it.
I find this odd, as in my pathfinder object create I have the lines

ai_path = path_add();
mp_grid_define_path(startInstance.x, startInstance.y, finishInstance.x, finishInstance.y);​

So it should be creating the path before defining it, and if not, then I should be getting the debug error right? Not sure where I'm going wrong here.
 

DukeSoft

Member
That is odd indeed! Could you share the piece of code thats creating the object, the creation code of obj_pathfinder, and the code thats throwing the error?

By the way, if you wrap your code in [ code ] and [ / code ] blocks (without spaces) it will be neatly formatted (monospace) on the forums :)
 
D

Darren

Guest
That is odd indeed! Could you share the piece of code thats creating the object, the creation code of obj_pathfinder, and the code thats throwing the error?

By the way, if you wrap your code in [ code ] and [ / code ] blocks (without spaces) it will be neatly formatted (monospace) on the forums :)
obj_pathfinder create event below, the wood stuff is all because the paths are being used to make the people gather wood, if there is no path available, they are idle

Code:
woodinst = instance_nearest(x, y, obj_wood);
startInstance = instance_create_layer(x,y,"Instances",obj_start);
finishInstance = instance_create_layer(woodinst.x,woodinst.y,"Instances",obj_finish);
ai_path = path_add();
mp_grid_define_path(startInstance.x, startInstance.y, finishInstance.x, finishInstance.y);

spd = 1;

woodcount = 0;
oldwoodcount = woodcount;

gatheringwood = 0;
idle = 1;

image_speed = 01;
pos = 1;

x_goto = path_get_point_x(ai_path, pos);
y_goto = path_get_point_y(ai_path, pos);
Below is my object that creates a wooden wall

Code:
var place_x = round(mouse_x/tile_size)*tile_size;
var place_y = round(mouse_y/tile_size)*tile_size;

x = place_x;
y = place_y;

{
_inst = instance_create_layer(place_x,place_y,"Instances",obj_wood_wall);
with (_inst)
    {
    mp_grid_add_instances(global.ai_grid, id, false);
    }
}

startInstance = obj_pathfinder.startInstance;
finishInstance = obj_pathfinder.finishInstance;

if mp_grid_define_path(startInstance.x, startInstance.y, finishInstance.x, finishInstance.y)
{
with (obj_pathfinder) if path_exists(ai_path)
    {
    x_goto = path_get_point_x(ai_path, pos);
    y_goto = path_get_point_y(ai_path, pos);
    }
}
else
{
if path_exists(ai_path) path_delete(ai_path);
}
below is the define path script

Code:
var _sx = argument0;
var _sy = argument1;
var _fx = argument2;
var _fy = argument3;

with (obj_pathfinder) {
  if (path_exists(ai_path)) {
    if (mp_grid_path(global.ai_grid, ai_path, _sx, _sy, _fx, _fy, true)) {
      path_set_kind(ai_path, 1);
      path_set_precision(ai_path, 8);
    } else {
      show_debug_message("ERROR: mp_grid_define_path() - No path created");
    }
  }
}
and here is the error

############################################################################################
FATAL ERROR in
action number 1
of Create Event
for object obj_pathfinder:

Variable obj_pathfinder.ai_path(100017, -2147483648) not set before reading it.
at gml_Script_mp_grid_define_path (line 15) - if (path_exists(ai_path)) {
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Script_mp_grid_define_path (line 15)
called from - gml_Object_obj_pathfinder_Create_0 (line 5) - mp_grid_define_path(startInstance.x, startInstance.y, finishInstance.x, finishInstance.y);

Hope this helps!
 

DukeSoft

Member
Hmm, this actually feels like a bug in GM:

-> Create instance (create script starts)
-> runs script within create event
-> uses "with" to go back to instance that was just created (but is not finished creating yet, because we're still in the create script)
-> can't find the ai_path because the create script didn't finish yet

What you could try, is removing the with() call there. Since you call the mp_grid_define_path() from the create event already, its already running in the scope of that instance. with() causes it to be run in _all_ instances.

My gut tells me this initial code should work, but its probably not what you mean to do with it.

I hope you can follow my train of thought here!

TL;DR: Replace mp_grid_define_path contents with this:
Code:
var _sx = argument0;
var _sy = argument1;
var _fx = argument2;
var _fy = argument3;

if (path_exists(ai_path)) {
 if (mp_grid_path(global.ai_grid, ai_path, _sx, _sy, _fx, _fy, true)) {
   path_set_kind(ai_path, 1);
   path_set_precision(ai_path, 8);
 } else {
   show_debug_message("ERROR: mp_grid_define_path() - No path created");
 }
}
 

DukeSoft

Member
Also, now that I'm reading the wood wall creation stuff - I think you're overcomplicating it. Let me rewrite a bit and add some comments;

Code:
var place_x = round(mouse_x/tile_size)*tile_size;
var place_y = round(mouse_y/tile_size)*tile_size;

var wallInstance = instance_create_layer(place_x,place_y,"Instances",obj_wood_wall);
mp_grid_add_instances(global.ai_grid, wallInstance, false); //Add the wall instance to the pathfinder

with (obj_pathfinder) { //Run this code inside of ALL the pathfinder instances
    //Startinstance and finishinstance are available in this scope 
    if !mp_grid_define_path(startInstance.x, startInstance.y, finishInstance.x, finishInstance.y) { //Try to find a new path (and the script applies it to ai_path)
        path_delete(ai_path); //If the result of the function was FALSE (e.g. path not found) - we delete the path.
    }
}
 

DukeSoft

Member
One word of advice: Try to look up how with() and variable scopes work. It looks like you either don't completely understand how it works and what it does, or your assumptions are wrong. I can see what you _think_ the code does, but it doesn't. I really suggest you thoroughly read @FrostyCat 's topic and try to understand what the code is doing exactly.
 
D

Darren

Guest
I
Also, now that I'm reading the wood wall creation stuff - I think you're overcomplicating it. Let me rewrite a bit and add some comments;

Code:
var place_x = round(mouse_x/tile_size)*tile_size;
var place_y = round(mouse_y/tile_size)*tile_size;

var wallInstance = instance_create_layer(place_x,place_y,"Instances",obj_wood_wall);
mp_grid_add_instances(global.ai_grid, wallInstance, false); //Add the wall instance to the pathfinder

with (obj_pathfinder) { //Run this code inside of ALL the pathfinder instances
    //Startinstance and finishinstance are available in this scope
    if !mp_grid_define_path(startInstance.x, startInstance.y, finishInstance.x, finishInstance.y) { //Try to find a new path (and the script applies it to ai_path)
        path_delete(ai_path); //If the result of the function was FALSE (e.g. path not found) - we delete the path.
    }
}
It's literally straight from Nocturne's pathfinding tutorial haha, I'm sure he knows what he's doing. I appreciate you cleaning up the code though. Unfortunately still getting errors using both those replacements, really not sure what I can do to stop them at this point! I did read the topic, unfortunately I'm not soaking up everything like a sponge but I keep coming back to it and attempting to learn.
 
D

Darren

Guest
Also, now that I'm reading the wood wall creation stuff - I think you're overcomplicating it. Let me rewrite a bit and add some comments;

Code:
var place_x = round(mouse_x/tile_size)*tile_size;
var place_y = round(mouse_y/tile_size)*tile_size;

var wallInstance = instance_create_layer(place_x,place_y,"Instances",obj_wood_wall);
mp_grid_add_instances(global.ai_grid, wallInstance, false); //Add the wall instance to the pathfinder

with (obj_pathfinder) { //Run this code inside of ALL the pathfinder instances
    //Startinstance and finishinstance are available in this scope
    if !mp_grid_define_path(startInstance.x, startInstance.y, finishInstance.x, finishInstance.y) { //Try to find a new path (and the script applies it to ai_path)
        path_delete(ai_path); //If the result of the function was FALSE (e.g. path not found) - we delete the path.
    }
}
Question about this, why would you try to delete the path if the path isn't found? Surely that would mean there is no path to delete.

I think what I need to do is edit the below for loop to also create the ai_path for each individual instance within my define path code, but I'm not sure how to do this without it creating the same path for everything.

Code:
with (obj_pathfinder) {
  if (path_exists(ai_path)) {
   if (mp_grid_path(global.ai_grid, ai_path, _sx, _sy, _fx, _fy, true)) {
     path_set_kind(ai_path, 1);
     path_set_precision(ai_path, 8);
   } else {
     show_debug_message("ERROR: mp_grid_define_path() - No path created");
   }
  }
}
 
Last edited by a moderator:
Top