XNA中的位标志
近日在研究StunEngine引擎时,遇到了一些看不懂的代码,搜索后才发现原来只是简单的位运算,没想到还能用在这种场合,具体信息可见msdn上的http://msdn.microsoft.com/zh-cn/library/6a71f45d(VS.80).aspx。
这篇文章来自于XNA UK User Group,http://xna-uk.net/,原文地址:http://xna-uk.net/blogs/randomchaos/archive/2010/03/24/bit-flags-in-xna.aspx,下面是译文。
什么是Bit Flags,它实际上是一种在一个变量中存储多个值的方法。上一次我使用Bit Flags好像是在几年前帮一个朋友编写一个MUD的时候,如果我没记错的话,我使用位运算存储玩家状态,他/她是否处于站立、行走、奔跑或游泳状态,还用在房间的位置,房间是否有一面向北的墙或天花板。
那么如何在C#中使用它们,为什么我们使用的值有限制?
首先你必须知道逻辑位运算(http://en.wikipedia.org/wiki/Bitwise_operation,logical bitwise operators)和它的工作原理,我们将使用它们在一个变量中测试和设置位,这个可以帮你理解为何我们只能使用特定的值。在计算机中整数值是由一个二进制值表示的,即一组0和1,让我们看一下数字0到10的二进制值是什么:
整数值 | 二进制值 |
0 | 00000000 |
1 | 00000001 |
2 | 00000010 |
3 | 00000011 |
4 | 00000100 |
5 | 00000101 |
6 | 00000110 |
7 | 00000111 |
8 | 00001000 |
9 | 00001001 |
10 | 00001010 |
上面的表格我展示了8位数字的二进制代码,即有8个位可以用来代表一个值。我们看到数字8的二进制代码是00001000,所以要表示数字8,我们只需将第四个位设置为1其他为0,8位数字最大为255。如果我们有一个整数发现它的第四个位是1,那么这个整数是8吗?不是,看一下9和10,它们的第四个位也是1,这就是为什么我们只能使用特定值的原因,这些值必须是2的整数次幂。
如果看一下下面的表格,你会发现只有2的整数次幂的数字,这样就不会有位交叉了:
整数值 | 二进制值 |
1 | 00000000 00000000 00000000 00000001 |
2 | 00000000 00000000 00000000 00000010 |
4 | 00000000 00000000 00000000 00000100 |
8 | 00000000 00000000 00000000 00001000 |
16 | 00000000 00000000 00000000 00010000 |
32 | 00000000 00000000 00000000 00100000 |
64 | 00000000 00000000 00000000 01000000 |
128 | 00000000 00000000 00000000 10000000 |
256 | 00000000 00000000 00000001 00000000 |
512 | 00000000 00000000 00000010 00000000 |
1024 | 00000000 00000000 00000100 00000000 |
2048 | 00000000 00000000 00001000 00000000 |
4096 | 00000000 00000000 00010000 00000000 |
8192 | 00000000 00000000 00100000 00000000 |
16384 | 00000000 00000000 01000000 00000000 |
32768 | 00000000 00000000 10000000 00000000 |
65536 | 00000000 00000001 00000000 00000000 |
131072 | 00000000 00000010 00000000 00000000 |
262144 | 00000000 00000100 00000000 00000000 |
524288 | 00000000 00001000 00000000 00000000 |
1048576 | 00000000 00010000 00000000 00000000 |
2097152 | 00000000 00100000 00000000 00000000 |
4194304 | 00000000 01000000 00000000 00000000 |
8388608 | 00000000 10000000 00000000 00000000 |
16777216 | 00000001 00000000 00000000 00000000 |
33554432 | 00000010 00000000 00000000 00000000 |
67108864 | 00000100 00000000 00000000 00000000 |
134217728 | 00001000 00000000 00000000 00000000 |
268435456 | 00010000 00000000 00000000 00000000 |
536870912 | 00100000 00000000 00000000 00000000 |
1073741824 | 01000000 00000000 00000000 00000000 |
2147483648 | 10000000 00000000 00000000 00000000 |
现在我们有了一个32位的二进制值表格表示flags,它们不会交叉。
我们如何设置并测试这些位?这就需要用到逻辑位运算了。要设置一个值的位,我们可以使用| (或)运算符,它会根据你的需要设置一个值中的位:
int value2 = 2; int value4 = 4; int value8 = 8; int value = 0; value = value2 |value8;
上面的操作中value的二进制代码为:00001010即整数10(电脑对二进制数进行了求和运算!) ,现在我们在一个变量中存储了两个值,value2表示武器是否已经被装备,而value8表示武器是否已经加载了弹药。如果我们调用 | 运算符你能获得相同的值(value2和value8),一旦位被设置就不会改变。
那么我们如何测试一个值是否被设置?这可以通过使用 & (与) 运算符做到,这个运算会进行如下所示的测试:
int value2 = 2; int value4 = 4; int value8 = 8; int value = 0; value = value2 | value8; int result = 0; result = value & value2; result = value & value4;
第一次我们测试value2存储在value中 ,这个value会赋值为value2,第二次我们测试不在value中的value4,这会让result为0。
如果我们只想打开或关闭一个位应该如何处理呢?这需要用到^ (异或)运算,这个运算会将1设置为0或将0设置为1,就好像一个电灯开关一样:
int value2 = 2; int value4 = 4; int value8 = 8; int value = 0; value ^= value2; value ^= value2;
第一次调用让value包含value2,第二次调用从value中移除value2。
我制作了一个简单的XNA项目使用上面的操作符,在这个项目中我使用一个枚举表示bit flags:
public enum GunStates : int { Empty = 1, Loading = 2, Shooting = 4, Equiped = 8, Loaded = 16 }
注意我将枚举强类型化为一个整数,你也可以使用short, long, byte或其他类型,我使用的设置和检查状态的方法如下所示:
public void SetGunState(GunStates stateSet) { state ^= stateSet; } public bool CheckGunState(GunStates stateChk) { return (state & stateChk) == stateChk; }
你还可以看一下位偏移(左移位<<和右移位>>),value2 << 1会将value2中的位左移一位,结果是4,如果将value2重新赋值为2并进行value2 << 2操作,则values2中的所有位左移2位,结果为8,要将value2重新设置为2可进行value2 >> 2的操作。
源代码GenericXNABitFlags.zip下载。
文件下载(已下载 1259 次)发布时间:2010/4/2 下午1:36:59 阅读次数:6186