Legacy GM point_direction error?

D

dcamod

Guest
My problem is that the AI will not stop at the correct place when at certain angles from the target and instead slide to either the left or the top depending on its angle from the target. The code seems to be recalculating a target even when it has reached its target.

Code:
if object_exists(obj_Primary_goal)
{
mp_grid_path(global.grid,path,x,y,obj_Primary_goal.x+lengthdir_x(64,point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y)),obj_Primary_goal.y+lengthdir_y(64,point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y)),1);

tx = path_get_point_x(path,targetpos);
ty = path_get_point_y(path,targetpos);

mp_potential_step_object(tx,ty,2,obj_par_solid);

}
Here is a video of it in action.
 
Last edited by a moderator:
D

dcamod

Guest
I could really use some help here please.
 
Last edited by a moderator:
D

dcamod

Guest
Bump?? Don't want to create a whole other thread and restate my problem but no one has replied in about a week yet so I am bumping it again.
I just want to know why the object is sliding towards the left and the top. I have already implemented a work around solution to this AI in my game. I am just curious as to why this is happening.
 
D

dcamod

Guest
Thanks for the reply. Could I bother you to explain why this creates the issue I am experiencing though?

Edit: This solution did not solve the problem. Just did a quick rewrite and it returned the same result.
When point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y) is between 180 and 270 it rotates clockwise until it reaches 180. When point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y is between 360 and 90 it rotates counter-clockwise until it reaches 90.
 
Last edited by a moderator:
What is the value of target_pos and where are you setting it?

Can you add a check to see what mp_grid_path() is returning - it returns false if a valid path was not found - does it ever return false?

Add a check/debug message on path_get_point_x/y() as well - do these functions ever return a value of 0 - according to the docs, 0 will be returned if you pass these functions an out of range point on the path.
 
D

dcamod

Guest
The first 46 frames it returns 0 for mp_grid_path and no value is displayed for path_get_pointx/y (not 0 or any other number only the value for mp_grid_path) until frame 47 where they all return a value and never return 0.

Here is the super long list of numbers. Top number is mp_grid_path and the next two are the path_get_point x then y respectively.

0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
376
120
1
360
136
1
360
136
1
360
136
1
360
136
1
360
136
1
360
152
1
360
152
1
360
152
1
360
152
1
360
152
1
360
152
1
344
152
1
344
152
1
344
152
1
344
152
1
344
152
1
344
152
1
344
168
1
344
168
1
344
168
1
344
168
1
344
168
1
344
168
1
328
168
1
328
168
1
328
168
1
328
168
1
328
168
1
328
168
1
328
168
1
328
168
1
328
168
1
312
168
1
312
168
1
312
168
1
312
168
1
312
168
1
312
168
1
312
168
1
312
168
1
300
172
1
300
172
1
300
172
1
300
172
1
300
172
1
300
172
1
300
172
1
300
172
1
300
171
1
300
171
1
300
170
1
300
169
1
299
169
1
299
168
1
299
167
1
298
167
1
298
166
1
298
165
1
298
164
1
297
164
1
297
163
1
297
162
1
296
162
1
296
161
1
295
161
1
295
160
1
295
159
1
294
159
1
294
158
1
294
157
1
293
157
1
293
156
1
292
156
1
292
155
1
292
154
1
291
154
1
291
153
1
290
153
1
290
152
1
289
152
1
289
151
1
289
150
1
288
150
1
288
149
1
287
149
1
287
148
1
286
148
1
286
147
1
285
147
1
285
146
1
284
146
1
284
145
1
283
145
1
283
144
1
282
144
1
282
143
1
281
143
1
281
142
1
280
142
1
279
142
1
279
141
1
278
141
1
278
140
1
277
140
1
277
139
1
276
139
1
275
139
1
275
138
1
274
138
1
274
137
1
273
137
1
272
137
1
272
136
1
271
136
1
270
136
1
270
135
1
269
135
1
269
134
1
268
134
1
267
134
1
267
133
1
266
133
1
265
133
1
264
133
1
264
132
1
263
132
1
262
132
1
262
131
1
261
131
1
260
131
1
259
131
1
259
130
1
258
130
1
257
130
1
256
130
1
255
130
1
255
129
1
254
129
1
253
129
1
252
129
1
251
129
1
251
128
1
250
128
1
249
128
1
248
128
1
247
128
1
246
128
1
245
128
1
244
128
1
243
128
1
242
128
1
241
128
1
240
128
1
240
128
1
240
128
1
240
128
1
240
128
1
240
128
1
240
128
1
240
128
1
240
128
1
240
128
1
240
128

