Witam, mój projekt dotyczy tego: https://en.wikipedia.org/wiki/Flocking_(behavior)
Sam algorytm już dawno napisałem, początkowo miało być to tylko dla mnie jakieś wyzwanie, teraz jednak stwierdziłem że zrobię z tego małą bibliotekę którą będę mógł wykorzystać w moich przyszłych projektach. Kod musiałem kilka razy bardzo przebudować. Jak na razie wygląda on tragicznie... Zanim go pokaże może przejdę najpierw do tego co właściwie chce osiągnąć:
No wiec początkowo klasy generowały stado rysując zwykłe trójkąty, oczywiście chciałem to rozwinąć, w ich miejsce można wstawić ryby, ptaki, antylopy, ludzi, Początkowo miałem zrobić abstrakcyjną klasę bazową o nazwie EntityShape a kolejne klasy miały z niej dziedziczyć. Zaletą takiego rozwiązania jest to że użytkownik jest zmuszony napisać definicje dla wszystkich czysto wirtualnych metod klasy bazowej, jakie są wymagane dla algorytmu. Relacja wygląda tak:
class EntityShape : public sf::Drawable {
public:
virtual void setRotationBasedVelocity(sf::Vector2f velocity) = 0;
virtual sf::Vector2f getSize() const = 0;
virtual sf::Vector2f getPosition() const = 0;
virtual void setSize(sf::Vector2f size) = 0;
virtual void setPosition(sf::Vector2f position) = 0;
virtual void setPosition(float x, float y) = 0;
virtual void move(sf::Vector2f vec) = 0;
virtual void setRotation(float angle) = 0;
virtual void setOrigin(sf::Vector2f vec) = 0;
virtual void setFillColor(sf::Color color) = 0;
};
class FishShape : public EntityShape {
private:
sf::RectangleShape m_fish;
static sf::Texture m_texture;
static bool isTextureLoaded;
public:
FishShape(float a, float b) {
if (!isTextureLoaded) {
m_texture.loadFromFile("fish.png");
isTextureLoaded = true;
}
m_fish.setTexture(&m_texture);
}
virtual void setRotationBasedVelocity(sf::Vector2f velocity) {
m_fish.setRotation(atan2(velocity.y, velocity.x)*(180.f / 3.14159265359f) + 90.f);
}
virtual sf::Vector2f getSize() const {
return m_fish.getSize();
}
virtual sf::Vector2f getPosition() const {
return m_fish.getPosition();
}
virtual void setSize(sf::Vector2f size) {
m_fish.setSize(size);
}
virtual void setPosition(sf::Vector2f position) {
m_fish.setPosition(position);
}
virtual void setPosition(float x, float y) {
m_fish.setPosition(x, y);
}
virtual void move(sf::Vector2f vec) {
m_fish.move(vec);
}
virtual void setRotation(float angle) {
m_fish.setRotation(angle);
}
virtual void setOrigin(sf::Vector2f vec) {
m_fish.setOrigin(vec);
}
virtual void setFillColor(sf::Color color) {
m_fish.setFillColor(color);
}
public:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override {
target.draw(m_fish);
}
};
sf::Texture FishShape::m_texture;
bool FishShape::isTextureLoaded = false;
( oczywiście trzeba wiele rzeczy poprawić bo na razie jest to tylko prowizorka, dlatego proszę... nie zwracajcie uwagi na ten konstruktor co przyjmuje dwa argumenty a nic z nimi nie robi )
Wadą jest tu bardzo wysoki poziom powtarzalnej abstrakcji ( taki mój termin ;) ). Sami widzicie o co chodzi, wywołujemy metody składowej o takiej samej nazwie jak metody aktualnej klasy, nwm czy to jest w ogóle wydajne ale też nie mam pojęcia jak to obejść...
Największy problem w całym projekcie jest taki że wykorzystuje i dziedziczenie i szablony, oto klasy mające związek z samym algorytmem:
///-------------------------------------------------------------------------
template <typename DrawableObject> class SingleEntity : public sf::Drawable {
private:
EntityShape* m_entity;
sf::Vector2f m_velocity;
private:
const long double m_SeparationCubedZone;
const int m_ID;
public:
SingleEntity(float angle, sf::Vector2f size, sf::Color color, float separationSquareRootZone, int ID, sf::Vector2f position);
public:
long double getSeparationSquaredZone() const;
float getSeparationSquareRootZone() const;
bool operator!=(const SingleEntity& otherEntity) const;
long double getDistanceSquared(sf::Vector2f position) const;
sf::Vector2f getVelocity() const;
sf::Vector2f getPosition() const;
public:
void operator+=(sf::Vector2f velocity);
void operator=(sf::Vector2f velocity);
void update(float speed, float elapedTime, sf::Vector2f windowSize);
public:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
};
///-------------------------------------------------------------------------
template <typename DrawableObject> class Flock : public sf::Drawable {
private:
const float m_TimeToStartFlocking;
private:
bool m_mathMode = false;
private:
sf::Vector2f m_windowSize;
private:
const float m_speed;
const long double m_NeighborhoodSquaredZone;
const float m_CohereWeight = 1.f;
const float m_SeparateWight = 6.f;
const float m_AlignmentWeight = 1.f;
const float m_MouseCursorSeparateWeight = 20.f;
const float m_MouseCursorCohereWeight = 3.f;
private:
std::list<SingleEntity<DrawableObject>> m_entities;
private:
//---------------------------------------------------------------------------------
sf::Vector2f cohere(const SingleEntity<DrawableObject>& currentEntity);
sf::Vector2f separate(const SingleEntity<DrawableObject>& currentEntity);
sf::Vector2f alignment(const SingleEntity<DrawableObject>& currentEntity);
sf::Vector2f mouseCursorSeparate(const SingleEntity<DrawableObject>& currentEntity, sf::Vector2f mouseCursor);
sf::Vector2f mouseCursorCohere(const SingleEntity<DrawableObject>& currentEntity, sf::Vector2f mouseCursor);
sf::Vector2f Flock::obstacleSeparate(const SingleEntity<DrawableObject>& currentEntity, std::list<FlockingObstacle>& obstacles);
sf::Vector2f flocking(const SingleEntity<DrawableObject>& currentEntity, sf::Vector2f mouseCursor, std::list<FlockingObstacle>& obstacles);
//---------------------------------------------------------------------------------
public:
Flock(std::list<SingleEntity<DrawableObject>>& entities, float speed, float neighborhoodSquareRootZone, sf::Vector2f windowSize, float timeToStartFlocking );
void update(float elapedTime, float realProgramTime, sf::Vector2f mouseCursor, std::list<FlockingObstacle>& obstacles);
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
public:
void setMathMode(bool mathMode);
};
Używam tu szablony do tego celu:
template <typename DrawableObject>
SingleEntity<DrawableObject>::SingleEntity(float angle, sf::Vector2f size, sf::Color color, float separationSquareRootZone, int ID, sf::Vector2f position)
: m_SeparationCubedZone(std::pow(separationSquareRootZone, 2)), m_ID(ID) {
m_entity = new DrawableObject(0,0);
angle *= (3.14159265359f / 180.f);
m_velocity = sf::Vector2f(std::cos(angle), std::sin(angle));
m_entity->setSize(size);
m_entity->setOrigin(m_entity->getSize() / 2.f);
m_entity->setFillColor(color);
m_entity->setPosition(position);
}
Tak wygląda konstruktor dla pojedynczej jednostki, szablon jest użyty żeby była możliwość przekazania jakiego typu klasy pochodnej chcemy użyć:
m_entity = new DrawableObject(0,0);
Nie wiem co zrobić, zrezygnować z szablonów a obiekt jednostki skonstruować zewnętrznie, zrezygnować z dziedziczenia? Nie podaję całego kodu bo problem dotyczy tylko możliwości wstawienia w miejsce jednostki dowolnego typu, który zawiera niezbędne metody.
Z góry dziękuje za pomoc :)