0

我需要为一个小游戏管理蚂蚁和殖民地(实际上是为了实验)。

我有一个 Element 类,它定义了游戏中的所有实体(蚂蚁、殖民地、食物和其他东西……)

所有其他类都派生自这个类。

我的问题 :

我有一个类来管理所有实体。玩家可以选择他想要的东西。选择的实体被存储:Element* selection;如果选择的实体是蚂蚁,玩家可以移动它。但是,因为选择变量是一个元素指针,显然我不能调用move()Ant 类中的方法。

我认为要测试的内容:

如果我实现了一个isMovable()返回 true 或 false 的 Element 方法,并且如果选择是可移动的,我会将它转换为 Ant 吗?我不知道什么是正确的解决方案。

我的搬家方法:

void Manager::movementEvent(sf::Vector2i mPosition)
{
    sf::Vector2f mousePosition = sf::Vector2f((float)mPosition.x, (float)mPosition.y);
    if(this->selection) {
        // I need to move the selected Ant
    }
}

谢谢您的帮助 !!

编辑

这是我的实际设计:

class Element {
private:
    sf::Vector2f position;
    int width, height;
public:
    Element();
    Element(sf::Vector2f position, int width, int height);
    Element(const Element & element);
    virtual ~Element();
};

class Colony: public Element {
private:
    int capacity;
    Queen *queen;

public:
    Colony();
    Colony(sf::Vector2f position, int width, int height, int capacity, Queen &queen);
    Colony(Colony const & colony);
    virtual ~Colony();

    Colony& operator=(Colony const& colony);
};

class Ant: public Element
{
private:
    sf::Vector2f destination;
    int number, age, speed;

public:
    Ant();
    Ant(sf::Vector2f position, int number, int age, int width, int height, int speed);
    Ant(const Ant & ant);
    virtual ~Ant();

    Ant& operator=(Ant const& ant);
};

class Manager {
private:
    std::vector<Element*> ants;
    std::vector<Element*> colonies;
    Element* selection;
    std::vector<Ant*> movement;
public:
    Manager();
    virtual ~Manager();

    std::vector<Element*> getAnts();
    std::vector<Element*> getColonies();

    void addAnt(Ant* ant);
    void addColony(Colony* colony);

    void removeAnt(Ant* ant);
    void removeColony(Colony* colony);

    void draw(sf::RenderWindow * window);
    void drawElement(sf::RenderWindow * window, std::vector<Element*> vector);

    void selectionEvent(sf::Vector2i mousePosition);
    bool checkSelection(sf::Vector2f mousePosition, std::vector<Element*> vector);

    void movementEvent(sf::Vector2i mousePosition);
};
4

3 回答 3

3

我宁愿避免一般的设计,因为它充其量是强制适合我的。

基类应该定义许多派生类之间共有的行为,并为该常见行为提供公共接口。但是,在这种情况下,在我看来,您的派生类实际上没有共同的行为,因此您几乎没有或根本没有它们之间有用的公共接口。

在这种情况下,通过强迫它们都从(基本上毫无意义的)“实体”类派生,您可能会失去比获得更多的东西。事实上,我建议几乎任何时候你发现自己在思考一个像“对象”或“实体”这样一般的类名,而这并不意味着一组有意义的行为,你很有可能是试图把不属于一起的东西推到一起。

综上所述,如果你真的坚持要这样做,我会坚持这样的基本格言,那就是说比问好。因此,我会在基类中定义一个try_to_move(或者可能只是命名它move),但提供一个失败的默认定义。然后在 Ant 类中覆盖它以实际移动。

class Entity { 
// ...
    virtual bool move_to(Location new_location) { 
        return false;
    }
};

class Ant : public Entity { 
// ...
    virtual bool move_to(Location new_location) { 
        my_location = new_location; 
        return true;
    }
};

这样你就可以告诉任何派生的东西Entity要移动——但是如果你告诉一个Food对象移动,它就会失败。这大大简化了调用代码。而不是像这样的模式:

if (object->can_move()) {
    if (object->move_to(new_location))
        // succeeded
    else
        // failed
}

我们得到如下代码:

if (object->move_to(new_location))
     // succeeded
else
     // failed

至少在典型情况下,即使我们已经告诉蚂蚁移动,我们最终也可能会处理失败的可能性,因此在要求对象真正移动之前添加询问对象是否可以移动的元素无论如何,我们什么也得不到。

根据情况,您可能需要稍微更改代码,因此不同的移动失败原因会返回不同的错误代码,因此当/如果失败时,您可以找出原因。或者,您可能更喜欢编写代码,使其要么成功移动,要么抛出异常。在这些情况下(您宁愿希望它至少在部分时间失败)这可能不是最好的选择,但它可能仍然值得考虑。

然而,我要重申,我认为更好的设计可能是只保留Ants 和Food分开,因此很容易将 Food 作为食物,将 Ants 作为蚂蚁,而不必在运行时整理某些东西是否是食物或蚂蚁知道如何与它互动。

于 2013-07-16T14:42:39.683 回答
1

您可以在基类上添加一个virtual方法move(),而不是只为Ant该类实现它,所以当它被检查Element为可移动时,它应该移动:

class Element
{
public:
   Element(bool movable) : m_movable(movable) {}

   virtual void move() {};
   bool isMovable() const { return m_movable; }

private:
   bool m_movable;
};

class Ant : public Element
{
public:
   Ant() : Element(true) {}

   void move() { /* move */ }
};

class Food : public Element
{
public:
   Food() : Element(false) {}
};

这样,每个派生类move()确实都有一个方法,但它是从基类继承的(所以它留空)。

编辑

奥卡姆剃刀告诉我们,在这种情况下,您也不需要bool m_movable标志,因此代码段简化为:

class Element
{
public:
   Element() {}

   virtual void move() {};
};

class Ant : public Element
{
public:
   Ant() {}

   void move() { /* move */ }
};

class Food : public Element
{
public:
   Food() {}
};
于 2013-07-16T14:42:23.213 回答
1

这真的闻起来像你在解决错误的问题。您将能够使用诸如isMovable和强制转换之类的标志来使其工作,但是您的代码可能会变得一团糟,让您头疼。
也许你的问题实际上是

“我有一门课来管理所有的实体”

如果它们没有任何关系,它们可能不应该表达与实体的 Is-A 关系。如果每种类型都有不同的容器,它可能会更干净。如何将用户想要的操作与“实体”联系起来将是另一回事。

于 2013-07-16T14:40:51.947 回答