简体   繁体   中英

classes with a lot of members

I am attempting to create a D&D encounter simulator. I have therefore created a class called "Actor" to emulate the behaviour of the players and the monsters. The problem is that while the class for the moment only have 3 member variable, as the simulation gets more accurate it will become necessary to add more member variables to best simulate the stats of the monsters and player eg strength, dexterity etc. (possibly more than 10 member variables) This leads to a constructor with a lot of parameters so the question then becomes; Is there a better way to organize this data, since it can all vary with every instance of the actor class?

Right now the user is required to type in all the states by hand, though I have plans to make file reading for monsters accessible later since monster stats only vary with monster type (dragon, skeleton etc.)

Note: These stats are very important and used to calculate the outcome of every action the "Actor" class can take in the encounter.

EDIT: A lot of people suggest using inheritance, but the fact is that (Monsters and Players) never have different stats, the monsters like the players are controlled by a player (The Game Master) This is a tabletop game and the simulator is supposed to help the game master balance encounter's ahead of a real game.

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 Constructor

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

As you bring up the example of stats yourself, those could go in a

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

You can then either source them from static class variables or static methods:

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

As a bonus, this also gives you a central place to apply or remove effects that change the stats temporarily, like equipping a "Ring of +1 strength" or being hit with a spell like Polymorph.

It's a basic concept of Polymorphism in C++, and I would suggest to start from reading about it

You can have for example your base class Actor will contain the basic information that every entity in your emulator has (monster, player etc.)

Then you can create a Monster and a Player class, which derive from your Actor class. Those will have their own unique information (tooth size, breaths under water, fly etc..)

If you want to dynamically create new stats for your player as the emulation goes on, I would suggest using smart pointers to hold these members for memory optimization.

If you don't want to have a parameter for each property of the stats of the actor in the constructor of the actor you can consider having a separate stats object and pass an instance of that stats to the constructor:

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_;
};

You could then have different strategies of creating an actor, like reading from a save file, or from a preset:

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;
}

That way you don't need to implement a getter/setter for each stats, and don't need to use friend for the functions that should initialize the stats. But the raw values are still protected, as soon as they are passed to the Actor .

This leads to a constructor with a lot of parameters so the question then becomes; Is there a better way to organize this data, since it can all vary with every instance of the actor class?
Right now the user is required to type in all the states by hand, though I have plans to make file reading for monsters accessible later since monster stats only vary with monster type (dragon, skeleton etc.)

It sounds like this question is less about how to engineer the OOP, but rather how to define all the stats for the program without it being hugely cumbersome.

Reading stats from files is a good idea. You could represent them in one or more JSON files and use Nlohmann's JSON library to read it into your program. You'll still have to write code that says how your program should use the read JSON data, and of course you need to write the JSON in the first place---no free lunch. But it could nevertheless be helpful for organizing the program.

Another thought: for actors like an army of orcs, or wherever there are multiple instances of the same kind, a useful approach could be to define an orc factory function that creates an orc actor and its stats. The factory could pseudorandomly vary speed, strength, etc. so that each orc is personalized a little different without having to manually write stats for each instance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM