用一个 int 来描述位置信息

一般的,游戏场景中对象的位置信息都是用两个 int 来描述,像这样:

struct _Pos {
    int x; // x轴坐标
    int y; // y轴坐标
} Pos;

使用的时候这样:

// 定义一个坐标
Pos pos = {10, 10};
// 获取x轴坐标
int x = pos.x;
// 获取y轴坐标
int y = pos.y;

然后我看到sdlpal中只用了一个无符号整形来描述,具体代码是这样的:

// 来自palcommon.h

typedef DWORD PAL_POS; // DWORD其实就是uint32_t

#define PAL_XY(x, y)    (PAL_POS)(((((WORD)(y)) << 16) & 0xFFFF0000) | (((WORD)(x)) & 0xFFFF))
#define PAL_X(xy)       (SHORT)((xy) & 0xFFFF)
#define PAL_Y(xy)       (SHORT)(((xy) >> 16) & 0xFFFF)
#define PAL_XY_OFFSET(xy, x, y)    (PAL_POS)(((((INT)(y) << 16) & 0xFFFF0000) + ((xy) & 0xFFFF0000)) | (((INT)(x) & 0xFFFF) + ((xy) & 0xFFFF)))

使用的时候这样:

// 定义一个坐标
PAL_POS pos = PAL_XY(10, 10);
// 获取x轴坐标
SHORT x = PAL_X(pos); // SHORT就是short
// 获取y轴坐标
SHORT y = PAL_Y(pos);

从上面可以看出,第二种不够第一种形象,可能还会难以理解。两种方法都有好有坏,第一种好理解但是占内存,第二种不够形象但是节省内存。拿int举例,假如int占4个字节,第一种就要占8个字节了,而第二种只占4个字节。下面解释下第二种方法的原理。


一个int如何表示一个点的坐标呢?显示器的分辨率一般是1920 * 1080,小一点或大一点的都有,我们可以定义任意一个坐标([0, 1920], [0, 1080]),超过这个范围的坐标就无法显示到屏幕上了。一个int占32位,可存放的最大数是2^31 - 1。我们的屏幕远没有这么大,就算用两个字节16位(最大可存放65535这个数)也是够用的。因此我们可以将一个int“分成”两部分用,分别存放x、y轴的坐标值。如何将两个整数合并成一个整数,然后还可以把这个整数还原成两个整数?四则运算可以将两个整数变成一个数,但是还原成原来的两个数就不好办了。但是我们可以通过位运算达到目的,宏PAL_XY(x, y)作用是将两个整数合成一个,PAL_X(xy)/PAL_Y(xy)的作用是获取原来的x、y值。

下面这行代码要用文字说明感觉挺麻烦的,直接举个栗子吧:将(2, 3)合成一个数,这里只用8位和0xF0/0xF来举例,你用32位和0xFFFF0000/0xFFFF也是一样。
十进制3转换为二进制-> 0000 0011 左移4-> 0011 00000xF0进行'与运算 &'的目的是将右边4位变成0,这里已经是0了。
十进制2转换成二进制-> 0000 00100xF进行'与运算 &'的目的是将左边4位变成0,右边4位不会变,所以无变化。
最后将 0011 00000000 0010 进行'或运算 |'得到 0011 0010。转换成十进制就是50,这无关紧要。
注意这里是将y左移了,所以y是存放到了左边两个字节里,而x是存放到了右边两个字节(假设int占4个字节)

#define PAL_XY(x, y)    (PAL_POS)(((((WORD)(y)) << 16) & 0xFFFF0000) | (((WORD)(x)) & 0xFFFF))

要从一个数获取原来的两个数,反其道行之就行了。x是存放在右边2个字节里的,所以直接’与’上一个0xFFFF(把左边2个字节的位都置为0)就可以得到了。获取y呢,可以直接按代码里的-将这个数右移16位,再’与’上0xFFFF就可以了,当然还可以先’与’上0xFFFF0000,再右移16位。

#define PAL_X(xy)       (SHORT)((xy) & 0xFFFF)
#define PAL_Y(xy)       (SHORT)(((xy) >> 16) & 0xFFFF)

解释下下面这个宏,x、y是相对于点xy的偏移量,需要注意的是:x、y并不是一个点的坐标,xy这个点也不一定是由x、y合成的。该宏的作用是算出相对于点xy在x轴方向偏移x个距离、y轴方向偏移y个距离后的点的坐标,这个坐标同样存放在PAL_POS这种类型的变量里。原理和普通坐标的计算一样:原点(x, y),偏移量a、b,偏移后的点(x + a, y + b)。不过这里需要用到位运算。具体是这样的:本来十进制的y是存放在一个完整的4字节里,通过这句(INT)(y) << 16) & 0xFFFF0000把它存放到了左边两个字节里,并且右边两个字节16位都为0,原理和上面讲的一样。这句(xy) & 0xFFFF0000是将点xy的右边两个字节16位置为0,这个就相当于y,而前一句获得的那个相当于b,相加就是y + b了(这里的两个转换是为了对应的上,不然直接相加是得不到正确结果的)。这句(INT)(x) & 0xFFFF是将原本存放在4个字节里的x存放到右边的两个字节里,左边两个字节16位都置为0了。相对应的(xy) & 0xFFFF这句将点xy的左边两个字节16位置为0,这个相当于x,然后相加就是x + a了。最后将得到的两个数‘或 |’运算一下就合成一个PAL_POS类型的点坐标了。直接加也行的,但是‘+’的效率比‘|’的效率低。

#define PAL_XY_OFFSET(xy, x, y)    (PAL_POS)(((((INT)(y) << 16) & 0xFFFF0000) + ((xy) & 0xFFFF0000)) | (((INT)(x) & 0xFFFF) + ((xy) & 0xFFFF))) 

可能上面说的有点乱,其实反过来理解也是一样的:将点xy还原成原坐标,记作(x1, y1),然后加上偏移量x、y得到点(x1 + x, y1 + y),最后将这个点转换回去就行了。完整代码就是PAL_POS((PAL_X(xy) + x), (PAL_Y(xy) + y))。这句代码只是帮助理解,可能会有问题,因为PAL_X()/PAL_Y()short类型的,x、y默认是int类型的,强制转换后相加的数据可能不准确,所以还是用原来的代码就行了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值