Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by luniac · Aug 19, 2013 at 07:43 PM · enumdictionaryif elsefixed array

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?

Comment
Add comment · Show 9
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image getyour411 · Aug 19, 2013 at 07:57 PM 0
Share

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?

avatar image luniac · Aug 20, 2013 at 01:35 AM 0
Share

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.

avatar image luniac · Aug 20, 2013 at 02:01 AM 0
Share

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.

avatar image getyour411 · Aug 20, 2013 at 04:12 AM 0
Share

How bout the Projector's "Ignore Layers" matrix?

avatar image luniac · Aug 20, 2013 at 04:27 PM 1
Share

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.

Show more comments

5 Replies

· Add your reply
  • Sort: 
avatar image
5
Best Answer

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
Comment
Add comment · Show 23 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image luniac · Aug 21, 2013 at 02:36 AM 0
Share

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.

avatar image aldonaletto · Aug 21, 2013 at 03:47 AM 2
Share

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];
avatar image luniac · Aug 21, 2013 at 08:57 AM 0
Share

Is there any particular reason you make it static? wouldn't constant be better in this case?

avatar image Bunny83 · Aug 21, 2013 at 10:52 AM 1
Share

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.

avatar image Bunny83 · Aug 30, 2013 at 01:24 AM 1
Share

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 ...

Show more comments
avatar image
1

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] );
Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image luniac · Aug 20, 2013 at 01:35 AM 0
Share

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));

avatar image Owen-Reynolds · Aug 20, 2013 at 03:47 AM 0
Share

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.

avatar image luniac · Aug 20, 2013 at 04:45 AM 0
Share

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.

avatar image
0

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.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image luniac · Aug 21, 2013 at 02:30 AM 0
Share

Pretty overkill, i only use the layers to deter$$anonymous$$e what objects to cast shadows on.

avatar image
0

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.

Comment
Add comment · Show 6 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image luniac · Aug 21, 2013 at 10:35 AM 0
Share

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.

avatar image luniac · Aug 21, 2013 at 03:24 PM 0
Share

I think i sorta get what you mean, but i only have one action to perform for each checking int, so its overkill

avatar image meat5000 ♦ · Aug 21, 2013 at 08:41 PM 0
Share

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.

avatar image luniac · Aug 22, 2013 at 05:45 AM 0
Share

I dont understand how itll jump directly to the case and how its better than a static fixed array presented above.

avatar image meat5000 ♦ · Aug 22, 2013 at 11:10 AM 0
Share

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

Show more comments
avatar image
0

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]();
          }
     }
 }
Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image luniac · Aug 29, 2013 at 10:19 AM 0
Share

that would have been my solution except that it would be more efficient to just have a static array.

avatar image NonGamingCoder · Aug 29, 2013 at 10:22 AM 0
Share

Nit picking... A dictionary lookup is really, really small.

avatar image luniac · Aug 29, 2013 at 10:27 AM 0
Share

lol well i guess either way works regardless :).

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

26 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

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

Flag structure variable in unity editor 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges