我正在努力将植绒算法实施到更大的系统中。 OGRE用于渲染,luabind用于能够从LUA,yatta yatta进行通信,这些东西不应该太重要。
我基本上按雷诺'boids-model实现了算法。这意味着,一个Boid(如“群中的一条鱼”)根据它在某个半径内的邻居移动。事情就是这样,它的基本复杂性是O(n²),因为如果它们在范围内,那么每个boid必须检查所有它们的flockmates,然后考虑一些因素来计算自己的运动。
算法本身已实现并可顺利运行。它接受所有不同尺寸的模型,它可以在2D和3D空间中工作,它可以很好地等等,我已经有一段时间了。
我的问题是,一旦我在boid数字中遇到一种“障碍”,算法就会在运行时崩溃,大约200-250甚至变化。现在,如果它变得越来越慢,直到我被打破到1 fps,我就能理解,算法的简单性能就是问题所在。但是,例如,199 boids完美运行,201根本不工作并且在运行时崩溃。这对我来说非常令人惊讶。
我实现了2个类:“Swarm”和“Boid”。 Swarm用于保存指向swarm的所有boid的指针但计算不多,在Boid中发生移动。
swarm.h:
#ifndef __SWARM_H__
#define __SWARM_H__
#include <vector>
#include "boid.h"
namespace Core {
class swarm {
public:
swarm();
~swarm();
bool move(float timeSinceLastFrame);
bool addBoid(boid *thisBoid);
bool removeBoidByName(std::string boidName);
bool writeAllBoidNames();
std::vector<boid*> getFlockMates();
private:
std::vector<boid*> flock;
float timePassed;
};
}
#endif
boid.h:
#ifndef __BOID_H__
#define __BOID_H__
#include "Ogre.h"
#include <vector>
#include <stdlib.h>
namespace Core {
class swarm;
class boid {
public:
boid(Ogre::SceneNode *thisNode, Ogre::String orientation, swarm *thisSwarm); // Constructor - direkter Node-Zugriff
boid(Ogre::MovableObject *thisObject, Ogre::String orientation, swarm *thisSwarm); // Constructor - Node-Zugriff über das Objekt
~boid(); // Destructor
Ogre::Vector3 getBoidPosition(); // gibt die derzeitige Position des Boids zurück - nötig für Cohesion und Separation
Ogre::Vector3 getBoidVelocity(); // gibt die derzeitige Geschwindigkeit und Richtung des Boids zurück - nötig für Alignement
std::string getBoidName(); // gibt den Namen eines Boids zurück
bool move(float timeSinceLastFrame); // bewegt das Boid
private:
swarm *flockMates; // pointer auf den Schwarm
float boidSize; // die Größe des Boids
Ogre::Vector3 boidOrientation; // Grundlegende Orientierung des Meshes eines Boids
Ogre::SceneNode *boidNode; // Pointer auf die SceneNode des Boids - das Objekt, das tatsächlich bewegt wird
Ogre::Vector3 velocity; // derzeitige, bzw. letzte Geschwindigkeit
};
}
#endif
正如你所看到的,我正在使用一个指针向量来组合swarm中的对象。在运行时,调用swarm :: move(),它遍历向量并为每个boid调用boid :: move()。
bool swarm::move(float timeSinceLastFrame) {
std::vector<boid*>::iterator iter;
for ( iter = flock.begin(); iter != flock.end(); iter++ ) {
(*iter)->move(timeSinceLastFrame);
}
return true;
}
boid :: move非常复杂,因为它根据很多东西来计算运动。我将发布 - imo - 在这里实际上很重要的点,而不是每一次乘法,因为我不想让你厌烦不必要的东西。编辑:好的,现在你已经获得了大部分代码。
bool boid::move(float timeSinceLastFrame) {
Ogre::Vector3 cohesion = Ogre::Vector3(0, 0, 0);
Ogre::Vector3 alignement = Ogre::Vector3(0, 0, 0);
Ogre::Vector3 separation = Ogre::Vector3(0, 0, 0);
int cohesionCount = 0;
int alignementCount = 0;
int separationCount = 0;
std::vector<boid*>::iterator iter;
std::vector<boid*> temp = flockMates->getFlockMates();
for ( iter = temp.begin(); iter != temp.end(); iter++ ) {
if ( (*iter) != this ) {
Ogre::Vector3 p1 = boidNode->getPosition();
Ogre::Vector3 p2 = (*iter)->getBoidPosition();
Ogre::Real distance = p1.distance(p2);
if ( distance <= 10*boidSize ) {
//cohesion
cohesionCount++;
cohesion += (*iter)->getBoidPosition();
//alignement
alignementCount++;
alignement += (*iter)->getBoidVelocity();
}
if ( distance <= 2.5*boidSize ) {
//separation
separationCount++;
Ogre::Vector3 away = boidNode->getPosition() - (*iter)->getBoidPosition();
away.normalise();
away*=boidSize;
away/=(distance/2);
separation += away;
}
}
}
/* do some more stuff */
/* actually move the boid */
return true;
};
因此,正如您所看到的,基本算法非常重,非常重要。我运行了一个boids向量,调用每个boid的方法然后再次遍历向量。其他计算是基本的,只是抛出变量,以便一切看起来都很好,没有任何指数增加复杂性。
现在,正如已经说过的那样,我预计渲染会在大量的boid中变慢。我希望帧率下降等等,但这根本不会发生。相反,算法运行得非常精细,具有高和流畅的帧速率,直到我大约200个boid +/-,然后一旦调用swarm :: move()就会立即崩溃。
我已经检查了几个松散的结尾。向量容器有足够的空间用于> 10亿个元素,所以不是这样。我还可以使用10000,20000个boid初始化所有内容,因此它也不是基本的内存分配问题。只要调用swarm :: move(),它就会崩溃。
那么,为什么这会导致200和几个boid崩溃?为什么帧率没有随着时间而下降?感谢您的帮助。我想我会提供所有必要的信息,但是,如果您需要额外的代码(或其他),请随时提出。
通过编辑的其他信息: 如果我通过点击而不是通过帧率手动触发swarm :: move,它不会改变。它仍适用于&lt; 200ish boids,它仍然无法使用更多。
Edit²: 编辑了boid :: move()方法,并注意到调试器捕获崩溃的位置。然而,它并没有捕获到boid 1的崩溃,这对我来说是有意义的,但是在boid 314(在这种情况下)。因此,该算法在相同的向量上运行313次,然后在第314次崩溃。这有什么意义吗?
Edit³: 有趣的是,调试内容让我感到困惑,远远超过实际指向问题所在。我再次更新了boid :: move(),我将抛出swarm :: getFlockMates的代码,我将在一秒钟内解释原因。
std::vector<boid*> swarm::getFlockMates() {
return flock;
}
令我困惑的是以下内容。在我将距离的计算更改为Ben voigt建议的内容之后,代码在最终的运动中崩溃了,值不应该崩溃。相反,我有一个1.000百万的cohesionCount,而alignementCount和separationCount仍然是0.这再次 - 对我来说根本没有意义。 cohesionCount不能高于boids的总量,此刻为1000(因此崩溃)。即使所有Boids都在内聚力范围内(距离<10 * boidSize),它的最大值也不能高于Flock中存储的boid总量。
这个说法,我是如何迭代我的羊群的?
答案 0 :(得分:3)
事实证明,我在这里提出的算法没有任何导致这个问题的缺陷。内存泄漏是我必须使用的一个Lua-Files,因为这个引擎(也就是这个算法)就是通过它来使用的。谢谢你们所有人的帮助!
答案 1 :(得分:2)
在他的书的第6.14章中,Daniel Shiffman提出了一些算法来提高性能。它表明它们不应该循环所有其他存在的boids,而只是那些接近它的那些,就像使用Quadtree一样。
答案 2 :(得分:0)
这是一个评论而非答案,但我不能写评论......我的猜测是,你的问题不在swarm::move()
,而是在此之前。如果你的一个指针没有指向boid
而是指向其他记忆,那么崩溃将是最明智的事情。
然而,我只能猜测,因为我不知道你的代码或什么样的崩溃终止了你的程序。顺便说一句,您应该使用调试器进行检查,大多数调试器会向您显示崩溃发生的确切位置,有时甚至是为什么。
答案 3 :(得分:0)
许多算法都有收敛半径。您的timeSinceLastFrame
参数是否与渲染率相关联?如果是这样,那么通过影响渲染率,种群大小反过来又会从根本上影响算法的收敛行为。如果您开始遇到稳定性问题,您也可以开始触发浮点异常。
尝试使timeSinceLastFrame
保持不变,现在你的运行速度比实时慢,但收敛将不再受计算和渲染时间的影响。