- Home /
Converting bunch of if else statements to an efficient data structure
So i got this every Lateupdate:
transform.parent.gameObject.layer = hit.transform.gameObject.layer;
if(transform.parent.gameObject.layer == 10)
shadowProjector.ignoreLayers = ~((1 << 10) + (1 << 21) + (1 << 9));
else if(transform.parent.gameObject.layer == 11)
shadowProjector.ignoreLayers = ~((1 << 11) + (1 << 21) + (1 << 22) + (1 << 9));
else if(transform.parent.gameObject.layer == 12)
shadowProjector.ignoreLayers = ~((1 << 12) + (1 << 22) + (1 << 23) + (1 << 9));
else if(transform.parent.gameObject.layer == 13)
shadowProjector.ignoreLayers = ~((1 << 13) + (1 << 23) + (1 << 24) + (1 << 9));
else if(transform.parent.gameObject.layer == 14)
shadowProjector.ignoreLayers = ~((1 << 14) + (1 << 24) + (1 << 25) + (1 << 9));
else if(transform.parent.gameObject.layer == 15)
shadowProjector.ignoreLayers = ~((1 << 15) + (1 << 25) + (1 << 26) + (1 << 9));
else if(transform.parent.gameObject.layer == 16)
shadowProjector.ignoreLayers = ~((1 << 16) + (1 << 26) + (1 << 27) + (1 << 9));
else if(transform.parent.gameObject.layer == 17)
shadowProjector.ignoreLayers = ~((1 << 17) + (1 << 27) + (1 << 28) + (1 << 9));
else if(transform.parent.gameObject.layer == 18)
shadowProjector.ignoreLayers = ~((1 << 18) + (1 << 28) + (1 << 29) + (1 << 9));
else if(transform.parent.gameObject.layer == 19)
shadowProjector.ignoreLayers = ~((1 << 19) + (1 << 29) + (1 << 30) + (1 << 9));
else if(transform.parent.gameObject.layer == 20)
shadowProjector.ignoreLayers = ~((1 << 20) + (1 << 30) + (1 << 9));
else if(transform.parent.gameObject.layer == 21)
shadowProjector.ignoreLayers = ~((1 << 21) + (1 << 10) + (1 << 11) + (1 << 9));
else if(transform.parent.gameObject.layer == 22)
shadowProjector.ignoreLayers = ~((1 << 22) + (1 << 11) + (1 << 12) + (1 << 9));
else if(transform.parent.gameObject.layer == 23)
shadowProjector.ignoreLayers = ~((1 << 23) + (1 << 12) + (1 << 13) + (1 << 9));
else if(transform.parent.gameObject.layer == 24)
shadowProjector.ignoreLayers = ~((1 << 24) + (1 << 13) + (1 << 14) + (1 << 9));
else if(transform.parent.gameObject.layer == 25)
shadowProjector.ignoreLayers = ~((1 << 25) + (1 << 14) + (1 << 15) + (1 << 9));
else if(transform.parent.gameObject.layer == 26)
shadowProjector.ignoreLayers = ~((1 << 26) + (1 << 15) + (1 << 16) + (1 << 9));
else if(transform.parent.gameObject.layer == 27)
shadowProjector.ignoreLayers = ~((1 << 27) + (1 << 16) + (1 << 17) + (1 << 9));
else if(transform.parent.gameObject.layer == 28)
shadowProjector.ignoreLayers = ~((1 << 28) + (1 << 17) + (1 << 18) + (1 << 9));
else if(transform.parent.gameObject.layer == 29)
shadowProjector.ignoreLayers = ~((1 << 29) + (1 << 18) + (1 << 19) + (1 << 9));
else if(transform.parent.gameObject.layer == 30)
shadowProjector.ignoreLayers = ~((1 << 30) + (1 << 19) + (1 << 20) + (1 << 9));
Seems to me that I can convert the if else statements to a dictionary type thing because it would be WAY more efficient to have a direct reference from a transform.parent.gameObject.layer to a shadowProjector.ignoreLayers value instead of doing if elses.
So could i initialize the dictionary in the Awake() function and then replace all the if elses with one line: shadowProjector.ignoreLayers = CurrentDict("transform.parent.gameObject.layer");
If i were to use a dictionary though, would it go from int to int? Can i store ~((1 << 10) + (1 << 21) + (1 << 9)) as an int? It's much more readable for me to leave it in that format where i can clearly see where the bits are being shifted instead of simplifying to an integer value.
And is dictionary even the best approach or should i use Enums or a fixed size array or something else? What's the most efficient structure for this?
I'll let the programmers comment on the data structure efficiency, my thought on seeing this was do you need to do this at all. Could you use the Project Settings / Physics matrix to set this up?
Hmm ill check now. Its been awhile since i implemented that code and im just co$$anonymous$$g back to optimize it, so i dont remember if i had a problem with using the matrix.
No the matrix does not pertain to what im doing. The implementation has to do with shadows, not collisions, so it has no effect whatsoever on what im trying to do.
Well I think it's possible to get rid of all the if elses completely with a nice dictionary that just spits out the correct value for the input. Seems better then making a formula for part of it and using if elses for the rest.
Answer by Bunny83 · Aug 20, 2013 at 05:57 PM
Hashtables or Dictionaries are really great datastructures, but not in this case. They create several arrays of KeyValuePairs (so called buckets) and put your key / values into those arrays. Hashtables are ment for huge amount of data. 32 values are nothing. In this case it would be very inefficient (memory-wise and performance-wise).
Since your incoming value is between 0 and 31 the best solution is simply an ordinary static array.
// C#
public static int[] layerMapping = new int[]
{
/* 0*/ 0,
/* 1*/ 0,
/* 2*/ 0,
/* 3*/ 0,
/* 4*/ 0,
/* 5*/ 0,
/* 6*/ 0,
/* 7*/ 0,
/* 8*/ 0,
/* 9*/ 0,
/*10*/ ~((1 << 10) | (1 << 21) | (1 << 9)),
/*11*/ ~((1 << 11) | (1 << 21) | (1 << 22) | (1 << 9)),
/*12*/ ~((1 << 12) | (1 << 22) | (1 << 23) | (1 << 9)),
/*13*/ ~((1 << 13) | (1 << 23) | (1 << 24) | (1 << 9)),
/*14*/ ~((1 << 14) | (1 << 24) | (1 << 25) | (1 << 9)),
/*15*/ ~((1 << 15) | (1 << 25) | (1 << 26) | (1 << 9)),
/*16*/ ~((1 << 16) | (1 << 26) | (1 << 27) | (1 << 9)),
/*17*/ ~((1 << 17) | (1 << 27) | (1 << 28) | (1 << 9)),
/*18*/ ~((1 << 18) | (1 << 28) | (1 << 29) | (1 << 9)),
/*19*/ ~((1 << 19) | (1 << 29) | (1 << 30) | (1 << 9)),
/*20*/ ~((1 << 20) | (1 << 30) | (1 << 9)),
/*21*/ ~((1 << 21) | (1 << 10) | (1 << 11) | (1 << 9)),
/*22*/ ~((1 << 22) | (1 << 11) | (1 << 12) | (1 << 9)),
/*23*/ ~((1 << 23) | (1 << 12) | (1 << 13) | (1 << 9)),
/*24*/ ~((1 << 24) | (1 << 13) | (1 << 14) | (1 << 9)),
/*25*/ ~((1 << 25) | (1 << 14) | (1 << 15) | (1 << 9)),
/*26*/ ~((1 << 26) | (1 << 15) | (1 << 16) | (1 << 9)),
/*27*/ ~((1 << 27) | (1 << 16) | (1 << 17) | (1 << 9)),
/*28*/ ~((1 << 28) | (1 << 17) | (1 << 18) | (1 << 9)),
/*29*/ ~((1 << 29) | (1 << 18) | (1 << 19) | (1 << 9)),
/*30*/ ~((1 << 30) | (1 << 19) | (1 << 20) | (1 << 9)),
/*31*/ 0
};
Now you can simply use the layer variable as index into this array. This is the fastest lookup you can perform. And this array takes only 128 bytes of memory. Since it contains constant data it can be static so every script that needs it can use it.
ps: Never use "+" to join bitfields. If you accidently have the same value twice it would form the next greater. Always use the binary or "|".
1 | 1 == 1
1 + 1 == 2
1 | 2 | 4 | 2 == 7
1 + 2 + 4 + 2 == 9
hmm that looks pretty good, ill try it asap. So i can simply do
shadowProjector.ignoreLayers = Array[transform.parent.gameObject.layer]
Didn't know about the bitfield thing, gonna have to check. Of course in my particular code there's no problem cause nothing repeats right? I never knew the "or" operator can be used to add values.
If you're using JS, the code becomes this:
static var layer$$anonymous$$apping: int[] = [
/* 0*/ 0,
/* 1*/ 0,
/* 2*/ 0,
/* 3*/ 0,
/* 4*/ 0,
/* 5*/ 0,
/* 6*/ 0,
/* 7*/ 0,
/* 8*/ 0,
/* 9*/ 0,
/*10*/ ~((1 << 10) | (1 << 21) | (1 << 9)),
/*11*/ ~((1 << 11) | (1 << 21) | (1 << 22) | (1 << 9)),
/*12*/ ~((1 << 12) | (1 << 22) | (1 << 23) | (1 << 9)),
/*13*/ ~((1 << 13) | (1 << 23) | (1 << 24) | (1 << 9)),
/*14*/ ~((1 << 14) | (1 << 24) | (1 << 25) | (1 << 9)),
/*15*/ ~((1 << 15) | (1 << 25) | (1 << 26) | (1 << 9)),
/*16*/ ~((1 << 16) | (1 << 26) | (1 << 27) | (1 << 9)),
/*17*/ ~((1 << 17) | (1 << 27) | (1 << 28) | (1 << 9)),
/*18*/ ~((1 << 18) | (1 << 28) | (1 << 29) | (1 << 9)),
/*19*/ ~((1 << 19) | (1 << 29) | (1 << 30) | (1 << 9)),
/*20*/ ~((1 << 20) | (1 << 30) | (1 << 9)),
/*21*/ ~((1 << 21) | (1 << 10) | (1 << 11) | (1 << 9)),
/*22*/ ~((1 << 22) | (1 << 11) | (1 << 12) | (1 << 9)),
/*23*/ ~((1 << 23) | (1 << 12) | (1 << 13) | (1 << 9)),
/*24*/ ~((1 << 24) | (1 << 13) | (1 << 14) | (1 << 9)),
/*25*/ ~((1 << 25) | (1 << 14) | (1 << 15) | (1 << 9)),
/*26*/ ~((1 << 26) | (1 << 15) | (1 << 16) | (1 << 9)),
/*27*/ ~((1 << 27) | (1 << 16) | (1 << 17) | (1 << 9)),
/*28*/ ~((1 << 28) | (1 << 17) | (1 << 18) | (1 << 9)),
/*29*/ ~((1 << 29) | (1 << 18) | (1 << 19) | (1 << 9)),
/*30*/ ~((1 << 30) | (1 << 19) | (1 << 20) | (1 << 9)),
/*31*/ 0
];
Use it like this:
shadowProjector.ignoreLayers = layer$$anonymous$$apping[transform.parent.gameObject.layer];
Is there any particular reason you make it static? wouldn't constant be better in this case?
Well, since it's static data (that doesn't change) it makes more sense as static array. If you attach this script to multiple objects there's only one array which is shared by all scripts. If it's stored in an instance variable each script will have it's own copy of the array which will just waste memory.
Here, just for you i've edited my comment just to be 100% correct.
Since you are totally nitpicking, i can as well:
"There's a huge conceptual difference between a var defined in a class scope (member vars), and an instance var".
Why you put "member var" behind "class scope", which you put aside an instance var? As you said yourself an instance variable is also a member variable and also in the scope of the class but not in the static scope of the class.
Just something to think about: If someone says "Don't use a local variable, store it in a member variable" what kind of member variable is ment here in 99% of all cases?
Btw i'm program$$anonymous$$g for about 2/3 of my life, so don't worry, I don't confuse myself. Oh, i still wait for your improved solution ...
Answer by Owen-Reynolds · Aug 19, 2013 at 07:57 PM
Is it a rule that everything must always use 9 and itself? If so, just leave those out of the list and always add them to everything. It seems like the other numbers follow a pattern, so a formula (with 1 or 2 ifs) is better than a table.
for a table, maybe a string with the layer numbers, like "10/21/9"?:
LayerTable[10] = "10/21/9";
// etc...
string w = LayerTable[n];
string[] nums = w.Split("/");
int mask = 0;
// add in each layer:
for(int i=0;i<nums.Length;i++)
mask += 1<< int.Parse( nums[i] );
I can do ~mask at the end right? so that could work i guess. but cant i just do LayerTable[10] = ~((1 << 10) + (1 << 21) + (1 << 9));
Generally the goal is to only enter the simplist interesting data. Retyping all those 1<<+
's is error-prone and makes the layer info harder to see. But whatever you feel comfortable with. Plenty or serious (non-game) code has ifs/switches longer and uglier than yours.
Wells i was gonna do a quick copy paste lol
So i guess i'll just try makin a dictionary with all the entries, should work.
Answer by Jamora · Aug 20, 2013 at 05:18 PM
Without seeing all of your code, I cannot say if my solution is overkill. If you have other behavior dependent on the layer number as well, or your layer number is part of a more complicated system, it might save you a lot of headaches. Otherwise it'll just end up causing a lot of clutter.
Basically, you should make a class for each different layer that you have behavior for. Then have an interface ( ILayerIgnorer or some such) which contains the necessary method declarations; in this case void SetIgnoreLayers();
. Each different layerclass then implements that interface and provides the correct layer to ignore.
If you have a lot of behavior dependent on the layer number you can pack all that functionality and data into those classes made specifically for the layers. Whereas if you only have your layer number and it is not (part of) a domain concept, you will end up with several classes + an interface that have only one method in them. That is just clutter.
A good way to test this is to try to come up with names for the layers. If the only thing you can think of are "Layer1", "Layer2", etc. then this solution was overkill. If you can EASILY think of, say "ProjectileIgnoreLayer" "TreeIgnoreLayer", etc. then this solution might work for you.
Pretty overkill, i only use the layers to deter$$anonymous$$e what objects to cast shadows on.
Answer by meat5000 · Aug 21, 2013 at 09:14 AM
http://answers.unity3d.com/questions/217024/please-explain-a-switch-statement.html
Instead of doing if else if else you can place your checking int (==1, ==2 etc) as your switch testing variable and specify cases for each answer. This means it would jump straight to the case you wanted. It is also possible to stack switch cases so you can make it perform multiple cases (not sure about Unityscript!). Also you get a default case, which is very useful.
You can also specify execution code in the cases, so not just to select variables but also to perform a function if a separate one is needed per case.
lol but would a switch statement jump directly to the correct case or go one by one iteratively until the case is reached? I think iterative because of the break statement often used in each case. Think about it.
I think i sorta get what you mean, but i only have one action to perform for each checking int, so its overkill
Not overkill. It seems the simplest solution. Its perfect for single action cases really. The reason for using it is simply to take out the else ifs. Its basically what you have above in parallel. It takes out the serialisation of the code and lets you jump to your specific case every time. Easy to implement, no extra nonsense to make it work, no wasted cpu cycles. Also helps to keep your framerates stable as you arent having to jump around a variable amount of else ifs :P The break statement tells your switch to exit as if you dont use it you can stack cases. As you dont want this you need break after each case.
I dont understand how itll jump directly to the case and how its better than a static fixed array presented above.
In your case I'd be tempted to try all the solutions on this page and run them fast and frequently to see which has the best and worst performance. I don't think any of them are hard to implement and therein you shall have your answer :P
Answer by NonGamingCoder · Aug 29, 2013 at 09:00 AM
Ouch!
What about something like this:
using System.Collections.Generic;
public class YourClass : MonoBehaviour
{
private Dictionary<int, Action> _ignoreLayersActions;
void Awake()
{
_ignoreLayersActions = new Dictionary<int, Action>();
// I'm only going to write one out here...
_ignoreLayersActions[10] = () => {
shadowProjector.ignoreLayers = ~((1 << 10) + (1 << 21) + (1 << 9));
}
// Repeat all of your cases here, or somewhere else.
}
void Update()
{
int layer = transform.parent.gameObject.layer;
if (_ignoreLayersActions.ContainsKey(layer)
{
// This will run the anonymous function that we defined above.
_ignoreLayersActions[layer]();
}
}
}
that would have been my solution except that it would be more efficient to just have a static array.
Nit picking... A dictionary lookup is really, really small.
Your answer
Follow this Question
Related Questions
Hashtable(or generic dictionary) vs Enum - when do I use which? 1 Answer
How to make a certain kind 2D Dictionary? 1 Answer
Unity enum map - how to make it more efficient/clean? Linear line complexity. Best practices. 1 Answer
How can I use an enum as parameter to run a method inside a different method? 1 Answer