• 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!

SOLVED Updating datastructure after event

minitaba

Member
Hello guys :)
I am currently making some kind of Life sim / RPG game. It also involves growing different plants and harvesting them.
I followed a lot of tutorials before and now trying to make my own code for some mechanics because I cant manage to find any information about it, but now i need some help.

A tutorial i have seen showed me how to make growing plants, but the series stopped before making them harvestable. No problem, i needes some hours (way too many haha) to make it possible and now i can harvest my crops.

Now i developed a bug which occurs when leaving the Room.

Example:
---I have 3 plants, all full grown. i harvest all of them, leave the room, everything works fine
---I have 3 plants, harvest 1 and leave the room, i get this error:


Unable to find instance for object index 100246
at gml_Object_crops_Other_5 (line 22) - ds_crops_data[# 2, slot] = inst.cropType;


I am pretty sure I am missing an update-system for the datastructure before the "room end" event safes my plants data in the ds_crops_data.
I already tried to copy the saving code from the "room end" and fitting it after the harvest function, but i had no luck with that.

I am not very good with datastructure I have to admit. I understand the basics behind them but the whole accessing and overwriting is a little complicated and i cant find any good documentation tbh.
Is there any good way to manage this?
I am also not too sure which parts of the code i should post because its pretty much code haha.

Thanks in advance :)

Best regards
 

Ommn

Member
I don't think the problem is with the DS.
The problem is because the object is not in the room.
you can use instance_exists for check if object exists or not.
or show us your codes in "room end" event
 

minitaba

Member
I don't think the problem is with the DS.
The problem is because the object is not in the room.
you can use instance_exists for check if object exists or not.
or show us your codes in "room end" event
True, the object is not in the room, but thats wanted. I harvest, and the obj_plant gets destroyed. So you think this may be a problem in general and i have to tell the game its fine that the object is missing?

Will post room end code later when i am home :) thanks so far


