GMLscripts.com

Discuss and collaborate on GML scripts
Invert

You are not logged in.

#1 2014-05-26 18:43:40

koltz
Member
Registered: 2014-05-26
Posts: 15

Help with instance_nth_nearest or something similar

I am creating a game that needs something like instance_nth_nearest but not quite. What I need is something like instance_nth_nearest, but only looking at 90 degree angles (up,down,left,right) and only within a certain length (32 pixels) of one object type. If that object type isn't in a direction, that direction is ignored. The player then hits a key on the keyboard to move the player in that specific direction with the matching object and it should stop when on top of that object (32 pixels). This continues with the instance updating to the newest nearest objects. When they press a matching key, it repeats itself over and over while ignoring the object the player is directly on. I was looking at range_finder but it doesn't return the instance.

Thanks for any help.

Offline

#2 2014-05-26 21:20:31

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Help with instance_nth_nearest or something similar

If you need the nearest instance in a direction, you should try collision_line_first().


Abusing forum power since 1986.

Offline

#3 2014-05-26 21:55:30

koltz
Member
Registered: 2014-05-26
Posts: 15

Re: Help with instance_nth_nearest or something similar

Thanks for the reply, Ok I have it implemented ( I tried collision_line_list previously) but the character doesn't move when the appropriate key is pressed.

Here is the obj_player - collision -w/ obj_letter

Expand    inst_right = collision_line_first(x,y,x+32,y,obj_letter,true,false);
    inst_left = collision_line_first(x,y,x-32,y,obj_letter,true,false);
    inst_up = collision_line_first(x,y,x,y-32,obj_letter,true,false);
    inst_down = collision_line_first(x,y,x,y+32,obj_letter,true,false);

obj_player - step event

Expandif ( keyboard_check_pressed(vk_anykey))
    {
        if keyboard_lastchar = inst_up.object_instance {
            if point_distance(x, y, inst_up.x, inst_up.y) <> 1 {
                image_angle = point_direction (x,y,inst_up.x,inst_up.y);
                move_towards_point(inst_up.x, inst_up.y, 1);
            } 
            else {
                inst_up = "";
                speed = 0;
            }
        }
        
        if keyboard_lastchar = inst_down.object_instance {
            if point_distance(x, y, inst_3.down, inst_down.y) <> 1 {
                image_angle = point_direction (x,y,inst_down.x,inst_down.y);
                move_towards_point(inst_down.x, inst_down.y, 1);
            }
            else {
                inst_down = "";
                speed = 0;
            } 
        }
    
        if keyboard_lastchar = inst_left.object_instance {
            if point_distance(x, y, inst_left.x, inst_left.y) <> 1 {
                image_angle = point_direction (x,y,inst_left.x,inst_left.y);
                move_towards_point(inst_left.x, inst_left.y, 1);
            }
            else {
                inst_left = "";
                speed = 0;
            } 
        }
    
        if keyboard_lastchar = inst_right.object_instance {
            if point_distance(x, y, inst_right.x, inst_right.y) <> 1 {
                image_angle = point_direction (x,y,inst_right.x,inst_right.y);
                move_towards_point(inst_right.x, inst_right.y, 1);
            }
            else {
                inst_right = "";
                speed = 0;
            } 
        }
    }

I have been hitting my head on the table this whole weekend on this. Thanks for the help.

Offline

#4 2014-05-27 00:08:01

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Help with instance_nth_nearest or something similar

Seems like there a lot of ways this approach can go wrong. I think this could be done more robustly but I'm not sure I totally understand the goal. I'm imagining letters spaced evenly on a grid with 32x32 pixel spacing. A player object moves from letter to neighboring letter when the matching key is pressed. I would expect the player object to only be able to change directions when it has arrived at the next letter.

If this is the case, I'd start with a movement system like this:

    Grid Based Movement Tutorial: http://www.8bitwarrior.com/?p=344

    Grid Based Movement HTML5 Demo: http://8bitwarrior.com/GridBasedMovement/

Then modify it so that the input uses whichever keys are nearby.

