Checking among arrays / property use

Started by lafouine88, Tue 28/05/2024 21:59:44

Previous topic - Next topic

lafouine88

Hi guys
I am a bit concerned about something..
I ve been working for the past months on creating a rpg interface in ags. It s not so easy but so far it seems to work.
Thought, I use a lot of what I call a "scanning function" to look for information in my struct of arrays. As so:
(Disregard the syntax errors, I m writing on my phone so it s much more difficult"^^
Code: ags

//Declared as a struct arrays in a script
Item[0].name=hammer;
Item[0].sprite"40;
//...more item stats
Item[1].name=Sword;
Item[1].sprite=41;
//And so on for all the items

//And for instance
Function mouse_over(){
Inventoryitem*selected=inventoryitem.GetatscreenXY(mouse.x,mouse.y);
//Here is the important part:
for int(i=0;i<itemlimit;i++){ 
if (selected.name==item[i].name){
///Do things using the database, like display the stats, etc.
}
}
}

So if it's not clear,I used a previous suggestion from Khris and eeri0 and scan my database for infos. It s a great and easy way to program stuff, and it works perfectly with 10 items.
 Question is, when the system is built and I finally add all the items, Say 200,300,500?(Creating many different items is not the hardest part so there could be many^^) Will this still work as fast or could there be freezes for non gaming computers?
I Guess I m underestimating the performances of a computer, but I would prefer to be sure before I ve spent a year building my system.

Thanks :)



Crimson Wizard

#1
There are always multiple ways to code same thing, fast ways and slow ways.

Sometimes it's possible to tell whether what you do is not optimal by looking at the code, but it's not always possible to say if it's going to be slow or not in practice.

If you like to get an idea about your code, then fill your arrays with large amounts of generated dummy data (code that in script), and run performance tests.
I mean, do something like this on startup:
Code: ags
int NUM_ITEMS_TO_FILL = 1000;
for (int i = 0; i < NUM_ITEMS_TO_FILL; i++)
{
    Item[i].name="blablablabla"; // or generate a random name, there are also ways to do that   
    Item[i].sprite = Random(MAX_SPRITES);
    Item[i].data = ...
}




Speaking on the particular code that you posted above, I think you may speed this up greatly if you do not iterate your arrays at all, but instead add a reference to your array's element to InventoryItem. I mean, have an array index connected to each InventoryItem.
This may be done either using Custom Properties, or a Dictionary.
https://adventuregamestudio.github.io/ags-manual/CustomProperties.html
https://adventuregamestudio.github.io/ags-manual/Dictionary.html

Then you can do this, for example:
Code: ags
Inventoryitem*selected=inventoryitem.GetatscreenXY(mouse.x,mouse.y);
int my_index = selected.GetProperty("MyIndex");
item[my_index]....

If you  don't like to setup all of these indexes in the editor, you may as well generate them in script, on startup. That would take some time, but will happen only once, so not a big problem.
Code: ags
for (int i = 0; i < Game.InventoryItemCount; i++)
{
    InventoryItem *invitem = inventory[i];
    for (int myitem = 0; myitem < itemlimit; myitem++)
    {
        if (invitem.name==item[myitem].name){
            invitem.SetProperty("MyIndex", myitem);
            break;
        }
    }
}


Overall, when you need to have 2 objects connected, don't do that with iterating arrays, instead create "links" for a faster access.
When possible use integer indexes instead of strings, because comparing strings is slower.
If you really need to use strings, check out "Dictionary", which is made for a faster searches by a string key.

PS. I also recommend renaming this topic, because right now its title does not exactly reflect the question.

PPS. Ah, I should also note that AGS has a limit of 300 inventory items, for some reason. If you think that your game will need more of these, then you would need to think of another solution (reusing InventoryItems for multiple purposes, for example, or else).

lafouine88

Thanks Crimson for the fast answer  :smiley:

At first I used properties, too many, and not the right way^^ meanning that for each inventory item I copied every single stat in a single propoerty. This was really tedious and I had the risk to forget one property among all others (but I didn't create a property for the item index to create an easy link"^^). I thought this system was too heavy so I completely went another direction. The one you suggest is clearly the best idea, I feel stupid for not coming up with it on my own^^ it helps a lot to see things from another point of view.


QuoteOverall, when you need to have 2 objects connected, don't do that with iterating arrays, instead create "links" for a faster access.
When possible use integer indexes instead of strings, because comparing strings is slower.
If you really need to use strings, check out "Dictionary", which is made for a faster searches by a string key.
That's copied, I ll be cautious with this :smiley:

QuotePPS. Ah, I should also note that AGS has a limit of 300 inventory items, for some reason. If you think that your game will need more of these, then you would need to think of another solution (reusing InventoryItems for multiple purposes, for example, or else).

This shouldn't be a problem. I am using some kind of conversion item to create inventory items only when necessary, and free them when destroyed. Like so:

Code: ags
  function Materialize_loot(int enemyId) {

      for (int li = 0; li < limitestuff; li++) { 
////I attribute loots at start up using a function given in a previous topic by Khris. But it's just data until triggered by this function.
///"count" is the amount of item[li] character[enemyId] has in its inventory
    int count = enemy_item[EiLookup(enemyId, li)].count;
    if (count > 0) {
      int tt=-1;

      ////check the inventory item doesn"t exist yet-->look for the index--->this step will be erased with "index" property
      for(int c=0;c<nombre_objets_inventory;c++){
        if(inventory[c].Name==butin[li].Nomobjet){
          tt=c;
        }
      }
    if(tt!=-1){  ////already created
        character[enemyId].AddInventory(inventory[tt]);
      }
      else{  //new creation

//inventaireused is an int that gives me the next free inventory item
      
    inventory[inventaireused].Name=butin[li].Nomobjet;
    inventory[inventaireused].Graphic=butin[li].sprite;
    inventory[inventaireused].CursorGraphic=butin[li].sprite;
    inventory[inventaireused].SetProperty("Couleur", butin[li].couleur); 


/////so this is where I'm going to set : 
inventory[inventaireused].SetProperty("index",li);

    character[enemyId].AddInventory(inventory[inventaireused]);
    
////the function that updates inventaireused
  inventaire_maj();
      }
    }
  }
  } 

Still, this function is another example of checking many occurencies. At the beginning it scans all the items to check wether the specific ennemy is supposed to have it. So anyway I guess I'll encounter the issue. But I'll do what you said and create 500 dummy items to check it works smoothly.

Thanks again :grin:  :grin:  :grin:

SMF spam blocked by CleanTalk