• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Randomizing mechanic slowly degrades

ww_y

Member
In the game I'm trying to make, I have 2 enemies that go from their starting x and y to a blue block, then back to their starting x and y, then pick a new blue block to go to and repeat the cycle. This works pretty great 8/10 times. However, as I've been watching the enemies start their routes I've noticed 2 peculiar I guess edge cases that arise from time to time when I run the game.

The first one is they will favor one blue block over the others, and typically the two enemies will favor the same blue block. It's very interesting to see happen after a series of runs, to be honest. One enemy will seem to just pick one block more and more until eventually 9/10 times its the only block it goes to, and the other enemy eventually follows suit. I have no idea why this is, or why it doesn't happen every time, but typically it happens after I run the game for a while. It's almost as if the randomized system I have for choosing the blocks stops being random at a certain point.

The second case is just general malfunctions. Sometimes one enemy will stop moving entirely, or they'll never begin to move at all and stay at their starting x and y. This is less common than the first case, and typically only happens to one of the enemies and not both. It is still problematic though since I don't want my player to be replaying the level and it suddenly does not work.

The code I have to shuffle the blocks, or in this case I call the DropBoxes, and the pathfinding is in a finite state machine. First I create a list in the create event of a present invisible game object. I also randomize the seed

GML:
randomize();
global.db_List = ds_list_create();
ds_list_add(global.db_List,oDB1,oDB2,oDB3,oDB4);
Then, I have the enemies start in their first state where they shuffle the list of dropboxes, assign the first value of the list to a variable the office workers use, remove said value from the list so that no other office worker can use the same dropbox and change the state of the enemy to a pathfinding state for the chosen dropbox:

GML:
function DropBoxShuffle(){
      
            ds_list_shuffle(global.db_List);
            myDropBox = ds_list_find_value(global.db_List, 0);
            ds_list_delete(global.db_List,myDropBox);
          
      
      
    state = ENEMYSTATE.WANDER;
}
This pathfinding state switches from two cases, one that starts the path to the dropbox and the other to return to the x, y. Notice I return the dropbox value to the list at the end of the route to the dropbox.

GML:
/// Have office worker go from objective then back to desk
  
    switch (route) {

    // path route to the drop box
        case 1:
          

            pathDB = path_add();
          
            dX = (myDropBox.x div 16)*16+8;
            dY = (myDropBox.y div 16)*16+8;
          
      
            if (mp_grid_path(global.grid, pathDB, x, y, dX, dY, true))
                {
                    path_start(pathDB, 1, path_action_stop, false);                                   
                }
            // Do something once the path has been complete
            if (path_position == 1)
                {
                    route = 2;
                    ds_list_add(global.db_List,myDropBox);
                    path_delete(pathDB);
                }
              
            break;
          
        // Path route to back to the desk once it reaches its destination
        case 2:
      
            pathRe = path_add();
          
            if (mp_grid_path(global.grid, pathRe, x, y, xTo, yTo, true))
                {
                    path_start(pathRe, 1, path_action_stop, false);                          
                }
              
            if (path_position == 1)
                {
                    route = 1;
                    state = ENEMYSTATE.SHUFFLE;
                    path_delete(pathRe);          
                }
  
            break;
    }
And it just goes round and round. Does anyone have any insights as to why this is happening and how I can improve the system?
 
Last edited:

Nidoking

Member
I'm confused. Are you creating a new path every step, or are you only doing that when you first choose the box, and then never actually checking for the end of the path?
 

ww_y

Member
I'm confused. Are you creating a new path every step, or are you only doing that when you first choose the box, and then never actually checking for the end of the path?
A path is created every step I suppose. Now that I've noticed that I feel like that'll cause a memory leak, but I'm not sure how to improve that. I've changed the "pathDB = path_add();" as well as pathRe to be in the initial DropBoxShuffle state. So now yes I only add the paths during the first initial state, thus only during one step. However, this has left the issues unchanged.
 

Nidoking

Member
ds_list_delete(global.db_List,myDropBox);
ds_list_delete takes an index, not a value. You need to delete entry 0, the one you just took out of the list. You're removing random values (if any - I have no idea what the result is if you try to delete an index that's not in the list) and then putting back the one you took out once the path finishes, so in the long term, you'll end up with that entry being most of the contents of a very large list. Check the contents of the list in the debugger and you'll see what I mean.
 

ww_y

