看了这位大哥基于格子的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>