基于格子的AOI 实现

看了这位大哥基于格子的aoi C++实现 https://www.h5cowboy.com/cpp/aoi-9grid.html 照代码抄了一份。

稍稍修改了下.

1、把 obj 分为 普通moveobj,和 witness。 witness 检测 moveobj aoi 消息。 moveobj之间,witness之间不产生aoi 消息。

2、obj 移动跨格子时,算 leave,enter 集合的方式感觉太麻烦。修改了下,使用矩形框相交的概念,算leave,enter集合。

3、moveobj leave witness 时,不马上 leave,先暂时进入一个 weakness 集合,等 moveobj 再 偏离一个格子后才leave。如果 这时候 moveobj 重新进入witness的九宫格,不再发生enter消息。moveobj 有个 shift标记,被标记的 不参与 前面的leave处理.


顺便写了个程序验证:

moveobj:100

witness:1

grid width:120


引用似乎正常.


运动时 weakness 引用 似乎也正常(红线引用).

moveobj:5000

witness:2000

grid:120

ms:10-20



moveobj:5000

witness:5000

grid:120

蹦了 ms 1000-2000


moveobj:5000

witness:2000

grid:120

witness 速度调大3倍。



又蹦了 -_-!!!!

去掉 那个 weakness 处理:


ms 0.1-1 抓狂


moveobj:5000

shiftobj:5000

witness:5000

moveobj 速度:3000

witness 速度:3000

grid width:300


ms 1-2

moveobj:5000

shiftobj:5000

witness:5000

moveobj 速度:3000

witness 速度:3000

grid width:120

ms 6-8.

好吧 我还是去掉 那个 weakness 集合吧难过难过难过难过难过难过

aoi方面的源代码和 那位大哥的基本差不多我就贴贴测试代码吧。


源码:

space_test_view.h:

class SpaceTestNode : public grid_space::CoordinateNode
{
public:
  float rotation = 0.0f;
  jdot_vec2f pos;
};

class SpaceTestWitness : public grid_space::CoordinateWitness
{
public:
  float rotation = 0.0f;
  jdot_vec2f pos;
};

class SpaceTestView : public jdot::View
{
  typedef jdot::View Super;
  JDOT_CLASS_BEGIN(SpaceTestView, Super)
    JDOT_B_PROPERTY("tick", UseTick);
    JDOT_B_PROPERTY("shift_count", ShiftNodeCount);
    JDOT_B_PROPERTY("node_count", NodeCount);
    JDOT_B_PROPERTY("witness_count", WitnessCount);
    JDOT_B_PROPERTY("node_vel", NodeVel);
    JDOT_B_PROPERTY("witness_vel", WitnessVel);
    JDOT_B_PROPERTY("grid_extent", GridExtent);
    JDOT_B_PROPERTY("view_count", ViewCount);
    JDOT_B_PROPERTY("weakness", EnableWeakness);
  JDOT_CLASS_END();

public:
  SpaceTestView();

public:
  void OnPaint(jdot::Painter&painter, float delta)override;
  void OnAnimate(float delta)override;

  void UpdateNodes(float delta);
  void UpdateWitnesses(float delta);
  void UpdateSpace(float delta);
  void PaintSpace(jdot::Painter& painter, float delta);

  bool OnNotify(jdot::View*sender, jdot::NtfArgs&args)override;

public:
  JDOT_BINDABLE_VAR_WRAP(float, UseTick) { 0 };
  JDOT_BINDABLE_VAR_WRAP_CHD(int, NodeCount,OnCountChanged) { 10 };
  JDOT_BINDABLE_VAR_WRAP_CHD(int, ShiftNodeCount, OnCountChanged) { 10 };
  JDOT_BINDABLE_VAR_WRAP_CHD(int, WitnessCount, OnCountChanged) { 10 };
  JDOT_BINDABLE_VAR_WRAP(int, NodeVel) { 100 };
  JDOT_BINDABLE_VAR_WRAP(int, WitnessVel) { 100 };
  JDOT_BINDABLE_VAR_WRAP(int, ViewCount) { 3 };
  JDOT_BINDABLE_VAR_WRAP_CHD(int, GridExtent, OnGridExtentChanged) { 60 };
  JDOT_BINDABLE_VAR_WRAP(bool, EnableWeakness) { true };

private:
  bool OnCountChanged(int oldVal, bool from_bind) { __modify(); return true; }
  bool OnGridExtentChanged(int oldVal, bool from_bind);

private:
  void __reset();
  void __clear();
  void __modify();

private:
  jdot::scene::SceneView m_sceneView;//painter for terrain.
  jdot::Painter m_painter;// terrain painter.

private:
  grid_space::CoordinateSpace m_space;