Member
ds_list_delete takes an index, not a value. You need to delete entry 0, the one you just took out of the list. You're removing random values (if any - I have no idea what the result is if you try to delete an index that's not in the list) and then putting back the one you took out once the path finishes, so in the long term, you'll end up with that entry being most of the contents of a very large list. Check the contents of the list in the debugger and you'll see what I mean.
Im not sure I understand, the gml manual defines the function ds_list_delete as a function that "can remove the value stored at a specific position within the list."
 
Yes, a specific position, as in position 0 in the list or position 1 in the list. You are instead giving it whatever value is stored at position 0 (ds_list_find_value() returns the value stored in whatever position you supply, ds_list_find_index() returns the position, if any, of whatever value you supply). To delete position 0 it's as simple as:
Code:
ds_list_delete(global.db_List,0);
 

Roderick

Member
Im not sure I understand, the gml manual defines the function ds_list_delete as a function that "can remove the value stored at a specific position within the list."
To put it in layman's terms, you tell it to delete Page 7, not the page that starts with "It was the best of times, it was the worst of times."

I think there's a ds_list function that will search for a certain value and return the index.
 

TheouAegis

Member
The arguments you use for ds_list_find_value need to be the same arguments you use for ds_list_delete in this case.
 

ww_y

Member
Yes, a specific position, as in position 0 in the list or position 1 in the list. You are instead giving it whatever value is stored at position 0 (ds_list_find_value() returns the value stored in whatever position you supply, ds_list_find_index() returns the position, if any, of whatever value you supply). To delete position 0 it's as simple as:
Code:
ds_list_delete(global.db_List,0);
To put it in layman's terms, you tell it to delete Page 7, not the page that starts with "It was the best of times, it was the worst of times."

I think there's a ds_list function that will search for a certain value and return the index.
The arguments you use for ds_list_find_value need to be the same arguments you use for ds_list_delete in this case.
gotcha, that makes sense. So I've changed the argument from ds_list_delete(db_List,0). It seems like it has impacted the first problem I'm having to some extent. The Office workers seem to have stopped favoring one dropbox over another. I'm having more of the second issue pop up though. In the image I have attached, the left office worker is doing just fine, however, the second one is stuck at that corner dropbox and does not move from there. This only happens after running for about 10 minutes. Two additional interesting notes is that the first office worker does not ever pick the dropbox that office worker 2 is stuck at, and getting stuck isn't specific to an office worker. Any office worker that I put on that side of the room will eventually get stuck at that dropbox after a while.
 

Attachments

Nidoking

Member
What steps have you taken to ensure that the second worker isn't getting stuck somewhere along the path and failing to reach path_position exactly equal to one?
 

ww_y

Member
What steps have you taken to ensure that the second worker isn't getting stuck somewhere along the path and failing to reach path_position exactly equal to one?
My bad I actually posted the wrong screenshot. The correct one is up now. There is nothing really in the way between the worker and the box. That side of the map has barely any obstacles, and it doesn't get stuck any of the other times it goes towards that box. Just after 10 minutes or so of running the game. That makes me think about what you said regarding the list and how it may be cluttering and making it harder and harder to choose a new box, but both workers use the same list so it doesnt make sense why the other one would also not be experiencing this at the same time.
 

Nidoking

Member
What steps have you taken to ensure that the second worker isn't getting stuck somewhere along the path and failing to reach path_position exactly equal to one?
Good that you've been thinking. What do you think about the answer to my question? How do you ensure that path_position will exactly equal one? I'm trying to suggest that your worker isn't getting the same box out of the list repeatedly - it's never reaching the end of the path to put the box back in the list and get a new one.
 

ww_y

Member
Good that you've been thinking. What do you think about the answer to my question? How do you ensure that path_position will exactly equal one? I'm trying to suggest that your worker isn't getting the same box out of the list repeatedly - it's never reaching the end of the path to put the box back in the list and get a new one.
I don't think I understand how it would not reach 1 if there are no collidable obstacles between an instance and the end x and y of a path set for it. I see now that the manual for game maker explains path_position as potentially being a decimal between 0 and 1, so I understand how that could be conceivably an issue. However I've tried moving the box and the worker in different configurations, changing the collision mask of both, and the center point of both their sprites and still at times it would get stuck. I've now rewritten that part of the function to check for if path_position > 0 instead, and it seems to work well now. Hopefully I don't find any more weird consequences from my sloppy code writing. Thank you all for your help!
 
Top