Windows How would I program pedestrian npcs? (Overhead view)

S

Sourgass

Guest
I have learned game maker through trial and error for many years, and I can make most things i desire to come about one way or the other. However, a pedestrian system for an open world (overhead view) seems impossible, since I simply don't understand what method (script/scripts) you would use to get it done.

I've tried to use paths, but it seems inevidable that i would have to use hundreds of them to make it look like anything, and whenever you interact with one, how would you make him snap back into the path without massive issues? The best solution as far as i can see, would be to use nodes which i would place along the roads which the pedestrians could respond to, and prevent them from wandering all over, or even worse, inside everything.

*PS: It is important that the characters are not limited to it's regular cycle, but must be capable of straying, then returning to the sidewalk (which would be where most of them are situated).

Thank you very much if you've got any ideas.
Untitled.jpg
 
Hm... That is a problem. You should just link them each with a real brain, should work, I think.

Just kidding. My take on this is that the node idea will work the best I think.

Here's what I'm adding. Every time they move off course, because they saw something or want to talk to the player or are chased by something (whatever) they run code that is something like this:

Code:
var_stored_direction = direction;
var_stored_speed = speed;
var_stored_x = x;
var_stored_y = y;

//Basically, save the info of where it is and where it's going
So, when it leaves course it keeps track of what it was doing, so that when it needs to go back on course, it could move back to that saved location, and when it reached those coordinated, switch states so it's just walking again and start moving in the direction it was before it was distracted.

Now this, does have a problem, which is if the NPC gets SUPER of course, it might not find it's way back, and if it walks of the sidewalk at a diagonal, then it would walk back the same way, which is probably ineffecient, instead of going straight back onto the sidewalk to the nearest spot like a real person would. So instead you could make it walk back to the nearest node, and just add nodes every couple grid spaces. That has the bonus that it doesn't need to keep track of where it was going the last time it was on the sidewalk, the node does that for them.

I hope this helps. If you have any questions, feel free to ask, and I will do my best to answer.
 
S

Sourgass

Guest
Hm... That is a problem. You should just link them each with a real brain, should work, I think.

Just kidding. My take on this is that the node idea will work the best I think.

Here's what I'm adding. Every time they move off course, because they saw something or want to talk to the player or are chased by something (whatever) they run code that is something like this:

Code:
var_stored_direction = direction;
var_stored_speed = speed;
var_stored_x = x;
var_stored_y = y;

//Basically, save the info of where it is and where it's going
So, when it leaves course it keeps track of what it was doing, so that when it needs to go back on course, it could move back to that saved location, and when it reached those coordinated, switch states so it's just walking again and start moving in the direction it was before it was distracted.

Now this, does have a problem, which is if the NPC gets SUPER of course, it might not find it's way back, and if it walks of the sidewalk at a diagonal, then it would walk back the same way, which is probably ineffecient, instead of going straight back onto the sidewalk to the nearest spot like a real person would. So instead you could make it walk back to the nearest node, and just add nodes every couple grid spaces. That has the bonus that it doesn't need to keep track of where it was going the last time it was on the sidewalk, the node does that for them.

I hope this helps. If you have any questions, feel free to ask, and I will do my best to answer.

The idea of storing the info as well as finding the "closest exit" onto the sidewalk sure helped! I will go with that, but there is still something I cannot grasp, which is how the npc is supposed to identify which node it's supposed to move to after it has reached it's current one. If I use this:

Code:
node = instance_nearest(x,y,obj_node)

it selects the closest node, but obviously it would then select the node that it is currently on, as long as I don't use several types of objects for the nodes. I need a script that is better and/or other scripts that can help "instance_nearest" out in determining where to go. I'll keep thinking for whatever that's worth, but thanks for the help, it's funny how such seemingly simple ideas can run you into issues :-)
 
I

icuurd12b42

Guest
http://www.host-a.net/u/icuurd12b42/tilepath.zip

Tilepath.gmk can be imported in studio. however it's really slow in studio while it was super efficient in GM6 and the code is not really easy to understand.

The basic concept is an AI live on tiles. he walks the tile, looks 1 or 2 foot steps ahead to see if there is a tile there, if not look left or right relative to it's direction (image_angle more accurately) and turn in the direction where a tile check was successful

