前言:
现在手游一般都有类似于任务,成就的系统。我们假设成就有很多很多条, 那服务器和客户端之间同步状态的流量会比较大。 有什么好的办法能减少流量消耗呢,我们可以使用BitMap。
正文
BitMap
- 定义:用一个bit位来标记某个元素对应的Value, 而Key即是该元素。
- 优点:
- 运算效率高(不需进行比较和移位)
- 占用内存少(0和1都是油意义的)
- 缺点:
- 所有的数据不能重复。
- 用途:可进行数据的快速查找,判重,删除
- 适用范围:一般来说数据范围是int的10倍以下
对于成就系统来说,成就是有自己的id,所以肯定不会重复。
实现思路:
- 总数为N, 所需内存空间:int [1 + N/32]
- 存入时:
- 十进制数0-N对应的在数组a中的下标:index_loc = N / 32
- i >> 5 等价 i / 32
- 求十进制数0-N对应的bit位: bit_loc = N % 32
- n & 31 等价 n % 32
代码实现:
class BitMap
{
const int BITSPERWORD = 32;
const int SHIFT = 5;
const int MASK = 0x1F; // 31
const int N = 10000000;
private int[] a;
//申请内存的大小
public BitMap() {
a = new int[1 + N / BITSPERWORD];
}
public BitMap(int totalCount) {
a = new int[1 + totalCount / BITSPERWORD];
}
public void SetBit(int i)
{
a[i >> SHIFT] |= (1 << (i & MASK));
}
//Clear 初始化所有的bit位为0
public void Clear(int i)
{
a[i >> SHIFT] &= ~(1 << (i & MASK));
}
//test 测试所在的bit为是否为1
public bool GetIsTrue(int i)
{
int index = i & MASK;
return (a[i >> SHIFT] & (1 << index)) != 0 ;
}
}
- 使用例子:
int count = 10000;
int randomCount = 10;
BitMap bitMap = new BitMap(count);
Random random = new Random();
for (int i = 0; i < randomCount; i++)
{
int num = random.Next(count);
Console.WriteLine(num);
bitMap.SetBit(num);
}
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
for (int i = 0; i < count; i++)
{
if (bitMap.GetIsTrue(i))
{
Console.WriteLine(i);
}
}
Console.ReadKey();
- 效果:
- 成就系统的设计思路:
- 所有的成就信息都是表格配置数据,包括奖励和达成条件。
- 当检测某个成就是否达成时, 客户端和服务端都是实时去算的。
- 成就ID配置时可以有个偏移值,比如说10000,而SC真正通信时id可以减去这个偏移值,从0开始。
- 使用BitMap记录下成就奖励是否领取, 同步这个状态,配合客户端本地计算,就能得到某个具体成就的状态:待达成,已达成还是已领取。
扩展:
https://github.com/lemire/javaewah
bitmap的开源实现有EWAH,采用RLE(Run Length Encoding)压缩,很好地解决了存储空间的浪费。
如有错误,欢迎指出。
email:dxmdxm1992#gmail.com