First of all, making your Deck
class inherit from Card
does not make sense. A deck is not a card, it’s a collection of cards.
Then, as indicated by Unity, instantiating monobehaviour can’t be done using a constructor.
I suggest you a complete rewrite of your system. You may feel worried at first, but I believe it will be easier to use in the long run. Separating the data from the UI will make the future changes easier.
I’ve commented the code so that you will know how to use and what to do with the scripts. But don’t hesitate to comment and ask questions if something is not clear.
Card.cs
Manage the data about cards
using UnityEngine;
public enum Suit
{
Diamonds,
Clovers,
Hearts,
Spades
}
// Create each card, one by one
// by clicking on "Assets / Create / ScriptableObjects / Card
// And fill the `DecksManager.Decks` array by dragging & dropping the scriptable objects in the inspector
[CreateAssetMenu( fileName = "Card", menuName = "ScriptableObjects/Card" )]
public class Card : ScriptableObject
{
[SerializeField] private int id;
[SerializeField] private Sprite sprite; // Drag & drop the sprite. It does not need to be in the `Resources` folder
[SerializeField, Range(1,13)] private int value = 1;
[SerializeField] private Suit suit;
public int ID
{
get { return id; }
private set { id = value; }
}
public Sprite Sprite
{
get { return sprite; }
private set { sprite = value; }
}
public int Value
{
get { return value; }
private set
{
if ( value <= 0 || value >= 14 )
throw new System.ArgumentException( "Value must be between 1 and 13" );
this.value = value;
}
}
public Suit Suit
{
get { return suit; }
private set { suit = value; }
}
public string SValue
{
get
{
switch ( Value )
{
case 11: return "Jack";
case 12: return "Queen";
case 13: return "King";
case 1: return "Ace";
default: return Value.ToString();
}
}
}
public Card( int id, int value, Suit suit, Sprite sprite )
{
ID = id;
Value = value;
Suit = suit;
Sprite = sprite;
}
}
Deck.cs
Manages the data about decks
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Deck
{
[SerializeField] private List<Card> cards = new List<Card>();
public int CardsCount { get { return cards.Count; } }
public Card this[int index] { get { return cards[index]; } }
}
CardsDatabase
Manages a collection of cards in the editor
This scriptableObject is not used anywhere else in the code, but you may want it in the future to have a complete list of your cards.
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
// Create this database by clicking on "Assets / Create / ScriptableObjects / CardsDatabase
// And click on the gear in the top-right corner of the inspector, then `FillDatabase`
// the database will be filled automatically with all the cards in your project
[CreateAssetMenu(fileName = "CardsDatabase", menuName = "ScriptableObjects/CardsDatabase")]
public class CardsDatabase : ScriptableObject
{
[SerializeField] private List<Card> cards;
public int CardsCount { get { return cards.Count; } }
public Card this[int index] { get { return cards[index]; } }
#if UNITY_EDITOR
[ContextMenu("FillDatabase")]
public void FillDatabase()
{
cards.Clear();
string[] guids = AssetDatabase.FindAssets( "t:Card" );
for ( int i = 0 ; i < guids.Length ; i++ )
{
string assetPath = AssetDatabase.GUIDToAssetPath( guids *);*
Card card = AssetDatabase.LoadAssetAtPath( assetPath );
if ( card != null )
cards.Add( card );
}
}
#endif
}
## CardRenderer.cs
Manages the visual representation of the card data
You have to create a prefab which will contain all the needed components to visually manage the card : SpriteRenderer, Animation, …
Once the prefab is created and this component attached to it, drag & drop in in the cardPrefab
field of the DecksManager
object in your scene
using UnityEngine;
public class CardRenderer : MonoBehaviour
{
// Drag & drop the spriteRenderer of the prefab
// this component is attached to
[SerializeField] SpriteRenderer spriteRenderer;
public void Setup( Card card )
{
spriteRenderer.sprite = card.Sprite;
}
}
## DeckRenderer.cs
Manages the visual representation of the deck data
You have to create a prefab which will contain all the needed components to visually manage the deck. Maybe nothing for now)
Once the prefab is created and this component attached to it, drag & drop in in the deckPrefab
field of the DecksManager
object in your scene
using System.Collections.Generic;
using UnityEngine;
public class DeckRenderer : MonoBehaviour
{
private List CardRenderers = new List();
public void Add( CardRenderer card )
{
CardRenderers.Add( card );
}
}
## DecksManager.cs
Responsible for the “logic” to instantiate and manage the decks
This component must be attached to an object in your scene, it can be a simple empty. Then, fill the inspector with the needed data.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DecksManager : MonoBehaviour
{
// Fill the inspector with the decks
[SerializeField] private List Decks;
// Create a prefab with a CardRenderer component
// and all the needed components to visually manage a card
[SerializeField] private CardRenderer cardPrefab;
// Create a prefab with a DeckRenderer component
// and all the needed components to visually manage a deck
[SerializeField] private DeckRenderer deckPrefab;
private List deckRenderers = new List();
void Start()
{
for ( int deckIndex = 0 ; deckIndex < Decks.Count ; deckIndex++ )
{
deckRenderers.Add( CreateDeckRenderer( Decks[deckIndex], deckIndex, transform ) );
}
}
private DeckRenderer CreateDeckRenderer( Deck deck, int deckIndex, Transform parent )
{
// Create the deckRenderer responsible for displaying a deck
DeckRenderer deckRenderer = Instantiate( deckPrefab, parent );
deckRenderer.name = string.Format( “DeckRenderer ({0})”, deckIndex );
// Instantiate all the card renderers for the deck
for ( int cardIndex = 0 ; cardIndex < deck.CardsCount ; cardIndex++ )
{
CardRenderer cardRenderer = CreateCardRenderer( deck[cardIndex], cardIndex, deckRenderer.transform );
deckRenderer.Add( cardRenderer );
}
return deckRenderer;
}
private CardRenderer CreateCardRenderer( Card card, int cardIndex, Transform parent )
{
CardRenderer cardRenderer = Instantiate( cardPrefab, parent );
cardRenderer.Setup( card );
return cardRenderer;
}
}