In theory he should never walk off a tile as long as the ai started on a tile...



Today I could probably rewrite this in fewer lines...
create
Code:
image_angle = choose(0,90,180,-90);
direction = image_angle;
speed = 2;
step
Code:
if(noone == collision_point(x+lengthdir_x(speed*2,image_angle),y+lengthdir_y(speed*2,image_angle),obj_tile,false,true))
{
    var rand_dir = choose(-1,1);
    if(noone != collision_point(x+lengthdir_x(speed*2,image_angle+90*rand_dir),y+lengthdir_y(speed*2,image_angle+90*rand_dir),obj_tile,false,true))
    {
        image_angle+=90*rand_dir;
    }
    else if(noone != collision_point(x+lengthdir_x(speed*2,image_angle+90*rand_dir*-1),y+lengthdir_y(speed*2,image_angle+90*rand_dir*-1),obj_tile,false,true))
    {
        image_angle+=90*rand_dir*-1;
    }
    else if(noone != collision_point(x+lengthdir_x(speed*2,image_angle+180),y+lengthdir_y(speed*2,image_angle+180),obj_tile,false,true))
    {
        image_angle+=180;
    }
}
direction = image_angle;
The example is more complex as it only checks occasionally for a new tile
You can use the tile to fill a grid if they are aligned and placed exactly on a grid cell, this way you can check for tiles using grid cells which is super efficient instead of the slow instance based check...

you can also have a mp_grid, initially all cells set as taken, and clear the grid cells where a tile is. like the inverse of the grid mentioned in the paragraph above. So you can do a*
 
I assume you mean that the problem is that the object will then not move, because it just goes to the node it's already on. The other thing you mentioned is that it doesn't let you use multiple node objects. Here is a solution for both of those problems:

First of all, you can set up the node objects such that you can see any node, and a specific node.

Create an object named something like: obj_node_parent, or just obj_node. Other node objects, for example, one for each direction would be named obj_node_left, obj_node_up, etc. Tell those ones to set obj_node_parent as their parent object. Then if you want to select all nodes, you use obj_node_parent, and if you want to select a specific type of node, use the direct object names. The directional nodes are included under obj_node_parent because they are children objects.

For finding the nodes, you need Finite State Machines. https://forum.yoyogames.com/index.php?threads/finite-state-machines.366/

This basically means that the code switches states between whether it's looking for a node, or not.

Here is a basic example. By basic I mean it doesn't cover everything you might want to do. It is fairly long, and I can't explain everything about it. Hopefully it makes sense to you. You will probably have to modify it to work for your specific game, if you choose to use it.

Code:
//Create event

var_state = 0;
var_speed = 4;
var_range = 320;
Code:
//Step event

inst_monster = instance_nearest(x, y, obj_monster);