or show us your codes in "room end" event
Thanks for looking over this :)
Code:
if(room == rm_farm) {
var inst_num = instance_number(obj_crop);
    if(inst_num == 0) { ds_grid_clear(ds_crops_data, -1); } //no data
    else
    {
    //there is Crops, save their data
    ds_grid_resize(ds_crops_data, ds_grid_width(ds_crops_data), inst_num);
   
    //loop through instance grid
    var gw = ds_grid_width(ds_crops_instances);
    var gh = ds_grid_height(ds_crops_instances);
    var slot = 0;
   
    var xx = 0; repeat(gw) {
        var yy = 0; repeat(gh) {
            var inst = ds_crops_instances[# xx, yy];
           
            // if instance in cell, save its data
            if(inst != 0){
                ds_crops_data[# 0, slot] = xx;
                ds_crops_data[# 1, slot] = yy;
                ds_crops_data[# 2, slot] = inst.cropType;
                ds_crops_data[# 3, slot] = inst.daysOld;
                slot += 1;
                }
            yy += 1;
            }
    xx += 1;
        }
    }
}
 
Last edited:

Ommn

Member
Try replacing the conditional "if(inst != 0)" with "if (inst != 0 and instance_exists(inst))"
Or you can review the entire code to make sure that the object is not destroyed
 

chamaeleon

Member
I don't like the testing being written this way. Using instance_exists() obscures the semantics a bit. Is it possible inst could contain a valid instance id? Is it possible inst contains a invalid instance id (the instance was destroyed)? Is it possible inst contains the value noone? All of these questions should be answerable by knowing what it stored in the grid. If an instance is destroyed the grid should be updated to reflect this fact, rather than testing whether the instance exist or not after retrieving a grid value. The value in itself should be sufficient to determine whether it's valid or not.
 

minitaba

Member
Script to generate the Instances of the crop:

Code:
function instance_create_crop(){

var cs = crops.cellSize;
var gx = argument0 div cs;
var gy = argument1 div cs;
var i_grid = crops.ds_crops_instances;
var cell = i_grid[# gx, gy];

if(cell == 0) {

        var xx = gx*cs;
        var yy = gy*cs;

        //check for soil
        var lay_id    =    layer_get_id("t_soil");
        var map_id    =    layer_tilemap_get_id(lay_id);
        var data    =    tilemap_get_at_pixel(map_id, argument0, argument1);

        if (data == 0) {
            show_debug_message("you can only plant crops on soil");
            return false;
        }    else {
            show_debug_message("Crop planted")
        }

        //Create the Instance
        var inst = instance_create_layer(xx+(cs/2), yy+(cs/2), "Instances", obj_crop);
        i_grid[# gx, gy] = inst;



        //Giving Crops Characteristics
        with(inst) {
            cropType = argument2;
            growStageDuration = crops.ds_crops_types[# 0, cropType];
        }

        return inst;
} else {
    show_debug_message("You can not plant here")
    return false;
}
}
just use the condition
The problem is that the "obj_crop" instance can exist more then once in a room, or am I wrong and i can adress every single "obj_crop" instance individually by ID? i have no clue how to get it for the code tbh

Thanks you 2 :D

EDIT: of course I know how to get the ID of an instance, but no clue how this would help ain any way
 
Last edited:

Ommn

Member
I advise you to remove the instance from DS when it is destroyed

When destroying the object, search in the ds_grid on the same id and then delete it.
 

chamaeleon

Member
Code:
        var inst = instance_create_layer(xx+(cs/2), yy+(cs/2), "Instances", obj_crop);
        i_grid[# gx, gy] = inst;
The problem is that the "obj_crop" instance can exist more then once in a room, or am I wrong and i can adress every single "obj_crop" instance individually by ID? i have no clue how to get it for the code tbh
You are creating instances and adding them to the grid. You now have a data structure containing some set of individual instances you can refer to without interfering with any other instance. I'd suggest possibly making the code
GML:
        var inst = instance_create_layer(xx+(cs/2), yy+(cs/2), "Instances", obj_crop);
        i_grid[# gx, gy] = inst;
        inst.gx = gx;
        inst.gy = gy;
Now the instance knows which grid cell it is contained in without having to search for it and you can, perhaps in the cleanup event of obj_crop,
GML:
crops.ds_crop_instances[# gx, gy] = noone; // gx and gy would here be the instance variables assigned above
Now the grid will not contain invalid instance references that you would otherwise have to test using instance_exist() and you can simply check if it is different from noone. If the grid cell contains noone don't do anything that would require a valid instance, if the value is not noone, you have an obj_crop instance in the cell and you can use it.

My view is simply that the grid should not contain invalid instance ids. That would tell me that I'm being sloppy with managing my resources.

Edit: It should almost go without saying that any initialization/clearing/resizing of crops.ds_crop_instances should be to contain the value noone in all new cells.
 
Last edited:

minitaba

Member
You are creating instances and adding them to the grid. You now have a data structure containing some set of individual instances you can refer to without interfering with any other instance. I'd suggest possibly making the code
GML:
        var inst = instance_create_layer(xx+(cs/2), yy+(cs/2), "Instances", obj_crop);
        i_grid[# gx, gy] = inst;
        inst.gx = gx;
        inst.gy = gy;
Now the instance knows which grid cell it is contained in without having to search for it and you can, perhaps in the cleanup event of obj_crop,
GML:
crops.ds_crop_instances[# gx, gy] = noone; // gx and gy would here be the instance variables assigned above
Now the grid will not contain invalid instance references that you would otherwise have to test using instance_exist() and you can simply check if it is different from noone. If the grid cell contains noone don't do anything that would require a valid instance, if the value is not noone, you have an obj_crop instance in the cell and you can use it.

My view is simply that the grid should not contain invalid instance ids. That would tell me that I'm being sloppy with managing my resources.

Edit: It should almost go without saying that any initialization/clearing/resizing of crops.ds_crop_instances should be to contain the value noone in all new cells.

this was EXTREMELY helpful, thank you so so much, now I understand much more.
Now, i played around with it now in many different ways, but the error still comes up in particular orders of events. I bet the problem is something way different, i have to go through it tomorrow and find the issue or start it from scratch..., I am pretty clueless now.
Thanks a lot again and have a great night
 

Nidoking

Member
If you're writing information about instances to a data structure, you should be using a with anyway, not looping over the data structure.
 

minitaba

Member
Now the instance knows which grid cell it is contained in without having to search for it and you can, perhaps in the cleanup event of obj_crop,
may I ask, just to be sure I understand, the variables "gx" and "gy" should be accessible in the created "obj_crop" instance, right? because if I try to use these variables in my code there always comes up an error saying its not set, and if I just put "gx = 0; gy = 0;" in the Create Event it just uses the "0"...
 

chamaeleon

Member
may I ask, just to be sure I understand, the variables "gx" and "gy" should be accessible in the created "obj_crop" instance, right? because if I try to use these variables in my code there always comes up an error saying its not set, and if I just put "gx = 0; gy = 0;" in the Create Event it just uses the "0"...
Yes, they would be accessible in the instance .. as long as the lines setting the variables have executed by the time something tries to use the variables for some purpose. This is of course critical, and all usual debugging methods should be applied to verify that this is the case.
 

minitaba

Member
Yes, they would be accessible in the instance .. as long as the lines setting the variables have executed by the time something tries to use the variables for some purpose. This is of course critical, and all usual debugging methods should be applied to verify that this is the case.
alright, thanks a lot.
After knowing this and trying for way too long again I am sure there is a different problem. It works all great most of the time but in some combinations , like "planting 3 new, harvesting 2 old plants" or "planting 3 of a kind and harvesting 1" it crashes again when leaving the room and shows the same Datastructure error (one variety is not "line 2" being the problem but "Line 3", i mean, that something i guess haha)

I guess its best to move on and look at it at another point / later in production, i dont think I will solve this Bug now.

Thanks a lot i learned much from you :)
 

chamaeleon

Member
"planting 3 new, harvesting 2 old plants" or "planting 3 of a kind and harvesting 1" it crashes again when leaving the room
Does planting 3 crops imply creating 3 instances? Are the 3 instances in the same grid cell (which would require a list or array to hold more than one) or are they in 3 different grid cells?
 

minitaba

Member
yeah, every "crop" is an own instance. They are in different grid cells, the cells are created when the instances are. It is very odd, when I Plant 3 crops, leave the room, come back, all fine. I harvest one crop, leave the room, game crashes. but if I harvest 2 of them and leave the room, it does not crash / has no issue with the DS entry of the Crop. I watched the Obejct ID and it always looks for an entry for a harvested crop that shouldnt even exist anymore because I destroyed it / changed the value.
 

chamaeleon

Member
yeah, every "crop" is an own instance. They are in different grid cells, the cells are created when the instances are. It is very odd, when I Plant 3 crops, leave the room, come back, all fine. I harvest one crop, leave the room, game crashes. but if I harvest 2 of them and leave the room, it does not crash / has no issue with the DS entry of the Crop. I watched the Obejct ID and it always looks for an entry for a harvested crop that shouldnt even exist anymore because I destroyed it / changed the value.
Probably would be helpful to share code the does harvesting/instance destruction then. Or just about any code related to this where you use the crop object name in expressions or statements and not instances.
 

minitaba

Member
Sure, thanks a lot :)

This is the Step-Event of the obj_crop which is the instance created every time i plant something
Enjoy my messy Code haha


Code:
#region
            if(cropType = crop.carrot)            { cropItem = 74; }
            if(cropType = crop.tomato)            { cropItem = 6;     }
            if(cropType = crop.strawberry)        { cropItem = 12; }    
            if(cropType = crop.pumpkin)            { cropItem = 18; }    
            if(cropType = crop.corn)            { cropItem = 24; }        
            if(cropType = crop.potato)            { cropItem = 30; }
            if(cropType = crop.watermelon)        { cropItem = 36; }
            if(cropType = crop.radish)            { cropItem = 42; }
            if(cropType = crop.salad)            { cropItem = 48; }
            if(cropType = crop.wheat)            { cropItem = 54; }
            if(cropType = crop.redrose)            { cropItem = 2;  }
            if(cropType = crop.pinkrose)        { cropItem = 8;  }
            if(cropType = crop.purplerose)        { cropItem = 14; }
            if(cropType = crop.bluerose)        { cropItem = 20; }
            if(cropType = crop.tulip)            { cropItem = 26; }
            if(cropType = crop.narcissus)        { cropItem = 32; }
            if(cropType = crop.narcissuspink)    { cropItem = 38; }
            if(cropType = crop.butterflower)    { cropItem = 44; }
            if(cropType = crop.lavender)        { cropItem = 50; }
            if(cropType = crop.roseflower)        { cropItem = 56; }
#endregion

//Harvest possible when fully grown
if(fullyGrown = true and keyboard_check_pressed(ord("H"))) {

    if(collision_rectangle(x-r, y-r, x+r, y+r, obj_player, false, false)){
var ci = cropItem
//add item in inventory to empty slot or to existing instance slot
    with(inventory) {
        var ds_inv    = ds_inventory;
        var picked_up    = false;
//check if existing
        var yy = 0; repeat(inv_slots){
            if(ds_inv[# 0, yy] == ci){ 
                ds_inv[# 1, yy] += 1;
                picked_up = true;
                break;
            } else{
                yy += 1;    
            }
        }
        
//otherwise, add to empty slot if one is free
                if(!picked_up) {
                    yy = 0; repeat(inv_slots){
                    if(ds_inv[# 0, yy] == item.none){
                        ds_inv[# 0, yy] = ci;
                        ds_inv[# 1, yy] += 1;
                        picked_up = true;
                        break;
                    }else{
                        yy += 1;    
                }
            }    
        }
    }
    if(picked_up = true){
        //ds_grid_clear(crops.ds_crops_instances,0);
        



     // Just a placeholder for testing, works the best so far  
 crops.ds_crops_instances[# gx, gy] = 0; 



    instance_destroy();}

    }
}
 

chamaeleon

Member
0 is not the same as noone (which is -4). You should set ds_crop_instances[# gx, gy] = noone not ds_crop_instances[# gx, gy] = 0 when destroying an instance. I assume the (gx, gy) entry is based on instance variables of the instance currently executing this step event. Not saying this will fix the problem necessarily. Perhaps additionally add debug code for sanity checking
GML:
        if(picked_up = true){
            //ds_grid_clear(crops.ds_crops_instances,0);
            // Just a placeholder for testing, works the best so far 
            if (crops.ds_crops_instances[# gx, gy] != id) {
                show_debug_message("Not clearing the correct crop instance in grid at coordinate " + string(gx) + ", " + string(gy));
            }
            crops.ds_crops_instances[# gx, gy] = noone;
            instance_destroy();
        }
Edit: I'd normally not clear the grid entry myself if that test fails, etc., but the if statement and message isn't there to protect from incorrect behavior but to determine whether data used is correct.
 
Last edited:

minitaba

Member
thank you :)
I tried it with "noone" as well, and now your code, still the same issue sadly. but good to know "noone" is the right path.

I just realized some kind of pattern when triggering the crashes. Are these variables "gx" and "gy" somwhow stored in the ds_crops_instance, created by the event which also creates the instance?

When I harvest everything, all works fine, but when i leave the room and re enter again (Which will clear the ds_crops_instance-Grid) it crashes.

this is coming up after leaving and re-Entering the room and trying to harvest crops that were planted before leaving the room
Code:
 Variable obj_crop.gy(100024, -2147483648) not set before reading it.
 
Last edited:

chamaeleon

Member
Are these variables "gx" and "gy" somwhow stored in the ds_crops_instance, created by the event which also creates the instance?
They are stored in the instance if you have code that sets them for the instance, otherwise not, of course. If your code now contains the lines I added to earlier to set gx and gy on the instance, they should be. Each crop instance would have their own values of course (the whole point of instances being they have their own set of instance variables).
 

minitaba

Member
They are stored in the instance if you have code that sets them for the instance, otherwise not, of course. If your code now contains the lines I added to earlier to set gx and gy on the instance, they should be. Each crop instance would have their own values of course (the whole point of instances being they have their own set of instance variables).

Ok thats strange, because when the ds is cleared in the end room event these variables seems to be missing in the instances. Oh and I did what you told me in the script of course.

This is exhausting lol
 

chamaeleon

Member
Ok thats strange, because when the ds is cleared in the end room event these variables seems to be missing in the instances. Oh and I did what you told me in the script of course.

This is exhausting lol
If the variables are not present after having been set on an instance, it is no longer the same instance being inspected when you clear the grid. If you observe that variables are missing I'd start with logging all creation of instances when you set gx and gy, and log all instances in the grid when you clear it.

obj_crop Create event (to make sure every created instance is logged)
GML:
show_debug_message("Creating a new obj_crop with id " + string(id));
Log the setting of gx and gy. Ideally this should happen for all obj_crop. If more obj_crop are created than you see these setting messages for that's an issue.
GML:
       var inst = instance_create_layer(xx+(cs/2), yy+(cs/2), "Instances", obj_crop);
        i_grid[# gx, gy] = inst;
        inst.gx = gx;
        inst.gy = gy;
        show_debug_message("Setting gx and gy on " + string(id) + " to " + string(gx) + ", " + string(gy));
Just before grid is being cleared
GML:
for (var row = 0; row < ds_grid_height(crops.ds_crops_instances); row++) {
    for (var col = 0; col < ds_grid_width(crops.ds_crops_instances); col++) {
        var inst = crops.ds_crops_instances[# col, row];
        if (inst != noone) {
            if (inst.object_index != obj_crop) {
                show_debug_message("Incorrect object instance stored in crop grid");
            }
            if (variable_instance_exists(inst, "gx") && variable_instance_exists(inst, "gy")) {
                if (inst.gx != col || inst.gy != row) {
                    show_debug_message("Instance " + string(id) + " has inconsistent coordinates " + string(inst.gx) + ", " + string(inst.gy) + " vs " + string(col) + ", " + string(row));
                }
            } else {
                show_debug_message("Instance " + string(id) + " does not have gx and/or gy set");
            }
        }
    }
}
Check for consistency in the output of ids and coordinates.
 

minitaba

Member
thank you so much for your work :D

there is something strange going on..

Creating a crop, just fine:
Code:
Crop planted
Creating a new obj_crop with id 100257
Setting gx and gy on 100063 to 6, 18
leaving the Room:
Code:
... 
Instance 100063 has inconsistent coordinates 6, 18 vs 41, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 42, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 43, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 44, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 45, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 46, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 47, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 48, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 49, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 50, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 51, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 52, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 53, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 54, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 55, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 56, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 57, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 58, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 59, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 60, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 61, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 62, 47
Instance 100063 has inconsistent coordinates 6, 18 vs 63, 47


^ this is going on hundreds of rows
Coming back in the room with the Crop:
Thats fine I guess, thats hoe the respawning system is set up:
Code:
Creating a new obj_crop with id 100258
Respawned a carrot
And trying to harvest it:
Code:
___________________________________________
############################################################################################
ERROR in
action number 1
of  Step Event0
for object obj_crop:

Variable obj_crop.gy(100024, -2147483648) not set before reading it.
 at gml_Object_obj_crop_Step_0 (line 60) -                      crops.ds_crops_instances[# gx, gy] = 0; // "Working" one
############################################################################################
gml_Object_obj_crop_Step_0 (line 60)


So, the first ID of the crop instance was 100063, the respawned one was 100258 and the game was looking for 100024.

It just gets more and more confusing lol
 

chamaeleon

Member
GML:
show_debug_message("Setting gx and gy on " + string(id) + " to " + string(gx) + ", " + string(gy));
should of course be
GML:
show_debug_message("Setting gx and gy on " + string(inst.id) + " to " + string(gx) + ", " + string(gy));
GML:
show_debug_message("Instance " + string(id) + " has inconsistent coordinates " + string(inst.gx) + ", " + string(inst.gy) + " vs " + string(col) + ", " + string(row));
should be
GML:
show_debug_message("Instance " + string(inst.id) + " has inconsistent coordinates " + string(inst.gx) + ", " + string(inst.gy) + " vs " + string(col) + ", " + string(row));
GML:
show_debug_message("Instance " + string(id) + " does not have gx and/or gy set");
should be
GML:
show_debug_message("Instance " + string(inst.id) + " does not have gx and/or gy set");
 

minitaba

Member
It freaking worked!!!!
a little playing around and it worked.

You are SO AWESOME seriously I am so happy right now. thanks to your help i found the issue and figured it out!

Thank you soo much! have a awesome week kind stranger. you are awesome
 

chamaeleon

Member
Getting rid of the use of argumentN because with functions it is mostly irrelevant except for using the argument array when you expect a variable number of arguments to be passed.
GML:
function respawn_crop(grid_x, grid_y, cropType, daysOld){
    if (ds_crops_instances[# grid_x, grid_y] != noone) {
        show_debug_message("Spawning crop in an occupied grid cell " + string(grid_x) + ", " + string(grid_y));
    }

    var xx = grid_x*cellSize;
    var yy = grid_y*cellSize;

    // Create the instance
    var inst = instance_create_layer( xx+(cellSize/2), yy+(cellSize/2), "Instances", obj_crop);
    show_debug_message("Respawned a " + ds_crops_types[# 2, cropType]);

    ds_crops_instances[# grid_x, grid_y] = inst;
    show_debug_message("Creating a new obj_crop with id " + string(inst.id));

    //Give crop the characteristics
    inst.cropType = cropType;
    inst.daysOld = daysOld;
    inst.gx = grid_x;
    inst.gy = grid_y;
    inst.growStageDuration= crops.ds_crops_types[# 0, cropType];

    return inst;
}
There is no need to use with just to assign instance variables to an instance. If you were to use with, you'd no longer use the dot prefix.
GML:
    inst.cropType = cropType;
    inst.daysOld = daysOld;
    inst.gx = grid_x;
    inst.gy = grid_y;
    inst.growStageDuration= crops.ds_crops_types[# 0, cropType];
is equivalent to
GML:
with (inst) {
    id.cropType = cropType;
    id.daysOld = daysOld;
    gx = grid_x;
    gy = grid_y;
    growStageDuration= crops.ds_crops_types[# 0, cropType];
}
Need to prefix for the first two because the name of the function argument is the same as the instance variable being set. One way to avoid it may be to have a different naming convention for function arguments, perhaps prefix with an underscore.
 
Last edited:

chamaeleon

Member
It freaking worked!!!!
a little playing around and it worked.

You are SO AWESOME seriously I am so happy right now. thanks to your help i found the issue and figured it out!

Thank you soo much! have a awesome week kind stranger. you are awesome
Happy to hear that things have improved! Hopefully you have taken to heart some ways you can debug (the way I attack it is just my way of letting the program tell me all about what is going on and looking for discrepancies). It will serve you well in the future.
 

minitaba

Member
Happy to hear that things have improved! Hopefully you have taken to heart some ways you can debug (the way I attack it is just my way of letting the program tell me all about what is going on and looking for discrepancies). It will serve you well in the future.
Definetly! I did have thought about improving the debugging messages and implementing them while coding, but i thought it will be just fine. I learnd a lot from this experience anddefinetly taken that to heart! thanks again and have a great time :)
 
Top