4
\$\begingroup\$

I'm developing a Pokémon/classic Final Fantasy inspired game, in the sense that game play consists in many creatures, possessing many moves with different effects (attacking, healing, buffing, debuffing, etc) taking turns fighting each other.

I'm having some doubts on how to implement the movement system, especially the part of how to organize the code responsible for the effects of the move.

Some important characteristics of the system:

  • Creatures should be able to learn and forget moves
  • All moves can be learned by more than one creature

I have come up with some ideas, but none seem very good. Is there any known standard in the industry for doing this, or any known optimal solutions?


Here are the ideas I had:

  • Every move inherits from an abstract class Move, overriding an execute() method.
  • A MoveProcessor class, containing a method executeMove(), called every time any move is made, receiving the move id code. Something like:

    switch(moveCode){
         case 1: //Attack code
         break;
         case 2: //Heal code
         break;
         //...
    }
    

I am developing in Unity and C#, but answers need not be specific to said platform and language.

Please note that this is not a duplicate of Modeling what moves a pokemon can learn or Storing data for a pokemon-like game, since those questions ask about storing move information, not implementation.

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

I see no other solution that having distinct 'moves' that are implemented in some mechanism that allows the 'owner' to invoke them without really knowing what those moves are.

However, it is unclear if you want to make the logical break on implementing distinct moves as a 'Move' with subclasses. You may find it more useful to think about the content (animations, sounds, particle effects), effects(buffs, debuffs), actions(damage) as the actual abstract objects, and the 'Move' as simply a container of animations, effects and actions performed when 'Invoke' is called. Trying to create a unique subclassed Move for every move available is going to be be brittle and will lead to you having to rework your hierarchy often. Instead I would consider a Move as having a bunch of different objects inside it, perhaps scheduled in steps, with timers. The values for the different actions, effects and content (like range of heals, time of a buff) would be provided as parameters, not hard coded in derived classes, when possible. It would make sense that a lot of this data come from a text, json, excel or xml file. I would further suggest that you should be able to define these objects as code, created on demand from the text file. Then you may plug them together in different combinations simply using the text system so you can mix and match them.

By making the Move a container, and breaking the pieces down into smaller elements you will find it easier to reuse them.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Kind of related gameprogrammingpatterns.com/subclass-sandbox.html \$\endgroup\$
    – Bálint
    Dec 13, 2017 at 14:12
  • \$\begingroup\$ @Bálint Yes that is a good resource, but I would argue that instead of a 'SuperPower' having simply protected methods, it might also have protected member-objects that are leveraged in a handful of similar but distinct super-powers, parameterized by colour, range, etc. Thanks for adding the link to a useful resource. \$\endgroup\$
    – Steven
    Dec 13, 2017 at 20:13
3
\$\begingroup\$

An alternative to every move inheriting from an abstract class Move, you can instead use interfaces in C#. This has the advantage of allowing your move classes to be more flexible while still sharing the execute method. An example of this could be:

public interface IMove
{
    void Execute();
}

The different moves:

public class Attack : IMove
{
    public void Execute()
    {
        //Attack logic here
    }
}

public class Heal : IMove
{
    public void Execute()
    {
        //Heal logic here
    }
}

And the creature class:

public class Creature
{
    public Creature()
    {
        Moves = new List<IMove>();
    }

    public List<IMove> Moves { get; set; }

    public void AddMove(IMove move)
    {            
        if (!Moves.Contains(move))
        {
            Moves.Add(move);
        }

    }

    public void RemoveMove(IMove move)
    {
        if (Moves.Contains(move))
        {
            Moves.Remove(move);
        }
    }

    public void ExecuteMove<TMove>()
        where TMove : IMove
    {
        var move = Moves.OfType<TMove>().SingleOrDefault();
        if (move != null)
        {
            move.Execute();
        }
        else
        {
            throw new DoesNotContainMoveException();
        }
    }
}

It can be used as follows:

Creature creature = new Creature();

creature.AddMove(new Attack());

creature.ExecuteMove<Attack>();

Console.ReadKey();
\$\endgroup\$

You must log in to answer this question.

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