if (var_state == 0) //Going about doing it's business
{
image_angle = direction; //Rotate sprite to match direction

//This chunk makes it change directions to match nodes underneath it.
if ((place_meeting(x, y, obj_node_right) == true) { direction = 0; }
if ((place_meeting(x, y, obj_node_up) == true) { direction = 90; }
if ((place_meeting(x, y, obj_node_left) == true) { direction = 180; }
if ((place_meeting(x, y, obj_node_down) == true) { direction = 270; }

//Check if there is a monster close enough to chase away NPC
//NOTE: This is just an example. The key point for any code that would distract the NPC is
//that it MUST end with var_state = <number greater than 1>;
if (distance_to_object(inst_monster) < var_range)
 {
 var_state = 1;
 }
}

if (var_state == 1) //Run away from monster
 {
 if (distance_to_object(monster) < var_range) //If close to monster
  {
  mp_potential_step(monster.x, monster.y, -var_speed, true); //Move away from monster
  image_angle = direction+180; //Rotate sprite to face away from monster
  }
 else //If safely away from monster
  {
  var_state = 2;
  }
 }

if (var_state == 2) //Move to nearest node
 {
 node = instance_nearest(x, y, obj_node); //Find nearest node
 mp_potential_step(node.x, node.y, var_speed, true); //Move toward nearest node
 image_angle = direction; //Face correct direction

 if (place_meeting(x, y, obj_node) == true) //If standing on node
 {
  var_state = 0; //Switch back to first state
 }
 }
I wrote this from memory, so it may not be 100% correct. Hopefully it gives you an idea.
 
S

Sourgass

Guest
Great stuff! I'll make sure to look in to your examples, if not entirely then essentially.

I've been trying out a method of 3 separate node objects. Whenever the npc reaches a node (indicated by a parent for all nodes) it checks if it's node object 1, 2 or 3. If it is 1, it then searches for 2, then for 3, and back to 1. This allowed me to make a path, and reverse the order for an npc if i wanted to. Needless to say, this got a bit messy, or rather tangled, as several connecting paths easily ended up creating loops where the npcs would get stuck and so on, as well as roads that were only accessable from one end and not the other.

I think im gonna try what you said about the nodes which change the direction rather than giving the npc a set position, that way i can simply let the npcs drift along the road in a set direction, which would allow me to make several objects for curves, dead ends, etc. When i think about it that shouldnt be much of a problem, if it wasn't for the fact that i can't stand to watch npcs pass thru each other. The only method of dodging obsticles that i know of is "mp_potential_step", which forces me to give the npc a set x and y value to look for, and it doesn't work that well, as the npcs end up tangeling together while trying to pass, sometimes taking long trips across the gardens and highways. ^_^ That issue should be easy to fox however, it's after all just a matter of looks.

Open world doesn't seem to be that popular in game maker, seeing how most npc movement explanations seem to revolve around patrolling rather than walking along roads and such.
 
A

anomalous

Guest
Open world is near and dear to my heart, but I suspect the primary reasons its not popular is that it's a HUGE time investment over other more lucrative designs and if it's not "your thing", it doesn't add that much appeal (apparently since all that other crap sells so much!) I haven't given up on open world either, 100% designing (mostly) open world currently.

Good advice in the thread.

Definitely consider using paths, but have the npc simply move towards each path point. You can then customize to your heart's content how that "move towards point" actually happens.

For picking a point, if you are not diverted, you pick the next point in the path after reaching one.
For picking a point if you were diverted, you can loop through all path points and choose the nearest.
If you use multiple paths, you can also loop through each path (see below), finding the nearest of any path.
If you only loop through one path per step, and keep the path points reasonable, you may not need to chunk more than that.

For generating paths, you could consider using particular tiles that you can identify, it could look like other sidewalks but you write a routine that will go through all map tiles (or a grid holding that data), and construct paths directly from the tiles you designated as "npc paths". Be sure to consolidate the points such that you don't have a point per tile though..obviously.

Stuff all those path indexes in a list/array, and you can then do anything with them. Go from one path to another, etc.

mp potential may be workable, but you can also do something like have them always turn left for X steps (or even check collision until safe to turn back) before trying to return to their path destination point. IT will take some tinkering, but if you get the overall skeleton down (pathing and strolling about) , that can come over time.
 
S

Sourgass

Guest
Absolutely! An open world provides so many possible activities compared to a more confined game, but it does come with an intense amount of visual work.

Are you telling me that there's a way to snap on to the path which has a point closest to the npc's current possition? This would solve most my problems right there, i wouldn't even mind designing a large amount of paths! I looked in to the path system and I just don't quite get how it works. I will learn eventually but I would love if you had the script for how to do it. In fact, i might be more or less set if i got this holy grail of a code!
 
A

anomalous

Guest
path_get_number gives you the number of defined points on a path.
path_get_point_x and y can give you the xy position of any "n" point on that path.

So you can do something like:
assume you created a path called "path"
var xx = -1;
var yy = -1;
var n = path_get_number(path);

for (var i=0;i<n;i++)
{
xx = path_get_point_x(i);
yy = path_get_point_y(i);
}

For each point, you could check point_distance to the enemy x/y position, and figure out the nearest.

As I mentioned, you can make a ds_list or something where you stuff all npc walking path indexes.
Cycle through each path, then the above cycles through each point. End result will be nearest point of any valid path. If there are a lot of paths or lots of points, you can break it up so they check one path per step or something.
 
Top