  jdot_vector< jdot_ptr<SpaceTestNode> > m_nodes;
  jdot_vector< jdot_ptr<SpaceTestNode> > m_shiftNodes;
  jdot_vector< jdot_ptr<SpaceTestWitness> > m_witnesses;

};

space_test_view.cpp:

SpaceTestView::SpaceTestView()
{
  m_painter.SetSceneView(&m_sceneView);

  __reset();
}

void SpaceTestView::OnAnimate(float delta)
{
  //++
  auto tnow = std::chrono::high_resolution_clock::now();

  UpdateNodes(delta);
  UpdateWitnesses(delta);
  UpdateSpace(delta);

  auto use = std::chrono::high_resolution_clock::now() - tnow;
  auto count = std::chrono::duration_cast<std::chrono::microseconds>(use).count();

  float fcount = count*0.001f;
  UseTick = fcount;

}

void SpaceTestView::OnPaint(jdot::Painter&painter, float delta)
{
  //++
  PaintSpace(painter, delta);
  //++
  m_sceneView.DrawScene();
}

void SpaceTestView::UpdateNodes(float delta)
{
  for (size_t i = 0; i < m_nodes.size(); ++i) {
    auto*obj = m_nodes.at(i).get();
    float rot = obj->rotation;
    rot += math::rand_minus1_1()*delta*1000.0f;
    obj->rotation = rot;
    float p_rot = math::radians(rot);

    auto pos = obj->pos + jdot_vec2f(cosf(p_rot), sinf(p_rot))*delta*NodeVel.GetValue();

    auto sz = jdot::GetRenderer()->GetWinSize();

    if (pos.x > sz.width) {
      pos.x = 0;
    }
    if (pos.y > sz.height) {
      pos.y = 0;
    }
    if (pos.x < 0) {
      pos.x = sz.width - 1.0f;
    }
    if (pos.y < 0) {
      pos.y = sz.height - 1.0f;
    }
    obj->pos = pos;
    //++
    obj->SetXY(pos.x, pos.y);
  }
}

void SpaceTestView::UpdateWitnesses(float delta)
{
  for (size_t i = 0; i < m_witnesses.size(); ++i) {
    auto*obj = m_witnesses.at(i).get();
    float rot = obj->rotation;
    rot += math::rand_minus1_1()*delta*1000.0f;
    obj->rotation = rot;
    float p_rot = math::radians(rot);

    auto pos = obj->pos + jdot_vec2f(cosf(p_rot), sinf(p_rot))*delta*WitnessVel.GetValue();

    auto sz = jdot::GetRenderer()->GetWinSize();

    if (pos.x > sz.width) {
      pos.x = 0;
    }
    if (pos.y > sz.height) {
      pos.y = 0;
    }
    if (pos.x < 0) {
      pos.x = sz.width - 1.0f;
    }
    if (pos.y < 0) {
      pos.y = sz.height - 1.0f;
    }
    obj->pos = pos;
    //++
    obj->SetXY(pos.x, pos.y);
  }
}

void SpaceTestView::UpdateSpace(float delta)
{
  for (size_t i = 0; i < m_nodes.size(); ++i) {
    m_space.Move(m_nodes.at(i).get());
  }
  for (size_t i = 0; i < m_witnesses.size(); ++i) {
    m_space.Move(m_witnesses.at(i).get());
  }
}

void SpaceTestView::PaintSpace(jdot::Painter& painter, float delta)
{
  auto&grids = m_space.GetGrids();
  int gridExtent = m_space.GetGridExtent();
  auto sz = jdot::GetRenderer()->GetWinSize();

  for (auto&kv : grids) {
    auto grid = kv.second;
    jdot_rectf rc;
    rc.set_xy(grid->GetGX()*gridExtent, grid->GetGY()*gridExtent);
    rc.set_size(jdot_vec2f(gridExtent, gridExtent));

    if (grid->GetWitnessesCount() <= 0) {
      painter.FillRect(nullptr, rc, 0xafE0FFFF);
    }
    else {
      painter.FillRect(nullptr, rc, 0xafB3EE3A);

    }
  }

  for (size_t i = 0; i < m_nodes.size(); ++i) {
    auto obj = m_nodes[i];
    jdot_rectf rc;
    rc.set_xy(obj->GetX(), obj->GetY());
    rc.set_size(jdot_vec2f(4.0f, 4.0f));
    painter.FillRect(nullptr, rc, jdot_color::Wheat);
  }

  for (size_t i = 0; i < m_shiftNodes.size(); ++i) {
    auto obj = m_shiftNodes[i];
    jdot_rectf rc;
    rc.set_xy(obj->GetX(), obj->GetY());
    rc.set_size(jdot_vec2f(4.0f, 4.0f));
    painter.FillRect(nullptr, rc, jdot_color::LightGray);
  }

  for (size_t i = 0; i < m_witnesses.size(); ++i) {
    auto obj = m_witnesses[i];
    jdot_rectf rc;
    rc.set_xy(obj->GetX(), obj->GetY());
    rc.set_size(jdot_vec2f(4.0f, 4.0f));
    painter.FillRect(nullptr, rc, jdot_color::Red);
    //++
    if ((int)i >= ViewCount) {
      continue;
    }

    auto gx = obj->GetX() / m_space.GetGridExtent();
    auto gy = obj->GetY() / m_space.GetGridExtent();
    JDOT_GRID_SPACE_FOR_9_GRID(gx, gy, m_space, {
      for (int i = 0; i < grid->GetNodesCount(); ++i) {
        auto*node = grid->GetNodeAt(i);
        if (node->IsShift()) {
          painter.DrawLine(obj->pos, jdot_vec2f(node->GetX(), node->GetY()), 0xafFFF0F5);
        }
        else {
          painter.DrawLine(obj->pos, jdot_vec2f(node->GetX(), node->GetY()), 0xafFFF68F);
        }
      }
    });

    auto&wns = obj->GetWeaknessNodes();
    for (auto&w : wns) {
      painter.DrawLine(obj->pos, jdot_vec2f(w->GetX(), w->GetY()), jdot_color::LightPink);
    }
  }
  ///+++++
  for (int i = 0; i < sz.width; i += gridExtent) {
    painter.DrawLine(jdot_vec2f(i, 0), jdot_vec2f(i, sz.height), jdot_color::DimGray);
  }
  for (int i = 0; i < sz.height; i += gridExtent) {
    painter.DrawLine(jdot_vec2f(0, i), jdot_vec2f(sz.width, i), jdot_color::DimGray);
  }

}

bool SpaceTestView::OnNotify(jdot::View*sender, jdot::NtfArgs&args)
{
  if (sender->Command == "!space_reset") {
    __reset();
  }
  return false;
}

void SpaceTestView::__reset()
{
  __clear();

  __modify();
}

void SpaceTestView::__clear()
{
  for (auto&n : m_nodes) {
    m_space.Leave(n);
  }
  for (auto&o : m_shiftNodes) {
    m_space.Leave(o);
  }
  for (auto&o : m_witnesses) {
    m_space.Leave(o);
  }
  m_nodes.clear();
  m_shiftNodes.clear();
  m_witnesses.clear();
}

void SpaceTestView::__modify()
{
  auto sz = jdot::GetRenderer()->GetWinSize();
  //++
  while ( (int)m_nodes.size() > NodeCount) {
    m_space.Leave(m_nodes.back());
    m_nodes.pop_back();
  }
  while ((int)m_nodes.size() < NodeCount) {
    auto obj = jdot::make_intrusive<SpaceTestNode>();
    obj->pos.set(math::random(0.0f,sz.x), math::random(0.0f,sz.y));
    obj->SetXY(obj->pos.x, obj->pos.y);
    m_nodes.emplace_back(obj);
    m_space.Join(obj);
  }

  //++
  while ((int)m_shiftNodes.size() > ShiftNodeCount) {
    m_space.Leave(m_shiftNodes.back());
    m_shiftNodes.pop_back();
  }
  while ((int)m_shiftNodes.size() < ShiftNodeCount) {
    auto obj = jdot::make_intrusive<SpaceTestNode>();
    obj->SetShift(true);
    obj->pos.set(math::random(0.0f, sz.x), math::random(0.0f, sz.y));
    obj->SetXY(obj->pos.x, obj->pos.y);
    m_shiftNodes.emplace_back(obj);
    m_space.Join(obj);
  }
  //++
  while ((int)m_witnesses.size() > WitnessCount) {
    m_space.Leave(m_witnesses.back());
    m_witnesses.pop_back();
  }
  while ((int)m_witnesses.size() < WitnessCount) {
    auto obj = jdot::make_intrusive<SpaceTestWitness>();
    obj->pos.set(math::random(0.0f, sz.x), math::random(0.0f, sz.y));
    obj->SetXY(obj->pos.x, obj->pos.y);
    obj->EnableWeakness(EnableWeakness);
    m_witnesses.emplace_back(obj);
    m_space.Join(obj);
  }
}

