How to attach a scriptable object asset onto a game object which gets created on runtime via code?

Today I decided to just create a simple gridbased SRPG map project for practice. I was mostly looking at games like Fire Emblem for reference, and started creating units on a grid that I could select and move around.

I started looking at ways to set up stats for units on the map and thought ScriptableObjects would be a good thing to use for certain properties that will never change on runtime. So for example:


public class UnitAttributes : ScriptableObject
{
    [SerializeField]
    private string unitName;
    [SerializeField]
    private Sprite portrait;
    [SerializeField]
    private int movement;
}

I have a UnitInstance prefab with an attached MonoBehaviour script like this:


public class UnitInstance : MonoBehaviour
{
    [SerializeField]
    private UnitAttributes attributes;

    private void Awake()
    {
        
    }
}

At first I just attached a UnitAttributes asset I created on the editor for an existing UnitInstance game object on the scene which works fine.

However, if I wanted dynamically put units on the map via UI at runtime with different UnitAttributes asset, what’s the best way to set the “attributes” field in the MonoBehaviour script?

Basically for now I’m just thinking of creating two buttons on the screen so that one will create a unit with X attributes while the other will create a unit with Y attributes on the map (X and Y would be assets of UnitAttributes I created beforehand).

So far I’ve seen Resources.Load but apparently that’s not a good way to do this? I’ve also seen mentions of AssetDatabase but not totally sure how this is used (seems like its similarly just going through folders?). Im thinking I dont understand SOs well enough but once again I’ve spent all day reading discussion topics about this and as a beginner its really hard to tell whats the best way to do something when lots of people are saying different things (and I can’t tell if some of them are suggesting bad practices or not).

Just get something going, anything going. There’s a ton of different resources out there about making RPGs, but RPGS are MASSIVELY complex things. Starting here is like making the Space Shuttle as your first homebuilt airplane.

Instead, take an afternoon and make any random ultra-simple game (think flappy birds or pong or whatever) and once it is operational, reorganize it to be controlled and configured by scriptable objects. Maybe just start by controlling initial health or score with a Scriptable Object, or velocity of movement, or physics parameters.

But give yourself something ultra simple where you can learn about what ScriptableObjects can do. And go through some tutorials on them.

You are not going to get around learning things by simply reading about them.

You read about things to learn about others experiences with them, experiences they had on their games with their approaches to how things are done.

In a vacuum this information is useless. Applied blindly, such information is downright destructive. For instance, this nonsense is all over the net:

Resources.Load() is fantastic. When you want simple bare-metal asset loading, just get-it-done, there’s nothing faster or with fewer moving parts.

Does Resources.Load() do everything? NO! Nothing does.

I like this approach:

Imphenzia: How Did I Learn To Make Games:

Two steps to tutorials and / or example code:

  1. do them perfectly, to the letter (zero typos, including punctuation and capitalization)
  2. stop and understand each step to understand what is going on.

If you go past anything that you don’t understand, then you’re just mimicking what you saw without actually learning, essentially wasting your own time. It’s only two steps. Don’t skip either step.

If you are doing grid-based…

Tile-based / grid-based 2D games: match3, tetris, chips challenge, rogue, etc:

For any tile-based game such as Match3 or Tetris or a grid-based Roguelike, do all the logical comparisons in your own data storage mechanism for the tiles, such as a 2D array of tiles.

Otherwise you needlessly bind your game logic into Unity objects and the Unity API, making it about 10x more complicated than it needs to be.

If you have no idea how to work with 2D arrays, hurry to some basic C# tutorials for the language portions of it, then look at any good tutorial that uses a 2D array

Here is my Match3 demo using this technique of storing data in a grid. Full source linked in game comments.

It stores all of its data in a 2D array:

PieceController[,] Board;

This allows for easy simple checking in code, not relying on anything like physics.

You should strive to use that pattern for all logic, then only use Unity to present what is happening in the game logic.

1 Like

Why not use a prefab / prefab variant for each different unit type? They can have the correct UnitAttribute assigned in the inspector. Your UI would have/load a reference to the various prefabs that can be placed on map.

2 Likes

What you’re asking for in principle is pretty straight forward. When you Object.Instantiate something, you get the copy as a return value. Then you do whatever you want with the copy afterwards.

Notably, you can reference prefabs by any component on the root-game object via the inspector. Instantiating via that component copies the whole game object, but returns the copied component.

Fairly straightforward:

public class UnitInstance : MonoBehaviour
{
    [SerializeField]
    private UnitAttributes _unitAttributes;
    
    public UnitAttributes UnitAttributes => _unitAttributes;
    
    public void SetUnitAttributes(UnitAttributes unitAttributes)
    {
        _unitAttributes = unitAttributes;
        // anything else that needs to happen
    }
}

// somewhere else
var unit = Object.Instantiate(unitPrefab, position, Quaternion.identity);
unit.SetUnitAttributes(unitAttributes);

Where unitPrefab is referencing a prefab via a UnitInstance component.

1 Like

Building on @spiney199, here is an example of a UnitInstanceCreator script which demonstrates how you would spawn new pieces. Of course you may want to change the position of each piece but this is the general idea:

using System.Collections.Generic;
using UnityEngine;

public class UnitInstanceCreator: MonoBehaviour
{
    [SerializeField] private UnitAttributes typeX;
    [SerializeField] private UnitAttributes typeY;
    [SerializeField] private UnitInstance prefab;
    [SerializeField] private GameObject parent;
    [SerializeField] private List<UnitInstance> spawned = new();

    private void OnEnable()
    {
        spawned.Clear();
    }

    public void AddUnitTypeX()
    {
        spawned.Add(CreateNewUnitInstance(typeX));
    }

    public void AddUnitTypeY()
    {
        spawned.Add(CreateNewUnitInstance(typeY));
    }

    private UnitInstance CreateNewUnitInstance(UnitAttributes unitAttributes)
    {
        UnitInstance unitInstance = Instantiate(prefab, Vector3.zero, Quaternion.identity, parent.transform);
        unitInstance.SetUnitAttributes(unitAttributes);
        return unitInstance;
    }
}
1 Like

So I’m just doing a tiny little vertical slice of what the full game would be like in my head, and because I’m thinking like that I think I just couldn’t help but think “what should I do if were to build a full game where I would have 50+ unique units” and got ahead of myself and started looking for ways to do it via “recommended methods” (when I dont even fully understand if SOs is the recommended way for this, or if I was designing it properly for larger scale in my head). And basically from what I was reading making these kinds of assets out of SOs is a good way to do it because SO assets are tiny as opposed to making like 50 prefabs or something (i dunno if this is even right, which just goes back to my rambling earlier about learning all this stuff).

But yea honestly I think I have a really bad habit of thinking too much about things I dont fully understand instead of just trying things out first, and as Kurt says maybe i should just go try much simpler stuff out first.

Also thanks to everyone else for the cool replies, especially Kurt for knocking some sense into me.