• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
Question by Aaron1Olson · Aug 15, 2019 at 08:13 PM · scripting problem

Properly Inherit Classes

Hi, I am a bit confused on how to properly inherit a class when Monobehavior is involved. I am trying to make a simple 2d blackjack game. I have a Cards class that is being inherited by a Decks class. I have both scripts attached to an empty objects as I will be trying to dynamically instantiate 52 cards from a prefab, and reassign the sprite to each once individually (and eventually control each separately). I would just like to know the Unity way to do this, for now I'm trying to instantiate the cards through Deck::Start() but unfortunately i cant use multiple inheritance in C# which is confusing me. I'm also getting an error about instantiating a null object and I'm trying to see where I went wrong there.

I can tell using the "new Card()" is messing it up based on the warnings

Attempting to make Deck inherit Monobehavior and making Card be a standalone class with public access varibles creates the problem of assigning the cardPrefab through the editor.

Any tips would be awesome, thanks.

EDIT: I also just realized ill probably be needing to move the Instantiate line after changing the sprites and etc, whoops

Here are my classes so far: Deck.css

 public class Deck : Card
 {
 
     private List<Card> deck;
     public Transform deckTransform; //for deck position on table
     
  
     void Start()
     {
 
         deck = new List<Card>() {};  //initialize deck
         //spades
         deck.Add(new Card("ace", "spades", 11)); deck.Add(new Card("2", "spades", 2)); deck.Add(new Card("3", "spades", 3)); deck.Add(new Card("4", "spades", 4)); 
 //(etc.... for all cards)
 
 
 
 
         foreach (var card in deck)
         {
             Instantiate(card.cardPrefab, deckTransform.position, Quaternion.identity); //instantiate game object
         card.cardSpriteR = card.cardPrefab.GetComponent<SpriteRenderer>(); // get Sprite Renderer Component
         card.cardSprite = Resources.Load<Sprite>("Images/Cards/" + card.spriteStr); //assign image to sprite var
         card.cardSpriteR.sprite = card.cardSprite; //set the card's sprite
          //create all cards, pass in the "Deck position"
         }
 
     }


Card.css

 public class Card : MonoBehaviour
 {
 
     public SpriteRenderer cardSpriteR { get; set; }
     public Sprite cardSprite { get; set; }
     public int value { get; set; }
     public string suit{ get; set; }
     public string sValue { get; set; } //card value as a string (for Ace, Jack, etc)
     public string spriteStr { get; set; } //sprite string becomes (sValue + "_of_" +  suit)
 
     //Public 
     public GameObject cardPrefab; 
     public Transform pCardsTransform; // this is going to be part of a different class for player card pos
     
 
     public Card(string sVal, string su, int v) {
         value = v;
         suit = su;
         sValue = sVal;
         spriteStr = sValue + "_of_" + suit;
     }
 
     public Card() { //should never run default constructor 
         value = 0;
         suit = "Doom";
         sValue = "Joker";
         spriteStr = sValue +"_of_" + suit;
     }
 
 }





Comment

People who like this

0 Show 1
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image I_Am_Err00r · Aug 15, 2019 at 09:01 PM 0
Share

When it comes to constructors, scriptable objects is the way to go with Unity. As far as helping you out and being able to instantiate all 52 cards randomly every time, I'm at work and don't have access to Unity or VS to be able to mock something like that up, but I'm sure you could create 13 (for the different numbers and face cards) scriptable objects with an int cardNumber and each have a suit identified as an int (so for example, spade = int 0, hear = int 1, etc); and then at start you can create a list of all the cards in the scene

 List<Card> cards = new List<Card>();
 
 Start()
 {
 cards = GameObject.FindGameObjectsOfType<Card>();
 Shuffle();
 }

And then run Shuffle(); I saw this on stackoverflow, I would prefer to debug it myself before I provide an example code for your project, but you look pretty competent and can probably dissect this yourself.


Here is some documentation, and here is a video on scriptable objects though.


Let me know if this helps at all

1 Reply

  • Sort: 
avatar image
Best Answer

Answer by Hellium · Aug 15, 2019 at 09:23 PM

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[i] );
             Card card = AssetDatabase.LoadAssetAtPath<Card>( 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<CardRenderer> CardRenderers = new List<CardRenderer>();
 
     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<Deck> 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<DeckRenderer> deckRenderers = new List<DeckRenderer>();
 
     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;
     }
 }





Comment
I_Am_Err00r
Aaron1Olson

People who like this

2 Show 6 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image I_Am_Err00r · Aug 15, 2019 at 10:18 PM 1
Share

Wow. Such answer. Much code.alt text

wow.png (140.0 kB)
avatar image Aaron1Olson · Aug 16, 2019 at 04:46 AM 0
Share

Awesome answer, I appreciate the effort and code explanations. This seems like it will be a lot easier to manage in the long run for sure. Thanks again. I'll let you know if I run into any issues / questions.

avatar image Hellium Aaron1Olson · Aug 16, 2019 at 04:53 AM 1
Share

I am glad you didn't "panic" because of the numerous files involved.

I've just noticed a line of the answer must be removed. The created DeckRenderer is added twicebto the list. Remove it from the CreateDeckRenderer function

  deckRenderers.Add( deckRenderer );

(Already removed it from my answer)

avatar image I_Am_Err00r Hellium · Aug 16, 2019 at 01:40 PM 1
Share

This is the best written and most comprehensive answer I've ever seen here; I've saved this post for future reference if I ever decide to make a card based game; thanks @Hellium for taking the time to write all that out! May this thread be the definitive answer moving forward for card based games!

Show more comments

Update about the future of Unity Answers

Unity Answers content will be migrated to a new Community platform and we are aiming to launch a public beta later in June. Please note, we are aiming to set Unity Answers to read-only mode on the 31st of May in order to prepare for the final data migration.

For more information, please read our full announcement.

Follow this Question

Answers Answers and Comments

261 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Can't use target, GetComponent, transform, or other shortcuts. 1 Answer

Problem with script? 0 Answers

How to play audio while reloading a weapon ammo in unity 3d C# Scripting ? 0 Answers

Complicated level change problem 1 Answer

Scale Based On Proximity (Script Problems) 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges