├── Makefile ├── README.md ├── aoi.c ├── aoi.h └── perf.c /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc -o perf -g -Wall aoi.c perf.c 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #aoi 2 | 3 | 一直想花点时间好好研究下游戏AOI的实现,把这一块完结掉,整合到 server 上面去。记录下云风的基本文章,顺序阅读 4 | 5 | 开发笔记 (13) : AOI 服务的设计与实现: http://blog.codingnow.com/2012/03/dev_note_13.html 6 | 7 | 开发笔记(26) : AOI 以及移动模块: http://blog.codingnow.com/2012/09/dev_note_26.html 8 | 9 | 开发笔记(28) : 重构优化: http://blog.codingnow.com/2012/11/dev_note_28.html 10 | 11 | ------------------------------------------ 12 | ####实体 13 | 14 | 每个实体都维护着一个版本号,当实体进入场景,移动,更新状态时,版本号+1。 15 | 实体的状态类型分别有:观察者(Watcher),被观察者(Marker),已离开(Drop)。 16 | `观察者`:可以观察半径内实体的状态。进入,移动,离开。 17 | `被观察者`:可以让观察者 观察 到自己。 18 | 19 | 结构体如下: 20 | ```c 21 | struct object { 22 | int ref; // 引用数 23 | uint32_t id; // 唯一标识 24 | int version; // 实体新进入场景 或 改变了状态 或 改变了位置, 则 version +1 25 | int mode; // 实体状态 26 | float last[3]; // 上一次位置坐标 27 | float position[3]; // 当前位置坐标 28 | }; 29 | ``` 30 | 31 | ------------------------------------------ 32 | ####更新状态 33 | 34 | 先看下接口 35 | ```c 36 | // space 场景管理对象 37 | // id 实体唯一标识 38 | // mod 状态 w(atcher) m(arker) d(rop) 39 | // pos 位置 x,y,z 40 | void aoi_update(struct aoi_space * space , uint32_t id, const char * mode , float pos[3]); 41 | ``` 42 | 43 | 当我们需要添加一个实体到场景,或需要移动实体位置,或要更改实体状态时,都统一调用 `aoi_update` 接口 44 | 45 | ------------------------------------------ 46 | ####aoi_message 接口 47 | 48 | ```c 49 | // ud 自定义参数 50 | // watcher 观察者 51 | // marker 被观察者 52 | typedef void (aoi_Callback)(void *ud, uint32_t watcher, uint32_t marker); 53 | 54 | 55 | // space 场景管理对象 56 | // cb aoi消息回调 57 | // ud 自定义参数,会在cb回调时带上 58 | void aoi_message(struct aoi_space *space, aoi_Callback cb, void *ud); 59 | ``` 60 | 61 | 调用 `aoi_message` 接口可获取实体信息通知。接口完成功能包括有: 62 | 63 | 1. 将 移动 的实体放进 `move` 集合中;将 微动 和 静止 的实体放入 `static` 集合中。 64 | move 集合分为 `watcher_move` 和 `marker_move`。代表 观察者移动集合 和 被观察者移动集合。 65 | static 集合也分为 `watcher_static` 和 `watcher_static`。代表 观察者静止集合 和 被观察者静止集合。 66 | `这一步是一个 O(n) 复杂度的操作` 67 | 68 | 2. 校验 (watcher_static 和 marker_move);(watcher_move 和 marker_static);(watcher_move 和 marker_move),循环检索实体两两之间的距离,如果小于感知半径,则发送 69 | 进入视野AOI消息(包括进入和移动),如果大于感知半径的2倍,则直接返回。如果以上条件都不符合,则将这两个实体放入到`热点对列表`中。 70 | `这3对循环检索,每一对的复杂度为 O(n)`。 71 | 72 | 3. 再下一次 tick 时间执行 aoi_message 接口时,我们先判断`热点对列表`。每个热点对,是我们需要尝试判断是否会触发 AOI 消息的两个 id 对。 73 | 如果一对`热点对`里,其中一方实体的状态改变了,则删除此`热点对`,因为等下上面的 1,2 步骤会处理。如果两个实体的状态的都没有改变,我们就比较 74 | 他们的距离,当距离小于感知半径时,发送AOI消息,并删除此热点对;否则保留此`热点对`等待下个 tick 处理。可能大家会问,为何要维护`热点对列表` 75 | 了?其实主要是用于处理实体的`微动`情况。实体的状态发生改变,包括实体进入场景,移动(移动距离超过感知半径的一半,如感知半径时20,那么移动>=10时才算移动), 76 | 离开。实体的`微动`是指移动距离小于半径的一半,微动是不会改变实体的状态,所以我们要在热点对里去判定。某个实体的微动是否进入到了其他实体的感知 77 | 范围内,或离开了其他实体的感知范围。 78 | `热点对列表操作复杂度为 O(n)` 79 | 80 | ------------------------------------------ 81 | ####总结 82 | 83 | 此 AOI模块 与场景大小无关,只与实体数量和位置有关。由上面的 aoi_message 接口可知,整个 AOI模块 的复杂度为 `O(n)`。 84 | 85 | 加入`热点对`是由上面第 2 步形成。既`热点对`肯定是一个观察者+一个被观察者,如果观察者和被观察者长时间处于微动或静止,而且他们的距离大于2倍 86 | 半径,他们将不会进入热点对,既不会被遍历,也没有比较距离的运算。 87 | 88 | 在逻辑层,收到AOI消息后,应该把实体加入到自己的关心列表中,以后在处理遍历这个列表时,有足够多的机会把不再关心的实体删掉。例如需要做身边广播 89 | 时我们就可以遍历下此列表。 90 | -------------------------------------------------------------------------------- /aoi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "aoi.h" 8 | 9 | // aoi 视野半径 10 | #define AOI_RADIUS 10.0f 11 | // aoi 微动距离 12 | #define AOI_NEAR 0.25f 13 | // aoi 视野半径平方 用于距离比较 14 | #define AOI_RADIUS2 (AOI_RADIUS * AOI_RADIUS) 15 | // aoi 实体微动判定 移动处于半径的一半, 则认为是微动 16 | #define AOI_IS_NEAR (AOI_RADIUS2 * 0.25f) 17 | // aoi 实体离开判定 移动处于半径的2倍, 则认为是离开 18 | #define AOI_IS_LEAVE (AOI_RADIUS2 * 4.0f) 19 | // 计算两点距离 x^2+y^2+z^2 直角三角形求斜边公式 c^2=a^2+b^2 20 | #define DIST2(p1,p2) ((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]) + (p1[2] - p2[2]) * (p1[2] - p2[2])) 21 | 22 | // 观察者 [0000 0001] 23 | #define MODE_WATCHER 1 24 | // 被观察者 [0000 0010] 25 | #define MODE_MARKER 2 26 | // 移动 [0000 0100] 27 | #define MODE_MOVE 4 28 | // 删除 [0000 1000] 29 | #define MODE_DROP 8 30 | 31 | #define INVALID_ID (~0) 32 | #define PRE_ALLOC 16 33 | 34 | 35 | // 实体 36 | struct object { 37 | int ref; // 引用数 38 | uint32_t id; // 唯一标识 39 | int version; // 实体新进入场景 或 改变了状态 或 改变了位置, 则 version 加1 40 | int mode; // 实体状态 41 | float last[3]; // 上一次位置坐标 42 | float position[3]; // 当前位置坐标 43 | }; 44 | 45 | // 实体集合 46 | struct object_set { 47 | int cap; // slot数组大小 48 | int number; // 当前分配到哪个索引 49 | struct object ** slot; // 实体数组 50 | }; 51 | 52 | // 热点对单链表 53 | struct pair_list { 54 | struct pair_list * next; 55 | struct object * watcher; // 观察者 56 | struct object * marker; // 被观察者 57 | int watcher_version; // 观察者 version 58 | int marker_version; // 被观察者 version 59 | }; 60 | 61 | // 62 | struct map_slot { 63 | uint32_t id; 64 | struct object * obj; 65 | int next; 66 | }; 67 | 68 | // 存放场景所有实体数组 69 | struct map { 70 | int size; // slot数组大小 71 | int lastfree; // slot数组最后一位索引值 72 | struct map_slot * slot; // 数组头指针 73 | }; 74 | 75 | struct aoi_space { 76 | aoi_Alloc alloc; 77 | void * alloc_ud; 78 | struct map * object; 79 | struct object_set * watcher_static; 80 | struct object_set * marker_static; 81 | struct object_set * watcher_move; 82 | struct object_set * marker_move; 83 | struct pair_list * hot; 84 | }; 85 | 86 | static struct object * 87 | new_object(struct aoi_space * space, uint32_t id) { 88 | struct object * obj = space->alloc(space->alloc_ud, NULL, sizeof(*obj)); 89 | obj->ref = 1; 90 | obj->id = id; 91 | obj->version = 0; 92 | obj->mode = 0; 93 | return obj; 94 | } 95 | 96 | static inline struct map_slot * 97 | mainposition(struct map *m , uint32_t id) { 98 | uint32_t hash = id & (m->size-1); 99 | return &m->slot[hash]; 100 | } 101 | 102 | static void rehash(struct aoi_space * space, struct map *m); 103 | 104 | // 插入实体到map 105 | // id是逻辑层自定义的, 所以会出现 id > m->size 的情况, 例如 m->size = 16, 执行3次实体插入, id分别为 5,37,15 106 | // 第1次插入 id=5 执行 @1 情况 107 | // 第2次插入 id=37 执行 @3 情况 108 | // 第3次插入 id=15 执行 @2 情况 109 | static void 110 | map_insert(struct aoi_space * space , struct map * m, uint32_t id , struct object *obj) { 111 | struct map_slot *s = mainposition(m, id); 112 | // @1 113 | if (s->id == INVALID_ID) { 114 | s->id = id; 115 | s->obj = obj; 116 | return; 117 | } 118 | // @3 119 | if (mainposition(m, s->id) != s) { 120 | struct map_slot * last = mainposition(m, s->id); 121 | while (last->next != s - m->slot) { 122 | assert(last->next >= 0); 123 | last = &m->slot[last->next]; 124 | } 125 | uint32_t temp_id = s->id; 126 | struct object * temp_obj = s->obj; 127 | last->next = s->next; 128 | s->id = id; 129 | s->obj = obj; 130 | s->next = -1; 131 | if (temp_obj) { 132 | map_insert(space, m, temp_id, temp_obj); 133 | } 134 | return; 135 | } 136 | // @2 137 | while (m->lastfree >= 0) { 138 | struct map_slot * temp = &m->slot[m->lastfree--]; 139 | if (temp->id == INVALID_ID) { 140 | temp->id = id; 141 | temp->obj = obj; 142 | temp->next = s->next; 143 | s->next = (int)(temp - m->slot); 144 | return; 145 | } 146 | } 147 | rehash(space, m); 148 | map_insert(space, m, id, obj); 149 | } 150 | 151 | // map 扩容 152 | static void 153 | rehash(struct aoi_space * space, struct map *m) { 154 | struct map_slot * old_slot = m->slot; 155 | int old_size = m->size; 156 | m->size = 2 * old_size; 157 | m->lastfree = m->size - 1; 158 | m->slot = space->alloc(space->alloc_ud, NULL, m->size * sizeof(struct map_slot)); 159 | int i; 160 | for (i=0; isize; i++) { 161 | struct map_slot * s = &m->slot[i]; 162 | s->id = INVALID_ID; 163 | s->obj = NULL; 164 | s->next = -1; 165 | } 166 | for (i=0; iobj) { 169 | map_insert(space, m, s->id, s->obj); 170 | } 171 | } 172 | // 释放旧内存 173 | space->alloc(space->alloc_ud, old_slot, old_size * sizeof(struct map_slot)); 174 | } 175 | 176 | static struct object * 177 | map_query(struct aoi_space *space, struct map * m, uint32_t id) { 178 | struct map_slot *s = mainposition(m, id); 179 | for (;;) { 180 | if (s->id == id) { 181 | if (s->obj == NULL) { 182 | s->obj = new_object(space, id); 183 | } 184 | return s->obj; 185 | } 186 | if (s->next < 0) { 187 | break; 188 | } 189 | s=&m->slot[s->next]; 190 | } 191 | struct object * obj = new_object(space, id); 192 | map_insert(space, m , id , obj); 193 | return obj; 194 | } 195 | 196 | static void 197 | map_foreach(struct map * m , void (*func)(void *ud, struct object *obj), void *ud) { 198 | int i; 199 | for (i=0; isize; i++) { 200 | if (m->slot[i].obj) { 201 | func(ud, m->slot[i].obj); 202 | } 203 | } 204 | } 205 | 206 | static struct object * 207 | map_drop(struct map *m, uint32_t id) { 208 | uint32_t hash = id & (m->size-1); 209 | struct map_slot *s = &m->slot[hash]; 210 | for (;;) { 211 | if (s->id == id) { 212 | struct object * obj = s->obj; 213 | s->obj = NULL; 214 | return obj; 215 | } 216 | if (s->next < 0) { 217 | return NULL; 218 | } 219 | s=&m->slot[s->next]; 220 | } 221 | } 222 | 223 | static void 224 | map_delete(struct aoi_space *space, struct map * m) { 225 | space->alloc(space->alloc_ud, m->slot, m->size * sizeof(struct map_slot)); 226 | space->alloc(space->alloc_ud, m , sizeof(*m)); 227 | } 228 | 229 | static struct map * 230 | map_new(struct aoi_space *space) { 231 | int i; 232 | struct map * m = space->alloc(space->alloc_ud, NULL, sizeof(*m)); 233 | m->size = PRE_ALLOC; 234 | m->lastfree = PRE_ALLOC - 1; 235 | m->slot = space->alloc(space->alloc_ud, NULL, m->size * sizeof(struct map_slot)); 236 | for (i=0; isize; i++) { 237 | struct map_slot * s = &m->slot[i]; 238 | s->id = INVALID_ID; 239 | s->obj = NULL; 240 | s->next = -1; 241 | } 242 | return m; 243 | } 244 | 245 | // 添加实体引用数 246 | inline static void 247 | grab_object(struct object *obj) { 248 | ++obj->ref; 249 | } 250 | 251 | // 释放实体 252 | static void 253 | delete_object(void *s, struct object * obj) { 254 | struct aoi_space * space = s; 255 | space->alloc(space->alloc_ud, obj, sizeof(*obj)); 256 | } 257 | 258 | // 减少实体引用数 259 | inline static void 260 | drop_object(struct aoi_space * space, struct object *obj) { 261 | --obj->ref; 262 | if (obj->ref <=0) { 263 | map_drop(space->object, obj->id); 264 | delete_object(space, obj); 265 | } 266 | } 267 | 268 | static struct object_set * 269 | set_new(struct aoi_space * space) { 270 | struct object_set * set = space->alloc(space->alloc_ud, NULL, sizeof(*set)); 271 | set->cap = PRE_ALLOC; 272 | set->number = 0; 273 | set->slot = space->alloc(space->alloc_ud, NULL, set->cap * sizeof(struct object *)); 274 | return set; 275 | } 276 | 277 | // 创建一个场景 278 | struct aoi_space * 279 | aoi_create(aoi_Alloc alloc, void *ud) { 280 | struct aoi_space *space = alloc(ud, NULL, sizeof(*space)); 281 | space->alloc = alloc; 282 | space->alloc_ud = ud; 283 | space->object = map_new(space); 284 | space->watcher_static = set_new(space); 285 | space->marker_static = set_new(space); 286 | space->watcher_move = set_new(space); 287 | space->marker_move = set_new(space); 288 | space->hot = NULL; 289 | return space; 290 | } 291 | 292 | static void 293 | delete_pair_list(struct aoi_space * space) { 294 | struct pair_list * p = space->hot; 295 | while (p) { 296 | struct pair_list * next = p->next; 297 | space->alloc(space->alloc_ud, p, sizeof(*p)); 298 | p = next; 299 | } 300 | } 301 | 302 | static void 303 | delete_set(struct aoi_space *space, struct object_set * set) { 304 | if (set->slot) { 305 | space->alloc(space->alloc_ud, set->slot, sizeof(struct object *) * set->cap); 306 | } 307 | space->alloc(space->alloc_ud, set, sizeof(*set)); 308 | } 309 | 310 | void 311 | aoi_release(struct aoi_space *space) { 312 | map_foreach(space->object, delete_object, space); 313 | map_delete(space, space->object); 314 | delete_pair_list(space); 315 | delete_set(space,space->watcher_static); 316 | delete_set(space,space->marker_static); 317 | delete_set(space,space->watcher_move); 318 | delete_set(space,space->marker_move); 319 | space->alloc(space->alloc_ud, space, sizeof(*space)); 320 | } 321 | 322 | inline static void 323 | copy_position(float des[3], float src[3]) { 324 | des[0] = src[0]; 325 | des[1] = src[1]; 326 | des[2] = src[2]; 327 | } 328 | 329 | static bool 330 | change_mode(struct object * obj, bool set_watcher, bool set_marker) { 331 | bool change = false; 332 | if (obj->mode == 0) { 333 | if (set_watcher) { 334 | obj->mode = MODE_WATCHER; 335 | } 336 | if (set_marker) { 337 | obj->mode |= MODE_MARKER; 338 | } 339 | return true; 340 | } 341 | if (set_watcher) { 342 | if (!(obj->mode & MODE_WATCHER)) { 343 | obj->mode |= MODE_WATCHER; 344 | change = true; 345 | } 346 | } else { 347 | if (obj->mode & MODE_WATCHER) { 348 | obj->mode &= ~MODE_WATCHER; 349 | change = true; 350 | } 351 | } 352 | if (set_marker) { 353 | if (!(obj->mode & MODE_MARKER)) { 354 | obj->mode |= MODE_MARKER; 355 | change = true; 356 | } 357 | } else { 358 | if (obj->mode & MODE_MARKER) { 359 | obj->mode &= ~MODE_MARKER; 360 | change = true; 361 | } 362 | } 363 | return change; 364 | } 365 | 366 | // 两个坐标点是否处于附近 367 | inline static bool 368 | is_near(float p1[3], float p2[3]) { 369 | return DIST2(p1,p2) < AOI_IS_NEAR; 370 | } 371 | 372 | // 两点之间距离 373 | inline static float 374 | dist2(struct object *p1, struct object *p2) { 375 | float d = DIST2(p1->position, p2->position); 376 | return d; 377 | } 378 | 379 | // 更新实体的状态和位置 380 | void 381 | aoi_update(struct aoi_space * space , uint32_t id, const char * modestring , float pos[3]) { 382 | struct object * obj = map_query(space, space->object, id); 383 | int i; 384 | bool set_watcher = false; 385 | bool set_marker = false; 386 | 387 | for (i=0; modestring[i]; ++i) { 388 | char m = modestring[i]; 389 | switch(m) { 390 | case 'w': 391 | set_watcher = true; 392 | break; 393 | case 'm': 394 | set_marker = true; 395 | break; 396 | case 'd': 397 | if (!(obj->mode & MODE_DROP)) { 398 | obj->mode = MODE_DROP; 399 | drop_object(space, obj); 400 | } 401 | return; 402 | } 403 | } 404 | 405 | if (obj->mode & MODE_DROP) { 406 | obj->mode &= ~MODE_DROP; 407 | grab_object(obj); 408 | } 409 | 410 | bool changed = change_mode(obj, set_watcher, set_marker); 411 | 412 | copy_position(obj->position, pos); 413 | if (changed || !is_near(pos, obj->last)) { 414 | // new object or change object mode 415 | // or position changed 416 | copy_position(obj->last , pos); 417 | obj->mode |= MODE_MOVE; 418 | ++obj->version; 419 | } 420 | } 421 | 422 | static void 423 | drop_pair(struct aoi_space * space, struct pair_list *p) { 424 | drop_object(space, p->watcher); 425 | drop_object(space, p->marker); 426 | space->alloc(space->alloc_ud, p, sizeof(*p)); 427 | } 428 | 429 | static void 430 | flush_pair(struct aoi_space * space, aoi_Callback cb, void *ud) { 431 | struct pair_list **last = &(space->hot); 432 | struct pair_list *p = *last; 433 | while (p) { 434 | struct pair_list *next = p->next; 435 | // 如果 观察者 或 被观察者 的状态改变了 436 | if (p->watcher->version != p->watcher_version || 437 | p->marker->version != p->marker_version || 438 | (p->watcher->mode & MODE_DROP) || 439 | (p->marker->mode & MODE_DROP) 440 | ) { 441 | drop_pair(space, p); 442 | *last = next; 443 | } else { 444 | float distance2 = dist2(p->watcher , p->marker); 445 | if (distance2 > AOI_IS_LEAVE) { 446 | drop_pair(space, p); 447 | *last = next; 448 | } else if (distance2 < AOI_RADIUS2) { 449 | cb(ud, p->watcher->id, p->marker->id); 450 | drop_pair(space, p); 451 | *last = next; 452 | } else { 453 | last = &(p->next); 454 | } 455 | } 456 | p=next; 457 | } 458 | } 459 | 460 | static void 461 | set_push_back(struct aoi_space * space, struct object_set * set, struct object *obj) { 462 | if (set->number >= set->cap) { 463 | int cap = set->cap * 2; 464 | void * tmp = set->slot; 465 | set->slot = space->alloc(space->alloc_ud, NULL, cap * sizeof(struct object *)); 466 | memcpy(set->slot, tmp , set->cap * sizeof(struct object *)); 467 | space->alloc(space->alloc_ud, tmp, set->cap * sizeof(struct object *)); 468 | set->cap = cap; 469 | } 470 | set->slot[set->number] = obj; 471 | ++set->number; 472 | } 473 | 474 | static void 475 | set_push(void * s, struct object * obj) { 476 | struct aoi_space * space = s; 477 | int mode = obj->mode; 478 | if (mode & MODE_WATCHER) { 479 | if (mode & MODE_MOVE) { 480 | set_push_back(space, space->watcher_move , obj); 481 | obj->mode &= ~MODE_MOVE; 482 | } else { 483 | set_push_back(space, space->watcher_static , obj); 484 | } 485 | } 486 | if (mode & MODE_MARKER) { 487 | if (mode & MODE_MOVE) { 488 | set_push_back(space, space->marker_move , obj); 489 | obj->mode &= ~MODE_MOVE; 490 | } else { 491 | set_push_back(space, space->marker_static , obj); 492 | } 493 | } 494 | } 495 | 496 | static void 497 | gen_pair(struct aoi_space * space, struct object * watcher, struct object * marker, aoi_Callback cb, void *ud) { 498 | if (watcher == marker) { 499 | return; 500 | } 501 | float distance2 = dist2(watcher, marker); 502 | if (distance2 < AOI_RADIUS2) { 503 | cb(ud, watcher->id, marker->id); 504 | return; 505 | } 506 | if (distance2 > AOI_IS_LEAVE) { 507 | return; 508 | } 509 | struct pair_list * p = space->alloc(space->alloc_ud, NULL, sizeof(*p)); 510 | p->watcher = watcher; 511 | grab_object(watcher); 512 | p->marker = marker; 513 | grab_object(marker); 514 | p->watcher_version = watcher->version; 515 | p->marker_version = marker->version; 516 | p->next = space->hot; 517 | space->hot = p; 518 | } 519 | 520 | static void 521 | gen_pair_list(struct aoi_space *space, struct object_set * watcher, struct object_set * marker, aoi_Callback cb, void *ud) { 522 | int i,j; 523 | for (i=0; inumber; i++) { 524 | for (j=0; jnumber; j++) { 525 | gen_pair(space, watcher->slot[i], marker->slot[j], cb, ud); 526 | } 527 | } 528 | } 529 | 530 | void 531 | aoi_message(struct aoi_space *space, aoi_Callback cb, void *ud) { 532 | flush_pair(space, cb, ud); 533 | space->watcher_static->number = 0; 534 | space->watcher_move->number = 0; 535 | space->marker_static->number = 0; 536 | space->marker_move->number = 0; 537 | map_foreach(space->object, set_push , space); 538 | gen_pair_list(space, space->watcher_static, space->marker_move, cb, ud); 539 | gen_pair_list(space, space->watcher_move, space->marker_static, cb, ud); 540 | gen_pair_list(space, space->watcher_move, space->marker_move, cb, ud); 541 | } 542 | 543 | // 默认内存分配器 544 | static void * 545 | default_alloc(void * ud, void *ptr, size_t sz) { 546 | if (ptr == NULL) { 547 | void *p = malloc(sz); 548 | return p; 549 | } 550 | free(ptr); 551 | return NULL; 552 | } 553 | 554 | // 使用默认内存分配器创建场景 555 | struct aoi_space * 556 | aoi_new() { 557 | return aoi_create(default_alloc, NULL); 558 | } 559 | -------------------------------------------------------------------------------- /aoi.h: -------------------------------------------------------------------------------- 1 | #ifndef _AOI_H 2 | #define _AOI_H 3 | 4 | #include 5 | #include 6 | 7 | typedef void * (*aoi_Alloc)(void *ud, void * ptr, size_t sz); 8 | typedef void (aoi_Callback)(void *ud, uint32_t watcher, uint32_t marker); 9 | 10 | struct aoi_space; 11 | 12 | struct aoi_space * aoi_create(aoi_Alloc alloc, void *ud); 13 | struct aoi_space * aoi_new(); 14 | void aoi_release(struct aoi_space *); 15 | 16 | // w(atcher) m(arker) d(rop) 17 | void aoi_update(struct aoi_space * space , uint32_t id, const char * mode , float pos[3]); 18 | void aoi_message(struct aoi_space *space, aoi_Callback cb, void *ud); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /perf.c: -------------------------------------------------------------------------------- 1 | #include "aoi.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | struct laoi_cookie { 8 | int count; 9 | int max; 10 | int current; 11 | }; 12 | 13 | struct laoi_space { 14 | uint32_t map_id; 15 | float map_x; 16 | float map_y; 17 | float map_z; 18 | struct aoi_space * space; 19 | struct laoi_cookie * cookie; 20 | }; 21 | 22 | struct laoi_cb { 23 | uint32_t cb_num; 24 | }; 25 | 26 | struct laoi_objs { 27 | float x; 28 | float y; 29 | float z; 30 | float mx; 31 | float my; 32 | }; 33 | 34 | static void * 35 | aoi_alloc(void * ud, void *ptr, size_t sz) { 36 | struct laoi_cookie * cookie = ud; 37 | if (ptr == NULL) { 38 | void *p = malloc(sz); 39 | ++ cookie->count; 40 | cookie->current += sz; 41 | if (cookie->max < cookie->current) { 42 | cookie->max = cookie->current; 43 | } 44 | return p; 45 | } 46 | -- cookie->count; 47 | cookie->current -= sz; 48 | free(ptr); 49 | return NULL; 50 | } 51 | 52 | // 获取当前系统的微秒数 53 | int64_t 54 | igetcurmicro() { 55 | struct timeval tv; 56 | gettimeofday(&tv, NULL); 57 | return tv.tv_sec*1000*1000 + tv.tv_usec; 58 | } 59 | 60 | // 获取当前系统的毫秒数 61 | int64_t 62 | igetcurtick() { 63 | return igetcurmicro()/1000; 64 | } 65 | 66 | 67 | // 一堆时间宏,常用来做性能日志 68 | #define __Millis igetcurtick() 69 | #define __Micros igetcurmicro() 70 | #define __Since(t) (__Micros - t) 71 | #define __Begin int64_t t = __Micros 72 | #define __Stop t = __Since(t) 73 | #define ilog(...) printf(__VA_ARGS__) 74 | 75 | static struct laoi_space * 76 | _aoi_create(uint32_t map_id, float map_x, float map_y, float map_z) { 77 | struct laoi_space * lspace = malloc(sizeof(*lspace)); 78 | lspace->map_id = map_id; 79 | lspace->map_x = map_x; 80 | lspace->map_y = map_y; 81 | lspace->map_z = map_z; 82 | lspace->cookie = malloc(sizeof(struct laoi_cookie)); 83 | lspace->cookie->count = 0; 84 | lspace->cookie->max = 0; 85 | lspace->cookie->current = 0; 86 | lspace->space = aoi_create(aoi_alloc, lspace->cookie); 87 | 88 | return lspace; 89 | } 90 | 91 | static int 92 | _aoi_update(struct laoi_space * lspace, uint32_t id, const char * mode, float pos_x, float pos_y, float pos_z) { 93 | struct aoi_space * space = lspace->space; 94 | if (pos_x > lspace->map_x || pos_y > lspace->map_y || pos_z > lspace->map_z || 95 | pos_x < 0 || pos_y < 0 || pos_z < 0) { 96 | printf("aoi update pos error. map_id=>%d map=>(%f,%f,%f) pos=>(%f,%f,%f)\n", 97 | lspace->map_id, lspace->map_x, lspace->map_y, lspace->map_z, pos_x, pos_y, pos_z); 98 | return 0; 99 | } 100 | float pos[3] = {pos_x, pos_y, pos_z}; 101 | aoi_update(space, id, mode, pos); 102 | 103 | return 1; 104 | } 105 | 106 | static void 107 | aoi_cb_message(void *ud, uint32_t watcher, uint32_t marker) { 108 | struct laoi_cb * clua = ud; 109 | clua->cb_num++; 110 | } 111 | 112 | static int 113 | _aoi_message(struct laoi_space * lspace) { 114 | struct aoi_space * space = lspace->space; 115 | struct laoi_cb clua = {0}; 116 | aoi_message(space, aoi_cb_message, &clua); 117 | 118 | return clua.cb_num; 119 | } 120 | 121 | static int 122 | _aoi_release(struct laoi_space * lspace) { 123 | struct aoi_space * space = lspace->space; 124 | aoi_release(space); 125 | lspace->space = NULL; 126 | free(lspace->cookie); 127 | lspace->cookie = NULL; 128 | free(lspace); 129 | 130 | return 1; 131 | } 132 | 133 | 134 | static void 135 | perf_aoi() { 136 | int obj_num = 1000; //实体数量 137 | float map_x = 1024; //场景宽 138 | float map_y = 1024; //场景长 139 | float map_z = 0; //场景高 140 | float move_index = 20; 141 | int i,ii,j,jj,k,kk; 142 | int id; 143 | int space_num = 1; //场景数量 144 | int move_num = 100; //实时移动数量 145 | int cb_num = 0; 146 | struct laoi_space * lspace[space_num]; 147 | struct laoi_objs objs[space_num][obj_num]; 148 | srand((uint32_t)time(NULL)); 149 | 150 | for (i = 0; i < space_num; ++i) { 151 | lspace[i] = _aoi_create(1001, map_x, map_y, map_z); 152 | for (ii = 0; ii < obj_num; ++ii) { 153 | objs[i][ii].x = (float)(rand()%(int)(map_x)); 154 | objs[i][ii].y = (float)(rand()%(int)(map_y)); 155 | objs[i][ii].z = 0; 156 | objs[i][ii].mx = (float)(rand()%(int)move_index); 157 | objs[i][ii].my = (float)(rand()%(int)move_index); 158 | _aoi_update(lspace[i], ii, "wm", objs[i][ii].x, objs[i][ii].y, 0); 159 | } 160 | cb_num = _aoi_message(lspace[i]); 161 | } 162 | 163 | for (k = 0; k < 20; ++k) { 164 | for (i = 0; i < space_num; ++i) { 165 | for (ii = 0; ii < move_num; ++ii) { 166 | id = rand()%obj_num; 167 | j = rand()%2; 168 | jj = rand()%2; 169 | if (j == 1) { 170 | objs[0][id].x += objs[i][ii].mx; 171 | if (objs[0][id].x > map_x) { 172 | objs[0][id].x -= objs[i][ii].mx; 173 | } 174 | } else { 175 | objs[0][id].x -= objs[i][ii].mx; 176 | if (objs[0][id].x < 1) { 177 | objs[0][id].x += objs[i][ii].mx; 178 | } 179 | } 180 | 181 | if (jj == 1) { 182 | objs[0][id].y += objs[i][ii].my; 183 | if (objs[0][id].y > map_y) { 184 | objs[0][id].y -= objs[i][ii].my; 185 | } 186 | } else { 187 | objs[0][id].y -= objs[i][ii].my; 188 | if (objs[0][id].y < 1) { 189 | objs[0][id].y += objs[i][ii].my; 190 | } 191 | } 192 | } 193 | } 194 | for (i = 0; i < space_num; ++i) { 195 | __Begin; 196 | ilog("开始测试实体移动检索耗时, 场景移动实体 %d 个\n", move_num); 197 | for (ii = 0; ii < move_num; ++ii) { 198 | id = rand()%obj_num; 199 | _aoi_update(lspace[i], id, "wm", objs[i][id].x, objs[i][id].y, 0); 200 | } 201 | cb_num = _aoi_message(lspace[i]); 202 | ilog("移动 %d 个实体后,需处理更新数量 %d \n", move_num, cb_num); 203 | __Stop; 204 | ilog("总耗时 %lld 毫秒, 平均一个实体耗时 %lld 毫秒 \n\n", t, t/move_num); 205 | printf("max memory = %d, current memory = %d\n", lspace[i]->cookie->max , lspace[i]->cookie->current); 206 | } 207 | 208 | sleep(1); 209 | } 210 | 211 | } 212 | 213 | int 214 | main(int argc, char const *argv[]) { 215 | perf_aoi(); 216 | return 0; 217 | } 218 | --------------------------------------------------------------------------------