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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

How do you add scriptable object data from a list in one script to another script at runtime?

Discussion in 'Scripting' started by addscion, May 31, 2020.

  1. addscion

    addscion

    Joined:
    Jan 12, 2018
    Posts:
    13
    I have a deck of cards represented by a list that holds scriptable objects for each card.
    For testing, pressing "D" draws a card by removing the 0 element and instantiating a card prefab. How can I add the scriptable object from the list to the new card prefab's display script to display its information?


    Deck:
    Untitled2.png


    Card Prefab:
    Untitled1.png


    DrawCard();
    Code (CSharp):
    1.  private void Update()
    2.     {
    3.         if (Input.GetKeyDown(KeyCode.D))
    4.         {
    5.             cards.Remove(cards[(0)]);
    6.             DrawCard();
    7.  
    8.         }
    9.     }
    10.     public void DrawCard()
    11.     {
    12.  
    13.         if (cards.Count > 0)
    14.         {
    15.             GameObject newCard = Instantiate(cardPrefab, transform.position, Quaternion.identity);
    16.             //newCard.name =
    17.  
    18.  
    19.         }
    20.         else{
    21.             return;
    22.         }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,779
    It's unclear what you're trying to do. Is this runtime? Editor time? Testing time?

    If you have a reference to the ScriptableObject you can copy that reference to anywhere you want. It will be a "pointer" to the same actual instance, usually mapping to an asset on disk. If you Instantiate a ScriptableObject instance you will have made a copy of it, copying the public field values, but it will NOT be stored on disk unless you use UnityEditor code to write it to a fresh file on disk.
     
  3. addscion

    addscion

    Joined:
    Jan 12, 2018
    Posts:
    13
    How can I instantite and copy a reference of the ScriptableObjects in the deck's list to use as pointers?
    Yes this is during runtime. The desired effect is to draw cards by instantiating prefabs and removing card asset scriptable objects from the list attached to the deck GameObject until the list/deck is empty of the scriptable cards. Am I unclear?
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,779
    It's not really necessary to remove them from the list, but you can. If you leave them in a list, you can have a pointer that moves along (think "index") through the shuffled list.

    I would just make a class that represents the deck and have methods that can do things such as : add a card, shuffle the deck, get any arbitrary card, or get out one card (the next card), etc. If you make such a class (and this is a common approach if you work through most card game tutorials), then you can test it independent of any other part of the game to ensure the logic is solid.

    Plus if you have it all in a class, then if you decide to change how you do something, only that one class would change, ideally.

    I don't think you even need to Instantiate the cards, unless you intend to modify them on the fly. Instantiating makes a full copy separate and distinct from the one on disk. I think you just want to have a reference to the ScriptableObject so you can retrieve information from it.
     
  5. addscion

    addscion

    Joined:
    Jan 12, 2018
    Posts:
    13
    Tried to iterate and got an error reading:
    AddComponent asking for "CardAsset" which is not a Unity engine type.


    Code (CSharp):
    1. public class Deck : MonoBehaviour
    2. {
    3.  
    4.     public GameObject cardPrefab;
    5.     public List<CardAsset> cards = new List<CardAsset>();
    6.  
    7.     void Awake()
    8.     {
    9.         cards.Shuffle();
    10.     }
    11.     private void Update()
    12.     {
    13.         if (Input.GetKeyDown(KeyCode.D))
    14.         {
    15.             cards.Remove(cards[(0)]);
    16.             DrawCard();
    17.  
    18.         }
    19.     }
    20.     public void DrawCard()
    21.     {
    22.  
    23.         if (cards.Count > 0)
    24.         {
    25.             GameObject newCard = Instantiate(cardPrefab, transform.position, Quaternion.identity);
    26.             for (int i = 0; i < cards.Count; i++)
    27.             {
    28.                 newCard.AddComponent(cards[i].GetType());
    29.             }
    30.  
    31.  
    32.         }
    33.         else
    34.         {
    35.             return;
    36.         }
    37.  
    38.     }
    39. }
    Any thoughts?
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,779
    GameObject.AddComponent creates a fresh instance of a script that derives from Component, such as MonoBehavior, Camera, etc.

    I assume your card is a ScriptableObject, not a Component. If you want to add a ScriptableObject reference either assign it to a variable, or add it to a collection, such as a List.
     
  7. addscion

    addscion

    Joined:
    Jan 12, 2018
    Posts:
    13
    Theoretically I want to spawn nude cards on a D keystroke and apply a card asset from the deck List to transform it into a card from the deck.


    Since I do not know how to assign it to a variable or access it from a List I tried this:
    Code (CSharp):
    1. public void DrawCard()
    2.     {
    3.  
    4.         if (cards.Count > 0)
    5.         {
    6.             GameObject newCard = Instantiate(cardPrefab, transform.position, Quaternion.identity);
    7.             for (int i = 0; i < cards.Count; i++)
    8.             {
    9.                 newCard.GetComponent<OneCardManager>().cardAsset = cards[i];
    10.             }
    11.  
    12.  
    13.         }
    14.         else
    15.         {
    16.             return;
    17.         }
    18.  
    19.     }

    The code is not adding the Card Asset to the Card Manager Component attached to the gameobject, but the code is not giving me any errors.

    And this is what the card manager looks like.
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. using System.Collections;
    4.  
    5. using UnityEngine.UI;
    6.  
    7. using TMPro;
    8.  
    9.  
    10. // holds the refs to all the Text, Images on the card
    11.  
    12. public class OneCardManager : MonoBehaviour {
    13.  
    14.  
    15.    // public GameObject cardCanvas;
    16.  
    17.  
    18.     public CardAsset cardAsset;
    19.  
    20.     public OneCardManager PreviewManager;
    21.  
    22.     [Header("Text Component References")]
    23.  
    24.     public TextMeshProUGUI NameText;
    25.  
    26.     public TextMeshProUGUI ManaCostText;
    27.  
    28.     public TextMeshProUGUI DescriptionText;
    29.  
    30.     public TextMeshProUGUI HealthText;
    31.  
    32.     public TextMeshProUGUI AttackText;
    33.  
    34.     [Header("Image References")]
    35.  
    36.     public Image CardTopRibbonImage;
    37.  
    38.     public Image CardLowRibbonImage;
    39.  
    40.     public Image CardGraphicImage;
    41.  
    42.     public Image CardBodyImage;
    43.  
    44.     public Image CardFaceFrameImage;
    45.  
    46.     public Image CardFaceGlowImage;
    47.  
    48.     public Image CardBackGlowImage;
    The card assets work when plugged into a manager in the inspector, I just can't figure how to take the assets from a list and add them to a manager component.
     
    Last edited: May 31, 2020