【问题标题】:classes with a lot of members有很多成员的班级
【发布时间】:2020-12-26 00:01:18
【问题描述】:

我正在尝试创建一个 D&D 遭遇模拟器。 因此,我创建了一个名为“Actor”的类来模拟玩家和怪物的行为。问题是,虽然该类目前只有 3 个成员变量,但随着模拟变得更加准确,有必要添加更多成员变量以最好地模拟怪物和玩家的统计数据,例如力量、灵巧等(可能超过 10 个成员变量) 这导致构造函数有很多参数,所以问题就变成了;有没有更好的方法来组织这些数据,因为它会随着 Actor 类的每个实例而变化?

现在用户需要手动输入所有状态,但我计划稍后可以读取怪物的文件,因为怪物的统计数据仅因怪物类型(龙、骷髅等)而异。

注意:这些统计数据非常重要,用于计算“Actor”类在遭遇战中可以采取的每个动作的结果。

编辑: 很多人建议使用继承,但事实是(怪物和玩家)从来没有不同的统计数据,像玩家这样的怪物是由玩家控制的(游戏大师)这是一个桌面游戏,模拟器应该有帮助游戏主平衡遭遇比真正的游戏更早。

演员.h

#ifndef actor_h_
#define actor_h_

#include "dice.h"

class Actor
{
  private:
    signed int hp;
    signed int ac; // Armor Class
    signed int dmg;
    
  public:
    Actor( signed int hp, int ac, int dmg);
    ~Actor();

    signed int getHP( void );
    signed int getAC( void );
    signed int getDmg( void );
    void setHP(signed int newHP);
    void setAC(signed int newAC);
    void setDmg(signed int newDmg);

    void attack(Actor* target);
};
#endif

Actor 构造函数

Actor::Actor(signed int hp, signed int ac, signed int dmg)
{
  this->hp = hp;
  this->ac = ac;
  this->dmg = dmg;
}
Actor::~Actor(){}

【问题讨论】:

  • 在我看来,Actor 应该是一个虚拟类——“PC”、“NPC”和“Monster”类继承它......因为并非所有这些类都需要相同的参数。
  • 你可能是对的,但问题是在 D&D 战斗中,怪物和 PC 使用相同的统计数据,因此它们需要相同的参数,而 NPC 不是一个因素,因为它们在战斗中由 PC 控制还利用与 PC 相当的统计数据
  • 有一本书叫做iOS Swift Game Development Cookbook,它讨论了处理这种情况的各种策略。信息很好,即使您必须从平台和语言中抽象出来。 (我知道,你可能不是为 iOS 编程,也不是在使用 Swift 编程。)也许有一本适用于你的平台和 C++ 的“游戏开发食谱”。
  • @MortenKristensen 所以你说的是有一个“CombatActor”,它拥有所有的战斗数据——然后是其他类型的演员
  • @UKMonkey 游戏是一款桌面角色扮演游戏(游戏不是在PC上玩的),故事和NPC由游戏大师控制,在游戏中玩家可以在战斗中遇到敌人遭遇。唯一的目标是给游戏大师一个模拟这些遭遇结果的工具,以确保遭遇不会太容易或太难。游戏的所有其他方面都与模拟器无关。

标签: c++ class constructor member class-design


【解决方案1】:

这导致构造函数有很多参数,所以问题就变成了;有没有更好的方法来组织这些数据,因为它会随着演员类的每个实例而变化?
现在用户需要手动输入所有状态,但我计划稍后可以访问怪物的文件读取,因为怪物统计数据仅因怪物类型(龙、骷髅等)而异。

听起来这个问题不是关于如何设计 OOP,而是如何定义程序的所有统计信息而不是非常麻烦。

从文件中读取统计数据是个好主意。您可以在一个或多个 JSON 文件中表示它们,并使用Nlohmann's JSON library 将其读入您的程序。你仍然需要编写代码来说明你的程序应该如何使用读取的 JSON 数据,当然你首先需要编写 JSON ——没有免费的午餐。但它仍然有助于组织计划。

另一个想法:对于像一群兽人这样的演员,或者任何有多个同类实例的地方,一个有用的方法可能是定义一个兽人factory function,以创建一个兽人演员及其统计数据。工厂可以伪随机地改变速度、力量等,从而使每个兽人的个性化略有不同,而无需为每个实例手动编写统计数据。

【讨论】:

  • 这听起来很对,因为我需要。我没有任何 JSON 经验,但我想每件事都有第一次。我喜欢关于工厂“个性化”兽人等小型奴才的建议。如果我确实决定使用这样的工厂,那么我是否需要为每个生物制作不同的工厂功能,或者你认为有可能制作一个工厂,从生物文件中读取一些统计数据,然后稍微调整它们在明确定义的范围内随机少量调整?
  • 是的,绝对有可能,而且我同意建造一个可以生产多种生物的工厂是个好主意。工厂可以将生物统计文件的文件名作为输入参数,然后您可以将不同的文件传递给它以制造不同种类的生物。 JSON 只是一种在文件中表示统计信息的方法的建议,但肯定有不止一个正确答案。做对你有用的事! =)
  • 非常感谢!如果我能让它工作,这解决了我的很多问题:)
【解决方案2】:

如果您不想在actor的构造函数中为actor的stats的每个属性都有一个参数,您可以考虑拥有一个单独的stats对象并将该stats的一个实例传递给构造函数:

struct ActorStats {
    signed int hp;
    signed int ac; // Armor Class
    signed int dmg;
}

struct Actor {
  Actor(ActorStats stats) : stats_(std::move(stats)) {}

  signed int getHP( void );
  signed int getAC( void );
  signed int getDmg( void );
  void setHP(signed int newHP);
  void setAC(signed int newAC);
  void setDmg(signed int newDmg);

  void attack(Actor* target);

  protected:
    ActorStats stats_;
};

然后你可以有不同的策略来创建一个actor,比如从一个保存文件中读取,或者从一个预设中读取:

ActorStats read_stats_from_file() {
    ActorStats stats;
    // some reading logic

    stats.hp = // value read from file

    return stats;
}

Actor create_actor() {
    ActorStats stats = read_stats_from_file();
    Actor actor(stats);

    return Actor;
}

这样你就不需要为每个统计数据实现一个getter/setter,也不需要为应该初始化统计数据的函数使用friend。但是原始值仍然受到保护,只要它们被传递给Actor

【讨论】:

    【解决方案3】:

    这是 C++ 中多态的一个基本概念,我建议从阅读它开始

    例如,您可以让基类 Actor 包含模拟器中每个实体(怪物、玩家等)所具有的基本信息

    然后您可以创建一个从您的 Actor 类派生的 Monster 和 Player 类。这些将有自己独特的信息(牙齿大小、水下呼吸、苍蝇等)

    如果您想在模拟进行时为您的播放器动态创建新的统计数据,我建议使用智能指针来保存这些成员以进行内存优化。

    【讨论】:

    • 多态性经常被过度使用并导致许多问题。特别是在这样的用例中,参与者类型的不同属性可能会以不太适合多态性的方式重叠。在这种情况下,合成通常是更好的解决方案。
    • 我的问题似乎模棱两可,让我澄清一下。怪物和玩家的统计数据没有区别,他们的统计数据没有区别。只是在实际游戏中,PC 将由人类控制,而怪物将由另一个人(游戏大师)控制,除此之外,您在智能指针上的注释非常适合操纵统计数据,所以我会请记住这一点,谢谢:)
    【解决方案4】:

    当你自己提出统计数据的例子时,那些可以放在一个

    struct StatsBlock {
        int str, dex, con, /*others*/;
    };
    

    然后您可以从静态类变量或静态方法中获取它们:

    class Skeleton : public Actor {
    ...
    public:
    static inline const StatsBlock = { 13, 8, ... };
    static StatsBlock rollStats() { ... );
    };
    

    作为奖励,这还为您提供了一个中心位置来应用或移除临时改变统计数据的效果,例如装备“+1 力量戒指”或被诸如变形术之类的法术击中。

    【讨论】:

    • 这是个好主意!杰出的。您是否认为使用您的示例但让用户可以选择输入自定义统计数据是一个可行的想法?比如反映来自不同游戏的玩家可能选择了不同的统计数据分布,从而考虑了玩家的选择?如果是这样,那是否需要它自己单独实现actor类? (可能是 Actor 的子类)。
    • 当然。只需创建一个 StatsBlock createFromPlayerInput() 并将结果传递给您的 PC 构造函数。
    • 太好了,这绝对值得研究,谢谢! :D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-15
    • 2014-11-19
    相关资源
    最近更新 更多