Legacy GM Is it a legit chase script?

mafon2

Member
I'm prototyping Pac-Man clone and wrote simple chase script, but it has 2 strange quirks:

1 - o_enemy don't want to chase o_player, it chases o_levelstart for some reason, though o_levelstart has nothing to do with the script or any object in the project, it used to draw a grid, I had to stick it to o_player and it worked, but after deleting it, o_enemy don't care for o_player anymore and just wanders away (?).

it says that there's a reference somewhere to o_levelstart, but I re-read all code 10 times and didn't find it. Ctrl+F doesn't show it either. The project used to crash without it, now it works :-\. And it's not like it's full of spaghetti - quite contrary.

2 - sometimes o_enemy walks through the walls on the left.


Script itself

Code:
// move speed

cell_size = 2;

// Checking distance to the target, different enemies have different targets (on create - target = "o_player" or target = "o_something_else")

A = point_distance(x, y-cell_size, target.x, target.y);
B = point_distance(x+cell_size, y, target.x, target.y);
C = point_distance(x, y+cell_size, target.x, target.y);
D = point_distance(x-cell_size, y, target.x, target.y);

// Excluding walls

if place_meeting(x,y-cell_size, o_wall)

    {
    A = 1000;
    }

if place_meeting(x+cell_size,y, o_wall)

    {
    B = 1000;
    }
    
if place_meeting(x,y+cell_size, o_wall)

    {
    C = 1000;
    }
    
if place_meeting(x-cell_size,y, o_wall)

    {
    D = 1000;
    }

// No turning back movement

// UP   
    
if facing = "up"
    {
    if A = min(A,B,D)
        {
        y = y - cell_size;
        facing = "up";
        }
    if B = min(A,B,D)
        {
        x = x + cell_size;
        facing = "right";
        }       
    if D = min(A,B,D)
        {
         x = x - cell_size;
        facing = "left";
        }
    }

// RIGHT   
        
if facing = "right"
    {
    if A = min(A,B,C)
        {
        y = y - cell_size;
        facing = "up";
        }
    if B = min(A,B,C)
        {
        x = x + cell_size;
        facing = "right";
        }       
    if C = min(A,B,C)
        {
        y = y + cell_size;
        facing = "down";
        }
    }

// DOWN
    
if facing = "down"
    {
    if B = min(B,C,D)
        {
        x = x + cell_size;
        facing = "right";
        }       
    if C = min(B,C,D)
        {
         y = y + cell_size;
        facing = "down";
        }
    if D = min(B,C,D)
        {
        x = x - cell_size;
        facing = "left";
        }
    }
    
// LEFT

if facing = "left"
    {
    if A = min(A,C,D)
        {
        y = y - cell_size;
        facing = "up";
        }       
    if C = min(A,C,D)
        {
        y = y + cell_size;
        facing = "down";
        }
    if D = min(A,C,D)
        {
        x = x - cell_size;
        facing = "left";
        }
    }
you can see broken "AI" in "action": https://radikal.ru/video/bqVCjdcXrcd (yellow is a "coward" and pink targets a space to the right of o_player for now)

it used to be fine, before I deleted o_levelstart -_-.
 

CloseRange

Member
And it's not like it's full of spaghetti - quite contrary.
I'm looking at the code and see quite the oppisite. It's like a pasta farm up there.
Joking aside (well not really a joke)
Code:
A = point_distance(x, y-cell_size, target.x, target.y);
B = point_distance(x+cell_size, y, target.x, target.y);
C = point_distance(x, y+cell_size, target.x, target.y);
D = point_distance(x-cell_size, y, target.x, target.y);
should be done in arrays and then you can simplify everything so much.

Ok this is a common mistake that might be causing your problem? There isn't enough code here to really determine especially with how you claim o_levelstart worked with them but where you keep doing if facing = "up" if facing = "right" ect
this should be a switch statement.
If the first test succeeds (facing up) and it switches to, lets say right.
Once it reaches the if facing = "right" further down this will ALSO succeed on the same frame. and so it's actually possible for every one of the if statements to pass and get executed.
Another thing that will help make your code less spaghetti and simpler:
you don'teven need those if statments... at the start you exclude walls by setting them to a high number. Do the same with opposing directions. Then you can use 1 single min statement because if it's a big number you should already guarantee that it will not be the smallest.
I tested the code out and cleaned it up:
Code:
// move speed
cell_size = 2;

// still think this should be an array but I'll leave that to you
A = point_distance(x, y-cell_size, target.x, target.y);
B = point_distance(x+cell_size, y, target.x, target.y);
C = point_distance(x, y+cell_size, target.x, target.y);
D = point_distance(x-cell_size, y, target.x, target.y);

// Excluding walls and opposing directions
// I set the number to higher because that was what caused enemies to move into walls.

if(place_meeting(x,y-cell_size, o_wall) || facing == "down")
    A = 10000;