I have noticed that when it reaches the end of the path the point_direction value changes when between the angles I specified above (360 and 90, 270 and 180). I think it is recalculating an end path because this value is changing when I reach the end of the path but this does not explain why it does not do that when between 90 and 180 or when between 270 and 360.
 
Last edited by a moderator:
I'm in the same boat as you then. I can see what is happening, but I don't know why, and I would just put in a workaround to say don't move if I am already within one or two pixels of the target.
 
L

Linkdeous

Guest
if distance_to_object(obj_player) > rangeyouwant it to stop {stop movement }
You could use this built in variable
 

GMWolf

aka fel666
Whoaaaaaa, what is the whole lengthdir stuff supposed to achieve?
Don't write super long lines like that, break it up.
 
D

dcamod

Guest
Whoaaaaaa, what is the whole lengthdir stuff supposed to achieve?
Don't write super long lines like that, break it up.
The code I pasted is purposely written that way because I want to eliminate as many factors I possibly can so it is easier to debug. The actual code is:

mp_grid_path(global.grid,path,x,y,obj_Primary_goal.x+lengthdir_x(irandom_range(64,256),point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y)),obj_Primary_goal.y+lengthdir_y(irandom_range(64,256),point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y)),1);.

The only way I know to shorten it up would be do write it this next way. Which I have already done in my own project and set it up with a boolean so that it does not constantly choose a new target every step.

ox = obj_Primary_goal.x+lengthdir_x(irandom_range(64,256),point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y))
oy = obj_Primary_goal.y+lengthdir_y(irandom_range(64,256),point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y))

mp_grid_path(global.grid,path,x,y,ox,oy,1);

This allows the instance to stop between 64 and 256 pixels away from the target at random but without a boolean it will run the code every step and constantly choose a new position. I did not post the code in this form because it will not help solve the issue of the instance sliding after it has reached it's goal and it would have likely confused people if they tried to reproduce it in a project of their own. I have already set up the boolean and a work around for this problem.

I am trying to find out why that specific code written that specific way has the bugs I am describing.


if distance_to_object(obj_player) > rangeyouwant it to stop {stop movement }
You could use this built in variable

When you use the basic distance_to_object you cannot get this range functionality I do not want it to stop 64 pixels away I just simplified it for debugging purposes in hopes that it would make it easier for someone else to help me figure out why this bug is happening when the code is written this specific way.

I'm in the same boat as you then. I can see what is happening, but I don't know why, and I would just put in a workaround to say don't move if I am already within one or two pixels of the target.
I agree. This is how I solved the issue in my project.
Code:
if obj_Primary_goal.moving = 1
{
    ox = floor(obj_Primary_goal.x+lengthdir_x(irandom_range(64,256),point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y)))
    oy = floor(obj_Primary_goal.y+lengthdir_y(irandom_range(64,256),point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y)))
    mp_grid_path(global.grid,path,x,y,ox,oy,1);
    tx = path_get_point_x(path,targetpos);
    ty = path_get_point_y(path,targetpos);
    if place_meeting(tx,ty,obj_par_solid) || point_distance(tx,ty,obj_Primary_goal.x,obj_Primary_goal.y) < 64
    {
        ox = tx+lengthdir_x(64,point_direction(tx,ty,x,y));
        oy = ty+lengthdir_y(64,point_direction(tx,ty,x,y));
    }
    mp_potential_step_object(tx,ty,2,obj_par_solid);
    alarm[0] = path_time + 90
}