Expandif (isMoving == false)
{
    // Disable keys by default.
    var key_up = -1;
    var key_down = -1;
    var key_left = -1;
    var key_right = -1;

    // Enable keys for neighboring instances, if any.
    // I'm assuming object_instance is a key code.
    with (collision_point(x, y-32, obj_letter, false, false)) key_up = object_instance; 
    with (collision_point(x, y+32, obj_letter, false, false)) key_down = object_instance;
    with (collision_point(x-32, y, obj_letter, false, false)) key_left = object_instance;
    with (collision_point(x+32, y, obj_letter, false, false)) key_right = object_instance;

    // Change literal key codes to key variables set above.
    // eg. vk_right becomes key_right
    if (keyboard_check(key_right))
    {
        isMoving = true;
        moveTimer = gridSize;
        speedX = moveSpeed;
        speedY = 0;
    }
   // And so forth...

If I'm off-base, can you post an image or the project itself so I have a better idea of what you trying to do?

Last edited by xot (2014-05-27 01:41:24)


Abusing forum power since 1986.

Offline

#5 2014-05-27 07:37:29

koltz
Member
Registered: 2014-05-26
Posts: 15

Re: Help with instance_nth_nearest or something similar

That is exactly what I was looking for. One issue I am not sure how to resolve.

Expand    with (collision_point(x, y-32, obj_letter, false, false)) key_up = object_instance;
    with (collision_point(x, y+32, obj_letter, false, false)) key_down = object_instance;
    with (collision_point(x-32, y, obj_letter, false, false)) key_left = object_instance;
    with (collision_point(x+32, y, obj_letter, false, false)) key_right = object_instance;

You are correct that object_instance refers to the letter that the player has to type. How do I reference it with the specific obj_letter objects depending on the direction it is going? I did this similarly with the instance_nth_nearest but again it was looking at all positions, not just up,down,left,right.


Here is one other question on something I need help on. With the letters being created in the grid, I am randomly generating them in the grid using irandom. My problem is how can I look at the surrounding other objects to make sure they aren't the same so if I press say S, there aren't multiple S' in different direction?

EDIT: Running as is, the player moves automatically on start with no keyboard control of movement. If I hit a key it will pause when it reaches the 32x32 cube defined in code, but once I release it just continues moving again. The only thing I can think of is that I am defining the object_instance variable in each letter object. I don't see anything wrong with the code. I have a parent object (obj_letter) being referenced in each child object. Each child object under the create is defining object_instance = "?" where the ? is the letter as defined in each obj_letter child (a,b,c,d,...).

TSV
S*N
RUP

Player is the asterisk, so in the example, pressing S would either take me up or down but I want to avoid having duplicates. Here is how I am filling the screen:

Expand//randomly display alphabet in room
var random_x
var random_y
var object_random
random_x = 32
random_y = 32
object_random=0
while (random_x <= 608 && random_y <= 736){
    object_random = irandom_range(1,26)
        if object_random = 1{
            instance_create(random_x,random_y,obj_letter_A)
            if random_x < 608{
                random_x += 32
            } else {
                random_x = 32
                random_y += 32
            }
        }
        (repeats for each letter (A - Z), object_random(1 - 26)
}

Thanks!

Last edited by koltz (2014-05-27 14:18:53)

Offline

#6 2014-05-27 14:42:12

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Help with instance_nth_nearest or something similar

How do I reference it with the specific obj_letter objects depending on the direction it is going?

I'm not sure I understand the question, but if you need to know the instance id associated with the key press, you could change the code a little:

Expand    key_up = collision_point(x, y-32, obj_letter, false, false);
...
    if (instance_exists(key_up))
    {
        if (keyboard_check(key_up.object_instance))
        {
            isMoving = true;
            moveTimer = gridSize;
            speedX = moveSpeed;
            speedY = 0;
            movingToward = key_up;
        }
    }

Now, regarding this:

Expand    (repeats for each letter (A - Z), object_random(1 - 26)

This is a lot of extra work. Ideally, assuming the letter objects function the same, you should have one letter object, and use instance variables to define the properties of each instance to make them unique. That will simplify and shorten your code tremendously and prevent a lot of duplicate work when you need to make changes that effect every letter.

Instead of repeating this 26 times:

Expand    object_random = irandom_range(1,26)
    if object_random = 1{
        instance_create(random_x,random_y,obj_letter_A)
        if random_x < 608{
            random_x += 32
        } else {
            random_x = 32
            random_y += 32
        }
    }
    // (repeats for each letter (A - Z), object_random(1 - 26)

... you can do this once:

Expand    object_random = irandom_range(1,26);
    var inst = instance_create(random_x, random_y, obj_letter);
    inst.value = object_random;
    inst.keycode = inst.value + 64;
    inst.letter = chr(inst.value + 64);
    // (that's all)

However, if you want to have unique objects for each letter, there is another way to simplify things:

Expandvar object_pool = ds_list_create();
ds_list_add(object_pool, object_letter_A);
ds_list_add(object_pool, object_letter_B);
...
ds_list_add(object_pool, object_letter_Z);

//randomly display alphabet in room
var random_x
var random_y
var object_random
random_x = 32
random_y = 32
object_random=0
while (random_x <= 608 && random_y <= 736){
    object_random = irandom_range(1,26)
    instance_create(random_x, random_y, ds_list_find_value(object_pool, object_random-1);
    if random_x < 608{
        random_x += 32
    } else {
        random_x = 32
        random_y += 32
    }
    // (that's all)
}
ds_list_destroy(object_pool);

You could simplify that further by using "for" loops instead of "while" loops.

Expand//randomly display alphabet in room
var random_x, random_y, object_random;
for (random_y=32; random_y<=736; random_y+=32) {
    for (random_x=32; random_x<=608; random_x+=32) {
        object_random = irandom_range(1,26);
        instance_create(random_x, random_y, ds_list_find_value(object_pool, object_random-1);
    }
}

Here is one other question on something I need help on. With the letters being created in the grid, I am randomly generating them in the grid using irandom. My problem is how can I look at the surrounding other objects to make sure they aren't the same so if I press say S, there aren't multiple S' in different direction?

To handle this, when you select a random letter, check two spaces above it, two spaces to the left, one space up-left and one space up-right to make sure it is not already used. If it is, select a new random letter and repeat the checks. As long as you are creating instances row by row, top to bottom, left to right, this will prevent any cell from having duplicate neighbors.

Expandvar random_x, random_y, object_random;
for (random_y=32; random_y<=736; random_y+=32) {
    for (random_x=32; random_x<=608; random_x+=32) {
        var up = collision_point(random_x, random_y-64, obj_letter, false, false);
        if (instance_exists(up)) up = up.object_index;
        var left = collision_point(random_x-64, random_y, obj_letter, false, false);
        if (instance_exists(left)) left = left.object_index;
        var upleft = collision_point(random_x-32, random_y-32, obj_letter, false, false);
        if (instance_exists(upleft)) upleft = upleft.object_index;
        var upright = collision_point(random_x+32, random_y-32, obj_letter, false, false);
        if (instance_exists(upright)) upright = upright.object_index;
        do {
            object_random = irandom_range(1,26);
            var obj = ds_list_find_value(object_pool, object_random-1);
        } until (obj != up && obj != left && obj != upleft && obj != upright);
        instance_create(random_x, random_y, obj);
    }
}

Or if you are using one object for all letters:

Expandvar random_x, random_y, object_random;
for (random_y=32; random_y<=736; random_y+=32) {
    for (random_x=32; random_x<=608; random_x+=32) {
        var up = collision_point(random_x, random_y-64, obj_letter, false, false);
        if (instance_exists(up)) up = up.value;
        var left = collision_point(random_x-64, random_y, obj_letter, false, false);
        if (instance_exists(left)) left = left.value;
        var upleft = collision_point(random_x-32, random_y-32, obj_letter, false, false);
        if (instance_exists(upleft)) upleft = upleft.value;
        var upright = collision_point(random_x+32, random_y-32, obj_letter, false, false);
        if (instance_exists(upright)) upright = upright.value;
        do {
            object_random = irandom_range(1,26);
        } until (object_random != up && object_random != left && object_random != upleft && object_random != upright);
        var inst = instance_create(random_x, random_y, obj_letter);
        inst.value = object_random;
        inst.keycode = inst.value + 64;
        inst.letter = chr(inst.value + 64);
    }
}

None of this code has been tested. I apologize if I've made any confusing errors.

EDIT: The neighbor checking algorithm was not designed or implemented correctly. This has been fixed.

Last edited by xot (2014-05-28 19:57:37)


Abusing forum power since 1986.

Offline

#7 2014-05-27 15:33:30

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Help with instance_nth_nearest or something similar

EDIT: Running as is, the player moves automatically on start with no keyboard control of movement. If I hit a key it will pause when it reaches the 32x32 cube defined in code, but once I release it just continues moving again.

I missed this. I don't know what could be happening based on your description. My only guess is, if one of the key codes you are looking for set to 0, that is the same is looking for no key being pressed. That could cause it to move by itself. Like above, I did not actually test to code I suggested.


Abusing forum power since 1986.

Offline

#8 2014-05-27 16:01:39

koltz
Member
Registered: 2014-05-26
Posts: 15

Re: Help with instance_nth_nearest or something similar

Ok, and again, thank you for the help!

Here are two images of what I am trying to do,

1st Image: Player (obj_player) starting, surrounded by letters (obj_letter). There is a letter underneath (J) the player as well in case they have to get back to that spot. So the player can hit either Z to go up, I to go left, K to go right, or T to go down. No other keys are available.

hqhx.png

2nd Image: Player presses Z to move up. They now have the option to press Y to go left, A to go right, or J (where the player was) to go back down. The blue = is a wall and they can't move that way so no option to move up.

smbw.png

That is the the main goal of the player.

Here is the code so far:

obj_player - Create

ExpandgridSize = 32;     // Should be power of 2 (...8,16,32...)
isMoving = false;  // Keeps track of when player is moving
moveTimer = 0;  // Counts down from grid size each step
moveSpeed = 1;     // Do not set higher than grid size!
speedX = 0;
speedY = 0;
image_speed = 1 / (room_speed / 10);

obj_player - Step

Expandif (isMoving == false)
{
    // Disable keys by default.
    var key_up = -1;
    var key_down = -1;
    var key_left = -1;
    var key_right = -1;
    // Enable keys for neighboring instances, if any.
    // I'm assuming object_instance is a key code.

    key_up = collision_point(x, y-32, obj_letter, false, false);
    key_down = collision_point(x, y+32, obj_letter, false, false);
    key_left = collision_point(x-32, y, obj_letter, false, false);
    key_right = collision_point(x+32, y, obj_letter, false, false);

    if (instance_exists(key_up))
    {
        if (keyboard_check(key_up.object_instance))
        {
            isMoving = true;
            moveTimer = gridSize;
            speedX = 0;
            speedY = -moveSpeed;
            movingToward = key_up;
        }
    }
        if (instance_exists(key_down))
    {
        if (keyboard_check(key_down.object_instance))
        {
            isMoving = true;
            moveTimer = gridSize;
            speedX = 0;
            speedY = moveSpeed;
            movingToward = key_down;
        }
    }
        if (instance_exists(key_left))
    {
        if (keyboard_check(key_left.object_instance))
        {
            isMoving = true;
            moveTimer = gridSize;
            speedX = -moveSpeed;
            speedY = 0;
            movingToward = key_left;
        }
    }
        if (instance_exists(key_right))
    {
        if (keyboard_check(key_right.object_instance))
        {
            isMoving = true;
            moveTimer = gridSize;
            speedX = moveSpeed;
            speedY = 0;
            movingToward = key_right;
        }
    }
 }   
if (isMoving == true)
{
   x += speedX;
   y += speedY;
   
   moveTimer -= moveSpeed;
   if (moveTimer == 0) isMoving = false;
}

No other code in obj_player.

obj_letter (Parent) - Create

Expandvar object_instance;
image_speed = 1 / (room_speed / 10);

obj_letter_A (child) - Create

Expandevent_inherited();
object_instance = "a";

obj_letter_B = "b" and so on through the alphabet. I have no other code outside of generating the random letters.

Offline

#9 2014-05-27 16:13:40

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Help with instance_nth_nearest or something similar

OK, I think I see the problem.

Expandobject_instance = "a";

When you are checking the keyboard, the key codes are numbers not strings. The key code for A is 65 which is also equal to ord("A") (its ASCII code). The key codes for letters and numbers on the main part of the keyboard can all be found with ord("1"), ord("A"), etc. Other key codes require the vk_something constants. Also, ord("A") is not the same as ord("a"). You must use uppercase letters for the equivalent key code.

When you try:

Expandif (keyboard_press("a"))

... it is interpreted as:

Expandif (keyboard_press(0))

Key code 0 is the same as vk_nokey which is why the object is moving when nothing is pressed.


Abusing forum power since 1986.

Offline

#10 2014-05-27 16:49:02

koltz
Member
Registered: 2014-05-26
Posts: 15

Re: Help with instance_nth_nearest or something similar

That worked, thanks!  I did have to use object_instance = ord("A"); to have it typed as lower case a. Based on your description, I should have had to have ord("a") instead. Would I have to put something like object_instance = ord("A") && vk_shift; or something similar?

One last question I guess on the typing aspect, would it be possible to cache inputted letters? So if the user types in one the the next characters in line instead of waiting for the player object to stop, it would just continue right away moving?

Now I will look at the randomization of characters on screen.

Again thanks for the help!

Offline

#11 2014-05-27 18:16:01

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Help with instance_nth_nearest or something similar

Would I have to put something like object_instance = ord("A") && vk_shift; or something similar?

Each key on the keyboard has only one code. If you need to distinguish between "A" and "a" you have to take extra steps. One of the simplest is to look at variable keyboard_lastchar. It could contain "A" or "a" depending on the state of the Shift key when typed. Or you could use keyboard_check(vk_shift) to see if Shift is being held down when "A" is pressed. Detecting if Caps Lock is enabled is more difficult. GM doesn't have built-in way to check, which is one of the reasons keyboard_lastchar is especially useful. There are some tricks involving simulated key presses and checking the results but they're a little klunky. There may be other ways. You probably won't need it but you can search GMC for Caps Lock to find out more.

One last question I guess on the typing aspect, would it be possible to cache inputted letters? So if the user types in one the the next characters in line instead of waiting for the player object to stop, it would just continue right away moving?

It's possible. Here is one of the simpler ways to do it.

The variable keyboard_string will record everything that is typed, up to 1024 characters. At the start of your game or round, clear this variable.

Expandkeyboard_string = "";

Then during the game, when the object is not moving, you look for any key presses by checking the length of keyboard_string. Read and remove the first key from keyboard_string and use it as the input. If it is a valid key, the object will start moving again. If it is not a valid key, the next key in keyboard_string will be checked during the next step. This will continue until a valid key is found or the string is emptied.

Expandif (isMoving == false)
{
    var key = -1;
    if (string_length(keyboard_string) > 0) {
        key = ord(string_upper(string_char_at(keyboard_string, 1)));
        keyboard_string = string_delete(keyboard_string, 1, 1);
    }
...
    if (instance_exists(key_up))
    {
        if (key == key_up.object_instance)
        {
...

I think that would work for you.

There are a few things to remember with keyboard_string. First, it can only contain characters that are visible. For instance, Arrow keys won't appear in it. Second, the use of the Caps Lock and Shift modifier keys will correctly add uppercase and lowercase letters to the string. Third, pressing Backspace will automatically remove characters from the end of the keyboard_string. So if a player makes a mistake when typing ahead, he could correct it with Backspace.


Abusing forum power since 1986.

Offline

#12 2014-05-28 18:53:01

koltz
Member
Registered: 2014-05-26
Posts: 15

Re: Help with instance_nth_nearest or something similar

Thanks for all your help. Do you take donations? I am thankful for your help and it has helped me progress quicker while learning a few advanced features of GML.

Also, is there a bug with random/irandom? Whenever I run a test, it keeps the same letter objects. There is an area that does have repeated letter objects spaced accordingly. See attached image of screenshot section of game. And I color coded the repeats.

p3em.png

Offline

#13 2014-05-28 19:40:56

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Help with instance_nth_nearest or something similar

It's not unusual to see duplicates in a small random sample of a small range of values.

However, the algorithm I came up with for preventing duplicate neighbors doesn't work and was also coded incorrectly. I was checking for collisions in the wrong place (relative to x,y instead of random_x,random_y) and I missed two cases to check for. You need to check two spaces up and two spaces left, as before, and also one space up-left, and one space up-right. Here is the revised algorithm.

Expandvar random_x, random_y, object_random;
for (random_y=32; random_y<=736; random_y+=32) {
    for (random_x=32; random_x<=608; random_x+=32) {
        var up = collision_point(random_x, random_y-64, obj_letter, false, false);
        if (instance_exists(up)) up = up.object_index;
        var left = collision_point(random_x-64, random_y, obj_letter, false, false);
        if (instance_exists(left)) left = left.object_index;
        var upleft = collision_point(random_x-32, random_y-32, obj_letter, false, false);
        if (instance_exists(upleft)) upleft = upleft.object_index;
        var upright = collision_point(random_x+32, random_y-32, obj_letter, false, false);
        if (instance_exists(upright)) upright = upright.object_index;
        do {
            object_random = irandom_range(1,26);
            var obj = ds_list_find_value(object_pool, object_random-1);
        } until (obj != up && obj != left && obj != upleft && obj != upright);
        instance_create(random_x, random_y, obj);
    }
}

Or if you are using one object for all letters:

Expandvar random_x, random_y, object_random;
for (random_y=32; random_y<=736; random_y+=32) {
    for (random_x=32; random_x<=608; random_x+=32) {
        var up = collision_point(random_x, random_y-64, obj_letter, false, false);
        if (instance_exists(up)) up = up.value;
        var left = collision_point(random_x-64, random_y, obj_letter, false, false);
        if (instance_exists(left)) left = left.value;
        var upleft = collision_point(random_x-32, random_y-32, obj_letter, false, false);
        if (instance_exists(upleft)) upleft = upleft.value;
        var upright = collision_point(random_x+32, random_y-32, obj_letter, false, false);
        if (instance_exists(upright)) upright = upright.value;
        do {
            object_random = irandom_range(1,26);
        } until (object_random != up && object_random != left && object_random != upleft && object_random != upright);
        var inst = instance_create(random_x, random_y, obj_letter);
        inst.value = object_random;
        inst.keycode = inst.value + 64;
        inst.letter = chr(inst.value + 64);
    }
}

As for donations, if wish you can donate to the site's upkeep via PayPal.
http://www.gmlscripts.com/paypal.html


Abusing forum power since 1986.

Offline

#14 2014-05-28 20:43:21

koltz
Member
Registered: 2014-05-26
Posts: 15

Re: Help with instance_nth_nearest or something similar

Thank you, that worked from what I see. I will do some more testing. Sent you a PayPal as well.

Offline

#15 2014-05-28 21:00:37

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Help with instance_nth_nearest or something similar

Always glad to help and many thanks for the donation.


Abusing forum power since 1986.

Offline

#16 2014-05-29 16:49:30

koltz
Member
Registered: 2014-05-26
Posts: 15

Re: Help with instance_nth_nearest or something similar

One more question, I am looking for something that makes enemies move around a maze moving towards the player. I have tried implementing some basic ones but they all just quickly move to were the player is. Do you know of one that could be implemented that has variations of random movement while progressing towards the player? IE, there are two close paths for the enemy to get to the player, instead of taking the shortest path, take the optional path instead. Or even just randomly move around the screen.

Thanks again for the help.

Offline

#17 2014-05-30 00:05:19

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Help with instance_nth_nearest or something similar

One simple thing to try is to use standard path finding but at each intersection there could be a random chance the monster chooses the "wrong" direction. At the next intersection the monster could compute a new path. Computing new paths at intersections or at time intervals is more interesting than continuously computing the optimal path.

When computing a new path, if the monster is not at a dead-end, you might also want to temporarily block the space behind the monster to prevent it from simply reversing direction, forcing it to wander more.

Another technique would be to target areas near the player or around other important points in the maze rather than the player himself.

There is also no reason a monster has to use one behavior all the time.

An example that uses all of these techniques is Pac-Man. Each of the monsters has at least three different behaviors. They all alternate between chasing Pac-Man and "scattering" to their respective home corners. When they are chasing Pac-Man, Blinky targets Pac-Man directly, Pinky targets the four spaces in front of Pac-Man, Inky targets a space determined by the positions of Inky, Blinky and Pac-Man, and Clyde targets Pac-Man but if he gets within a certain distance he targets his home corner. Finally, when Pac-Man is chasing the monsters, they move randomly.

You can read a very detailed description of Pac-Man AI here.
http://home.comcast.net/~jpittman2/pacm … ssier.html

Last edited by xot (2014-05-30 00:11:41)


Abusing forum power since 1986.

Offline

#18 2014-06-04 12:37:19

koltz
Member
Registered: 2014-05-26
Posts: 15

Re: Help with instance_nth_nearest or something similar

I followed a tutorial online by Ghazworks on Youtube. They did everything in Actions and my window was freaking out because of all of the actions so I converted it to GML. If I kept the speed at 4 for the enemies, they would move around fine with the Actions, but if it was less, it would get locked when hitting a wall. When I converted to GML, they just get stuck when hitting the first wall. So when enemy_speed = 4, it ran fine in actions, but not in GML.

Expandif (place_snapped(32,32)) {
    if (global.can_move = true) {
        direction = direction;
        speed = enemy_speed;
        which_way = 0;
        //horizontal movement
        if vspeed = 0 {
            //check for open position
            if (!place_meeting(x + hspeed, y, obj_wall)) {
                which_way = 3;
            }
            if (!place_meeting(x, y + hspeed, obj_wall)) {
                which_way = 1;
            }
            if (!place_meeting(x, y - hspeed, obj_wall)) {
                which_way = 5;
            }
            // check which way enemy is going
            
            // if going forward       
            if (which_way = 3) {
                exit;
            }
            
            // if going down
            if (which_way = 1) {
                direction = 270;
                exit;
            }
            
            // if going up
            if (which_way = 5) {
                direction = 90;
                exit;
            }
            
            // if going forward & down
            if (which_way = 4) {
                if irandom(1) = 0 {
                    if (!place_meeting(x, y + hspeed, obj_door)) {
                        direction = 270;
                        exit;
                    }
                }
            }
            
            // if going forward & up
            if (which_way = 8) {
                if irandom(1) = 0 {
                    direction = 90;
                    exit;
                }
            }
            
            // if going up & down
            if (which_way = 6) {
                if irandom(1) = 0 {
                    direction = 270;
                    exit;
                } else {
                    direction = 90;
                    exit;
                }
            }
            
            // if doing a three way :)
            if (which_way = 9) {
                if irandom(1) = 0 {
                    exit;
                }
            } else {
                if irandom(1) = 0 {
                    direction = 90;
                    exit;
                } else {
                    direction = 270;
                    exit;
                }
            }
            
            // if not moving
            if (which_way = 0) {
                direction = 180;
                exit;
            }
        }
        // end horizontal moving
        else {
        //vertical movement
            //check for open position
            if (!place_meeting(x, y + hspeed, obj_wall)) {
                which_way = 3;
            }
            if (!place_meeting(x + hspeed, y, obj_wall)) {
                which_way = 1;
            }
            if (!place_meeting(x - hspeed, y, obj_wall)) {
                which_way = 5;
            }
            // check which way enemy is going
            
            // if going forward       
            if (which_way = 3) {
                exit;
            }
            
            // if going right
            if (which_way = 1) {
                direction = 0;
                exit;
            }
            
            // if going left
            if (which_way = 5) {
                direction = 180;
                exit;
            }
            
            // if going forward & right
            if (which_way = 4) {
                if irandom(1) = 0 {
                    direction = 0;
                    exit;
                }
            }
            
            // if going forward & up
            if (which_way = 8) {
                if irandom(1) = 0 {
                    direction = 180;
                    exit;
                }
            }
            
            // if going up & down
            if (which_way = 6) {
                if irandom(1) = 0 {
                    direction = 0;
                    exit;
                } else {
                    direction = 180;
                    exit;
                }
            }
            
            // if doing a three way :)
            if (which_way = 9) {
                if irandom(1) = 0 {
                    exit;
                }
            } else {
                if irandom(1) = 0 {
                    direction = 0;
                    exit;
                } else {
                    direction = 180;
                    exit;
                }
            }
            
            // if not moving
            if (which_way = 0) {
                direction = 180;
                exit;
            }
        // end vertical moving
        }
    }  
}

Offline

#19 2014-06-04 18:27:34

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Help with instance_nth_nearest or something similar

I think this must be the tutorial:

This part here:

Expand            //check for open position
            if (!place_meeting(x + hspeed, y, obj_wall)) {
                which_way = 3;
            }
            if (!place_meeting(x, y + hspeed, obj_wall)) {
                which_way = 1;
            }
            if (!place_meeting(x, y - hspeed, obj_wall)) {
                which_way = 5;
            }

The "Set Variable" actions have "Relative" checked. That means the value is added to the variable. For instance, instead of "which_way = 3" it should be "which_way += 3". There are similar problems half-way down.

I didn't try or follow the whole tutorial, that's just the first thing I saw. Did you try the GML code posted in the comments?

If I kept the speed at 4 for the enemies, they would move around fine with the Actions, but if it was less, it would get locked when hitting a wall.

Whatever speed you choose for the monsters, it needs to equal (32/N), where N is an integer (and also the number steps it takes for the monster to go 32 pixels). So a speed of 3 would not work; no integer value of N satisfies the equation 3 = (32/N). At a speed of 3, on the 10th step the monster will have moved 30 pixels and on the following step it will have moved 33 pixels. It will skip right past 32, so the place_snapped(32,32) check will fail to see when the monster reaches the next tile. What could work is 3.2 since it is equal to (32/N), when N = 10.

However, you need to be careful with certain floating point decimal values. They may look like they should add up to 32 but rounding errors might not result in 32 exactly, so place_snapped(32,32) might not return the expected value. For instance, if you add (32/7) seven times, you will get 31.99999999999. Likewise ((32/7)+(32/7)+(32/7)+(32/7)+(32/7)+(32/7)+(32/7) == 32) will evaluate to false. GM:Studio has a way to set it up to accept comparison results which are "close enough". See math_set_epsilon() for more information. I'm not 100% sure math_set_epsilon() will help with built-in functions like this but it should.

That said, it may be better perform a different check. When dealing with floating point numbers, it is generally better to check if the difference between two values is less than a very small value than to check if the two values are equal. One could check the distance from the nearest tile and if it is less than speed, or speed/2, it is "close enough", and at that point you can move the monster to the tile's position using move_snapped(32,32) to keep things neat and tidy. This move might look a little jerky depending on the selected speed. You should aim for a speed that satisfies speed = (32/N) to eliminate this jumping effect.

Expand/// distance_snap(hsnap, vsnap)
//
//    Returns the distance to the nearest point
//    on a grid defined by hsnap and vsnap.
//
/// GMLscripts.com/license
{
/*  WHEW! FIGURED IT OUT.
    var hsnap, vsnap, hhalf, vhalf, hdist, vdist;
    hsnap = argument0;
    vsnap = argument1;
    hhalf = hsnap / 2;
    vhalf = vsnap / 2;
    hdist = ((((x + hhalf) mod hsnap) + hsnap) mod hsnap) - hhalf;
    vdist = ((((y + vhalf) mod vsnap) + vsnap) mod vsnap) - vhalf;
    return point_distance(0, 0, hdist, vdist);
*/
/*  OH! AREN'T WE CLEVER.
    var ox, oy, dist;
    ox = x;
    oy = y;
    move_snap(argument0, argument1);
    dist = point_distance(x, y, ox, oy);
    x = ox;
    y = oy;
    return dist;
*/
/*  BOY, I'M DUMB.  */
    var sx, sy;
    sx = round(x / argument0) * argument0;
    sy = round(y / argument1) * argument1;
    return point_distance(x, y, sx, sy);
}

HOWEVER, the BIG complication with this method is that you need to make sure the monster doesn't just snap back to the tile he is leaving. Since simple solutions are usually better than complicated ones, I'd first try to get the AI to work as designed by careful selection of speeds, using math_set_epsilon() if needed, and snapping the monster's position to the grid when it reaches a grid space to remove any accumulated rounding errors.

If I were structuring the AI, I might have used Alarms timed to go off after N number steps when speed = (32/N). When the Alarm fires, I'd move_snapped(32,32) the monster to exactly align with the grid and then perform the AI decision making, set the monster in motion, and set the alarm to go off just as the monster reaches the next grid space.

Last edited by xot (2014-06-20 19:17:42)


Abusing forum power since 1986.

Offline

#20 2014-06-04 21:24:32

koltz
Member
Registered: 2014-05-26
Posts: 15

Re: Help with instance_nth_nearest or something similar

You know, I didn't even look at the comments. I went a little different method but mostly the same. I pasted their code in place of mine and it works with the speed set at 4. I think the other issue I was having looking at it was technically my grid was 16 x 16 because I had an odd spacing at the top. Once I changed those two things, speed at 4 worked again. Unfortunately for my game it is very fast and need it slower. Based on what you are saying though I should be able to set speeds at 1 or 2 as well? They both divide into 32 cleanly (and 16) but they still make the player freeze.

Update: Switched back to my code with your fixes and the ghosts act much better than the other code though the setting of 4 for speed is still required..

Last edited by koltz (2014-06-04 22:04:55)

Offline

Board footer

Powered by FluxBB