if(place_meeting(x+cell_size,y, o_wall) || facing == "left")
    B = 10000;
if(place_meeting(x,y+cell_size, o_wall) || facing == "up")
    C = 10000;
if(place_meeting(x-cell_size,y, o_wall) || facing == "right")
    D = 10000;

switch(min(A, B, C, D)) {
    case A:
        y -= cell_size;
        facing = "up";
        break;
    case B:
        x += cell_size;
        facing = "right"
        break;
    case C:
        y += cell_size;
        facing = "down"
        break;
    case D:
        x -= cell_size;
        facing = "left"
        break;
}
from 123 lines to 43 :p though I did move some brackets and comments

The script itself still produces not the best ai for enemies but that's just the nature of the system. I'd suggest using game maker's built in path finding but that choice is up to you.
 
I know this doesn't help you directly with your current issues but there's a brilliant article all about pacman ghost behaviour and how they actually work. May be of some help with your project.
LINK
 

mafon2

Member
Thanks, I'm "a big boy", but my approach is very brutish, I can't get the hang of anything more sophisticated than "if, then, else". I think I kinda understood what you have done with the switch, but I've never could have express it myself.

Oh, and that part with "||" is very cool.

---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---
And here goes a long tale with happy ending, you can skip to the ending.
---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---0---

I tested your script and now they walk through the upper walls sometimes >_>

I changed layout few times and each time they found a place to circle around and sometimes 1 or 2 escaped the fence through the upper wall skipping o_O.

You can check their behaviour here: https://radikal.ru/video/44NsgGFyACN (the pink one operates with the old script)

I'm baffled, hence the only other code is:

Code:
CREATE for o_enemy1

facing = choose("up", "right", "down", "left");
target = "o_player";
chasing = true;
and there's repeating 1-step alarm with

Code:
if chasing = true
    {
    script_execute(scr_chase3)
    }
else
    {
    script_execute(scr_runaway2)
    }

// they change behaviour with the C key - kinda comical :-)  Escaping script is the same as chasing, but reversed.
I deleted all the other instances in the room, including o_player and enemmies behaviour didn't changed. Ok. I think there's an error with adressing target. But what's strange is that it used to work! I changed "target" in script to "o_player" and now they react to it, but still escape through upper walls occasionally. Sometimes it's because they are in the dead end and it's understandable, but sometimes I can't grasp why they do it.

target changed to o_player: https://radikal.ru/video/X9OnxmQay9V

-------------------------
but, of course, it's not how I want to address the target, because the script must be universal.

so I added step with "targetX = o_player.x" and "targetY = o_player.y" for o_enemy

and changed

A = point_distance(x, y-cell_size, target.x, target.y);

to

A = point_distance(x, y-cell_size, targetX, targetY);

And here it was – the universal script, phew. I would be embarassed if my head wasn't feeling like a balloon.
 

CloseRange

Member
Wait.... wait wait wait:
Code:
target = "o_player";
is that actually how you have the code? When referencing an object you shouldn't use quotes. That creates a string and you can't get an object's variables like that.
If it ever worked like that then when it did work was 100% pure luck. I'm more suprised it did anything at all instead of throwing an error.
 
Code:
target = "o_player";
You're assigning a string to the variable target, but in the Step event, you are using the target variable as if it were an object/instance id.

This is what was causing the o_levelstart errors. Instead of referring to an instance of the object o_player, target had a value that referenced o_levelstart.

You need to write it without the quotes

Code:
target = o_player;
 

TheouAegis

Member
o_levelstart is 0.
"o_player" is also 0.
Therefore, target is o_levelstart.

Then you deleted o_levelstart and o_player moved up in the listu o_player became 0, thus the code seemed to work.

Resource names are constants (although GM does store them as strings still for debugging purposes).

As for why the enemies clipping through walls, i'd guess it's because maybe you have the sprite origin at(0,0) and your cell_size is just high enough that the enemy is looking outside the field and sees there's no wall.
 

CloseRange

Member
the sprite origin at(0,0) and your cell_size is just high enough that the enemy is looking outside the field and sees there's no wall.
@TheouAegis I doubt that's the problem. The top of the script is where he sets cell_size and he sets it to 2. He said the problem went away when he changed the way he was targeting things so I assume it was just a side effect of that.
Also I never knew a string was referenced as 0, interesting.
 

mafon2

Member
@TheouAegis

You took this "mystery" and unmasked it, like a pro.



About wall-clipping, as I said, I think it's occuring, when o_enemy is trapped (faces dead-end or stumbles upon another o_enemy), so it just needs to be addressed with the turn-around option or excluding dead ends & allowing enemiess to pass each other.

@Appletreeman

Oh, thank you for the link, it's most interesting. I dug up my copy of GMS actually after watching video about ghosts behaviour in Pac-Man. I always liked how each one of them has it's own character.
 
Last edited:
Top