Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Idiomatic Way to Create Card Instances

Discussion in 'Scripting' started by slpearlman, Jan 13, 2023.

  1. slpearlman

    slpearlman

    Joined:
    Aug 29, 2014
    Posts:
    8
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4.  
    5. [CreateAssetMenu(fileName = "CardSO", menuName = "ScriptableObjects/Card")]
    6. public class CardSO : ScriptableObject
    7. {
    8.     public string cardName;
    9.     public Image cardImage;
    10.     // There could be more specific SOs for creatures, spells, etc.
    11. }
    12.  
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. [RequireComponent(typeof(Image))]
    7. public class IsCard : MonoBehaviour
    8. {
    9.  
    10.     public CardSO cardSO;
    11.  
    12.     // Start is called before the first frame update
    13.     void Start()
    14.     {
    15.         if (cardSO == null)
    16.         {
    17.             return;
    18.         }
    19.  
    20.         Image image = GetComponent<Image>();
    21.         image.sprite = cardSO.cardImage.sprite;
    22.         //Reference/Utilize other properties when applicable.
    23.         //If this were a creature, perhaps we could have a mono behavior that stores power/toughness, etc.
    24.         //public int power;
    25.         //power = creatureCardSO.power;
    26.     }
    27. }
    28.  
    I'm extremely new to Unity, and I'm trying to figure out if this design makes sense. I'm creating a card game in which multiple instances of the same card will exist. I'm trying to determine if the "idiomatic" way to do this is as follows:

    Create a Scriptable Object to contain card metadata.
    Then create specific Scriptable Objects for each card.
    Then at runtime, whenever I need a specific card, I create a gameObject add the (simple for example purposes) IsCard Mono Behavior and associate IsCard with whatever ScriptableObject corresponds to the specific card in question to extract/assign metadata.

    Does this make sense or is there a more performant/idiomatic way to do this?
     
  2. SF_FrankvHoof

    SF_FrankvHoof

    Joined:
    Apr 1, 2022
    Posts:
    780
    Skip the ScriptableObject for each card. Use a Prefab there instead.
    The meta-data SO is a good idea though.
     
  3. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,883
    Opposite to above, I would use scriptable objects to design the raw data for a card. How the cards appear can easily be generate at runtime off of a single template.

    For gameplay you'd naturally have a monobehaviour component that holds onto whatever cards is in play. However I would at least encapsulate the runtime mutable data into a plain class, if this is one of those Hearthstone-like games were cards gain persistent changes. Your deck of cards yet to be drawn will probably be make up of these plain objects too.

    Don't worry about performance. A card game's under the hood logic is never going to strain even an relic of a mobile phone. Most of your optimisation is going to be in the visuals side of things, but as always only optimise when issues arise and ALWAYS use the profiler to pinpoint the issue, rather than shooting blindly.

    Note for the future: a project like this is 100% going to require custom inspector/editor work to be even possible to make. So I'd definitely take the time to learn some of that, or look for existing tools on the asset store.
     
  4. jbnlwilliams1

    jbnlwilliams1

    Joined:
    May 21, 2019
    Posts:
    267
    slpearlman likes this.
  5. slpearlman

    slpearlman

    Joined:
    Aug 29, 2014
    Posts:
    8
    Thanks y'all. I just wanted to make sure that I wasn't misunderstanding something. I haven't gotten to this point yet, but assuming the above was correct, my biggest "concern" was when/how to load card data into memory, but I can just use a profiler when that time comes. I realized that I can create a prefab for each card (scriptable object +mono behavior(s)) or I can create the cards from SOs + MBs at runtime (as opposed to loading the prefabs) and wasn't sure which was considered best practice, but it likely doesn't matter much for a game this simple?
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    I really like this pattern:

    Factory Pattern in lieu of AddComponent (for timing and dependency correctness):

    https://gist.github.com/kurtdekker/5dbd1d30890c3905adddf4e7ba2b8580

    In your case you might pass in a ScriptableObject defining your card to a generic card factory thingy.

    "Hey make me this card" and the SO would contain the text, the mana, the image, etc.

    For UI stuff I like the dynamic of not using a prefab for the card but rather having one "example blank card" lying in the UI and then just cloning it. Attached is an example project of this kind of dynamic UI generation.

    The absolute LAST thing you should be concerned about is performance here. Seriously.
     

    Attached Files:

    spiney199 and slpearlman like this.
  7. slpearlman

    slpearlman

    Joined:
    Aug 29, 2014
    Posts:
    8
    Thanks, everyone :)