Hi,
I’m working on a single player card game idea, in the same vein as Slay the Spire, Meteorfall: Journey, or Dream Quest. I’m trying to figure out how to best structure the code for the Effects the Cards will have. The Effects will need to follow certain criteria in the game:
- Can have their values modified in game; ex. “Deal 5 Damage” can become “Deal 10 Damage”.
- Effects can be added/removed from cards during gameplay; ex. “Deal 5 Damage” becomes “Deal 5 Damage AND Heal 5 Damage”.
- Effect values should be easily modified in-editor for easy balancing by anyone on the team.
- Effects are independent from other stats like Card Level or Mana Cost.
- The structure should scale well; we plan on having 320 or so cards in the final product.
- The structure shouldn’t clutter up the project if at all possible.
Our first pass at tackling this problem was based off a reply by the Meteorfall developer in this thread. We’re doing a similar setup, where each individual card will have its own script, though we’re using Scriptable Objects rather than Monobehaviour Objects.
Our Card.cs script handles all of the visuals, as well as holding a List of CardEffects that it will cycle through and trigger when the card is played.
public class Card : MonoBehaviour
{
public List<CardEffect> cardEffects = new List<CardEffect>();
public void PlayCard()
{
foreach (CardEffect ce in cardEffects)
{
ce.ApplyEffect();
}
GameplayManager.Instance.activeCharacter.DiscardCard(this);
}
}
Then we have a CardEffect class that inherits from Scriptable Object, and has a couple basic functions in it in order to do some functionality when called upon:
public class CardEffect : ScriptableObject
{
public virtual void ApplyEffect()
{
}
public virtual string CardDescription()
{
return "";
}
}
And finally, we have a script per card essentially, that inherits from the CardEffect.cs:
[CreateAssetMenu(fileName ="DealDamage", menuName ="CardEffects/DamageEffects")]
public class DealDamage : CardEffect
{
public int damage;
public override void ApplyEffect()
{
GameplayManager.Instance.DamageCharacter(damage, false);
}
public override string CardDescription()
{
return "Deal " + damage + " Damage.";
}
}
However, there’s a few problems with this implementation. First of all, as far as I know, modifying values of a Scriptable Object in game would modify the Asset itself, thus affecting all cards that use that Scriptable Object, as well as changing the value for future sessions as well. This method also creates quite a bit of clutter, as even for a Damage Effect on multiple cards, we’d need a unique Scriptable Object for each different integer value we want, so it makes a lot of clutter in the project. And finally, this is going to create a whole lot of scripts, assuming I make a unique .cs file for each card.
So I’m wondering what other options I have available to me for accomplishing the goals listed above. I’ve attempted to create an abstract CardEffect class that doesn’t inherit from Scriptable Object or MonoBehaviour. However, I can’t seem to access the values of the CardEffect and classes that inherit from it in a list of CardEffects in the Editor, even if I make the class Serializable. I’ve tried to check out various other threads and tutorials on how to tackle this problem, but nothing seems to be quite the right fit for our game. Any help would be greatly appreciated!