37
\$\begingroup\$

I am in the planning phase of a small RPG style game.

The character will have have a set of attributes, like strength, agility, etc which are represented as integers. The character also will have a set of attacks represented as an attack class.

On each attack I want it to do damage based on the characters attributes, eg: the attack "sword slash" will do 10 dmg + the value of the characters strength.

The way I was thinking to do this is to have an abstract attack class, which has an abstract Attack method, and for each attack I create one class that implements the Attack method.

public class SwordSlash:Attack
{
    public void Attack(Character attacker, Character defender)
    {
        defender.DoDamage(10 + attacker.Strength);
    }
}

I see that this will make it a nightmare to maintain.

Does anyone have an idea of how I can accomplish this in a nicer way?

What I think is the main problem is how to input the correct attribute, based on the attack.

\$\endgroup\$

4 Answers 4

34
\$\begingroup\$

You should probably go for a data-driven design here.

Make a generic Attack class which contains the parameters you want to work with - base damage, which stats affects the damage, a set of potential status effects... stuff like that:

public enum AttackStat
{
  Strength,
  Agility,
  Intellect
  // etc.
}

public class Attack
{    
  private int baseDamage;
  private AttackStat stat;
  private double damageMultiplier;
  // ...and so on

  public void Attack(Character attacker, Character defender)
  {
    defender.DoDamage(baseDamage + attacker.GetStatValue(stat) * damageMultiplier);
  }    
}

// Put a method on Character to fetch the appropriate value given an AttackStat:
public int GetStatValue(AttackStat s)
{
  switch(s)
  {
    case AttackStat.Strength:
      return strength;
    case AttackStat.Agility:
      return agility;
    // etc.
  }
}

Then, place your attacks in a file, e.g. an XML file, and load the data from there:

<Attacks>
  <Attack name="Sword Slash" damage="10" stat="Strength" multiplier="1" />
  <!-- More attacks here -->
</Attacks>

You could even extend this to draw values from multiple stats, say, a Fireball where the damage is calculated from both an Intellect and a Fire stat:

<Attack name="Fireball" damage="20">
  <StatModifier stat="Intellect" multiplier="0.4" />
  <StatModifier stat="Fire" multiplier="0.8" />
</Attack>

If you don't want to use the same basic damage formula for everything (e.g. calculate magic damage differently from physical damage), create subclasses of Attack for each formula you need and override Attack, and specify which type you want in your XML file.

\$\endgroup\$
6
  • 1
    \$\begingroup\$ +1, but I would even replace GetStatValue with a lookup table of some sort to avoid maintaining that switch statement. \$\endgroup\$
    – user744
    Commented Nov 16, 2010 at 11:41
  • 1
    \$\begingroup\$ The problem with this method is that you can only have generic data-driven attacks - you can't have anything that uses special logic. You will end up with a very generic set of items (as you get in warcraft \$\endgroup\$
    – Iain
    Commented Nov 16, 2010 at 12:28
  • 2
    \$\begingroup\$ @Iain: That is very easily solved by simply adding more data to allow this. E.g., you might have a SpecialAttack subclass which does more things, or calculates damage in an entirely different way. It's just a matter of identifying the behavior you need, and then expressing that as data. \$\endgroup\$ Commented Nov 16, 2010 at 13:13
  • 2
    \$\begingroup\$ @Iain: In addition to just adding more fields, you can also solve that by some of the data fields being expressions or code blocks in e.g. Lua. Good use of orthogonal components also makes more interesting results. \$\endgroup\$
    – user744
    Commented Nov 16, 2010 at 13:44
  • 1
    \$\begingroup\$ +1 for the general idea of being data-driven. I don't agree with suggesting xml. There are better formats out there - yaml, json or a plain .lua file if you are embedding Lua. \$\endgroup\$
    – egarcia
    Commented Jan 7, 2011 at 15:42
7
\$\begingroup\$

You want to read Aggregate Objects via Components and Game Object Construction Rabbit Hole, from the Replica Island blog.

\$\endgroup\$
1
\$\begingroup\$

I would have a weapon class that has an attack method which you override with the behaviour you want. You can then also handle how the weapon looks in game, in inventory, how much it sells for etc in the same class.

\$\endgroup\$
4
  • 6
    \$\begingroup\$ -1, not only is this not data-driven, it's deep hierarchy rather than component-driven. It's the worst possible solution. \$\endgroup\$
    – user744
    Commented Nov 16, 2010 at 13:44
  • 4
    \$\begingroup\$ Just because this particular method isn't data-driven doesn't make it a bad choice and the hierarchy wouldn't be that deep anyways. It is simple but still powerful (the UnrealEngine is a perfect example of this) if done correctly (ie. no hard-coded values). Sure it has its downsides, but further down the development cycle of a data-driven system I'm sure its downsides are show. I think your basic OOP design is still a valid solution in this and if he wants on the fly editing of default values, it can be implemented on top of a hierarchy system just as easily. \$\endgroup\$
    – user631
    Commented Nov 17, 2010 at 3:10
  • 6
    \$\begingroup\$ Not everything has to be data-driven - it depends on the scale of the game. It's probably a bit arrogant to think that my answer is "wrong", but thank you for your honesty. I think this is just a clash of styles between what works for me, making Flash games every day, and your more traditional model of development. I can tell you my approach is much faster to implement and you have better compile-time checking. Your comment re. Lua assumes the asker is working on a platform that would support that. \$\endgroup\$
    – Iain
    Commented Nov 17, 2010 at 11:07
  • 2
    \$\begingroup\$ Being an RPG game, it's probably pretty impractical to implement every item like that. \$\endgroup\$ Commented Oct 10, 2012 at 6:00
1
\$\begingroup\$

I'm really new to this, but the way I'd do it is to create a generic attack class.

When one character instance wants to attack another character instance it would create an instance of the attack class, filled with the data required, and the ID of the character that created it. Adjustments from gear would then be applied to the attack object, using data that could be entered in an xml document or similar.

This class instance would then be wrapped inside another class, to provide hooks for the environment to determine range or similar. If the attack is valid the attack instance would be passed to the character being attacked, who would apply the effects.

Hope that made sense.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .