普通的场景地图是二维的,一般思路都是aoi,大部分都是按照格子去划分。
aoi一般维护了3可见列表visiable_list, enter_list, levelist。每个实体都有visiable raidus[可见半径]。
也可以采用十字链表去做维护。
所谓的十字链表是x 方向 y方向的指针构成,性能极其高效。这个代码我准备贴上去,代码是借鉴一个博客http://www.codedump.info/?p=388写的,不过他代码写的很渣很多BUG,但是我复杂把代码贴上去,看一下就发现几个BUG,后来我同事移植到程序种,发现真心渣。
另外外面通用的做法是做一个二维数组,数组放之一个链表或者数组指针。这样的话每一次都要全局分配N*N格子指针数组。
维护的逻辑都查不多。寻找某个点是否有实体,(y-1)*ROW+LINE就可以算出来所在的位置。
#ifndef _SCENE_H
#define _SCENE_H
//#include "sprite.h"
#include <ext/hash_map>
#include <map>
#include <list>
#include <set>
#include <vector>
using namespace std;
#include "object.h"
#include "npos.h"
#include "sceneconfig.h"
#include "skill/skillconfig.h"
#include "sgconf.h"
class PkObject;
#define DISTANCE 150
#include <set>
class Scene
{
public:
Scene();
~Scene();
void loop();
// 业务逻辑需要的接口
void notify_next_monster_fresh(uint32_t interval, uint32_t type); // 通知客户端下一次刷怪间隔
void reset_noti_next_time() {this->alrdy_noti = 0;} // 重置标志位
void init_monster(); // 初始化怪物
void refresh_monster(); // 新版刷怪
void active_next_mp(uint32_t id); // 激活下一个刷怪点
void produce_base(); // 生成基地
int pkg_all_base_tower();
Vector2 getFhd(int camp);
void setSceneId( int id);
int getSceneId();
Object* getObjInScene(uint32_t id);
void insertTargetObj(Object *obj, Object *iter,int release,Vector2 &pos, int r ,int camp, std::set<Object *> &targetList);
void brocastMsg(int cmdid, int len);
void onEnter(Object *role, bool isNotify);
void onLeave(Object *role, bool sendToSelf);
void brocastMsgExceptMe(Object *role, int cmdid, int len);
void getTargetList(Object *object,const SkillConfig *base, Vector2 &pos, uint32_t camp, std::set<Object *> &targetList);
void getNearObjList(Object *object, std::set<Object *> &targetList, float distance);
void getAITargetList(PkObject *obj,int tarType ,Vector2 &pos, uint32_t r, std::set<PkObject *> &targetList);
void getVisibleTargetList(Object *object, const SkillConfig *base, Vector2 &pos, uint32_t camp, std::set<Object *> &targetList);
void sendMsgToView(Object *role, int cmdid, int len);
void sendToViewAndCamp(Object *role, int cmdid, int len);
void Add(Object* object);
void Move(int id, int x, int y);
void Leave(int id);
void print_objs();
uint32_t getPlayerCount();
void set_wincamp(int camp) { this->wincamp = camp;}
void set_start(bool is_start) { this->is_start = is_start;}
uint32_t incr_gpmonster_cnt(uint32_t id);
uint32_t decr_gpmonster_cnt(uint32_t id);
public:
//aoi 逻辑整理
uint32_t sceneid; // 场景或者副本ID
uint32_t guan; // 关卡ID
uint32_t chapter; // 章节ID
uint32_t create_time; // Scene的创建时间
uint64_t cur_ms; // 当前毫秒
uint32_t cur_sec; // 当前秒
uint32_t npc_id; // 用户创建Npc的自增npc_id
int32_t last_sg_key; // 上一次刷怪使用的sg-key值
int32_t last_tm_key; // 上一次刷怪使用的tm-key值
uint32_t alrdy_noti; // 通知刷怪时间标志位
uint32_t wincamp; // 0:未分胜负,1:己方胜利,2:电脑胜利
bool is_start; // 是否开始场景loop中的动作:如刷怪等
std::map<uint32_t, t_waveConf*> waveconf_map; // 当前刷怪配置
std::map<uint32_t, uint32_t> mon_interval_map; // 刷怪间隔map
std::map<uint32_t, uint32_t> gpmonster_cnt_map; // 聚集点存活怪物数量
ObjMap obj_map_;
ObjMap role_map_;
ObjList obj_x_list_;
ObjList obj_y_list_;
ObjMap move_map_;
ObjMap enter_map_;
ObjMap leave_map_;
SceneConfig *pConfig;
int state;
//需要删除的尸体
std::set<int> _del_npc;
private:
void Update(Object *object);
void GetRangeSet(Object *object, ObjMap *set);
void UpdateObjectPosition(Object *object, int x, int y);
};
#endif
#include "util/util.h"
#include "sceneconfig.h"
#include "global.h"
#include "proto/online_enum.h"
#include "npc.h"
#include "astar.h"
#include "sprite.h"
#include "Ctimer.h"
#include "scene.h"
#include "intersect.h"
#include "intersact2.h"
#include "pkobject.h"
#include "./model/mlogic.pb.h"
int debug_AOI = 0;
Scene::Scene()
{
state = 0;
pConfig = NULL;
npc_id = 100000;
last_sg_key = 0;
last_tm_key = 0;
alrdy_noti = 0;
is_start = false;
wincamp = 0;
this->obj_map_.clear();
this->obj_x_list_.clear();
this->obj_y_list_.clear();
this->enter_map_.clear();
this->move_map_.clear();
this->leave_map_.clear();
}
Scene::~Scene()
{
DEBUG_LOG("DEL SCENE\t[%lu]",
this->obj_map_.size());
//删掉场景内存数据
this->obj_x_list_.clear();
this->obj_y_list_.clear();
this->enter_map_.clear();
this->move_map_.clear();
this->leave_map_.clear();
FOREACH(this->obj_map_, itr) {
if (itr->second->type == Object_Player)
continue;
delete itr->second;
}
this->obj_map_.clear();
}
//场景帧循环
void Scene::loop()
{
if(is_start) {
uint64_t lcur_ms = getCurrentMsTime();
this->cur_sec = lcur_ms / 1000;
if (lcur_ms - this->cur_ms < 20)
return;
this->cur_ms = lcur_ms;
//游戏回合结束
//TODO 回合时间需要统一定义
if(this->cur_sec - this->create_time >= 480) {
DEBUG_LOG("times up[cur_sec:%d][create_time:%d]", this->cur_sec, this->create_time);
this->set_wincamp(1);
this->set_start(false);
return;
}
//this->shuaguai();
this->refresh_monster();
FOREACH(_del_npc, iter){
Leave(*iter);
PkObject *obj = (PkObject *)getObjInScene(*iter);
if(obj){
delete obj;
}
}
_del_npc.clear();
//复活数组
std::vector<uint32_t> vect;
FOREACH(this->obj_map_, iter) {
PkObject *pk= (PkObject*)iter->second;
//判断类型是NPC 尸体存在的时间如果存在
if(pk->get_type() == Object_NPC &&
pk->getState() == SceneObject_Death &&
this->cur_sec > pk->_del_body_time &&
!pk->relive_time) {
_del_npc.insert(pk->id);
}
bool brun = true;
//删除实体 添加实体
if(pk->get_type() == Object_Player && pk->getState() == SceneObject_Death){
uint32_t current_time = cur_sec;
if(current_time >= pk->relive_time) {
vect.push_back(pk->id);
brun = false;
}
}
if(brun){
pk->loop(lcur_ms);
}
}
FOREACH(vect, iter2){
PkObject *obj = (PkObject *)getObjInScene(*iter2);
Leave(*iter2);
obj->_pos = getFhd(0);
Add(obj);
obj->relive();
}
}
}
void Scene::notify_next_monster_fresh(uint32_t interval, uint32_t type)
{
NextMonsterTimeNotify nextMonsterTimeNotify;
nextMonsterTimeNotify.set_time(interval);
nextMonsterTimeNotify.set_monstertype(type);
int index = nextMonsterTimeNotify.ByteSize();
nextMonsterTimeNotify.SerializeToArray(SEND_CLI_BUF, index);
this->brocastMsg(noti_next_shuaguai_cmd, index);
}
void Scene::refresh_monster()
{
//HDEBUG_LOG("htest sgd refresh_monster");
uint32_t pass_time = this->cur_sec - this->create_time;
FOREACH(this->waveconf_map, iter) {
waveConf* pWaveConf = iter->second;
std::map<uint32_t, uint32_t>::iterator iter_interval = this->mon_interval_map.find(pWaveConf->id);
if(iter_interval == this->mon_interval_map.end()) {
return;
}
uint32_t* interval = &iter_interval->second;
if(pass_time < *interval) {
return;
}
sgdConf* psgd = pConfig->get_sgdConf(pWaveConf->sgdid);
if (!psgd) {
ERROR_LOG("error sgd ID\t[g:%u c:%u %u]", this->guan, this->chapter, pWaveConf->sgdid);
return;
}
// 获取野怪路径的配置
ygpConf* pygp = pConfig->get_ygpConf(psgd->pathid);
if (!pygp) {
ERROR_LOG("error path ID\t[g:%u c:%u %u]", this->guan, this->chapter, psgd->pathid);
return;
}
//FOREACH(pygp->ygp, itr) {
// HDEBUG_LOG("htest monster path:(%f,%f)", itr->x, itr->y);
//}
uint32_t monsterid = pWaveConf->mon_id;
uint32_t monster_cnt = pWaveConf->mon_cnt;
HDEBUG_LOG("htest sgd monster [monsterid:%u][monster_cnt:%u]", monsterid, monster_cnt);
npc_base_t* pnb = get_npc_base(monsterid);
if (pnb == NULL) {
ERROR_LOG("err npcID\t[g:%u c:%u npcID:%u]", this->guan, this->chapter, monsterid);
continue;
}
for(int i = 0; i < (int)monster_cnt; i++) {
int offset = i * 15;//随机一个偏移值
int x = psgd->pos.x + offset;
int y = psgd->pos.y + offset;
if(this->pConfig->m_arr_bytes[y / GRID_SIZE][x / GRID_SIZE]) {
x = psgd->pos.x;
y = psgd->pos.y;
}
Npc* pn = new Npc(++this->npc_id, Object_NPC, (NpcType)pnb->npcType, psgd->camp, x, y, 0, 0, OFFENSE, pnb);
HDEBUG_LOG("htest sgd ID\t[sgdid:%u pathid:%u][id:%u]", psgd->sgdid, psgd->pathid, pWaveConf->id);
this->Add(pn);
pn->startMove(pygp->ygp, e_march);
//pn->adjustPath();
}
*interval += pWaveConf->interval;
// 只广播下一波偷袭怪的时间
HDEBUG_LOG("htest sgd next_monster_fresh[interval:%u][type:%u][sgdid:%u][id:%u][totaltime:%d][pasttime:%d]",
pWaveConf->interval, psgd->type, pWaveConf->sgdid, pWaveConf->id, *interval, pass_time);
if(psgd->type == 1) {
this->notify_next_monster_fresh(pWaveConf->interval, psgd->type);
}
}
}
void Scene::active_next_mp(uint32_t id)
{
std::map<uint32_t, t_waveConf*>::iterator iter = this->waveconf_map.find(id);
if(iter != this->waveconf_map.end()) {
this->waveconf_map.erase(iter);
}
if (!this->pConfig)
return ;
guanConf* psg = get_guanConf(this->guan, this->chapter);
if (!psg || psg->sg_map.size() == 0) {
return;
}
t_waveConf* pWaveConf = NULL;
FOREACH(psg->sg_map, iter_sg) {
if(id == iter_sg->second.pre_id) {
HDEBUG_LOG("htest sgd active next mp[curid:%u][nextid:%u]", id, iter_sg->first);
pWaveConf = &iter_sg->second;
break;
}
}
SgdDestroyNotify sgdDestroyNotify;
sgdDestroyNotify.set_currentid(id);
if(pWaveConf != NULL) {
this->waveconf_map[pWaveConf->id] = pWaveConf;
uint32_t pass_time = this->cur_sec - this->create_time;
this->mon_interval_map[pWaveConf->id] = pWaveConf->starttime + pass_time;
sgdDestroyNotify.set_nextid(pWaveConf->id);
} else {
sgdDestroyNotify.set_nextid(0);
}
int index = sgdDestroyNotify.ByteSize();
sgdDestroyNotify.SerializeToArray(SEND_CLI_BUF, index);
this->brocastMsg(noti_sgd_destroy_cmd, index);
HDEBUG_LOG("sgdDestroyNotify");
}
void Scene::produce_base()
{
if (!this->pConfig)
return ;
FOREACH(pConfig->m_jidi_map, it) {
jidiConf* pj = &it->second;
npc_base_t* pnb = get_npc_base(pj->deployid);
if (pnb == NULL) {
DEBUG_LOG("err BASE id\t[g:%u c:%u baseID:%u]", this->guan, this->chapter, pj->deployid);
continue;
}
Npc* pn = new Npc(it->first, Object_NPC, (NpcType)pnb->npcType, pj->camp, pj->pos.x, pj->pos.y, pj->dir.x, pj->dir.y, OFFENSE, pnb );
HDEBUG_LOG("htest base produce BASE TOWER INFO[id=%u npcID:%u subtype:%u x=%f, y=%f]",
pn->id, pn->npcID, pn->subtype, pn->_pos.x, pn->_pos.y);
this->Add(pn);
}
}
void Scene::init_monster()
{
if (!this->pConfig) {
HDEBUG_LOG("htest init_monster monster config is null");
return ;
}
guanConf* psg = get_guanConf(this->guan, this->chapter);
if (!psg || psg->sg_map.size() == 0) {
HDEBUG_LOG("htest init_monster no monster config\t[g:%u c:%u]", this->guan, this->chapter);
return;
}
this->waveconf_map.clear();
SgdLocationNotify sgdLocationNotify;
FOREACH(psg->sg_map, iter) {
t_waveConf* pWaveConf = &iter->second;
HDEBUG_LOG("htest init_monster id [id:%d] [preid:%d] ", pWaveConf->id, pWaveConf->pre_id);
sgdConf* p_sgdConf = pConfig->get_sgdConf(pWaveConf->sgdid);
if (!p_sgdConf) {
ERROR_LOG("error sgd ID\t[g:%u c:%u sgdid%u]", this->guan, this->chapter, pWaveConf->sgdid);
return;
}
// 加载当前刷怪配置
if(pWaveConf->pre_id == 0) {
this->waveconf_map[pWaveConf->id] = pWaveConf;
this->mon_interval_map[pWaveConf->id] = pWaveConf->starttime;
// 只广播下一波偷袭怪时间
if(p_sgdConf->type == 1) {
this->notify_next_monster_fresh(pWaveConf->starttime, p_sgdConf->type);
}
}
// 获取聚集点的配置
if(pWaveConf->gpid == 0) {
continue;
}
if(pWaveConf->mon_cnt > 0) {
SgdLocationNotify_Location* location = sgdLocationNotify.add_location();
location->set_id(pWaveConf->id);
location->set_x(p_sgdConf->pos.x);
location->set_y(p_sgdConf->pos.y);
}
sgdConf* psgd = pConfig->get_sgdConf(pWaveConf->gpid);
if (!psgd) {
HDEBUG_LOG("htest init_monster error sgd ID\t[g:%u c:%u %u]", this->guan, this->chapter, pWaveConf->gpid);
ERROR_LOG("error sgd ID\t[g:%u c:%u %u]", this->guan, this->chapter, pWaveConf->gpid);
return;
}
FOREACH(pWaveConf->gpmon_map, iter_gpmon) {
uint32_t monsterid = iter_gpmon->first;
uint32_t monster_cnt = iter_gpmon->second;
npc_base_t* pnb = get_npc_base(monsterid);
if (pnb == NULL) {
HDEBUG_LOG("htest init_monster err npcID\t[g:%u c:%u npcID:%u]", this->guan, this->chapter, monsterid);
ERROR_LOG("err npcID\t[g:%u c:%u npcID:%u]", this->guan, this->chapter, monsterid);
continue;
}
for(uint32_t i = 0; i < monster_cnt; i++) {
int offset = random() % 50;//随机一个偏移值
Npc* pn = new Npc(++this->npc_id, Object_NPC, (NpcType)pnb->npcType, psgd->camp, psgd->pos.x + offset, psgd->pos.y + offset, 0, 0, DEFENCE, pnb);
pn->startDefend();
pn->gpid = pWaveConf->id;
int count = this->incr_gpmonster_cnt(pWaveConf->id);
HDEBUG_LOG("htest init_monster sgd gp ID\t[sgdid:%u pathid:%u][monster_cnt:%d]", psgd->sgdid, psgd->pathid, count);
this->Add(pn);
}
}
}
int index = sgdLocationNotify.ByteSize();
sgdLocationNotify.SerializeToArray(SEND_CLI_BUF, index);
this->brocastMsg(noti_sgd_location_cmd, index);
HDEBUG_LOG("sgdLocationNotify [index:%d]", index);
}
uint32_t Scene::incr_gpmonster_cnt(uint32_t id)
{
std::map<uint32_t, uint32_t>::iterator iter = this->gpmonster_cnt_map.find(id);
if(iter == this->gpmonster_cnt_map.end()) {
this->gpmonster_cnt_map[id] = 1;
return 1;
}
uint32_t* count = &iter->second;
return ++(*count);
}
uint32_t Scene::decr_gpmonster_cnt(uint32_t id)
{
std::map<uint32_t, uint32_t>::iterator iter = this->gpmonster_cnt_map.find(id);
if(iter == this->gpmonster_cnt_map.end()) {
return 0;
}
uint32_t* count = &iter->second;
return --(*count);
}
void Scene::setSceneId(int id)
{
this->sceneid = id;
}
int Scene::getSceneId()
{
return this->sceneid;
}
Object* Scene::getObjInScene(uint32_t id)
{
std::map<int, Object *>::iterator iter = this->obj_map_.find(id);
if (iter != this->obj_map_.end()){
return iter->second;
}
return NULL;
}
//广播数据内存需要拷贝到公共缓存区里面
void Scene::brocastMsg(int cmdid, int len)
{
/*
FOREACH(this->obj_map_, iter) {
PkObject *pkobj = (PkObject*)iter->second;
if(pkobj->type == Object_Player)
{
Crole* pcr = reinterpret_cast<Crole*>(pkobj);
if (!pcr->player_control()) continue;
pkobj->send_msg_ex(cmdid, 0, len, false );
}
}
*/
FOREACH(this->role_map_, iter) {
Crole* pcr = reinterpret_cast<Crole*>(iter->second);
if (!pcr->player_control()) continue;
pcr->send_msg_ex(cmdid, 0, len, false );
}
}
uint32_t Scene::getPlayerCount()
{
uint32_t size = 0;
FOREACH(this->obj_map_, iter) {
PkObject *pkobj = (PkObject*)iter->second;
if(pkobj->type == Object_Player)
{
size++;
}
}
return size;
}
//广播消息给视野范围的玩家 包括自己
void Scene::sendMsgToView(Object *role, int cmdid, int len)
{
PkObject *msgobj = (PkObject *)role;
FOREACH(msgobj->visible_map, iter) {
PkObject *pkobj = iter->second;
if(pkobj->type == Object_Player)
{
Crole* pcr = reinterpret_cast<Crole*>(pkobj);
if (!pcr->player_control()) continue;
pcr->send_msg_ex(cmdid, 0, len, false);
}
}
if(msgobj->type == Object_Player) {
Crole* pcr = reinterpret_cast<Crole*>(msgobj);
if (!pcr->player_control()) return;
pcr->send_msg_ex(cmdid, 0, len, false);
}
}
void Scene::brocastMsgExceptMe(Object *role, int cmdid, int len)
{
/*
FOREACH(this->obj_map_, iter) {
PkObject *pkobj = (PkObject*)iter->second;
if(pkobj != role && pkobj->type == Object_Player)
{
Crole* pcr = reinterpret_cast<Crole*>(pkobj);
if (!pcr->player_control()) continue;
pkobj->send_msg_ex(cmdid, 0, len, false);
}
}
*/
FOREACH(this->role_map_, iter) {
Crole* pcr = reinterpret_cast<Crole*>(iter->second);
if (role == pcr) continue;
if (!pcr->player_control()) continue;
pcr->send_msg_ex(cmdid, 0, len, false );
}
}
void Scene::sendToViewAndCamp(Object* role, int cmdid, int len)
{
PkObject *msgobj = (PkObject *)role;
FOREACH(msgobj->visible_map, iter) {
PkObject *pkobj = iter->second;
if(pkobj->type == Object_Player) {
Crole* pcr = reinterpret_cast<Crole*>(pkobj);
if (!pcr->player_control()) continue;
pcr->send_msg_ex(cmdid, 0, len, false);
}
}
if (msgobj->type != Object_Player) return;
FOREACH(this->role_map_, iter) {
Crole* pcr = reinterpret_cast<Crole*>(iter->second);
if (pcr->camp != msgobj->camp) continue;
if (!pcr->player_control()) continue;
if (KEY_IN_MAP(msgobj->visible_map, pcr->id)) continue;
pcr->send_msg_ex(cmdid, 0, len, false );
}
}
/*
该函数不能在中间return,必须执行到 最后的clear 处
*/
void Scene::Update(Object *object)
{
static char self_info[102400] = {0};
int s_idx = sizeof(protocol_t);
static char other_info[102400] = {0};
int index = 0;
PkObject* my_pk = reinterpret_cast<PkObject*>(object);
// send ENTER msg
if (!enter_map_.empty()) {
if(debug_AOI) KDEBUG_LOG(object->id, "ENTER VIEW SIZE\[%lu]", enter_map_.size());
// send others to me
if (my_pk->type == Object_Player) {
Crole* prole = reinterpret_cast<Crole*>(my_pk);
if (prole->player_control()) {
int num = 0;
NpcEnterViewNotify npcEnterViewNotify;
FOREACH(enter_map_, eitr) {
PkObject* pp = reinterpret_cast<PkObject*>(eitr->second);
if (pp->subtype != NpcType_Tower && pp->subtype != NpcType_Base) {
num ++;
//o_idx = pp->pkg_enter_map_info(other_info, o_idx);
pp->pkg_enter_map_info(&npcEnterViewNotify);
}
}
if (num) {
index = npcEnterViewNotify.ByteSize();
npcEnterViewNotify.SerializeToArray(&other_info[s_idx], index);
prole->send_msg(other_info, noti_npc_enter_view_cmd, 0, index, false);
if(debug_AOI) KDEBUG_LOG(prole->id, "SEND TO ME ENTER SIZE\[%u]", num);
}
}
}
// send me to others
NpcEnterViewNotify npcEnterViewNotify2;
my_pk->pkg_enter_map_info(&npcEnterViewNotify2);
index = npcEnterViewNotify2.ByteSize();
npcEnterViewNotify2.SerializeToArray(&self_info[s_idx], index);
FOREACH(enter_map_, eiter) {
PkObject* po = reinterpret_cast<PkObject*>(eiter->second);
// ----
my_pk->insert_to_visible_map(po);
po->insert_to_visible_map(my_pk);
// ----
if(debug_AOI) KDEBUG_LOG(object->id, "VIEW OB\[%u x:%f y:%f]", eiter->first, po->_pos.x, po->_pos.y);
if (po->type == Object_Player) {
Crole* prole = reinterpret_cast<Crole*>(po);
if (prole->player_control()) {
prole->send_msg(self_info, noti_npc_enter_view_cmd, 0, index, false);
if(debug_AOI) KDEBUG_LOG(prole->id, "OB ENTER VIEW\[%u]",object->id);
}
}
}
}
ObjMap::iterator iter;
// send MOVE msg
if (this->cur_ms - my_pk->_last_sync_time > 300) {
if (!move_map_.empty()) {
NpcMoveNotify npcMoveNotify;
my_pk->pkg_move_info(&npcMoveNotify);
index = npcMoveNotify.ByteSize();
npcMoveNotify.SerializeToArray(&self_info[s_idx], index);
}
for (iter = move_map_.begin(); iter != move_map_.end(); ++iter) {
PkObject* po = reinterpret_cast<PkObject*>(iter->second);
if (po->type == Object_Player) {
Crole* prole = reinterpret_cast<Crole*>(po);
if (prole->player_control()) {
prole->send_msg(self_info, noti_npc_move_cmd, 0, index, false);
if(debug_AOI) KDEBUG_LOG(prole->id, "OB MOVE VIEW\[%u]", object->id);
}
}
// 受控状态下需要同步给客户端
if (my_pk->type == Object_Player) {
Crole* my_role = reinterpret_cast<Crole*>(my_pk);
if (my_role->player_control() && my_role->move_mode != e_client) {
my_role->send_msg(self_info, noti_npc_move_cmd, 0, index, false);
}
}
}
my_pk->_last_sync_time = this->cur_ms;
}
// send LEAVE msg
if (!leave_map_.empty()) {
// send me to others
int num = 0;
NpcLeaveViewNotify npcLeaveViewNotify;
my_pk->pkg_leave_map_info(&npcLeaveViewNotify);
index = npcLeaveViewNotify.ByteSize();
npcLeaveViewNotify.SerializeToArray(&self_info[s_idx], index);
FOREACH(leave_map_, liter) {
PkObject* po = reinterpret_cast<PkObject*>(liter->second);
//----
my_pk->erase_from_visible_map(po);
po->erase_from_visible_map(my_pk);
//----
if (po->type == Object_Player) {
Crole* prole = reinterpret_cast<Crole*>(po);
if (prole->player_control()) {
prole->send_msg(self_info, noti_npc_leave_view_cmd, 0, index, false);
if(debug_AOI) KDEBUG_LOG(prole->id, "OB LEAVE VIEW\[%u]", object->id);
}
}
}
// send others to me
if (object->type == Object_Player) {
Crole* prole = reinterpret_cast<Crole*>(object);
if (prole->player_control()) {
NpcLeaveViewNotify npcLeaveViewNotify1;
FOREACH(leave_map_, litr) {
PkObject* pp = reinterpret_cast<PkObject*>(litr->second);
if (pp->subtype != NpcType_Tower && pp->subtype != NpcType_Base) {
num ++;
pp->pkg_leave_map_info(&npcLeaveViewNotify1);
}
}
if (num) {
int index = npcLeaveViewNotify1.ByteSize();
npcLeaveViewNotify1.SerializeToArray(&other_info[s_idx], index);
prole->send_msg(other_info, noti_npc_leave_view_cmd, 0, index, false);
if(debug_AOI) KDEBUG_LOG(prole->id, "SEND TO ME LEAVE SIZE\[%u]", num);
}
}
}
}
// clean the move, leave, enter set
enter_map_.clear();
move_map_.clear();
leave_map_.clear();
}
void Scene::Add(Object* object)
{
int distance = DISTANCE;
if (obj_map_.find(object->id) != obj_map_.end()) {
ERROR_LOG("Object already in scene\t[O:%u S:%u]", object->id, this->sceneid);
return;
}
obj_map_[object->id] = object;
if (object->type == Object_Player)
role_map_[object->id] = object;
object->setScene(this);
ObjList::iterator iter, pos;
bool flag;
ObjMap x_set;
if(debug_AOI) KDEBUG_LOG(object->id, "ADD OBJ\t[x:%f y:%f]", object->_pos.x, object->_pos.y);
// iterator x-axis object list
flag = false;
for (iter = obj_x_list_.begin(); iter != obj_x_list_.end(); ++iter) {
if(debug_AOI) KDEBUG_LOG(object->id, "Search X list\t[id:%u type:%u x:%f y:%f]",
(*iter)->id, (*iter)->type, (*iter)->_pos.x, (*iter)->_pos.y);
int diff = (*iter)->_pos.x - object->_pos.x;
// into the x set
if (abs(diff) <= distance) {
x_set[(*iter)->id] = *iter;
if(debug_AOI) KDEBUG_LOG(object->id, "ADD X\t[id:%u]", (*iter)->id);
}
if (!flag && diff > 0) {
pos = iter;
flag = true;
}
if (diff > distance) {
break;
}
}
if (flag) {
obj_x_list_.insert(pos, object);
object->x_pos = --pos;
} else {
/* modify by james
obj_x_list_.push_front(object);
object->x_pos = obj_x_list_.begin();
*/
obj_x_list_.push_back(object);
iter = obj_x_list_.end();
object->x_pos = --iter;
}
// iterator y-axis object list
flag = false;
for (iter = obj_y_list_.begin(); iter != obj_y_list_.end(); ++iter) {
if(debug_AOI) KDEBUG_LOG(object->id, "Search Y list\t[id:%u type:%u x:%f y:%f]",
(*iter)->id, (*iter)->type, (*iter)->_pos.x, (*iter)->_pos.y);
int diff = (*iter)->_pos.y - object->_pos.y;
// into the enter set
if (abs(diff) <= distance && x_set.find((*iter)->id) != x_set.end()) {
enter_map_[(*iter)->id] = *iter;
if(debug_AOI) KDEBUG_LOG(object->id, "ADD Y/E\t[id:%u]", (*iter)->id);
}
if (!flag && diff > 0) {
pos = iter;
flag = true;
}
if (diff > distance) {
break;
}
}
if (flag) {
obj_y_list_.insert(pos, object);
object->y_pos = --pos;
} else {
/* modify by james
obj_y_list_.push_front(object);
object->y_pos = obj_y_list_.begin();
*/
obj_y_list_.push_back(object);
iter = obj_y_list_.end();
object->y_pos = --iter;
}
PkObject* pkobj = (PkObject*)object;
pkobj->noti_self_enter_scene();
Update(object);
}
void Scene::UpdateObjectPosition(Object *object, int x, int y) {
int old_x = object->_pos.x;
int old_y = object->_pos.y;
object->_pos.x = x;
object->_pos.y = y;
ObjList::iterator iter, pos;
// find the new x pos
if (x > old_x) {
if (object->x_pos != obj_x_list_.end()) {
iter = object->x_pos;
++iter;
obj_x_list_.erase(object->x_pos);
while (iter != obj_x_list_.end()) {
if (object->_pos.x - (*iter)->_pos.x < 0) {
pos = iter;
break;
}
++iter;
}
if (iter != obj_x_list_.end()) {
obj_x_list_.insert(pos, object);
object->x_pos = --pos; // add by james
} else {
obj_x_list_.push_back(object);
ObjList::iterator it = obj_x_list_.end(); // add by james
object->x_pos = --it; // add by james
}
}
} else if (x < old_x) {
if (object->x_pos != obj_x_list_.begin()) {
iter = object->x_pos;
--iter;
obj_x_list_.erase(object->x_pos);
/* modidy by james
while (iter != obj_x_list_.begin()) {
if (object->_pos.x - (*iter)->_pos.x > 0) {
pos = ++iter;
break;
}
--iter;
}
*/
while (1) {
if (object->_pos.x - (*iter)->_pos.x > 0) {
pos = ++iter;
break;
}
if (iter == obj_x_list_.begin()) {
break;
}
--iter;
}
if (iter != obj_x_list_.begin()) {
obj_x_list_.insert(pos, object);
object->x_pos = --pos; // add by james
//object->x_pos = obj_x_list_.insert(pos, object); // add by james
} else {
obj_x_list_.push_front(object);
object->x_pos = obj_x_list_.begin(); // add by james
}
}
}
// find the new y pos
if (y > old_y) {
if (object->y_pos != obj_y_list_.end()) {
iter = object->y_pos;
++iter;
obj_y_list_.erase(object->y_pos);
while (iter != obj_y_list_.end()) {
if (object->_pos.y - (*iter)->_pos.y < 0) {
pos = iter;
break;
}
++iter;
}
if (iter != obj_y_list_.end()) {
obj_y_list_.insert(pos, object);
object->y_pos = --pos; // add by james
} else {
obj_y_list_.push_back(object);
ObjList::iterator it = obj_y_list_.end(); // add by james
object->y_pos = --it; // add by james
}
}
} else if (y < old_y) {
if (object->y_pos != obj_y_list_.begin()) {
iter = object->y_pos;
--iter;
obj_y_list_.erase(object->y_pos);
/* modify by james
while (iter != obj_y_list_.begin()) {
if (object->_pos.y - (*iter)->_pos.y > 0) {
pos = ++iter;
break;
}
--iter;
}
*/
while (1) {
if (object->_pos.y - (*iter)->_pos.y > 0) {
pos = ++iter;
break;
}
if (iter == obj_y_list_.begin()) {
break;
}
--iter;
}
if (iter != obj_y_list_.begin()) {
obj_y_list_.insert(pos, object);
object->y_pos = --pos; // add by james
//object->y_pos = obj_y_list_.insert(pos, object); // add by james
} else {
obj_y_list_.push_front(object);
object->y_pos = obj_y_list_.begin(); // add by james
}
}
}
}
void Scene::print_objs()
{
FOREACH(obj_map_, itr) {
Object* po = itr->second;
if (debug_AOI) DEBUG_LOG("[id:%u x:%f y:%f]", po->id, po->_pos.x, po->_pos.y);
}
}
void Scene::Move(int id, int x, int y)
{
if (x <= 0 || y <= 0)
KDEBUG_LOG(id, "ERRORPOS\t[(%d,%d)]", x, y);
ObjMap::iterator obj_iter = obj_map_.find(id);
if (obj_iter == obj_map_.end()) {
return;
}
Object *object = obj_iter->second;
ObjList::iterator list_iter;
ObjMap old_set, new_set;
old_set.clear();
new_set.clear();
//print_objs();
// get the old set
GetRangeSet(object, &old_set);
if (debug_AOI) KDEBUG_LOG(id, "old set\t[%lu x:%f y:%f]", old_set.size(),
object->_pos.x, object->_pos.y);
// update object position
UpdateObjectPosition(object, x, y);
//print_objs();
// get the new set
GetRangeSet(object, &new_set);
if (debug_AOI) KDEBUG_LOG(id, "new set\t[%lu x:%f y:%f]", new_set.size(),
object->_pos.x, object->_pos.y);
// move_set = old_set MIX new_set
ObjMap::iterator iter;
for (iter = old_set.begin(); iter != old_set.end(); ++iter) {
if (new_set.find(iter->first) != new_set.end()) {
move_map_[iter->first] = iter->second;
}
}
if (debug_AOI) KDEBUG_LOG(id, "move set\t[%lu x:%f y:%f]", move_map_.size(),
object->_pos.x, object->_pos.y);
// leave_set = old_set SUB move_set
for (iter = old_set.begin(); iter != old_set.end(); ++iter) {
if (move_map_.find(iter->first) == move_map_.end()) {
leave_map_[iter->first] = iter->second;
}
}
// enter_set = new_set SUB move_set
for (iter = new_set.begin(); iter != new_set.end(); ++iter) {
if (move_map_.find(iter->first) == move_map_.end()) {
enter_map_[iter->first] = iter->second;
}
}
Update(object);
}
//插入目标对象
void Scene::insertTargetObj(Object *obj, Object *iter,int release, Vector2 &pos,int r,int camp,std::set<Object*> &targetList)
{
if(obj != iter)
{
if(obj->type == Object_Player || obj->type == Object_NPC)
{
PkObject *pkobj = (PkObject *)(iter);
if(camp)
{
if(pkobj->getCamp() == obj->getCamp() && pkobj->getState() == SceneObject_Normal)
{
if(release)
{
// Vector2 tmpPos = pos - pkobj->_pos;
// float sqrtDist = r - tmpPos.sqrtMagnitude();
// if(sqrtDist > 0.0001)
// {
targetList.insert(iter);
// KDEBUG_LOG(pkobj->id, "搜索到目标对象【%.2f,%.2f】", pkobj->_pos.x, pkobj->_pos.y);
// }
}else
{
targetList.insert(iter);
}
}
}else
{
if(pkobj->getCamp() != obj->getCamp() && pkobj->getState() == SceneObject_Normal)
{
if(release)
{
// Vector2 tmpPos = pos - pkobj->_pos;
// float sqrtDist = r - tmpPos.sqrtMagnitude();
// if(sqrtDist > 0.0001)
// {
targetList.insert(iter);
// KDEBUG_LOG(pkobj->id, "释放者[%.2f, %.2f] 坐标 [%.2f, %.2f]搜索到目标对象【%.2f,%.2f】",obj->_pos.x, obj->_pos.y,pos.x, pos.y , pkobj->_pos.x, pkobj->_pos.y);
// }
}else
{
targetList.insert(iter);
}
}
}
}
}
}
void Scene::getAITargetList(PkObject *obj,int tarType ,Vector2 &pos, uint32_t r, std::set<PkObject *> &targetList){
PkObject *castobj = (PkObject *)obj;
for(map<uint32_t, PkObject*>::iterator objIter = castobj->visible_map.begin(); objIter != castobj->visible_map.end(); objIter++){
PkObject *tarObj = NULL;
if(tarType){
tarObj = (PkObject *)(objIter->second);
// 如果目标是敌方,不需要包括自己和友方
if(0 == tarObj->camp || tarObj->getState() != SceneObject_Normal){
continue;
}
}
stCircleParam param;
param.pCircle1Pos = pos;
param.fCircle1Radius = r;
param.fCircle2Radius = tarObj->getActorConfig()->modelRadius;
param.pCircle2Pos = tarObj->_pos;
bool ret = intersactCircle(¶m);
HDEBUG_LOG("htest near getAITargetList circle1[%.2f, %.2f, r=%d] circle2[%.2f, %.2f], [ret=%d]",
param.pCircle1Pos.x, param.pCircle1Pos.y, r, param.pCircle2Pos.x, param.pCircle2Pos.y, ret);
if(ret){
targetList.insert(tarObj);
}
}
DEBUG_LOG("getAITargetList size :=%ld",targetList.size());
}
void Scene::getVisibleTargetList(Object *object, const SkillConfig *base, Vector2 &pos, uint32_t camp, std::set<Object *> &targetList)
{
Vector2 spos;
ObjMap x_set;
ObjList::iterator iter;
if(camp){
targetList.insert(object);
}
if(base->release){
spos = pos;
}else{
spos = object->_pos;
}
PkObject *castobj = (PkObject *)object;
for(map<uint32_t, PkObject*>::iterator objIter = castobj->visible_map.begin(); objIter != castobj->visible_map.end(); objIter++){
PkObject *tarObj = (PkObject *)(objIter->second);
switch(base->range){
case eSkillcircle:{
stCircleParam param;
param.pCircle1Pos = spos;
param.fCircle1Radius = base->radius;
param.fCircle2Radius = 0;
param.pCircle2Pos = tarObj->_pos;
bool ret = intersactCircle(¶m);
if(ret) {
insertTargetObj(castobj, tarObj, base->release, spos, base->radius, camp, targetList);
}
}
break;
case eSkillSector:{
stSectorParam param;
param.pSectorPos = spos ;
param.fSectorRotation = tarObj->direct;
param.fSectorAngle = base->angle;
param.fSectorRadius = base->radius;
param.pCirclePos = tarObj->_pos;
param.fCircleRadius = 0.0;
bool bret = intersactArc(¶m);
if (bret) {
insertTargetObj(castobj, tarObj, base->release, spos, base->radius, camp, targetList);
}
}
break;
case eSkillLine:{
#if 0
stRectParam param;
param.pSectorPos = castobj->_pos;
Vector2 rotation = castobj->_pos - tarObj->_pos;
param.fSectorAngle = angleWithYaxis(rotation);
param.fRectWidth = base->height;
param.fRectLength = base->width;
param.pCirclePos = tarObj->_pos;
param.fCircleRadius = 0;
bool ret = intersactRectangle(¶m);
if(ret) {
insertTargetObj(castobj, tarObj, base->release, spos, base->radius, camp, targetList);
}
#endif
#if 0
struct stMidRectParam
{
double fRectWidth;
double fRectLength;
Vector2 pMidPos; //中心点位置
Vector2 pFacePos; //面向
Vector2 pCirclePos; //判断目标点
double fCircleRadius;
};
#endif
stMidRectParam param;
param.fRectWidth = base->width;
param.fRectLength = base->length;
param.pMidPos = castobj->_pos;
param.pFacePos = castobj->direct;
param.pCirclePos = tarObj->_pos;
param.fCircleRadius = 0;
DEBUG_LOG("搜索矩形区域 %d %d Mid[%.2f,%.2f], Face[%.2f,%.2f], Circle[%.2f,%.2f]",base->width,\
base->length, \
castobj->_pos.x, \
castobj->_pos.y, \
castobj->direct.x, \
castobj->direct.y, \
tarObj->_pos.x, \
tarObj->_pos.y);
bool ret = intersactMidRectangle(¶m);
if(ret){
insertTargetObj(castobj, tarObj, base->release, spos, base->radius, camp, targetList);
}
}
break;
case eRectangle:{
#if 0
stRectParam param;
param.pSectorPos = spos;
param.fSectorAngle = angleWithYaxis(castobj->direct);
param.fRectWidth = base->width;
param.fRectLength = base->height;
param.pCirclePos = tarObj->_pos;
param.fCircleRadius = 0;
bool ret = intersactRectangle(¶m);
if(ret){
insertTargetObj(castobj, tarObj, base->release, spos, base->radius, camp, targetList);
}
#endif
if(fabs(castobj->_pos.x - tarObj->_pos.x) < base->length && fabs(castobj->_pos.y - tarObj->_pos.y) < base->width){
insertTargetObj(castobj, tarObj, base->release, spos, base->radius, camp, targetList);
}
}
break;
default:
{
}
}
}
}
void Scene::getNearObjList(Object *object, std::set<Object *> &obj_list, float distance)
{
ObjMap x_set;
ObjList::iterator iter;
Vector2 spos;
// iterator x-axis object list, x轴是从小到大顺序排列的
if (object->x_pos != obj_x_list_.end()) {
iter = object->x_pos;
while (1) {
++iter;
if (iter == obj_x_list_.end()) break;
if ((*iter)->_pos.x - object->_pos.x > distance) break;
float sqrtDist = (object->_pos - (*iter)->_pos).sqrtMagnitude();
if (sqrtDist < distance) obj_list.insert(*iter);
}
}
if (object->x_pos != obj_x_list_.begin())
{
iter = object->x_pos;
while (1)
{
--iter;
if (object->_pos.x - (*iter)->_pos.x > distance) break;
float sqrtDist = (object->_pos - (*iter)->_pos).sqrtMagnitude();
if (sqrtDist < distance) obj_list.insert(*iter);
if (iter == obj_x_list_.begin())break;
}
}
}
void Scene::GetRangeSet(Object *object, ObjMap *set) {
ObjMap x_set;
ObjList::iterator iter;
int distance = DISTANCE;
// iterator x-axis object list
if (object->x_pos != obj_x_list_.end()) {
iter = object->x_pos;
while (1) {
++iter;
if (iter == obj_x_list_.end()) {
break;
}
if (debug_AOI)
KDEBUG_LOG(object->id, "next-x: id-%u x-%f y-%f", (*iter)->id, (*iter)->_pos.x, (*iter)->_pos.y);
//if (object->_pos.x - (*iter)->_pos.x > distance) {
if ((*iter)->_pos.x - object->_pos.x > distance) {
break;
}
x_set[(*iter)->id] = *iter;
}
}
if (object->x_pos != obj_x_list_.begin()) {
iter = object->x_pos;
while (1) {
--iter;
if (debug_AOI)
KDEBUG_LOG(object->id, "pre-x: id-%u x-%f y-%f", (*iter)->id, (*iter)->_pos.x, (*iter)->_pos.y);
//if ((*iter)->_pos.x - object->_pos.x > distance) {
if (object->_pos.x - (*iter)->_pos.x > distance) {
break;
}
x_set[(*iter)->id] = *iter;
if (iter == obj_x_list_.begin()) {
break;
}
}
}
// iterator y-axis object list
if (object->y_pos != obj_y_list_.end()) {
iter = object->y_pos;
while (1) {
++iter;
if (iter == obj_y_list_.end()) {
break;
}
if (debug_AOI)
KDEBUG_LOG(object->id, "next-y: id-%u x-%f y-%f",(*iter)->id, (*iter)->_pos.x, (*iter)->_pos.y);
//if (object->_pos.y - (*iter)->_pos.y > distance) {
if ((*iter)->_pos.y - object->_pos.y > distance) {
break;
}
if (x_set.find((*iter)->id) != x_set.end()) {
(*set)[(*iter)->id] = *iter;
}
}
}
if (object->y_pos != obj_y_list_.begin()) {
iter = object->y_pos;
while (1) {
--iter;
if (debug_AOI)
KDEBUG_LOG(object->id, "pre-y: id-%u x-%f y-%f", (*iter)->id, (*iter)->_pos.x, (*iter)->_pos.y);
// modify by james
//if ((*iter)->_pos.y - object->_pos.y > distance) {
if ( object->_pos.y - (*iter)->_pos.y > distance) {
break;
}
if (x_set.find((*iter)->id) != x_set.end()) {
(*set)[(*iter)->id] = *iter;
}
if (iter == obj_y_list_.begin()) {
break;
}
}
}
}
void Scene::Leave(int id) {
ObjMap::iterator obj_iter = obj_map_.find(id);
if (obj_iter == obj_map_.end()) {
return;
}
Object *object = obj_iter->second;
//obj_map_.erase(object->id);
object->setScene(NULL);
DEBUG_LOG("Scene::Leave[id=%d ]", id);
// get the leave set
GetRangeSet(object, &leave_map_);
Update(object);
obj_x_list_.erase(object->x_pos);
obj_y_list_.erase(object->y_pos);
obj_map_.erase(object->id);
if (object->type == Object_Player)
role_map_.erase(object->id);
}
int Scene::pkg_all_base_tower()
{
AllBaseInfoNotify allBaseInfoNotify;
FOREACH(this->obj_map_, iter) {
Object *temp = iter->second;
if(temp->type == Object_NPC
&& ( temp->subtype == NpcType_Shop
|| temp->subtype == NpcType_Base
|| temp->subtype == NpcType_Tower
)) {
Npc* pk = (Npc*)temp;
npc_base_t* pnb = dynamic_cast<npc_base_t*>(pk->getActorConfig());
if (!pnb) continue;
AllBaseInfoNotify_Building* building = allBaseInfoNotify.add_building();
building->set_id(pk->id);
building->set_type(pk->subtype);
building->set_npcid(pk->npcID);
building->set_camp(pk->camp);
building->set_x(pk->_pos.x);
building->set_y(pk->_pos.y);
building->set_dirx(pk->direct.x);
building->set_diry(pk->direct.y);
building->set_hp(pk->propAbility[ehp]);
building->set_hpmax(pk->propAbility[emax_hp]);
building->set_level(pnb->level);
HDEBUG_LOG("htest base BASE TOWER INFO[id=%u npcID:%u subtype:%u x=%f, y=%f]",
pk->id, pk->npcID, pk->subtype, pk->_pos.x, pk->_pos.y);
DEBUG_LOG("BASE TOWER INFO[id=%u npcID:%u subtype:%u x=%f, y=%f]",
pk->id, pk->npcID, pk->subtype, pk->_pos.x, pk->_pos.y);
}
}
int i = allBaseInfoNotify.ByteSize();
allBaseInfoNotify.SerializeToArray(SEND_CLI_BUF, i);
return i;
}
Vector2 Scene::getFhd(int camp)
{
Vector2 pos(0,0);
FOREACH(pConfig->m_fhd_map, itr) {
if (itr->second.camp == camp)
return itr->second.pos;
}
return pos;
}
本文介绍了一种基于AOI(Area of Interest)算法的场景管理实现方式,详细阐述了如何利用十字链表进行对象管理和更新,以及如何通过场景划分提高效率。文中还涉及了怪物刷新、角色进入和离开场景等具体实现细节。
4423

被折叠的 条评论
为什么被折叠?



