Reading a file to create Assets from a Scriptable Object

I have a Scriptable Object, Card. Using this object, I want to create multiple assets by reading a file that gives me the properties of each card. I do not want to hand-create each of these assets.

This functionality would occur every so often when a new cards become available.

Can someone point me in the right direction to accomplish this? My google-fu is not working great.

TIA!

There’s a few steps involved in this. First being the file these cards are generated from itself, and what format it is.

Then you read the file, deserialise the data is necessary (such when using JSON), then instance each scriptable object, and then write them to disk.

If I’m understanding correctly you have a file of card data that you want to load into a Scriptable Object Card?

Assuming the file data is JSON format:

{
  "cards": [
    {
      "cardName": "Fireball",
      "description": "Deals 5 damage to a target.",
      "manaCost": 3,
      "attack": 0,
      "defense": 0,
      "rarity": "Common"
    },
    {
      "cardName": "Healing Touch",
      "description": "Restores 5 health to a friendly target.",
      "manaCost": 2,
      "attack": 0,
      "defense": 0,
      "rarity": "Uncommon"
    }
    // Add more cards as needed
  ]
}

and your scriptable object ‘Card’ is something like this

using UnityEngine;

[CreateAssetMenu(fileName = "NewCard", menuName = "Card")]
public class Card : ScriptableObject
{
    public string cardName;
    public string description;
    public int manaCost;
    public int attack;
    public int defense;
    public string rarity;
}

You will need to create a data class for the JSON deserialization, as you cannot directly deserialize to an SO.

using System;
using System.Collections.Generic;

[Serializable]
public class CardDataList
{
    public List<CardData> cards;
}

[Serializable]
public class CardData
{
    public string cardName;
    public string description;
    public int manaCost;
    public int attack;
    public int defense;
    public string rarity;
}

Then you can create a custom editor window to import your card assets.

using UnityEngine;
using UnityEditor;
using System.IO;

public class CardImporter : EditorWindow
{
    private string jsonFilePath = "Assets/Resources/Data/cards.json";
    private string outputFolder = "Assets/Resources/Cards";

    [MenuItem("Tools/Card Importer")]
    public static void ShowWindow()
    {
        GetWindow<CardImporter>("Card Importer");
    }

    private void OnGUI()
    {
        GUILayout.Label("Import Cards from JSON", EditorStyles.boldLabel);
        
        jsonFilePath = EditorGUILayout.TextField("JSON File Path", jsonFilePath);
        outputFolder = EditorGUILayout.TextField("Output Folder", outputFolder);

        if (GUILayout.Button("Import Cards"))
        {
            ImportCards();
        }
    }

    private void ImportCards()
    {
        if (!File.Exists(jsonFilePath))
        {
            EditorUtility.DisplayDialog("Error", "JSON file not found at " + jsonFilePath, "OK");
            return;
        }

        string jsonContent = File.ReadAllText(jsonFilePath);
        CardDataList cardDataList = JsonUtility.FromJson<CardDataList>(jsonContent);

        if (cardDataList == null || cardDataList.cards == null)
        {
            EditorUtility.DisplayDialog("Error", "Failed to parse JSON.", "OK");
            return;
        }

        // Ensure the output folder exists
        if (!AssetDatabase.IsValidFolder(outputFolder))
        {
            AssetDatabase.CreateFolder("Assets/Resources", "Cards");
        }

        foreach (CardData data in cardDataList.cards)
        {
            // Create a new Card asset
            Card card = ScriptableObject.CreateInstance<Card>();
            card.cardName = data.cardName;
            card.description = data.description;
            card.manaCost = data.manaCost;
            card.attack = data.attack;
            card.defense = data.defense;
            card.rarity = data.rarity;
            // Assign additional fields as needed

            // Define the asset path
            string assetPath = Path.Combine(outputFolder, data.cardName + ".asset").Replace("\\", "/");

            // Check if the asset already exists to avoid duplicates
            Card existingCard = AssetDatabase.LoadAssetAtPath<Card>(assetPath);
            if (existingCard != null)
            {
                // Optionally update existing asset
                EditorUtility.CopySerialized(card, existingCard);
                EditorUtility.SetDirty(existingCard);
                Debug.Log("Updated existing card: " + data.cardName);
            }
            else
            {
                // Create new asset
                AssetDatabase.CreateAsset(card, assetPath);
                Debug.Log("Created new card: " + data.cardName);
            }
        }

        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();

        EditorUtility.DisplayDialog("Import Complete", "Cards have been imported successfully.", "OK");
    }
}

They key parts of this script is deserializing your data:

CardDataList cardDataList = JsonUtility.FromJson<CardDataList>(jsonContent);

And then creating new Scriptable Objects from that deserialized data

Card card = ScriptableObject.CreateInstance<Card>();
..
AssetDatabase.CreateAsset(card, assetPath);
..
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();

If you’re doing it frequently you can probably hardcode the json and output paths and change it to a menu item

1 Like

Thank you so much! Your example worked perfectly and was exactly what I was looking for.

1 Like