bool SpaceTestView::OnGridExtentChanged(int oldVal, bool from_bind)
{
  if (from_bind && GridExtent>0) {
    __clear();
    m_space.SetGridExtent(GridExtent);
    __modify();
  }
  return true;
}

ui:

<?xml version="1.0" encoding="utf-8"?>
<root jdot:theme="editor"  xmlns:ui="placeholder" xmlns:jdot="placeholder">
  <SpaceTestView name="*" valign="stretch" halign="stretch">
    <V pass_pick="true">
      <Button text="返回选择界面" command="!change_state" command_args="menu"/>
      <Button text="重新开始" command="!space_reset"/>
      <View w="600">
        <Slider name="#node-count" halign="stretch" valign="center" range="5000" value="@{*.node_count:both}"/>
        <H halign="center" valign="center">
          <Label text="node count:"/>
          <TextField text="@{#node-count.value:both}" halign="center" valign="center" />
        </H>
      </View>
      <View w="600">
        <Slider name="#shift-count" halign="stretch" valign="center" range="5000" value="@{*.shift_count:both}"/>
        <H halign="center" valign="center">
          <Label text="shift count:"/>
          <TextField text="@{#shift-count.value:both}" halign="center" valign="center" />
        </H>
      </View>
      <View w="600">
        <Slider name="#witness-count" halign="stretch" valign="center" range="5000" value="@{*.witness_count:both}"/>
        <H halign="center" valign="center">
          <Label text="witness count:"/>
          <TextField text="@{#witness-count.value:both}" halign="center" valign="center" />
        </H>
      </View>
      <View w="600">
        <Slider name="#witness-vel" halign="stretch" valign="center" range="3000" value="@{*.witness_vel:both}"/>
        <H halign="center" valign="center">
          <Label text="witness vel:"/>
          <TextField text="@{#witness-vel.value:both}" halign="center" valign="center" />
        </H>
      </View>
      <View w="600">
        <Slider name="#node-vel" halign="stretch" valign="center" range="3000" value="@{*.node_vel:both}"/>
        <H halign="center" valign="center">
          <Label text="node vel:"/>
          <TextField text="@{#node-vel.value:both}" halign="center" valign="center" />
        </H>
      </View>
      <View w="600">
        <Slider name="#grid-extent" halign="stretch" valign="center" range="3000" value="@{*.grid_extent:both}"/>
        <H halign="center" valign="center">
          <Label text="grid extent:"/>
          <TextField text="@{#grid-extent.value:both}" halign="center" valign="center" />
        </H>
      </View>
      <View w="600">
        <Slider name="#view_count" halign="stretch" valign="center" range="3000" value="@{*.view_count:both}"/>
        <H halign="center" valign="center">
          <Label text="view count:"/>
          <TextField text="@{#view_count.value:both}" halign="center" valign="center" />
        </H>
      </View>
      <CheckButton text="weakness" checked="@{*.weakness:both}"/>
      <H w="100">
        <Label text="cost time(ms):"/>
        <Label text="@{*.tick}" halign="right"/>
      </H>
    </V>
  </SpaceTestView>
</root>

Unity中实现AOI(Area of Interest,兴趣区域)九宫格算法的方法如下: 首先,创建一个AOIManager类,用于管理和维护AOI区域和对象。该类需要包含以下几个关键组成部分: 1. AOI格子的定义:定义一个格子的大小,并将整个场景划分为多个格子。可以使用二维数组来表示格子,每个元素存储在该格子中的对象列表。 2. 对象的定义:定义一个对象的结构,包括对象的唯一标识ID和对象的位置信息。 3. 对象的管理:AOIManager类需要负责管理所有对象的位置和状态。当一个对象进入或离开一个格子时,需要更新该对象在AOIManager中的位置信息。 4. AOI九宫格算法的实现:在AOIManager中实现九宫格算法,根据一个对象的位置信息,计算出该对象所在的格子以及该对象周围的格子。 5. 状态更新:当一个对象的位置发生变化时,需要通过九宫格算法计算该对象应该进入或离开的格子,并更新该对象的位置信息。 6. 感知范围:根据九宫格算法计算出的周围格子,可以获取到该对象周围的其他对象。可以根据需要定义对象的感知范围,只获取特定范围内的对象。 总结一下,Unity实现AOI九宫格算法需要创建AOIManager类,实现格子划分和对象管理,以及实现九宫格算法来计算对象所在的格子和对象的感知范围。这种方法可以提高游戏中大量对象的更新效率,减少不必要的计算和通信开销,提升游戏性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值