I'm trying to construct a unit indexer using dynamic arrays in galaxy+ +, but ran into a problem.
The concept is simple: If a new unit enters the game, extend the variable's array by one and set the variable to the new unit. Then, give the new unit the same index in one of its custom values. Add 1 to the variable holding the maximum count of indexes used. A periodic trigger runs each second to see if any unit is no longer alive, and if so, shift down all subsequent indexes by one and remove the last array.
The part I have trouble with is the recycling part. The code thinks that the unit with the final index is dead and ends up reducing the array by one every time the recycling trigger runs (That is, every second). I can't figure out what's wrong.
Here's the code:
unit[]Lib_UIS_Unit=newunit[16]();intLib_UIS_MaxIndex=15;//--------------------------------------------------------------------------------------------------unitGetIndexUnit(intwhichIndex){returnLib_UIS_Unit[whichIndex];}intGetUnitIndex(unitwhichUnit){returnFixedToInt(UnitGetCustomValue(whichUnit,32));}//--------------------------------------------------------------------------------------------------boolLib_UIS_UnitCreated(booltestConds,boolrunActions)// If unit enters game, run {Lib_UIS_Unit->Resize(++Lib_UIS_MaxIndex);Lib_UIS_Unit[Lib_UIS_MaxIndex]=EventUnit();// Reference to get unit from integerUnitSetCustomValue(EventUnit(),32,IntToFixed(Lib_UIS_MaxIndex));returntrue;}voidLib_UIS_UnitCreated_Init(){triggert=TriggerCreate("Lib_UIS_UnitCreated");TriggerAddEventUnitRegion(t,null,RegionEntireMap(),true);}//--------------------------------------------------------------------------------------------------boolLib_UIS_Recycle(booltestConds,boolrunActions){for(inti=16;i<Lib_UIS_MaxIndex;i++){if(UnitIsAlive(Lib_UIS_Unit[i])==false){// If a unit is gone from the game, bring down arrayTriggerDebugOutput(1,"i = "+IntToText(i),true);for(inti2=i;i2<Lib_UIS_MaxIndex;i2++){UnitSetCustomValue(Lib_UIS_Unit[i2+1],32,IntToFixed(i2));Lib_UIS_Unit[i2]=Lib_UIS_Unit[i2+1];//TriggerDebugOutput(1, "i2 = " + IntToText(i2), true);}Lib_UIS_Unit->Resize(--Lib_UIS_MaxIndex);}}returntrue;}voidLib_UIS_Recycle_Init(){triggert=TriggerCreate("Lib_UIS_Recycle");TriggerAddEventTimePeriodic(t,1,c_timeGame);// 1 is enough since units aren't going to be created that rapidly. If necessary, make it smaller.}//--------------------------------------------------------------------------------------------------voidLib_UIS_Init(){Lib_UIS_UnitCreated_Init();Lib_UIS_Recycle_Init();}
I considered using a static system, but I'm trying to set up a system to link other dynamic array variables to store values. Considering the sheer number of units potentially used in a game, I figured dynamic would be best for the memory.
If you use unit groups...... units are removed from it as they die, and you don't need to keep track of their size or index
but when you need to add a new unit to it and you want that unit to have another variable to coincide with it you need to do something like this
Global Variable
UnitData[0-16000][values] type: real default value: 0
index 1 of values holes whether its open or not
Function (get open Index) :type integer
variables
i
index = -1
actions
for each i 0 to 16000
if
UnitData[i][values] = 0
then
UnitData[i][values] = 1
set index = i
set i - 16001
return index
use a function like that to find your open indexs
check for -1 which means there are no unit indexs open
then create the unit add it to the unit group.
apply the open index of the unit to its custom value
then you can easily loop on the unit group and refer to the index in the custom value to find other stuff in another array.
you also should have an event event that either fires when units die or periodically to loop through
check for is "unit alive" if its not set the iUnitData[i][values] = 0 to open that back up
if you want even more flexibility you use use a string for the Unit values...
It may not be all that dynamic.... but having the memory declared the whole time lets you know your in good shape and it wont bog the game down if it starts filling up.
Also requires less maintenance... I'm not exactly sure about your "re-size array:" your using but I imagine it loops quite a bit. When the game already handles resizing unit groups for you.
Unit groups don't work because it doesn't let me track each individual unit efficiently. Each unit needs to have its own unique index so that it can be used to call the specific array on a variable. The process does loop quite a bit, but only so far as it needs to (as many units there are and again if a unit is dead). So that means that if there's only 50 units present, then it'll only loop 50 times, which isn't too bad. It was fine even when I set it static to 500. Still, games tend to be dynamic and it's possible to have an instance when there are over 500 units (especially since projectiles are considered units as well). On the other hand, if there's only 50 units, then I would rather loop 50 times and use memory for 50 instances rather than 500.
It may not be all that dynamic.... but having the memory declared the whole time lets you know your in good shape and it wont bog the game down if it starts filling up.
While this is probably as close as dynamic can get for galaxy scripting, it is never something you should do. I've just come to terms with galaxy not supporting dynamic allocation and code with that in mind.
I was just referring to SoulCarverr's post when he said it's not dynamic, it's just a big array. That defeats the purpose of something being dynamic, because if it's there the whole time then it's not dynamic. It won't effect game speed, only memory (as long as you loop for the actual count and not the entire array limit, as you said).
Well, sure it's a big array, but the length of the array varies, and that's what matters. Even if the variable is "empty," it still allocates memory for it. Using this method allows me to adjust the length of the array, meaning that the variables only use as much space as necessary. Static, predefined array lengths don't allow that.
Another question: For what do you need a unit index? In WC3, it was used mostly to extend the custom value of a unit, since that was limited to one integer per unit. Now that we have 32 fixed values per unit and can easily simulate more by using charges, what exactly would you use the index for?
Yup. Exactly that. I'm trying to extend the custom value of a unit. The game I'm creating requires a lot of variables for each unit unfortunately (The game is based on 8 different elements, so things get multiplied quite a bit). I'm also hoping to code and implement a sort of a physics library, and that'll take up a lot of memory too since each projectile would need its own set of data (mass, radius, velocity, etc.) I might be able to use byte instead of int for some of the variables to cut down on memory, but even then it gets pretty taxing.
For the majority of the game, the number of units in a single instance won't exceed 50 (Far less probably), but there may be instances where can go up far higher (Probably not even up to 150, but games can be pretty dynamic and unpredictable). I'm still in the early stages of the game, but I want to make sure that my foundations are solid so that I don't go back and change something so fundamental. A unit indexing system tends to be rather fundamental. :/
You could use the UnitGetCustomValueEx function provided by the GAx3 mod, which essentially provides unlimited useable custom values. Internally, it looks like this:
As you can see, it adds charges to the unit, which are never used for anything. The charges are prefixed with "GAx3_" so you should probably not use this prefix for your ability charges. Other than that, they do exactly the same thing Custom Value does: They store a fixed value in the unit with a key.
If you use the mod, the function is available in Gui as well.
I use GAx3 specifically for this purpose. Well, that and I can create properties on a unit object on the fly. I just made a post on the battle.net forums, and I saw this just now as well, so thought I'd post.
Use Renee's GAx3 mod, it also has a UnitGetHandleID function or whatever, something like that. It basically assigns a unique ID value to a unit whenever it's called. Using this, you can construct more javascript-like objects, wheres properties can be made dynamically without being declared.
So, take for example you wanted to store a string in a unit (Keep in mind you can create a unit just to act as an object that contains everything, and hide it away). You would make an action like this (Parameters: Unit, String, PropertyName)
This is what it looks like in GUI, when I use this function. "Store string <string> as property <propertyName> on unit <Unit>". And of course, to make it have a variable array amount, you do "Store string <string> as property <propertyName+integer> on unit <Unit>". The array number is simply attached to the end of the propertyname.
Let's say I want to store the text "Burning Exile" on my MasterVariableHolder (a unit that is invisible/hidden/invuln that I placed in a corner of the map). I want this string to be a variable/property called Land0 (Or gv_land[0] equivalent, if we did not use this trick). I would simply do:
Store string "Burning Exile" as property ("Land" + GetLandCount()) on unit MasterVariableHolder.
As the player gets more lands, I can simply just let the function increase the array size (it is not doing anything, but logically it is increasing it as GetLandCount() returns a higher number)
To get the value, it's pretty simple to figure out. But here's what I would do to get the name of a land array 0 (this is what mine looks like in GUI)
Get the string property ("Land" + "0" on unit MasterVariableHolder.
Creating 2d arrays is no problem, or even associative arrays. ("Land" + ("Player" + GetPlayerNumber()) + ("World" + GetWorldNumber()) ) would be an instance of 2d array.
An instance of an associative array is pretty simple... ("Land" + "thirdLand") is one example...
Now, records are pretty static, and you must declare every variable/property that will be in that record/struct/whatever it is.
On the other hand, you can simply create a unit for every object you want, and then just create variables/properties on the fly.
Simple just naming any kind of propertyName in any of the functions I made above will create that variable/property. This is very powerful, especially if you don't want to keep going back and forth to your structs/records to do some minute thing.
I'm trying to construct a unit indexer using dynamic arrays in galaxy+ +, but ran into a problem.
The concept is simple: If a new unit enters the game, extend the variable's array by one and set the variable to the new unit. Then, give the new unit the same index in one of its custom values. Add 1 to the variable holding the maximum count of indexes used. A periodic trigger runs each second to see if any unit is no longer alive, and if so, shift down all subsequent indexes by one and remove the last array.
The part I have trouble with is the recycling part. The code thinks that the unit with the final index is dead and ends up reducing the array by one every time the recycling trigger runs (That is, every second). I can't figure out what's wrong.
Here's the code:
I considered using a static system, but I'm trying to set up a system to link other dynamic array variables to store values. Considering the sheer number of units potentially used in a game, I figured dynamic would be best for the memory.
If you use unit groups...... units are removed from it as they die, and you don't need to keep track of their size or index
but when you need to add a new unit to it and you want that unit to have another variable to coincide with it you need to do something like this
use a function like that to find your open indexs
check for -1 which means there are no unit indexs open
then create the unit add it to the unit group. apply the open index of the unit to its custom value
then you can easily loop on the unit group and refer to the index in the custom value to find other stuff in another array.
you also should have an event event that either fires when units die or periodically to loop through check for is "unit alive" if its not set the iUnitData[i][values] = 0 to open that back up
if you want even more flexibility you use use a string for the Unit values...
It may not be all that dynamic.... but having the memory declared the whole time lets you know your in good shape and it wont bog the game down if it starts filling up.
Also requires less maintenance... I'm not exactly sure about your "re-size array:" your using but I imagine it loops quite a bit. When the game already handles resizing unit groups for you.
Unit groups don't work because it doesn't let me track each individual unit efficiently. Each unit needs to have its own unique index so that it can be used to call the specific array on a variable. The process does loop quite a bit, but only so far as it needs to (as many units there are and again if a unit is dead). So that means that if there's only 50 units present, then it'll only loop 50 times, which isn't too bad. It was fine even when I set it static to 500. Still, games tend to be dynamic and it's possible to have an instance when there are over 500 units (especially since projectiles are considered units as well). On the other hand, if there's only 50 units, then I would rather loop 50 times and use memory for 50 instances rather than 500.
While this is probably as close as dynamic can get for galaxy scripting, it is never something you should do. I've just come to terms with galaxy not supporting dynamic allocation and code with that in mind.
Can you explain why I shouldn't use it? I only know that it uses strings to store the variables, but I'm clueless beyond that.
@DieHappy1234: Go
I was just referring to SoulCarverr's post when he said it's not dynamic, it's just a big array. That defeats the purpose of something being dynamic, because if it's there the whole time then it's not dynamic. It won't effect game speed, only memory (as long as you loop for the actual count and not the entire array limit, as you said).
Well, sure it's a big array, but the length of the array varies, and that's what matters. Even if the variable is "empty," it still allocates memory for it. Using this method allows me to adjust the length of the array, meaning that the variables only use as much space as necessary. Static, predefined array lengths don't allow that.
EDIT: It posted twice for some reason. Ignore this.
Another question: For what do you need a unit index? In WC3, it was used mostly to extend the custom value of a unit, since that was limited to one integer per unit. Now that we have 32 fixed values per unit and can easily simulate more by using charges, what exactly would you use the index for?
Yup. Exactly that. I'm trying to extend the custom value of a unit. The game I'm creating requires a lot of variables for each unit unfortunately (The game is based on 8 different elements, so things get multiplied quite a bit). I'm also hoping to code and implement a sort of a physics library, and that'll take up a lot of memory too since each projectile would need its own set of data (mass, radius, velocity, etc.) I might be able to use byte instead of int for some of the variables to cut down on memory, but even then it gets pretty taxing.
For the majority of the game, the number of units in a single instance won't exceed 50 (Far less probably), but there may be instances where can go up far higher (Probably not even up to 150, but games can be pretty dynamic and unpredictable). I'm still in the early stages of the game, but I want to make sure that my foundations are solid so that I don't go back and change something so fundamental. A unit indexing system tends to be rather fundamental. :/
You could use the UnitGetCustomValueEx function provided by the GAx3 mod, which essentially provides unlimited useable custom values. Internally, it looks like this:
As you can see, it adds charges to the unit, which are never used for anything. The charges are prefixed with "GAx3_" so you should probably not use this prefix for your ability charges. Other than that, they do exactly the same thing Custom Value does: They store a fixed value in the unit with a key.
If you use the mod, the function is available in Gui as well.
Huh. Interesting. Should look into that. Is it compatible with G+ +?
@DieHappy1234: Go
I use GAx3 specifically for this purpose. Well, that and I can create properties on a unit object on the fly. I just made a post on the battle.net forums, and I saw this just now as well, so thought I'd post.
Use Renee's GAx3 mod, it also has a UnitGetHandleID function or whatever, something like that. It basically assigns a unique ID value to a unit whenever it's called. Using this, you can construct more javascript-like objects, wheres properties can be made dynamically without being declared.
So, take for example you wanted to store a string in a unit (Keep in mind you can create a unit just to act as an object that contains everything, and hide it away). You would make an action like this (Parameters: Unit, String, PropertyName)
and to get the data, you would do (Parameters: Unit, PropertyName)
This is what it looks like in GUI, when I use this function. "Store string <string> as property <propertyName> on unit <Unit>". And of course, to make it have a variable array amount, you do "Store string <string> as property <propertyName+integer> on unit <Unit>". The array number is simply attached to the end of the propertyname.
Let's say I want to store the text "Burning Exile" on my MasterVariableHolder (a unit that is invisible/hidden/invuln that I placed in a corner of the map). I want this string to be a variable/property called Land0 (Or gv_land[0] equivalent, if we did not use this trick). I would simply do:
As the player gets more lands, I can simply just let the function increase the array size (it is not doing anything, but logically it is increasing it as GetLandCount() returns a higher number)
To get the value, it's pretty simple to figure out. But here's what I would do to get the name of a land array 0 (this is what mine looks like in GUI)
Creating 2d arrays is no problem, or even associative arrays. ("Land" + ("Player" + GetPlayerNumber()) + ("World" + GetWorldNumber()) ) would be an instance of 2d array.
An instance of an associative array is pretty simple... ("Land" + "thirdLand") is one example...
Breaking this into two posts for aesthetics.
Now, records are pretty static, and you must declare every variable/property that will be in that record/struct/whatever it is.
On the other hand, you can simply create a unit for every object you want, and then just create variables/properties on the fly.
Simple just naming any kind of propertyName in any of the functions I made above will create that variable/property. This is very powerful, especially if you don't want to keep going back and forth to your structs/records to do some minute thing.
Yes! Thank you! This was exactly what I was looking for. I remember using hashtables in WC3 for this purpose but couldn't find a similar way to do it.