if obj_Primary_goal.moving = 0 && path_time > 0 && alarm[0] > -1
{
    mp_grid_path(global.grid,path,x,y,ox,oy,1);
    tx = path_get_point_x(path,targetpos);
    ty = path_get_point_y(path,targetpos);
    if place_meeting(tx,ty,obj_par_solid) || point_distance(tx,ty,obj_Primary_goal.x,obj_Primary_goal.y) < 64
    {
        ox = tx+lengthdir_x(64,point_direction(tx,ty,x,y));
        oy = ty+lengthdir_y(64,point_direction(tx,ty,x,y));
    }
    mp_potential_step_object(tx,ty,2,obj_par_solid);
}
Thanks for your replies guys. I know this is confusing.
 
A

Ampersand

Guest
Code:
mp_grid_path(global.grid,path,x,y,obj_Primary_goal.x+lengthdir_x(irandom_range(64,256),point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y)),obj_Primary_goal.y+lengthdir_y(irandom_range(64,256),point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y)),1);.

The only way I know to shorten it up would be do write it this next way. Which I have already done in my own project and set it up with a boolean so that it does not constantly choose a new target every step.

ox = obj_Primary_goal.x+lengthdir_x(irandom_range(64,256),point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y))
oy = obj_Primary_goal.y+lengthdir_y(irandom_range(64,256),point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y))

mp_grid_path(global.grid,path,x,y,ox,oy,1);
would be easier to work with as
Code:
var range = irandom_range(64, 256) ;
var angle = point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y) ;
var ox = obj_Primary_goal.x + lengthdir_x(range, angle) ;
var oy = obj_Primary_goal.y + lengthdir_y(range, angle) ;

mp_grid_path(global.grid,path,x,y,ox,oy,1);
Spaces are your friend. So are declared variables. The overhead for a local var is so low that you really don't need to optimize them out, and it makes your code much easier to read and also makes it much easier to modify. For instance here you can change the range values in half the time (rather negligible, I know, but a good habit to get into). Also your original code was calling irandom_range twice -- meaning that your lengthdir_* calls will not land in a uniform distribution across the radius of the "finish range".

Regarding the actual issue, you're going to need an AI action state or a flag to see whether to move towards target or stop (or strafe, whatever).

When in a certain range, set the flag to true. When outside a certain range + some extra radius padding, set the flag to false. That way it won't be jittering between states too often.

The best way to do this would be to check first if he's too close, if so retreat; if he's in a good fighting range, either don't move or just stutter step around; and if he's too far away, chase the target. If you set up the ranges and their padding right they shouldn't jump between movement states too much. This also lightens up the load on the pathfinding, as you can go ahead and only find the path the first step that the movement state changes.

Basically what you're trying to do here is achieve an aggro system -- even if that's not how you've been thinking about it so far.


Once this is done you could take it a few steps further into a true aggro system -- instead of moving towards an object x/y, you can just have a "target_x/y" that our enemy will move to. Then you can update this target_x/y only when the enemy has a clear line of sight of his target. This will make for the ability to escape around corners, out of sight and hiding in nooks and such, because the enemy will only move towards the last spot that he saw the enemy.

AI turns into a big fun mess really quickly, and it can seem overwhelming to get natural emergent behaviours out of simple code, but it definitely easier than it seems.
 
Last edited by a moderator:
D

dcamod

Guest
Okay, first of all, thanks for the reply that is a much clearer way to write the code and I will use that. That being said...my issue is not how to set up an AI. I have already done this in my project and moved on. I know all about state machines. My problem is really a question not an issue with my game. I have found a work around by setting a flag to true or false(boolean). I am not working on an aggro system.

My issue is this happens:

When point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y) is between 180 and 270 it rotates clockwise until it reaches 180. When point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y is between 360 and 90 it rotates counter-clockwise until it reaches 90.

With this code:
Code:
mp_grid_path(global.grid,path,x,y,obj_Primary_goal.x+lengthdir_x(64,point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y)),obj_Primary_goal.y+lengthdir_y(64,point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y)),1);
or to organize it better:
Code:
var range = 64 ;
var angle = point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y) ;
var ox = obj_Primary_goal.x + lengthdir_x(range, angle) ;
var oy = obj_Primary_goal.y + lengthdir_y(range, angle) ;

