AOI

AOI服务与算法详解
本文详细介绍了一种基于格子的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

 

转载于:https://www.cnblogs.com/sniperHW/archive/2012/09/29/2707953.html

<think>好的,我现在要解决用户关于如何正确调用AOI的问题。首先,我需要回顾用户提供的引用内容,看看里面有没有相关的调用示例或方法。 用户提供了五个引用,其中引用[1]提到了使用AOI工厂生成实例,并给出了JavaScript的示例代码。引用[2]是关于AB PLC的编程示例,可能涉及不同的编程语言和平台。引用[3]是关于二次开发的开源协议,可能不直接涉及调用方法。引用[4]提到了用OpenCV调用YOLO做目标检测,可能和AOI的应用场景相关。引用[5]则是一个实战指南的项目地址,可能包含更多详细信息。 用户的问题主要集中在如何正确调用AOI或获取调用示例,所以需要重点看引用[1]和引用[2]。引用[1]中的示例代码展示了如何创建AOI实例和更新位置,这是JavaScript的例子。引用[2]提到了AB PLC的示例,可能使用结构化文本或其他PLC编程语言,但用户提供的压缩包可能包含具体的代码示例,不过用户这里没有给出具体代码,只有描述。 接下来,我需要整理这些信息,分步骤说明调用方法。首先可能需要区分不同平台或环境下的调用方式,比如Node.js的pomelo-aoi模块,以及AB PLC的编程方法。然后,结合用户提供的引用内容,给出具体的代码示例和步骤,同时注意遵守引用中的开源协议(引用[3])。 另外,用户可能需要了解通用的调用流程,比如初始化实例、配置参数、更新对象位置等。引用[1]中的例子已经涵盖了这些步骤,但可能需要更详细的解释。例如,创建AOI实例时需要的参数(minX, maxX等)以及它们的作用,还有updatePosition方法的使用。 还需要考虑用户可能的错误点,比如未正确配置参数导致实例创建失败,或者未遵循开源协议进行二次开发。根据引用[3],任何基于AOI开源材料的二次开发必须开源并授权专利,这点在回答中需要提醒用户注意。 此外,引用[4]中的OpenCV和YOLO调用方法虽然不直接相关,但可能作为AOI在目标检测中的应用案例,可以适当提及,但用户的问题更关注调用方法本身,所以可能不需要深入展开。 最后,生成相关问题,帮助用户进一步理解AOI的不同方面,如参数优化、错误处理、性能提升等,这些问题应基于引用内容中的信息,比如引用[2]提到的调试和优化,引用[5]的实战指南可能涉及最佳实践。 总结回答结构: 1. 分环境说明调用方法(Node.js和AB PLC) 2. 提供具体代码示例和参数解释 3. 注意事项(如开源协议) 4. 相关问题 需要确保使用正确的LaTeX格式,行内公式用$,独立公式用$$,但当前问题中可能不需要数学公式,所以可能不涉及。检查是否有需要强调的技术术语,可能需要用代码块包裹。</think>### AOI调用方法及示例 AOI(自动光学检测)的调用方法因应用场景和开发框架而异。以下是基于不同平台的调用示例及注意事项: #### 1. Node.js环境下的AOI调用(基于pomelo-aoi模块) 引用[1]中的示例展示了如何通过工厂模式生成AOI实例,并更新对象位置: ```javascript var aoiManager = require('pomelo-aoi').manager; // 创建AOI实例,需定义区域范围和网格大小 var aoiInstance = aoiManager.createAOI({ minX: 0, maxX: 100, minY: 0, maxY: 100, nodeWidth: 10, nodeHeight: 10 }); // 更新对象位置 aoiInstance.updatePosition("entity1", 50, 60); ``` **参数说明**: - `minX/maxX`和`minY/maxY`:定义检测区域的坐标范围。 - `nodeWidth/nodeHeight`:网格划分的尺寸,影响检测精度和性能[^1]。 #### 2. PLC环境下的AOI调用(Allen-Bradley PLC示例) 引用[2]提到,AB PLC通过结构化文本实现AOI逻辑,例如数字信号转换: ```structuredtext AOI_DigitalConverter Instance1( InputSignal := SensorData, OutputValue => ProcessedValue ); ``` **核心步骤**: - 定义AOI模块的输入/输出变量。 - 调用预定义的AOI函数块(如`AOI_DigitalConverter`),关联硬件信号[^2]。 #### 3. 注意事项 - **开源协议**:若基于AOI开源库二次开发,需按引用[3]要求开源成果并授权专利[^3]。 - **调试工具**:引用[2]和[5]均建议使用IDE内置调试器(如RSLogix 5000或Node.js调试工具)验证AOI逻辑[^2][^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值