zoukankan      html  css  js  c++  java
  • AOI

    AOI服务作为网络游戏中的中的一个重要组件,用于为地图中的对象根据当前坐标更新关注列表.对于玩家而言,在A关注列表中的对象,其状态发生改变时,需要通知A,这样A才能看到在视野内

    其它对象的移动,战斗等。对于NPC而且,关注列表中的对象表示在自己一定范围内的对象,可作为AI选择的攻击目标。

    典型的AOI算法包括格子,十字链表等,关于十字链表法可参考:http://www.codedump.info/?p=388

    本文介绍一种基于格子的算法.

    本算法实现的目标是支持可变长视距,根据视距半径,计算出一个包围这个视野圆的最小正方形,然后计算出这个正方形包含在哪些格子中,这些格子中的对象都有可能是可见对象.

    首先介绍基本的数据结构:

    单元格:

    struct map_block
    {
        struct double_link aoi_objs;
        uint32_t x;
        uint32_t y;
    };

    地图中的所有对象都归属于一个单元格管理,所以单元格中有一个双向链表,方便对象的添加和删除.x和y表示单元格在地图中的行列坐标.

    struct aoi_object
    {
        struct double_link_node block_node;             //ͬһ¸ömap_blockÄڵĶÔÏóÐγÉÒ»¸öÁÐ±í      
        struct map_block *current_block;    
        uint32_t aoi_object_id; 
        struct bit_set self_view_objs;                  //×Ô¼º¹Û²ìµ½µÄ¶ÔÏ󼯺Ï
        struct point2D current_pos;                     //µ±Ç°×ø±ê
        uint32_t view_radius;                           //¿ÉÊÓ°ë¾¶
    };

    aoi_object代表地图中对象,view_radius表示其实际可视半径,self_view_objs记录了当前关注的对象集,用一个bit_set实现,目前我将位集的大小设置为65536,也就是地图中最多可容纳65536个对象,

    这样,当对象进入视野时就根据aoi_object_id设置相应的位,离开时清除相应的位,还可以快速的判断一个对象是否在自己的视野中.

    基本算法如下:

    1)当对象A进入地图时,以标准视距作为半径,算出相关的单元格,遍历单元格中的对象,计算出两对象A和B的distance,如果distance < A->view_radius则B进入A的视野,如果distance < B->view_radius

    则A也进入B的视野.

    2) 对象A离开地图时,以标准视距作为半径,算出相关的单元格,遍历单元格中的对象,计算出两对象A和B的distance,如果distance < B->view_radius则A离开B的视野.

    3)当对象A在地图中移动时,以标准视距作为半径,分别为老坐标和新坐标计算出相关的单元格,这里的单元格会被分成三类,1:新进入的,2:离开的,3:无变化的.对于1,2类分别按1),2)处理即可.

    对于第3类,格子中的对象需要做进一步的判断(参看后面贴出的实现代码)

    上面提出了一个标准视距的概念,主要用于处理以下问题,A视距为100,B为50,A静止B向A移动,如果以B的实际视距做计算,则只有当B在A的50范围内时A才能看到B,而实际上B在A的100范围

    内时A就应该看到B了。如果将标准视距设置为100就不存在这样的问题了.

    但是,标准视距离设置得过大,会导致计算更多的格子,所以标准视距一般设置为玩家的可视距离.这又出现了另一个问题,游戏设定中,可能会出现少数超远视距的对象。例如,标准视距是50

    但某些对象的视距是100,超视距对象在地图中相对来说是稀少的,所以对超视距的对象可做特殊处理.首先,超视距对象有一个更新间隔,每次更新视野后,记录下变更时间.触发视野变更的条件之

    一是执行move,但如果超视距对象长时间静止就需要主动调用一个函数,以触发其视野变更.处理代码如下:

    static inline tick_super_object(struct map *m,struct aoi_object *o)
    {
        uint32_t now = GetCurrentMs();
        if(now - o->last_update_tick >= UPDATE_INTERVAL)
        { 
            //remove out of view object first
            uint32_t i = 0;
            for( ; i < MAX_BITS; ++i)
            {
                if(o->self_view_objs.bits[i] > 0)
                {
                    uint32_t j = 0;
                    for( ; j < sizeof(uint32_t); ++j)
                    {
                        if(o->self_view_objs.bits[i] & (1 << j))
                        {
                            uint32_t aoi_object_id = i*sizeof(uint32_t) + j;
                            struct aoi_object *other = m->all_aoi_objects[aoi_object_id];
                            uint64_t distance = cal_distance_2D(&o->current_pos,&other->current_pos);
                            if(distance > o->view_radius)
                                leave_me(m,o,other);
                        }
                    }
                }
            }
            //process enter view
            uint32_t x1,y1,x2,y2;
            cal_blocks(m,&o->current_pos,o->view_radius,&x1,&y1,&x2,&y2);
            uint32_t y = y1;
            uint32_t x;
            for( ; y <= y2; ++y)
            {
                for( x=x1; x <= x2; ++x)
                {
                    struct map_block *bl = get_block(m,y,x);
                    struct aoi_object *cur = (struct aoi_object*)bl->aoi_objs.head.next;
                    while(cur != (struct aoi_object*)&bl->aoi_objs.tail)
                    {
                        if(is_set(&o->self_view_objs,cur->aoi_object_id) == 0)
                        {
                            uint64_t distance = cal_distance_2D(&o->current_pos,&cur->current_pos);
                            if(o->view_radius >= distance)
                                enter_me(m,o,cur);
                        }
                        cur = (struct aoi_object *)cur->block_node.next;
                    }
                }        
            }        
            o->last_update_tick = now;    
        }
        
    }

    完整代码如下:https://github.com/sniperHW/kendylib/tree/master/aoi

  • 相关阅读:
    C#磁吸屏幕窗体类库
    准备
    我写的诗
    How to turn off a laptop keyboard
    How to tell which commit a tag points to in Git?
    Why should I care about lightweight vs. annotated tags?
    How to get rid of “would clobber existing tag”
    Facebook, Google and Twitter threaten to leave Hong Kong over privacy law changes
    The need for legislative reform on secrecy orders
    Can a foreign key be NULL and/or duplicate?
  • 原文地址:https://www.cnblogs.com/sniperHW/p/2707953.html
Copyright © 2011-2022 走看看