我试图从Tiled utility's map format documentation中找出这段代码的目的。
const int gid = data[i] |
data[i + 1] << 8 |
data[i + 2] << 16 |
data[i + 3] << 24;
看起来有一些“or-ing”和位移,但我不知道这个目的是什么,在使用平铺程序的数据的上下文中。
答案 0 :(得分:5)
Tiled将其“Global Tile ID”(GID)数据层存储在32位整数数组中,在XML文件中进行base64编码和(可选)压缩。
根据文档,这些32位整数以小端格式存储 - 也就是说,整数的第一个字节包含数字的最低有效字节。作为类比,在十进制中,在little-endian中写入数字“1234”看起来像4321
- 4
是数字中最不重要的数字(表示只有4的值), 3
是下一个最不重要的(代表30),依此类推。这个例子和Tiled正在做的唯一区别是我们使用十进制数字,而Tiled使用的是字节,这些字节实际上是数字,每个数字可以容纳256个不同的值而不是10个。
如果我们考虑十进制数字的代码,实际上很容易理解它正在做什么。它基本上通过这样做来重建数字中的整数值:
int digit[4] = { 4, 3, 2, 1 }; // our decimal digits in little-endian order
int gid = digit[0] +
digit[1] * 10 +
digit[2] * 100 +
digit[3] * 1000;
它只是将每个数字移动到位以创建完整的整数值。 (在二进制中,8位的倍数位乘以十进制乘以10的幂;它将值移入下一个“有效数字”位置)
关于big-endian和little-endian的更多信息以及为什么差异很重要可以在On Holy Wars And A Plea For Peace中找到,这是1980年的一篇重要(并且有趣的书面)文档,其中Danny Cohen认为需要标准化网络协议的单字节排序。 (剧透:big-endian最终赢得了这场战斗,因此整数的大端表示现在是表示文件和网络传输中整数的标准方式 - 并且已经持续了数十年.Tiled使用小端整数文件格式有点不寻常。导致需要像您引用的代码一样的代码,以便可靠地将数据文件中的小端整数转换为计算机的本机格式。如果他们将数据存储为标准的大端格式,每个操作系统提供标准的实用程序函数,用于从big-endian到native进行来回转换,你可以简单地调用ntohl()
来组装本机格式的整数,而不需要编写和理解这种字节操作代码手动)。
答案 1 :(得分:1)
如您所述,<<
运算符将位向左移位给定数字。
该块采用data[]
数组,该数组有四个(可能是一个字节)元素,并将这四个值“编码”为一个整数。
示例时间!
data[0] = 0x3A; // 0x3A = 58 = 0011 1010 in binary
data[1] = 0x48; // 0x48 = 72 = 0100 1000 in binary
data[2] = 0xD2; // 0xD2 = 210 = 1101 0010 in binary
data[3] = 0x08; // 0x08 = 8 = 0000 1000 in binary
int tmp0 = data[0]; // 00 00 00 3A = 0000 0000 0000 0000 0000 0000 0011 1010
int tmp1 = data[1] << 8; // 00 00 48 00 = 0000 0000 0000 0000 0100 1000 0000 0000
int tmp2 = data[2] << 16; // 00 D2 00 00 = 0000 0000 1101 0010 0000 0000 0000 0000
int tmp3 = data[3] << 24; // 08 00 00 00 = 0000 1000 0000 0000 0000 0000 0000 0000
// "or-ing" these together will set each bit to 1 if any of the bits are 1
int gid = tmp1 | // 00 00 00 3A = 0000 0000 0000 0000 0000 0000 0011 1010
tmp2 | // 00 00 48 00 = 0000 0000 0000 0000 0100 1000 0000 0000
tmp3 | // 00 D2 00 00 = 0000 0000 1101 0010 0000 0000 0000 0000
tmp4; // 08 00 00 00 = 0000 1000 0000 0000 0000 0000 0000 0000
gid == 147998778;// 08 D2 48 3A = 0000 1000 1101 0010 0100 1000 0011 1010
现在,您刚刚将四个单字节值编码为一个四字节整数。
如果你(正确地)想知道,为什么当你只能使用byte
并将四个单字节数据直接存储到四个字节时,有人想要完成所有这些工作,那么你应该看看这个问题:
int, short, byte performance in back-to-back for-loops
奖金示例!
要获取您的编码值,我们使用“and”运算符和右移>>
:
int gid = 147998778; // 08 D2 48 3A = 0000 1000 1101 0010 0100 1000 0011 1010
// "and-ing" will set each bit to 1 if BOTH bits are 1
int tmp0 = gid & // 08 D2 48 3A = 0000 1000 1101 0010 0100 1000 0011 1010
0x000000FF; // 00 00 00 FF = 0000 0000 0000 0000 0000 0000 1111 1111
int data0 = tmp0; // 00 00 00 3A = 0000 0000 0000 0000 0000 0000 0011 1010
int tmp1 = gid & // 08 D2 48 3A = 0000 1000 1101 0010 0100 1000 0011 1010
0x0000FF00; // 00 00 FF 00 = 0000 0000 0000 0000 1111 1111 0000 0000
tmp1; //value of tmp1 00 00 48 00 = 0000 0000 0000 0000 0100 1000 0000 0000
int data1 = tmp1 >> 8; // 00 00 00 48 = 0000 0000 0000 0000 0000 0000 0100 1000
int tmp2 = gid & // 08 D2 48 3A = 0000 1000 1101 0010 0100 1000 0011 1010
0x00FF0000; // 00 FF 00 00 = 0000 0000 1111 1111 0000 0000 0000 0000
tmp2; //value of tmp2 00 D2 00 00 = 0000 0000 1101 0010 0000 0000 0000 0000
int data2 = tmp2 >> 16; // 00 00 00 D2 = 0000 0000 0000 0000 0000 0000 1101 0010
int tmp3 = gid & // 08 D2 48 3A = 0000 1000 1101 0010 0100 1000 0011 1010
0xFF000000; // FF 00 00 00 = 1111 1111 0000 0000 0000 0000 0000 0000
tmp3; //value of tmp3 08 00 00 00 = 0000 1000 0000 0000 0000 0000 0000 0000
int data3 = tmp3 >> 24; // 00 00 00 08 = 0000 0000 0000 0000 0000 0000 0000 1000
不需要tmp3的最后一个“和”,因为移位时“掉落”的位只是丢失而进入的位是零。所以:
gid; // 08 D2 48 3A = 0000 1000 1101 0010 0100 1000 0011 1010
int data3 = gid >> 24; // 00 00 00 08 = 0000 0000 0000 0000 0000 0000 0000 1000
但我想提供一个完整的例子。