mp_grid_path(global.grid,path,x,y,ox,oy,1);
Watch the video in my original post and you will see the issue.

Also, if you have an alternative to moving to a random place within a range(n,n) that would be helpful. I had a hard time coming up with the concept I have and an alternative might help or even be easier.
 
Last edited by a moderator:

bbbower

Member
Perhaps you can solve this in a manner such as this?
Code:
// Do your angle range here
var range = 64 ;
var angle = point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y) ;
if !distance_to_point(obj_Primary_goal.x, obj_Primary_goal.y) < range
{
    // Do your moving / pathfinding here
    var ox = obj_Primary_goal.x + lengthdir_x(range, angle) ;
    var oy = obj_Primary_goal.y + lengthdir_y(range, angle) ;
    mp_grid_path(global.grid,path,x,y,ox,oy,1);
}
I'm still trying to grasp what the lengthdir is needed for but I have not used the mp_ functions, however this should work.
 
Last edited:
D

dcamod

Guest
The length_dir is used because I am setting a range of distances not a single distance. Try to do this with distance_to_object or distance_to_point and it will not work. I already set the range there using irandom_range(n,n).

I think everyone is missing the point of what I am trying to solve...maybe I should change the title.

My issue is this happens:


When the instance reaches it's destination and point_direction is between 180 and 270 it rotates and slides clockwise until it reaches 180. When the instance reaches it's destination and point_direction is between is between 360 and 90 it rotates counter-clockwise and slides until it reaches 90. It does not do that when between 90 and 180 or when between 270 and 360.

With this code:
Code:
var range = 64 ;
var angle = point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y) ;
var ox = obj_Primary_goal.x + lengthdir_x(range, angle) ;
var oy = obj_Primary_goal.y + lengthdir_y(range, angle) ;

mp_grid_path(global.grid,path,x,y,ox,oy,1);
I just want to find out why.
 
Last edited by a moderator:

bbbower

Member
The length_dir is used because I am setting a range of distances not a single distance. Try to do this with distance_to_object or distance_to_point and it will not work. I already set the range there.

I think everyone is missing the point of what I am trying to solve...maybe I should change the title.



mp_grid_path(global.grid,path,x,y,ox,oy,1);[/CODE]
No I understand that part, did you try my solution?
 
D

dcamod

Guest
Re-read my previous post? I don't think you understand.

I already know how to get the code to function.

Code:
var range = 64 // for ranged it would be(irandom_range(64,256)) but that is not my issue ;
var angle = point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y) ;

if object_exists(obj_Primary_goal) // or if flag = true or if distance > N etc...but that is not my issue;
{
var ox = obj_Primary_goal.x + lengthdir_x(range, angle) ;
var oy = obj_Primary_goal.y + lengthdir_y(range, angle) ;

mp_grid_path(global.grid,path,x,y,ox,oy,1); <<<<<<<<<< This is my issue

tx = path_get_point_x(path,targetpos);
ty = path_get_point_y(path,targetpos);

mp_potential_step_object(tx,ty,2,obj_par_solid);

}
 
Last edited by a moderator:

bbbower

Member
I see you are asking why not how to fix.
Why is because you are changing your angle and it is slightly altering the length_dir formula's causing the mp_ function to recalculate its route. Make your debug also display the lengthdirx/y values you'll see them tick and change.

new x new y angle changes slightly
new angle changes lengthdir new route determined new x / y
This repeats until they just kinda randomly stabilize.
[edit]
I ask 'why' all the time and get a 'how' answer, I feel dumb for missing that XD
[/edit]
 
D

dcamod

Guest
That is what I thought initially also. The problem with that logic is this.

When the instance reaches it's destination and point_direction is between 180 and 270 it rotates and slides clockwise until it reaches 180. When the instance reaches it's destination and point_direction is between is between 360 and 90 it rotates counter-clockwise and slides until it reaches 90. It does not do that when between 90 and 180 or when between 270 and 360.

It only happens between certain angles.
 
A

Ampersand

Guest
Yeah, your issue is the random stop range that you're not setting flags for...

create
Code:
march = false ;
min_stop = 64 ;
max_stop = 256 ;
chase_pad = 64 ;
step
Code:
var range = irandom_range(min_stop, max_stop) ;
var angle = point_direction(obj_Primary_goal.x,obj_Primary_goal.y,x,y) ;

var ox = obj_Primary_goal.x + lengthdir_x(range, angle) ;
var oy = obj_Primary_goal.y + lengthdir_y(range, angle) ;

if point_distance(x, y, ox, oy) < irandom_range(64, 256)
{
    march = false ;
}
if point_distance(x, y, ox, oy) > max_stop + chase_pad
{
    march = true ;
}

if march mp_potential_step_object(tx,ty,2,obj_par_solid) ;
Please try this code. Telling us what your issue is when you can't solve the issue is of no help, especially when you don't want to try the solutions I'm offering.

Besides, mp_potential_step is messy and if you're using the maximum delta angle per step part of it, you'll get weird stuff like this where they're going to wander on the edges of the range because they can only change so much direction per step.

I literally just now tried the above code, and it does not have the issue in your video with a quick blank project. Worked fine.

Understand that your issue is most likely the way the x offset and y offset is calculated within mp_potential_step. It looks like the angle can get just right so that it moves down a pixel into range but not over a pixel into range, and so it moves and tries to turn, and it chases these half pixels all the way down... x = round(x) ; y = round(x) ; at the end of your event may even solve your issue if you really insist on leaving your logic the way it is. A padded aggro radius is what you want -- and please do not misunderstand what I mean by aggro, I do not mean a target finding system, I am talking specifically about a padded range system.

If you simply refuse to use the solutions offered, I'm not sure why you're asking for help... You're asking someone to explain an oddity of a function that no one tends to use in a decent project because of the many oddities it likes to bring up and because of the fact that you can honestly make a better version of the script yourself that doesn't look like the object is being reeled in around the corner on a fishing line... I'm just trying to offer the solutions I've found for a wonky function that I stopped using for the same reasons you're finding now.

Regardless where the issue of the "floatiness/creeping" comes from, adding a little padding around the radius and a flag will fix your issue. Might be worth a try to just throw in the x = round(x) ; y = round(y) ; if you really don't want a flag check. You may even be able to round just the object's position on the distance check to solve this issue. I think it is some interaction between the way mp_potential_step finds the x/y offsets per step and the fact that the position is in decimals... Maybe it rounds position that's input while the distance check does not, or something along those lines.

If you want to go on an adventure to find the cause I'll join you. It was just my understanding you wanted a solution :p
 
Last edited by a moderator:

bbbower

Member
That's likely due to some range problems with whatever algorithm is used in the point_direction function. It's most likely just the way it rounds the decimals up or down between certain ranges to give a stable pointing direction in return the 1 and 4 quadrants just happen to not round the same way. My guess is that part is linked to that being gamemakers natural x/y axis flow, starting top left and going bottom right. I had a simlar issue using a distance formula in a java game due to the way floating points translate to on screen coordinates it would make my character tick back and forth when it reached a point I thought it should stabilize naturally.
 
Last edited:
D

dcamod

Guest
To Ampersand:

Already tried all of your solutions before starting this thread and asking the question. I fully understand what you mean. I am not looking for a solution in code. I am trying to understand why this is happening with this specific logic. Nothing more, and nothing less. If you had read my other posts more clearly you would realize I have already stated these things. Maybe go back and read them again? I appreciate you trying to help me. I would not ask for help for no reason and then ignore advice.

To bbbower:
Thank you for the answer that is more along the lines of what I am looking for. I hope someone can give some way of getting hard data on that guess though or perhaps offer an alternative to using the point_direction function.
 
Last edited by a moderator:

bbbower

Member
You can look here to design your own point direction.
http://www.davdata.nl/math/vectdirection.html
Look at that arctan diagram and you'll notice the similarity to the curve problem you have.

But I think you may end up with the same problem I did and have to take the range check route. Something to do with those floating points on the tangent curve vs the natural axis.
 
Top